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