<?php

/**
 * ImpressCMS AUTOTASKSs Library - Crontab Support
 *
 * @copyright	The ImpressCMS Project http://www.impresscms.org/
 * @license		http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU General Public License (GPL)
 * @package		core
 * @since		1.2 alpha 2
 * @author		MekDrop <mekdrop@gmail.com>
 */

/**
 * some parts are taked from CronTab class developed by cjpa@audiophile.com
 */
class IcmsAutoTasksCron
extends icms_sys_autotasks_System {

	private $_lines = array();
	private $_line_id = -1;

	/**
	 * Constructor
	 */
	function __construct() {
		if ($this->canRun()) {
			$this->readCronTab();
		}
	}

	/**
	 * return if we need to start it
	 */
	function needStart() {
		return false;
	}

	/**
	 * check if can run
	 * @return bool
	 */
	function canRun() {
		static $canRun = null;
		if ($canRun === null) {
			$crons = null; $return = null;
			// checking if cron servise is active
			exec( 'ps -ef | grep cron | grep -v grep', $crons, $return);
			$canRun = is_array($crons) && (count($crons) > 0) && ( (int) ($return) === 0);
			if ($canRun) {
				// checking if we have access to use cron
				exec( 'crontab -l', $crons, $return);
				$crons = @implode("\r\n", $crons);
				$canRun = (strpos($crons, 'not allowed to use this program (crontab)') === false);
			}
		}
		return $canRun;
	}

	/**
	 * Get crontab command line
	 * @return string
	 */
	function getCronCommandLine() {
		if (trim($user = $this->getCronTabUser())=='') {
			return 'crontab';
		} else {
			return 'crontab -u '.$user;
		}
	}

	/**
	 * Set Checking Interval (if not enabled enables automated tasks system
	 * @param  int	$interval	interval of checking for new tasks
	 * @return bool				returns true if start was succesfull
	 */
	function start(int $interval) {
		$id = $this->getProcessId();
		if ($id < 0) {
			$this->_line_id = count($this->_lines);
		} else {
			//if ($this->getInterval() == $interval) return false;
		}
		$arx = &$this->getIntervalArray($interval);
		$arx['command'] = $this->getCommandLine();
		$this->_lines[$this->_line_id] = array($arx, 4);
		$this->writeCronTab();
		return true;
	}

	function getInterval() {
		return $this->getNormalValue($this->_lines[$this->_line_id][0]['minute']) +
		$this->getNormalValue($this->_lines[$this->_line_id][0]['hour']) * 60 +
		$this->getNormalValue($this->_lines[$this->_line_id][0]['day']) * 60 * 24 +
		$this->getNormalValue($this->_lines[$this->_line_id][0]['month']) * 60 * 24 * 30;
	}

	function &getIntervalArray($interval) {
		$hours = $days = $months = 0;
		if ($interval>60) {
			$minutes   = $interval % 60;
			$interval -= $minutes * 60;
			if ($interval > 24) {
				$hours   = $interval % 24;
				$interval -= $hours * 60;
				if ($interval > 30) {
					$days   = $interval % 30;
					$interval -= $hours * 60;
					if ($interval > 12) {
						$months = 12;
					} else {
						$months = $interval;
					}
				} else {
					$days = $interval;
				}
			} else {
				$hours = $interval;
			}
		} else {
			$minutes = $interval;
		}
		$hours	 = $this->getCronTabValue($hours);
		$days	 = $this->getCronTabValue($days);
		$months	 = $this->getCronTabValue($months);
		$rez = array( "minute" => $this->getCronTabValue($minutes), "hour" => $this->getCronTabValue($hours), "dayofmonth" => $this->getCronTabValue($days), "month" => $this->getCronTabValue($months), 'dayofweek' => '*');
		return $rez;
	}

	function getCronTabValue($number) {
		if ($number == 0) return '*';
		return '*/'.$number;
	}

	function getNormalValue($crontab_number) {
		if ($crontab_number == '*') return 0;
		return (int) (substr($crontab_number,2));
	}

	/**
	 * Stops automated tasks system
	 * @return bool returns true if was succesfull
	 */
	function stop() {
		$id = $this->getProcessId();
		if ($id < 0) return false;
		unset($this->_lines[$id]);
		$this->writeCronTab();
		return true;
	}

	/**
	 *  checks if core is enabled
	 *
	 * @return bool
	 */
	function isEnabled() {
		return ($this->getProcessId()>-1);
	}

	/**
	 * gets command executed
	 * @return string
	 */
	function getCommandLine() {
		$atasks_handler = &icms_getModuleHandler('autotasks', 'system');
		$config_atasks = &$atasks_handler->getConfig();
		if (($config_atasks['autotasks_helper_path'] = trim($config_atasks['autotasks_helper_path'])) != '') {
			if (substr($config_atasks['autotasks_helper_path'], -1) != '/') {
				$config_atasks['autotasks_helper_path'] .= '/';
			}
		}
		$autotasks_helper_path = $config_atasks['autotasks_helper_path'].str_replace(array('%path%','%url%'),array(ICMS_ROOT_PATH, ICMS_URL),trim($config_atasks['autotasks_helper']))  . '/include/autotasks.php > /dev/null';
		return $autotasks_helper_path;
	}

	/**
	 * gets running process id
	 *
	 * @return int
	 */
	function getProcessId() {
		$this->_lines = array();
		$this->readCronTab();
		$cmd = $this->getCommandLine();
		$this->_line_id = -1;
		foreach ($this->_lines as $id => $line) {
			if ($line[1] != 4) continue;
			if (isset($line[0]['command'])) {
				$line = $line[0];
			}
			if (strpos($line['command'], ICMS_ROOT_PATH . '/include/autotasks.php')!==false) {
				$this->_line_id = (int)$id;
				break;
			}
			if (strpos($line['command'], ICMS_URL . '/include/autotasks.php')!==false) {
				$this->_line_id = (int)$id;
				break;
			}
		}
		return $this->_line_id;
	}

	function getCronTabUser() {
		static $user = null;
		if ($user === null) {
			$atasks_handler = &icms_getModuleHandler('autotasks', 'system');
			$config = &$atasks_handler->getConfig();
			$user = $config['autotasks_user'];
		}
		if (trim("$user") == '') $user = '';
		return $user;
	}

	/**
	 *	Reads cron tab file and parses to $this->_lines array
	 */
	function readCronTab() {
		exec( $this->getCronCommandLine()." -l 2>&1", $crons, $return);
		if ($return != 0) return false;

		foreach ( $crons as $line) {
			$line = trim( $line ); // discarding all prepending spaces and tabs
			// empty lines..
			if (!$line) {
				$this->_lines[] = array('',0);
				continue;
			}
			// checking if this is a comment
			if ($line[0] == "#") {
				$this->_lines[] = array($line,1);
				continue;
			}
			// Checking if this is an assignment
			if (preg_match( "/(.*)=(.*)/", $line, $assign )) {
				$this->_lines[] = array(array( "name" => $assign[1], "value" => $assign[2] ),2);
				continue;
			}
			// Checking if this is a special -entry. check man 5 crontab for more info
			if ($line[0] == '@') {
				$this->_lines[] = array( preg_split( "/[ \t]/", $line, 2 ), 3);
				continue;
			}
			// It's a regular crontab-entry
			$ct = preg_split( "/[ \t]/", $line, 6 );
			$this->_lines[] = array(array( "minute" => $ct[0], "hour" => $ct[1], "dayofmonth" => $ct[2], "month" => $ct[3], "dayofweek" => $ct[4], "command" => $ct[5] ), 4);
		}

		return true;
	}

	/**
	 * Writes crontab files back to where it belongs
	 */
	function writeCronTab() {
		$filename = tempnam(ICMS_CACHE_PATH, 'cron');
		$file = fopen( $filename, "w" );
		foreach ($this->_lines as $current_line) {
			switch ( $current_line[1]) {
				case 1: // comment
					$line = $current_line[0];
					break;
				case 2: //assign
					$line = $current_line[0]['name'] . " = " . $current_line[0]['value'];
					break;
				case 4: //comand
					$line = implode( ' ', $current_line[0] );
					break;
				case 3: //special
					$line = implode( ' ', $current_line[0] );
					break;
				case 0: //empty line
					$line = "\n"; // an empty line in the crontab-file
					break;
				default:
					die('ERROR: Unknown type of line.');
			}
			fwrite( $file, $line . "\n" );
		}
		fclose( $file );

		exec( $this->getCronCommandLine()." $filename 2>&1", $returnar, $return );
		if ($return != 0) {
			die("Error running crontab ($return). $filename not deleted\n");
		} else {
			unlink( $filename );
		}
	}

}

?>