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