1<?
2/**
3 * Cronojob
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Luigi Micco <l.micco@tiscali.it>
7 */
8
9/***************************************************************************
10This program is free software; you can redistribute it and/or
11modify it under the terms of the GNU General Public License
12as published by the Free Software Foundation; either version 2
13of the License, or (at your option) any later version.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23****************************************************************************
24
25This script can be called using an HTML img tag, for example:
26
27<img src="pseudocron.php" width="1" height="1" alt="" />
28
29The advantage of using this script is that pseudocron is called in a separate
30request and thus does not slow down output of the main page as it would if called
31from there.
32
33***************************************************************************/
34define("PC_MINUTE",	1);
35define("PC_HOUR",	2);
36define("PC_DOM",	3);
37define("PC_MONTH",	4);
38define("PC_DOW",	5);
39define("PC_CMD",	7);
40define("PC_COMMENT",	8);
41define("PC_CRONLINE", 20);
42
43// set some info about Dokuwiki
44define('DOKU_INC', realpath(dirname(__FILE__).'/../../../').'/');
45define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
46require_once(DOKU_INC.'inc/utf8.php');
47require_once(DOKU_INC.'inc/pageutils.php');
48
49$cronojob = new cronojob();
50$cronojob->dojobs();
51$cronojob->sendGif();
52
53/***************************************************************************/
54
55class cronojob {
56
57  // The string that contains the job descriptions.
58  // One job for line
59
60  // #scheduled jobs
61  // #comments start with #
62  // #mi  h  d  m  dow  job  comment';
63
64  // For a description of the format, see http://www.unixgeeks.org/security/newbie/unix/cron-1.html
65  // and http://www.bitfolge.de/pseudocron
66  var $cronTab;
67
68  // The directory where the script can store information on completed jobs and its log file.
69  // include trailing slash
70  var $writeDir;
71
72  // Control logging, true=use log file, false=don't use log file
73  var $useLog;
74
75  // Where to send cron results.
76  var $sendLogToEmail;
77
78  // Maximum number of jobs run during one call of pseudocron.
79  // Set to a low value if your jobs take longer than a few seconds and if you scheduled them
80  // very close to each other. Set to 0 to run any number of jobs.
81  var $maxJobs = 1;
82
83  // Turn on / off debugging output
84  // DO NOT use this on live servers!
85  var $debug = false;
86
87  /****************************************/
88  /*		don't change anything here		*/
89  /****************************************/
90
91  var $resultsSummary;
92  var $pluginname;
93  var $dokuwikititle;
94  var $ID;
95  var $conf;
96
97  function cronojob($debug = false) {
98    global $ID;
99    global $conf;
100
101    $ID = cleanID($_REQUEST['id']);
102    $this->debug = $debug;
103    $this->writeDir = DOKU_INC."data/tmp/";
104    $this->pluginname = "cronojob";
105
106    // legge la configurazione
107    $path = DOKU_PLUGIN.$this->pluginname.'/conf/';
108    $conf = array();
109    if (@file_exists($path.'default.php')) {
110      include($path.'default.php');
111    }
112    $conf_plug = $conf;
113
114    $path = DOKU_INC.'conf/';
115    $conf = array();
116    if (@file_exists($path.'dokuwiki.php')) include($path.'dokuwiki.php');
117    if (@file_exists($path.'local.php')) include($path.'local.php');
118
119    $this->dokuwikititle = $conf['title'];
120    foreach ($conf_plug as $key => $value) {
121      if (isset($conf['plugin'][$this->pluginname][$key])) $conf_plug[$key] = $conf['plugin'][$this->pluginname][$key];
122    }
123    // legge la configurazione
124
125    $this->cronTab = $conf_plug['cronotab'];
126    $this->sendLogToEmail = $conf_plug['email'];
127    if ($conf_plug['maxjobs'] > 0) $this->maxjobs = $conf_plug['maxjobs'];
128    $this->useLog = $conf_plug['uselog'];
129
130  }
131
132  function dojobs() {
133    if ($this->debug) echo "<pre>";
134    $jobs = $this->parseCronFile($this->cronTab);
135    $jobsRun = 0;
136    for ($i=0;$i<count($jobs);$i++) {
137      if ($this->maxJobs==0 || $jobsRun<$this->maxJobs) {
138        if ($this->runJob($jobs[$i])) $jobsRun++;
139      }
140    }
141    if ($this->debug) echo "</pre>";
142
143  }
144
145  function runJob($job) {
146    global $conf;
147    global $ID;
148    global $cronojob;
149
150    // creo una funzione di servizio per gli script
151    function logMessage($msg) {
152      global $cronojob;
153      $cronojob->logMessage($msg);
154    }
155
156    $this->resultsSummary = "";
157
158    $lastActual = $job["lastActual"];
159    $lastScheduled = $job["lastScheduled"];
160
161    if ($lastScheduled<time()) {
162      $this->logMessage("Running 	".$job[PC_CRONLINE]);
163      $this->logMessage(" Last run: ".date("r",$lastActual).", Last scheduled: ".date("r",$lastScheduled));
164      $e = @error_reporting(0);
165      if ($this->debug) {
166        include(DOKU_PLUGIN.$this->pluginname."/jobs/".$job[PC_CMD]);		// display errors only when debugging
167      } else {
168        @include(DOKU_PLUGIN.$this->pluginname."/jobs/".$job[PC_CMD]);		// any error messages are supressed
169      }
170      @error_reporting($e);
171      $this->markLastRun($job[PC_CMD], $lastScheduled);
172      $this->logMessage(" Completed	".$job[PC_CRONLINE]);
173      if ($this->sendLogToEmail!="") {
174        @mail($this->sendLogToEmail, "[".$this->dokuwikititle."][".$this->pluginname."] ".$job[PC_COMMENT], $this->resultsSummary);
175      }
176      return true;
177    } else {
178      if ($this->debug) {
179        $this->logMessage("Skipping 	".$job[PC_CRONLINE]);
180        $this->logMessage(" Last run: ".date("r",$lastActual).", Last scheduled: ".date("r",$lastScheduled));
181        $this->logMessage(" Completed	".$job[PC_CRONLINE]);
182      }
183      return false;
184    }
185
186  }
187
188  function logMessage($msg) {
189    if ($msg[strlen($msg)-1]!="\n") {
190      $msg.="\n";
191    }
192    if ($this->debug) echo $msg." ".$this->useLog;
193    $this->resultsSummary.= $msg;
194    if ($this->useLog) {
195      $logfile = $this->writeDir."pseudo-cron.log";
196      $file = fopen($logfile,"a");
197      fputs($file,date("r",time())."  ".$msg);
198      fclose($file);
199    }
200  }
201
202  function lTrimZeros($number) {
203    while ($number[0]=='0') {
204      $number = substr($number,1);
205    }
206    return $number;
207  }
208
209  function multisort(&$array, $sortby, $order='asc') {
210     foreach($array as $val) {
211         $sortarray[] = $val[$sortby];
212     }
213     $c = $array;
214     $const = $order == 'asc' ? SORT_ASC : SORT_DESC;
215     $s = array_multisort($sortarray, $const, $c, $const);
216     $array = $c;
217     return $s;
218  }
219
220  function parseElement($element, &$targetArray, $numberOfElements) {
221    $subelements = explode(",",$element);
222    for ($i=0;$i<$numberOfElements;$i++) {
223      $targetArray[$i] = $subelements[0]=="*";
224    }
225
226    for ($i=0;$i<count($subelements);$i++) {
227      if (preg_match("~^(\\*|([0-9]{1,2})(-([0-9]{1,2}))?)(/([0-9]{1,2}))?$~",$subelements[$i],$matches)) {
228        if ($matches[1]=="*") {
229          $matches[2] = 0;		// from
230          $matches[4] = $numberOfElements;		//to
231        } elseif ($matches[4]=="") {
232          $matches[4] = $matches[2];
233        }
234        if ($matches[5][0]!="/") {
235          $matches[6] = 1;		// step
236        }
237        for ($j=$this->lTrimZeros($matches[2]);$j<=$this->lTrimZeros($matches[4]);$j+=$this->lTrimZeros($matches[6])) {
238          $targetArray[$j] = TRUE;
239        }
240      }
241    }
242  }
243
244  function incDate(&$dateArr, $amount, $unit) {
245
246    if ($this->debug) echo sprintf("Increasing from %02d.%02d. %02d:%02d by %d %6s ",$dateArr[mday],$dateArr[mon],$dateArr[hours],$dateArr[minutes],$amount,$unit);
247    if ($unit=="mday") {
248      $dateArr["hours"] = 0;
249      $dateArr["minutes"] = 0;
250      $dateArr["seconds"] = 0;
251      $dateArr["mday"] += $amount;
252      $dateArr["wday"] += $amount % 7;
253      if ($dateArr["wday"]>6) {
254        $dateArr["wday"]-=7;
255      }
256
257      $months28 = Array(2);
258      $months30 = Array(4,6,9,11);
259      $months31 = Array(1,3,5,7,8,10,12);
260
261      if (
262        (in_array($dateArr["mon"], $months28) && $dateArr["mday"]==28) ||
263        (in_array($dateArr["mon"], $months30) && $dateArr["mday"]==30) ||
264        (in_array($dateArr["mon"], $months31) && $dateArr["mday"]==31)
265      ) {
266        $dateArr["mon"]++;
267        $dateArr["mday"] = 1;
268      }
269
270    } elseif ($unit=="hour") {
271      if ($dateArr["hours"]==23) {
272        $this->incDate($dateArr, 1, "mday");
273      } else {
274        $dateArr["minutes"] = 0;
275        $dateArr["seconds"] = 0;
276        $dateArr["hours"]++;
277      }
278    } elseif ($unit=="minute") {
279      if ($dateArr["minutes"]==59) {
280        $this->incDate($dateArr, 1, "hour");
281      } else {
282        $dateArr["seconds"] = 0;
283        $dateArr["minutes"]++;
284      }
285    }
286    if ($this->debug) echo sprintf("to %02d.%02d. %02d:%02d\n",$dateArr[mday],$dateArr[mon],$dateArr[hours],$dateArr[minutes]);
287  }
288
289  function getLastScheduledRunTime($job) {
290
291    $extjob = Array();
292    $this->parseElement($job[PC_MINUTE], $extjob[PC_MINUTE], 60);
293    $this->parseElement($job[PC_HOUR], $extjob[PC_HOUR], 24);
294    $this->parseElement($job[PC_DOM], $extjob[PC_DOM], 31);
295    $this->parseElement($job[PC_MONTH], $extjob[PC_MONTH], 12);
296    $this->parseElement($job[PC_DOW], $extjob[PC_DOW], 7);
297
298    $dateArr = getdate($this->getLastActualRunTime($job[PC_CMD]));
299    $minutesAhead = 0;
300    while (
301      $minutesAhead<525600 AND
302      (!$extjob[PC_MINUTE][$dateArr["minutes"]] OR
303      !$extjob[PC_HOUR][$dateArr["hours"]] OR
304      (!$extjob[PC_DOM][$dateArr["mday"]] OR !$extjob[PC_DOW][$dateArr["wday"]]) OR
305      !$extjob[PC_MONTH][$dateArr["mon"]])
306    ) {
307      if (!$extjob[PC_DOM][$dateArr["mday"]] OR !$extjob[PC_DOW][$dateArr["wday"]]) {
308        $this->incDate($dateArr,1,"mday");
309        $minutesAhead+=1440;
310        continue;
311      }
312      if (!$extjob[PC_HOUR][$dateArr["hours"]]) {
313        $this->incDate($dateArr,1,"hour");
314        $minutesAhead+=60;
315        continue;
316      }
317      if (!$extjob[PC_MINUTE][$dateArr["minutes"]]) {
318        $this->incDate($dateArr,1,"minute");
319        $minutesAhead++;
320        continue;
321      }
322    }
323
324    //if ($this->debug) print_r($dateArr);
325
326    return mktime($dateArr["hours"],$dateArr["minutes"],0,$dateArr["mon"],$dateArr["mday"],$dateArr["year"]);
327  }
328
329  function getJobFileName($jobname) {
330    $jobfile = $this->writeDir.urlencode($jobname).".job";
331    return $jobfile;
332  }
333
334  function getLastActualRunTime($jobname) {
335    $jobfile = $this->getJobFileName($jobname);
336    if (file_exists($jobfile)) {
337      return filemtime($jobfile);
338    }
339    return 0;
340  }
341
342  function markLastRun($jobname, $lastRun) {
343    $jobfile = $this->getJobFileName($jobname);
344    touch($jobfile);
345  }
346
347  function parseCronFile($cronTabFile) {
348    $file = explode("\n", $cronTabFile);
349//    $file = file($cronTabFile);
350//    $file = $cronTabFile;
351    $job = Array();
352    $jobs = Array();
353    for ($i=0;$i<count($file);$i++) {
354      if ($file[$i][0]!='#') {
355  //			old regex, without dow abbreviations:
356  //			if (preg_match("~^([-0-9,/*]+)\\s+([-0-9,/*]+)\\s+([-0-9,/*]+)\\s+([-0-9,/*]+)\\s+([-0-7,/*]+|Sun|Mon|Tue|Wen|Thu|Fri|Sat)\\s+([^#]*)(#.*)?$~i",$file[$i],$job)) {
357        if (preg_match("~^([-0-9,/*]+)\\s+([-0-9,/*]+)\\s+([-0-9,/*]+)\\s+([-0-9,/*]+)\\s+([-0-7,/*]+|(-|/|Sun|Mon|Tue|Wed|Thu|Fri|Sat)+)\\s+([^#]*)\\s*(#.*)?$~i",$file[$i],$job)) {
358          $jobNumber = count($jobs);
359          $jobs[$jobNumber] = $job;
360          if ($jobs[$jobNumber][PC_DOW][0]!='*' AND !is_numeric($jobs[$jobNumber][PC_DOW])) {
361            $jobs[$jobNumber][PC_DOW] = str_replace(
362              Array("Sun","Mon","Tue","Wed","Thu","Fri","Sat"),
363              Array(0,1,2,3,4,5,6),
364              $jobs[$jobNumber][PC_DOW]);
365          }
366          $jobs[$jobNumber][PC_CMD] = trim($job[PC_CMD]);
367          $jobs[$jobNumber][PC_COMMENT] = trim(substr($job[PC_COMMENT],1));
368          $jobs[$jobNumber][PC_CRONLINE] = $file[$i];
369        }
370        $jobfile = $this->getJobFileName($jobs[$jobNumber][PC_CMD]);
371
372        $jobs[$jobNumber]["lastActual"] = $this->getLastActualRunTime($jobs[$jobNumber][PC_CMD]);
373        $jobs[$jobNumber]["lastScheduled"] = $this->getLastScheduledRunTime($jobs[$jobNumber]);
374      }
375    }
376
377    $this->multisort($jobs, "lastScheduled");
378
379    if ($this->debug) var_dump($jobs);
380    return $jobs;
381  }
382
383  function sendGif() {
384    if($this->debug) return;
385    $img = base64_decode("R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==");
386
387/*
388    $img = base64_decode("iVBORw0KGgoAAAANSUhEUgAAAFAAAAAPCAIAAAD8q9/YAAAA5ElEQVRIieVXuw7DIAw8qnwrnrt0
3894AM6sDD7a+lAQylg4kp5VOIm63TINucYxVhrMRMWAMx8dRkngYiWNTQ/n+bog9u3oEPxuD8B3K4u
39042xM1/DS4Ti+g3LOEylMfpoWAOWQJzIzSo0UVwc3Nd10EB0mAzKfzjnWTAMfnA8up0mllIxSU8YD
391mUbTPdVzGLKf8m6TTBj42UUqcZd12L2gocMtthweZ1W2MTYTQifKkgSHK+RhVr9euegqZWmgpMF3
392z61Mw0AYKGOtZeZJ3mEimu5Zmq7h9RuWt9EAyuXxVzCz/S29AGJYqkfnR9EBAAAAAElFTkSuQmCC");
393*/
394
395    Header("Content-Type: image/gif");
396    Header('Content-Length: '.strlen($img));
397    header('Connection: Close');
398    echo $img;
399  }
400
401}
402
403?>
404
405