xref: /dokuwiki/inc/common.php (revision c41c03f3ff20658df6ec6c2e7808c5051a802a70)
1<?
2/**
3 * Common DokuWiki functions
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Andreas Gohr <andi@splitbrain.org>
7 */
8
9  require_once("conf/dokuwiki.php");
10  require_once("inc/io.php");
11  require_once('inc/utf8.php');
12
13  //set up error reporting to sane values
14  error_reporting(E_ALL ^ E_NOTICE);
15
16  //make session rewrites XHTML compliant
17  ini_set('arg_separator.output', '&amp;');
18
19  //init session
20  session_name("DokuWiki");
21  session_start();
22
23  //kill magic quotes
24  if (get_magic_quotes_gpc()) {
25    if (!empty($_GET))    remove_magic_quotes($_GET);
26    if (!empty($_POST))   remove_magic_quotes($_POST);
27    if (!empty($_COOKIE)) remove_magic_quotes($_COOKIE);
28    if (!empty($_REQUEST)) remove_magic_quotes($_REQUEST);
29    if (!empty($_SESSION)) remove_magic_quotes($_SESSION);
30    ini_set('magic_quotes_gpc', 0);
31  }
32  set_magic_quotes_runtime(0);
33  ini_set('magic_quotes_sybase',0);
34
35  //disable gzip if not available
36  if($conf['usegzip'] && !function_exists('gzopen')){
37    $conf['usegzip'] = 0;
38  }
39
40/**
41 * remove magic quotes recursivly
42 *
43 * @author Andreas Gohr <andi@splitbrain.org>
44 */
45function remove_magic_quotes(&$array) {
46  foreach (array_keys($array) as $key) {
47    if (is_array($array[$key])) {
48      remove_magic_quotes($array[$key]);
49    }else {
50      $array[$key] = stripslashes($array[$key]);
51    }
52  }
53}
54
55/**
56 * Returns the full absolute URL to the directory where
57 * DokuWiki is installed in (includes a trailing slash)
58 *
59 * @author Andreas Gohr <andi@splitbrain.org>
60 */
61function getBaseURL($abs=false){
62  global $conf;
63  //if canonical url enabled always return absolute
64  if($conf['canonical']) $abs = true;
65
66  //relative URLs are easy
67  if(!$abs){
68    $dir = dirname($_SERVER['PHP_SELF']).'/';
69    $dir = preg_replace('#//#','/',$dir);
70    $dir = preg_replace('#\/$#','/',$dir); #bugfix for weird WIN behaviour
71    return $dir;
72  }
73
74  $port = ':'.$_SERVER['SERVER_PORT'];
75  //remove port from hostheader as sent by IE
76  $host = preg_replace('/:.*$/','',$_SERVER['HTTP_HOST']);
77
78  // see if HTTPS is enabled - apache leaves this empty when not available,
79  // IIS sets it to 'off', 'false' and 'disabled' are just guessing
80  if (preg_match('/^(|off|false|disabled)$/i',$_SERVER['HTTPS'])){
81    $proto = 'http://';
82    if ($_SERVER['SERVER_PORT'] == '80') {
83      $port='';
84    }
85  }else{
86    $proto = 'https://';
87    if ($_SERVER['SERVER_PORT'] == '443') {
88      $port='';
89    }
90  }
91  $dir = (dirname($_SERVER['PHP_SELF'])).'/';
92  $dir = preg_replace('#//#','/',$dir);
93  $dir = preg_replace('#\/$#','/',$dir); #bugfix for weird WIN behaviour
94
95  return $proto.$host.$port.$dir;
96}
97
98/**
99 * Return info about the current document as associative
100 * array.
101 *
102 * @author Andreas Gohr <andi@splitbrain.org>
103 */
104function pageinfo(){
105  global $ID;
106  global $REV;
107  global $USERINFO;
108  global $conf;
109
110  if($_SERVER['REMOTE_USER']){
111    $info['user']     = $_SERVER['REMOTE_USER'];
112    $info['userinfo'] = $USERINFO;
113    $info['perm']     = auth_quickaclcheck($ID);
114  }else{
115    $info['user']     = '';
116    $info['perm']     = auth_aclcheck($ID,'',null);
117  }
118
119  $info['namespace'] = getNS($ID);
120  $info['locked']    = checklock($ID);
121  $info['filepath']  = realpath(wikiFN($ID,$REV));
122  $info['exists']    = @file_exists($info['filepath']);
123  if($REV && !$info['exists']){
124    //check if current revision was meant
125    $cur = wikiFN($ID);
126    if(@file_exists($cur) && (@filemtime($cur) == $REV)){
127      $info['filepath'] = realpath($cur);
128      $info['exists']   = true;
129      $REV = '';
130    }
131  }
132  if($info['exists']){
133    $info['writable'] = (is_writable($info['filepath']) &&
134                         ($info['perm'] >= AUTH_EDIT));
135  }else{
136    $info['writable'] = ($info['perm'] >= AUTH_CREATE);
137  }
138  $info['editable']  = ($info['writable'] && empty($info['lock']));
139  $info['lastmod']   = @filemtime($info['filepath']);
140
141  return $info;
142}
143
144/**
145 * adds a message to the global message array
146 *
147 * Levels can be:
148 *
149 * -1 error
150 *  0 info
151 *  1 success
152 *
153 * @author Andreas Gohr <andi@splitbrain.org>
154 */
155function msg($message,$lvl=0){
156  global $MSG;
157  $errors[-1] = 'error';
158  $errors[0]  = 'info';
159  $errors[1]  = 'success';
160
161  if(!isset($MSG)) $MSG = array();
162  $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message);
163}
164
165/**
166 * This builds the breadcrumb trail and returns it as array
167 *
168 * @author Andreas Gohr <andi@splitbrain.org>
169 */
170function breadcrumbs(){
171  global $ID;
172  global $ACT;
173  global $conf;
174  $crumbs = $_SESSION[$conf['title']]['bc'];
175
176  //first visit?
177  if (!is_array($crumbs)){
178    $crumbs = array();
179  }
180  //we only save on show and existing wiki documents
181  if($ACT != 'show' || !@file_exists(wikiFN($ID))){
182    $_SESSION[$conf['title']]['bc'] = $crumbs;
183    return $crumbs;
184  }
185  //remove ID from array
186  $pos = array_search($ID,$crumbs);
187  if($pos !== false && $pos !== null){
188    array_splice($crumbs,$pos,1);
189  }
190
191  //add to array
192  $crumbs[] =$ID;
193  //reduce size
194  while(count($crumbs) > $conf['breadcrumbs']){
195    array_shift($crumbs);
196  }
197  //save to session
198  $_SESSION[$conf['title']]['bc'] = $crumbs;
199  return $crumbs;
200}
201
202/**
203 * Filter for page IDs
204 *
205 * This is run on a ID before it is outputted somewhere
206 * currently used to replace the colon with something else
207 * on Windows systems and to have proper URL encoding
208 *
209 * @author Andreas Gohr <andi@splitbrain.org>
210 */
211function idfilter($id){
212  global $conf;
213  if ($conf['useslash'] && $conf['userewrite']){
214    $id = strtr($id,':','/');
215  }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' &&
216      $conf['userewrite']) {
217    $id = strtr($id,':',';');
218  }
219  $id = urlencode($id);
220  $id = str_replace('%3A',':',$id); //keep as colon
221  $id = str_replace('%2F','/',$id); //keep as slash
222  return $id;
223}
224
225/**
226 * This builds a link to a wikipage (using getBaseURL)
227 *
228 * @author Andreas Gohr <andi@splitbrain.org>
229 */
230function wl($id='',$more='',$script='doku.php',$canonical=false){
231  global $conf;
232  $more = str_replace(',','&amp;',$more);
233
234  $id    = idfilter($id);
235  $xlink = getBaseURL($canonical);
236
237  if(!$conf['userewrite']){
238    $xlink .= $script;
239    $xlink .= '?id='.$id;
240    if($more) $xlink .= '&amp;'.$more;
241  }else{
242    $xlink .= $id;
243    if($more) $xlink .= '?'.$more;
244  }
245
246  return $xlink;
247}
248
249/**
250 * Just builds a link to a script
251 *
252 * @author Andreas Gohr <andi@splitbrain.org>
253 */
254function script($script='doku.php'){
255  $link = getBaseURL();
256  $link .= $script;
257  return $link;
258}
259
260/**
261 * Return namespacepart of a wiki ID
262 *
263 * @author Andreas Gohr <andi@splitbrain.org>
264 */
265function getNS($id){
266 if(strpos($id,':')!==false){
267   return substr($id,0,strrpos($id,':'));
268 }
269 return false;
270}
271
272/**
273 * Returns the ID without the namespace
274 *
275 * @author Andreas Gohr <andi@splitbrain.org>
276 */
277function noNS($id){
278  return preg_replace('/.*:/','',$id);
279}
280
281/**
282 * Spamcheck against wordlist
283 *
284 * Checks the wikitext against a list of blocked expressions
285 * returns true if the text contains any bad words
286 *
287 * @author Andreas Gohr <andi@splitbrain.org>
288 */
289function checkwordblock(){
290  global $TEXT;
291  global $conf;
292
293  if(!$conf['usewordblock']) return false;
294
295  $blocks = file('conf/wordblock.conf');
296  $re = array();
297  #build regexp from blocks
298  foreach($blocks as $block){
299    $block = preg_replace('/#.*$/','',$block);
300    $block = trim($block);
301    if(empty($block)) continue;
302    $re[]  = $block;
303  }
304  if(preg_match('#('.join('|',$re).')#si',$TEXT)) return true;
305  return false;
306}
307
308/**
309 * Return the IP of the client
310 *
311 * Honours X-Forwarded-For Proxy Headers
312 *
313 * @author Andreas Gohr <andi@splitbrain.org>
314 */
315function clientIP(){
316  $my = $_SERVER['REMOTE_ADDR'];
317  if($_SERVER['HTTP_X_FORWARDED_FOR']){
318    $my .= ' ('.$_SERVER['HTTP_X_FORWARDED_FOR'].')';
319  }
320  return $my;
321}
322
323/**
324 * Checks if a given page is currently locked.
325 *
326 * removes stale lockfiles
327 *
328 * @author Andreas Gohr <andi@splitbrain.org>
329 */
330function checklock($id){
331  global $conf;
332  $lock = wikiFN($id).'.lock';
333
334  //no lockfile
335  if(!@file_exists($lock)) return false;
336
337  //lockfile expired
338  if((time() - filemtime($lock)) > $conf['locktime']){
339    unlink($lock);
340    return false;
341  }
342
343  //my own lock
344  $ip = io_readFile($lock);
345  if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
346    return false;
347  }
348
349  return $ip;
350}
351
352/**
353 * Lock a page for editing
354 *
355 * @author Andreas Gohr <andi@splitbrain.org>
356 */
357function lock($id){
358  $lock = wikiFN($id).'.lock';
359  if($_SERVER['REMOTE_USER']){
360    io_saveFile($lock,$_SERVER['REMOTE_USER']);
361  }else{
362    io_saveFile($lock,clientIP());
363  }
364}
365
366/**
367 * Unlock a page if it was locked by the user
368 *
369 * @author Andreas Gohr <andi@splitbrain.org>
370 * @return bool true if a lock was removed
371 */
372function unlock($id){
373  $lock = wikiFN($id).'.lock';
374  if(@file_exists($lock)){
375    $ip = io_readFile($lock);
376    if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
377      @unlink($lock);
378      return true;
379    }
380  }
381  return false;
382}
383
384/**
385 * Remove unwanted chars from ID
386 *
387 * Cleans a given ID to only use allowed characters. Accented characters are
388 * converted to unaccented ones
389 *
390 * @author Andreas Gohr <andi@splitbrain.org>
391 */
392function cleanID($id){
393  global $conf;
394  global $lang;
395  $id = trim($id);
396  $id = utf8_strtolower($id);
397
398  //alternative namespace seperator
399  $id = strtr($id,';',':');
400  if($conf['useslash']) $id = strtr($id,'/',':');
401
402  //FIXME use config to ask for deaccenting
403  $id = utf8_deaccent($id,-1);
404
405  //remove specials (only ascii specials are removed)
406  $id = preg_replace('#[ !"§$%&()\[\]{}\\?`\'\#~*+=,<>\|^°@µ¹²³¼½¬]#u','_',$id);
407
408/* DELETEME legacy code
409  if(!$conf['localnames']){
410    if($lang['encoding'] == 'iso-8859-15'){
411      // replace accented chars with unaccented ones
412      // this may look strange on your terminal - just don't touch
413      $id = strtr(
414      strtr($id,
415       'ŠŽšžŸÀÁÂÃÅÇÈÉÊËÌÍÎÏÑÒÓÔÕØÙÚÛÝàáâãåçèéêëìíîïñòóôõøùúûýÿ',
416       'szszyaaaaaceeeeiiiinooooouuuyaaaaaceeeeiiiinooooouuuyy'),
417       array('Þ' => 'th', 'þ' => 'th', 'Ð' => 'dh', 'ð' => 'dh', 'ß' => 'ss',
418             'Œ' => 'oe', 'œ' => 'oe', 'Æ' => 'ae', 'æ' => 'ae', 'µ' => 'u',
419             'ü' => 'ue', 'ö' => 'oe', 'ä' => 'ae', 'Ü' => 'ue', 'Ö' => 'ö',
420             'Ä' => 'ae'));
421    }
422    $WORD = 'a-z';
423  }else{
424    $WORD = '\w';
425  }
426  //special chars left will be converted to _
427  $id = preg_replace('#[^'.$WORD.'0-9:\-\.]#','_',$id);
428*/
429
430  //clean up
431  $id = preg_replace('#__#','_',$id);
432  $id = preg_replace('#:+#',':',$id);
433  $id = trim($id,':._-');
434  $id = preg_replace('#:[:\._\-]+#',':',$id);
435
436  return($id);
437}
438
439/**
440 * returns the full path to the datafile specified by ID and
441 * optional revision
442 *
443 * @author Andreas Gohr <andi@splitbrain.org>
444 */
445function wikiFN($id,$rev=''){
446  global $conf;
447  $id = cleanID($id);
448  $id = str_replace(':','/',$id);
449  if(empty($rev)){
450    return $conf['datadir'].'/'.$id.'.txt';
451  }else{
452    $fn = $conf['olddir'].'/'.$id.'.'.$rev.'.txt';
453    if(!$conf['usegzip'] || @file_exists($fn)){
454      //return plaintext if exists or gzip is disabled
455      return $fn;
456    }else{
457      return $fn.'.gz';
458    }
459  }
460}
461
462/**
463 * Returns the full filepath to a localized textfile if local
464 * version isn't found the english one is returned
465 *
466 * @author Andreas Gohr <andi@splitbrain.org>
467 */
468function localeFN($id){
469  global $conf;
470  $file = './lang/'.$conf['lang'].'/'.$id.'.txt';
471  if(!@file_exists($file)){
472    //fall back to english
473    $file = './lang/en/'.$id.'.txt';
474  }
475  return cleanText($file);
476}
477
478/**
479 * convert line ending to unix format
480 *
481 * @see    formText() for 2crlf conversion
482 * @author Andreas Gohr <andi@splitbrain.org>
483 */
484function cleanText($text){
485  $text = preg_replace("/(\015\012)|(\015)/","\012",$text);
486  return $text;
487}
488
489/**
490 * Prepares text for print in Webforms by encoding special chars.
491 * It also converts line endings to Windows format which is
492 * pseudo standard for webforms.
493 *
494 * @see    cleanText() for 2unix conversion
495 * @author Andreas Gohr <andi@splitbrain.org>
496 */
497function formText($text){
498  $text = preg_replace("/\012/","\015\012",$text);
499  return htmlspecialchars($text);
500}
501
502/**
503 * Returns the specified local text in parsed format
504 *
505 * @author Andreas Gohr <andi@splitbrain.org>
506 */
507function parsedLocale($id){
508  //disable section editing
509  global $parser;
510  $se = $parser['secedit'];
511  $parser['secedit'] = false;
512  //fetch parsed locale
513  $html = io_cacheParse(localeFN($id));
514  //reset section editing
515  $parser['secedit'] = $se;
516  return $html;
517}
518
519/**
520 * Returns the specified local text in raw format
521 *
522 * @author Andreas Gohr <andi@splitbrain.org>
523 */
524function rawLocale($id){
525  return io_readFile(localeFN($id));
526}
527
528
529/**
530 * Returns the parsed Wikitext for the given id and revision.
531 *
532 * If $excuse is true an explanation is returned if the file
533 * wasn't found
534 *
535 * @author Andreas Gohr <andi@splitbrain.org>
536 */
537function parsedWiki($id,$rev='',$excuse=true){
538  $file = wikiFN($id,$rev);
539  $ret  = '';
540
541  //ensure $id is in global $ID (needed for parsing)
542  global $ID;
543  $ID = $id;
544
545  if($rev){
546    if(@file_exists($file)){
547      $ret = parse(io_readFile($file));
548    }elseif($excuse){
549      $ret = parsedLocale('norev');
550    }
551  }else{
552    if(@file_exists($file)){
553      $ret = io_cacheParse($file);
554    }elseif($excuse){
555      $ret = parsedLocale('newpage');
556    }
557  }
558  return $ret;
559}
560
561/**
562 * Returns the raw WikiText
563 *
564 * @author Andreas Gohr <andi@splitbrain.org>
565 */
566function rawWiki($id,$rev=''){
567  return io_readFile(wikiFN($id,$rev));
568}
569
570/**
571 * Returns the raw Wiki Text in three slices.
572 *
573 * The range parameter needs to have the form "from-to"
574 * and gives the range of the section.
575 * The returned order is prefix, section and suffix.
576 *
577 * @author Andreas Gohr <andi@splitbrain.org>
578 */
579function rawWikiSlices($range,$id,$rev=''){
580  list($from,$to) = split('-',$range,2);
581  $text = io_readFile(wikiFN($id,$rev));
582  $text = split("\n",$text);
583  if(!$from) $from = 0;
584  if(!$to)   $to   = count($text);
585
586  $slices[0] = join("\n",array_slice($text,0,$from));
587  $slices[1] = join("\n",array_slice($text,$from,$to + 1  - $from));
588  $slices[2] = join("\n",array_slice($text,$to+1));
589
590  return $slices;
591}
592
593/**
594 * Joins wiki text slices
595 *
596 * function to join the text slices with correct lineendings again.
597 * When the pretty parameter is set to true it adds additional empty
598 * lines between sections if needed (used on saving).
599 *
600 * @author Andreas Gohr <andi@splitbrain.org>
601 */
602function con($pre,$text,$suf,$pretty=false){
603
604  if($pretty){
605    if($pre && substr($pre,-1) != "\n") $pre .= "\n";
606    if($suf && substr($text,-1) != "\n") $text .= "\n";
607  }
608
609  if($pre) $pre .= "\n";
610  if($suf) $text .= "\n";
611  return $pre.$text.$suf;
612}
613
614/**
615 * print debug messages
616 *
617 * little function to print the content of a var
618 *
619 * @author Andreas Gohr <andi@splitbrain.org>
620 */
621function dbg($msg,$hidden=false){
622  (!$hidden) ? print '<pre class="dbg">' : print "<!--\n";
623  print_r($msg);
624  (!$hidden) ? print '</pre>' : print "\n-->";
625}
626
627/**
628 * Add's an entry to the changelog
629 *
630 * @author Andreas Gohr <andi@splitbrain.org>
631 */
632function addLogEntry($id,$summary=""){
633  global $conf;
634  $id     = cleanID($id);
635  $date   = time();
636  $remote = $_SERVER['REMOTE_ADDR'];
637  $user   = $_SERVER['REMOTE_USER'];
638
639  $logline = join("\t",array($date,$remote,$id,$user,$summary))."\n";
640
641  $fh = fopen($conf['changelog'],'a');
642  if($fh){
643    fwrite($fh,$logline);
644    fclose($fh);
645  }
646}
647
648/**
649 * returns an array of recently changed files using the
650 * changelog
651 *
652 * @author Andreas Gohr <andi@splitbrain.org>
653 */
654function getRecents($num=0,$incdel=false){
655  global $conf;
656  $recent = array();
657  if(!$num) $num = $conf['recent'];
658
659  $loglines = file($conf['changelog']);
660  rsort($loglines); //reverse sort on timestamp
661
662  foreach ($loglines as $line){
663    $line = rtrim($line);        //remove newline
664    if(empty($line)) continue;   //skip empty lines
665    $info = split("\t",$line);   //split into parts
666    //add id if not in yet and file still exists and is allowed to read
667    if(!$recent[$info[2]] &&
668       (@file_exists(wikiFN($info[2])) || $incdel) &&
669       (auth_quickaclcheck($info[2]) >= AUTH_READ)
670      ){
671      $recent[$info[2]]['date'] = $info[0];
672      $recent[$info[2]]['ip']   = $info[1];
673      $recent[$info[2]]['user'] = $info[3];
674      $recent[$info[2]]['sum']  = $info[4];
675      $recent[$info[2]]['del']  = !@file_exists(wikiFN($info[2]));
676    }
677    if(count($recent) >= $num){
678      break; //finish if enough items found
679    }
680  }
681  return $recent;
682}
683
684/**
685 * Saves a wikitext by calling io_saveFile
686 *
687 * @author Andreas Gohr <andi@splitbrain.org>
688 */
689function saveWikiText($id,$text,$summary){
690  global $conf;
691  global $lang;
692  umask($conf['umask']);
693  // ignore if no changes were made
694  if($text == rawWiki($id,'')){
695    return;
696  }
697
698  $file = wikiFN($id);
699  $old  = saveOldRevision($id);
700
701  if (empty($text)){
702    // remove empty files
703    @unlink($file);
704    $del = true;
705    $summary = $lang['deleted']; //autoset summary on deletion
706  }else{
707    // save file (datadir is created in io_saveFile)
708    io_saveFile($file,$text);
709    $del = false;
710  }
711
712  addLogEntry($id,$summary);
713  notify($id,$old,$summary);
714
715  //purge cache on add by updating the purgefile
716  if($conf['purgeonadd'] && (!$old || $del)){
717    io_saveFile($conf['datadir'].'/.cache/purgefile',time());
718  }
719}
720
721/**
722 * moves the current version to the attic and returns its
723 * revision date
724 *
725 * @author Andreas Gohr <andi@splitbrain.org>
726 */
727function saveOldRevision($id){
728	global $conf;
729  umask($conf['umask']);
730  $oldf = wikiFN($id);
731  if(!@file_exists($oldf)) return '';
732  $date = filemtime($oldf);
733  $newf = wikiFN($id,$date);
734  if(substr($newf,-3)=='.gz'){
735    io_saveFile($newf,rawWiki($id));
736  }else{
737    io_makeFileDir($newf);
738    copy($oldf, $newf);
739  }
740  return $date;
741}
742
743/**
744 * Sends a notify mail to the wikiadmin when a page was
745 * changed
746 *
747 * @author Andreas Gohr <andi@splitbrain.org>
748 */
749function notify($id,$rev="",$summary=""){
750  global $lang;
751  global $conf;
752  $hdrs ='';
753  if(empty($conf['notify'])) return; //notify enabled?
754
755  $text = rawLocale('mailtext');
756  $text = str_replace('@DATE@',date($conf['dformat']),$text);
757  $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text);
758  $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text);
759  $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text);
760  $text = str_replace('@NEWPAGE@',wl($id,'','',true),$text);
761  $text = str_replace('@DOKUWIKIURL@',getBaseURL(true),$text);
762  $text = str_replace('@SUMMARY@',$summary,$text);
763
764  if($rev){
765    $subject = $lang['mail_changed'].' '.$id;
766    $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",'',true),$text);
767    require_once("inc/DifferenceEngine.php");
768    $df  = new Diff(split("\n",rawWiki($id,$rev)),
769                    split("\n",rawWiki($id)));
770    $dformat = new UnifiedDiffFormatter();
771    $diff    = $dformat->format($df);
772  }else{
773    $subject=$lang['mail_newpage'].' '.$id;
774    $text = str_replace('@OLDPAGE@','none',$text);
775    $diff = rawWiki($id);
776  }
777  $text = str_replace('@DIFF@',$diff,$text);
778
779  if (!empty($conf['mailfrom'])) {
780    $hdrs = 'From: '.$conf['mailfrom']."\n";
781  }
782  @mail($conf['notify'],$subject,$text,$hdrs);
783}
784
785/**
786 * Return a list of available page revisons
787 *
788 * @author Andreas Gohr <andi@splitbrain.org>
789 */
790function getRevisions($id){
791  $revd = dirname(wikiFN($id,'foo'));
792  $revs = array();
793  $clid = cleanID($id);
794  if(strrpos($clid,':')) $clid = substr($clid,strrpos($clid,':')+1); //remove path
795
796  if (is_dir($revd) && $dh = opendir($revd)) {
797    while (($file = readdir($dh)) !== false) {
798      if (is_dir($revd.'/'.$file)) continue;
799      if (preg_match('/^'.$clid.'\.(\d+)\.txt(\.gz)?$/',$file,$match)){
800        $revs[]=$match[1];
801      }
802    }
803    closedir($dh);
804  }
805  rsort($revs);
806  return $revs;
807}
808
809/**
810 * downloads a file from the net and saves it to the given location
811 *
812 * @author Andreas Gohr <andi@splitbrain.org>
813 */
814function download($url,$file){
815  $fp = @fopen($url,"rb");
816  if(!$fp) return false;
817
818  while(!feof($fp)){
819    $cont.= fread($fp,1024);
820  }
821  fclose($fp);
822
823  $fp2 = @fopen($file,"w");
824  if(!$fp2) return false;
825  fwrite($fp2,$cont);
826  fclose($fp2);
827  return true;
828}
829
830/**
831 * extracts the query from a google referer
832 *
833 * @author Andreas Gohr <andi@splitbrain.org>
834 */
835function getGoogleQuery(){
836  $url = parse_url($_SERVER['HTTP_REFERER']);
837
838  if(!preg_match("#google\.#i",$url['host'])) return '';
839  $query = array();
840  parse_str($url['query'],$query);
841
842  return $query['q'];
843}
844
845/**
846 * Try to set correct locale
847 *
848 * @author Andreas Gohr <andi@splitbrain.org>
849 */
850function setCorrectLocale(){
851  global $conf;
852  global $lang;
853
854  $enc = strtoupper($lang['encoding']);
855  foreach ($lang['locales'] as $loc){
856    //try locale
857    if(@setlocale(LC_ALL,$loc)) return;
858    //try loceale with encoding
859    if(@setlocale(LC_ALL,"$loc.$enc")) return;
860  }
861  //still here? try to set from environment
862  @setlocale(LC_ALL,"");
863}
864
865/**
866 * Return the human readable size of a file
867 *
868 * @param       int    $size   A file size
869 * @param       int    $dec    A number of decimal places
870 * @author      Martin Benjamin <b.martin@cybernet.ch>
871 * @author      Aidan Lister <aidan@php.net>
872 * @version     1.0.0
873 */
874function filesize_h($size, $dec = 1)
875{
876  $sizes = array('B', 'KB', 'MB', 'GB');
877  $count = count($sizes);
878  $i = 0;
879
880  while ($size >= 1024 && ($i < $count - 1)) {
881    $size /= 1024;
882    $i++;
883  }
884
885  return round($size, $dec) . ' ' . $sizes[$i];
886}
887
888/**
889 * Run a few sanity checks
890 *
891 * @author Andreas Gohr <andi@splitbrain.org>
892 */
893function check(){
894  global $conf;
895  global $INFO;
896
897  if(version_compare(phpversion(),'4.3.0','<')){
898    msg('Your PHP version is too old ('.phpversion().' vs. 4.3.+ recommended)',-1);
899  }elseif(version_compare(phpversion(),'4.3.10','<')){
900    msg('Consider upgrading PHP to 4.3.10 or higher for security reasons (your version: '.phpversion().')',0);
901  }else{
902    msg('PHP version '.phpversion(),1);
903  }
904
905  if(is_writable($conf['changelog'])){
906    msg('Changelog is writable',1);
907  }else{
908    msg('Changelog is not writable',-1);
909  }
910
911  if(is_writable($conf['datadir'])){
912    msg('Datadir is writable',1);
913  }else{
914    msg('Datadir is not writable',-1);
915  }
916
917  if(is_writable($conf['olddir'])){
918    msg('Attic is writable',1);
919  }else{
920    msg('Attic is not writable',-1);
921  }
922
923  if(is_writable($conf['mediadir'])){
924    msg('Mediadir is writable',1);
925  }else{
926    msg('Mediadir is not writable',-1);
927  }
928
929  if(is_writable('conf/users.auth')){
930    msg('conf/users.auth is writable',1);
931  }else{
932    msg('conf/users.auth is not writable',0);
933  }
934
935  msg('Your current permission for this page is '.$INFO['perm'],0);
936
937  if(is_writable($INFO['filepath'])){
938    msg('The current page is writable by the webserver',0);
939  }else{
940    msg('The current page is not writable by the webserver',0);
941  }
942
943  if($INFO['writable']){
944    msg('The current page is writable by you',0);
945  }else{
946    msg('The current page is not writable you',0);
947  }
948}
949?>
950