<?php

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package clearFusionCMS
 * @copyright Copyright (c) 2012 - 2013 clearFusionCMS. All rights reserved.
 * @link http://clearfusioncms.com
 */

/**
 * cfCronController Class
 *
 * The action class to trigger cron.
 *
 * @package clearFusionCMS
 */
class cfCronController extends flController {
	/**
	 * Construct the cron controller object.
	 *
	 * @param flRouter $router The router object.
	 * @param flRequest $request The request object.
	 * @throws flExceptionNotFound
	 */
	function __construct($router, $request) {
		parent::__construct($router, $request);

		// Look up the site that we're running
		if(!($r = clearFusionCMS::getDb()->fetchRow('SELECT shortname,displayname,redirectto,twitterusername,puressl FROM cf_sites WHERE domain=?', $request->getServerName())))
			throw new flExceptionNotFound();

		// If redirected site
		if(!empty($r['redirectto']))
			clearFusionCMS::getResponse()->redirectTo($r['redirectto'] . $this->request->getSelf(), true);

		clearFusionCMS::setSite($r['shortname'], $r['displayname'], $r['twitterusername'], $r['puressl']);
	}

	/**
	 * Run cron functions.
	 */
	function runAction() {

		// Send response
		$this->response->noCache()
			->compressOutput(false)
			->setHeader('Connection', 'Close')
			->setBody('')
			->sendResponse();

		$db = clearFusionCMS::getDb();
		$vars = new flVariables('cfSystemCron', $db);

		if($this->request->hasParam('key')) {
			if($this->request->getParam('key') == $vars->key) {
				// Run a task
				if($this->request->hasParam('trigger') && preg_match('/^[a-z0-9_]+\.[a-z0-9_]+$/i', $this->request->getParam('trigger'))) {
					list($c, $f) = explode('.', $this->request->getParam('trigger'));
					$o = new $c();
					$o->$f(new flEvent($this->request->getParam('event')));
				}
				// See which tasks are due and trigger them
				else {
					$cron = new cfCron($db);
					$tasks = $cron->getReadyTasks();
					if(count($tasks)) {
						// Run real worker closing connection before it's finished so we don't stall caller for longer than we have to
						$u = parse_url($this->router->assemble('cfManagerCron', array(), true));
						if($u['scheme'] == 'https') {
							$port = 443;
							$ssl = 'ssl://';
						}
						else {
							$port = 80;
							$ssl = '';
						}

						foreach($tasks as $evt) {
							$results = $db->query('SELECT evtclass,evtfunc FROM fl_events WHERE event=?', $evt);
							while($row = $results->fetch()) {
								$errno = 0;
								$errstr = '';
								if($socket = @fsockopen($ssl . $u['host'], $port, $errorno, $errorstr, 10)) {
									$task = urlencode($row['evtclass'] . '.' . $row['evtfunc']);
									fwrite($socket, "GET $u[path]?key=$vars->key&trigger=$task&event=$evt HTTP/1.1\r\nHost: $u[host]\r\nConnection: Close\r\n\r\n");
									fclose($socket);
								}
							}
						}
					}
				}
			}
		}
		// No key given so assume pseudo cron & rate limit to once per 5 minute
		else {
			$now = new flDate();
			$last = new flDate($vars->lastrun);
			if(empty($vars->lockcode)) {

				// If min time between runs
				if($now->getTimestamp() - $last->getTimestamp() >= 300) {
					// Lock
					if($vars->setIfNotChanged('lockcode', uniqid('', true))) {
						// Run real worker closing connection before it's finished so we don't stall caller for longer than we have to
						$u = parse_url($this->router->assemble('cfManagerCron', array(), true));
						if($u['scheme'] == 'https') {
							$port = 443;
							$ssl = 'ssl://';
						}
						else {
							$port = 80;
							$ssl = '';
						}

						$errno = 0;
						$errstr = '';
						if($socket = @fsockopen($ssl . $u['host'], $port, $errorno, $errorstr, 10)) {
							fwrite($socket, "GET $u[path]?key=$vars->key HTTP/1.1\r\nHost: $u[host]\r\nConnection: Close\r\n\r\n");
							fclose($socket);

							// Update last run time
							$vars->lastrun = $now->toString();
						}

						// Unlock
						$vars->lockcode = '';
					}
				}
			}
			// If already locked then test if stalled and force unlock
			else if($now->getTimestamp() - $last->getTimestamp() > 600)
				$vars->lockcode = '';
		}
	}
}
