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