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 = utf8_stripspecials($id,'_','_:.-'); 373 374 //clean up 375 $id = preg_replace('#__#','_',$id); 376 $id = preg_replace('#:+#',':',$id); 377 $id = trim($id,':._-'); 378 $id = preg_replace('#:[:\._\-]+#',':',$id); 379 380 return($id); 381} 382 383/** 384 * returns the full path to the datafile specified by ID and 385 * optional revision 386 * 387 * The filename is URL encoded to protect Unicode chars 388 * 389 * @author Andreas Gohr <andi@splitbrain.org> 390 */ 391function wikiFN($id,$rev=''){ 392 global $conf; 393 $id = cleanID($id); 394 $id = str_replace(':','/',$id); 395 if(empty($rev)){ 396 $fn = $conf['datadir'].'/'.$id.'.txt'; 397 }else{ 398 $fn = $conf['olddir'].'/'.$id.'.'.$rev.'.txt'; 399 if($conf['usegzip'] && !@file_exists($fn)){ 400 //return gzip if enabled and plaintext doesn't exist 401 $fn .= '.gz'; 402 } 403 } 404 $fn = utf8_encodeFN($fn); 405 return $fn; 406} 407 408/** 409 * Returns the full filepath to a localized textfile if local 410 * version isn't found the english one is returned 411 * 412 * @author Andreas Gohr <andi@splitbrain.org> 413 */ 414function localeFN($id){ 415 global $conf; 416 $file = './lang/'.$conf['lang'].'/'.$id.'.txt'; 417 if(!@file_exists($file)){ 418 //fall back to english 419 $file = './lang/en/'.$id.'.txt'; 420 } 421 return cleanText($file); 422} 423 424/** 425 * convert line ending to unix format 426 * 427 * @see formText() for 2crlf conversion 428 * @author Andreas Gohr <andi@splitbrain.org> 429 */ 430function cleanText($text){ 431 $text = preg_replace("/(\015\012)|(\015)/","\012",$text); 432 return $text; 433} 434 435/** 436 * Prepares text for print in Webforms by encoding special chars. 437 * It also converts line endings to Windows format which is 438 * pseudo standard for webforms. 439 * 440 * @see cleanText() for 2unix conversion 441 * @author Andreas Gohr <andi@splitbrain.org> 442 */ 443function formText($text){ 444 $text = preg_replace("/\012/","\015\012",$text); 445 return htmlspecialchars($text); 446} 447 448/** 449 * Returns the specified local text in parsed format 450 * 451 * @author Andreas Gohr <andi@splitbrain.org> 452 */ 453function parsedLocale($id){ 454 //disable section editing 455 global $parser; 456 $se = $parser['secedit']; 457 $parser['secedit'] = false; 458 //fetch parsed locale 459 $html = io_cacheParse(localeFN($id)); 460 //reset section editing 461 $parser['secedit'] = $se; 462 return $html; 463} 464 465/** 466 * Returns the specified local text in raw format 467 * 468 * @author Andreas Gohr <andi@splitbrain.org> 469 */ 470function rawLocale($id){ 471 return io_readFile(localeFN($id)); 472} 473 474 475/** 476 * Returns the parsed Wikitext for the given id and revision. 477 * 478 * If $excuse is true an explanation is returned if the file 479 * wasn't found 480 * 481 * @author Andreas Gohr <andi@splitbrain.org> 482 */ 483function parsedWiki($id,$rev='',$excuse=true){ 484 $file = wikiFN($id,$rev); 485 $ret = ''; 486 487 //ensure $id is in global $ID (needed for parsing) 488 global $ID; 489 $ID = $id; 490 491 if($rev){ 492 if(@file_exists($file)){ 493 $ret = parse(io_readFile($file)); 494 }elseif($excuse){ 495 $ret = parsedLocale('norev'); 496 } 497 }else{ 498 if(@file_exists($file)){ 499 $ret = io_cacheParse($file); 500 }elseif($excuse){ 501 $ret = parsedLocale('newpage'); 502 } 503 } 504 return $ret; 505} 506 507/** 508 * Returns the raw WikiText 509 * 510 * @author Andreas Gohr <andi@splitbrain.org> 511 */ 512function rawWiki($id,$rev=''){ 513 return io_readFile(wikiFN($id,$rev)); 514} 515 516/** 517 * Returns the raw Wiki Text in three slices. 518 * 519 * The range parameter needs to have the form "from-to" 520 * and gives the range of the section. 521 * The returned order is prefix, section and suffix. 522 * 523 * @author Andreas Gohr <andi@splitbrain.org> 524 */ 525function rawWikiSlices($range,$id,$rev=''){ 526 list($from,$to) = split('-',$range,2); 527 $text = io_readFile(wikiFN($id,$rev)); 528 $text = split("\n",$text); 529 if(!$from) $from = 0; 530 if(!$to) $to = count($text); 531 532 $slices[0] = join("\n",array_slice($text,0,$from)); 533 $slices[1] = join("\n",array_slice($text,$from,$to + 1 - $from)); 534 $slices[2] = join("\n",array_slice($text,$to+1)); 535 536 return $slices; 537} 538 539/** 540 * Joins wiki text slices 541 * 542 * function to join the text slices with correct lineendings again. 543 * When the pretty parameter is set to true it adds additional empty 544 * lines between sections if needed (used on saving). 545 * 546 * @author Andreas Gohr <andi@splitbrain.org> 547 */ 548function con($pre,$text,$suf,$pretty=false){ 549 550 if($pretty){ 551 if($pre && substr($pre,-1) != "\n") $pre .= "\n"; 552 if($suf && substr($text,-1) != "\n") $text .= "\n"; 553 } 554 555 if($pre) $pre .= "\n"; 556 if($suf) $text .= "\n"; 557 return $pre.$text.$suf; 558} 559 560/** 561 * print debug messages 562 * 563 * little function to print the content of a var 564 * 565 * @author Andreas Gohr <andi@splitbrain.org> 566 */ 567function dbg($msg,$hidden=false){ 568 (!$hidden) ? print '<pre class="dbg">' : print "<!--\n"; 569 print_r($msg); 570 (!$hidden) ? print '</pre>' : print "\n-->"; 571} 572 573/** 574 * Add's an entry to the changelog 575 * 576 * @author Andreas Gohr <andi@splitbrain.org> 577 */ 578function addLogEntry($date,$id,$summary=""){ 579 global $conf; 580 $id = cleanID($id);//FIXME not needed anymore? 581 582 if(!@is_writable($conf['changelog'])){ 583 msg($conf['changelog'].' is not writable!',-1); 584 return; 585 } 586 587 if(!$date) $date = time(); //use current time if none supplied 588 $remote = $_SERVER['REMOTE_ADDR']; 589 $user = $_SERVER['REMOTE_USER']; 590 591 $logline = join("\t",array($date,$remote,$id,$user,$summary))."\n"; 592 593 //FIXME: use adjusted io_saveFile instead 594 $fh = fopen($conf['changelog'],'a'); 595 if($fh){ 596 fwrite($fh,$logline); 597 fclose($fh); 598 } 599} 600 601/** 602 * returns an array of recently changed files using the 603 * changelog 604 * 605 * @author Andreas Gohr <andi@splitbrain.org> 606 */ 607function getRecents($num=0,$incdel=false){ 608 global $conf; 609 $recent = array(); 610 if(!$num) $num = $conf['recent']; 611 612 if(!@is_readable($conf['changelog'])){ 613 msg($conf['changelog'].' is not readable',-1); 614 return $recent; 615 } 616 617 $loglines = file($conf['changelog']); 618 rsort($loglines); //reverse sort on timestamp 619 620 foreach ($loglines as $line){ 621 $line = rtrim($line); //remove newline 622 if(empty($line)) continue; //skip empty lines 623 $info = split("\t",$line); //split into parts 624 //add id if not in yet and file still exists and is allowed to read 625 if(!$recent[$info[2]] && 626 (@file_exists(wikiFN($info[2])) || $incdel) && 627 (auth_quickaclcheck($info[2]) >= AUTH_READ) 628 ){ 629 $recent[$info[2]]['date'] = $info[0]; 630 $recent[$info[2]]['ip'] = $info[1]; 631 $recent[$info[2]]['user'] = $info[3]; 632 $recent[$info[2]]['sum'] = $info[4]; 633 $recent[$info[2]]['del'] = !@file_exists(wikiFN($info[2])); 634 } 635 if(count($recent) >= $num){ 636 break; //finish if enough items found 637 } 638 } 639 return $recent; 640} 641 642/** 643 * gets additonal informations for a certain pagerevison 644 * from the changelog 645 * 646 * @author Andreas Gohr <andi@splitbrain.org> 647 */ 648function getRevisionInfo($id,$rev){ 649 global $conf; 650 651 if(!$rev) return(null); 652 653 $info = array(); 654 if(!@is_readable($conf['changelog'])){ 655 msg($conf['changelog'].' is not readable',-1); 656 return $recent; 657 } 658 $loglines = file($conf['changelog']); 659 $loglines = preg_grep("/$rev\t\d+\.\d+\.\d+\.\d+\t$id\t/",$loglines); 660 rsort($loglines); //reverse sort on timestamp (shouldn't be needed) 661 $line = split("\t",$loglines[0]); 662 $info['date'] = $line[0]; 663 $info['ip'] = $line[1]; 664 $info['user'] = $line[3]; 665 $info['sum'] = $line[4]; 666 return $info; 667} 668 669/** 670 * Saves a wikitext by calling io_saveFile 671 * 672 * @author Andreas Gohr <andi@splitbrain.org> 673 */ 674function saveWikiText($id,$text,$summary){ 675 global $conf; 676 global $lang; 677 umask($conf['umask']); 678 // ignore if no changes were made 679 if($text == rawWiki($id,'')){ 680 return; 681 } 682 683 $file = wikiFN($id); 684 $old = saveOldRevision($id); 685 686 if (empty($text)){ 687 // remove empty files 688 @unlink($file); 689 $del = true; 690 //autoset summary on deletion 691 if(empty($summary)) $summary = $lang['deleted']; 692 }else{ 693 // save file (datadir is created in io_saveFile) 694 io_saveFile($file,$text); 695 $del = false; 696 } 697 698 addLogEntry(@filemtime($file),$id,$summary); 699 notify($id,$old,$summary); 700 701 //purge cache on add by updating the purgefile 702 if($conf['purgeonadd'] && (!$old || $del)){ 703 io_saveFile($conf['datadir'].'/.cache/purgefile',time()); 704 } 705} 706 707/** 708 * moves the current version to the attic and returns its 709 * revision date 710 * 711 * @author Andreas Gohr <andi@splitbrain.org> 712 */ 713function saveOldRevision($id){ 714 global $conf; 715 umask($conf['umask']); 716 $oldf = wikiFN($id); 717 if(!@file_exists($oldf)) return ''; 718 $date = filemtime($oldf); 719 $newf = wikiFN($id,$date); 720 if(substr($newf,-3)=='.gz'){ 721 io_saveFile($newf,rawWiki($id)); 722 }else{ 723 io_makeFileDir($newf); 724 copy($oldf, $newf); 725 } 726 return $date; 727} 728 729/** 730 * Sends a notify mail to the wikiadmin when a page was 731 * changed 732 * 733 * @author Andreas Gohr <andi@splitbrain.org> 734 */ 735function notify($id,$rev="",$summary=""){ 736 global $lang; 737 global $conf; 738 $hdrs =''; 739 if(empty($conf['notify'])) return; //notify enabled? 740 741 $text = rawLocale('mailtext'); 742 $text = str_replace('@DATE@',date($conf['dformat']),$text); 743 $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text); 744 $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text); 745 $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text); 746 $text = str_replace('@NEWPAGE@',wl($id,'',true),$text); 747 $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text); 748 $text = str_replace('@SUMMARY@',$summary,$text); 749 $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text); 750 751 if($rev){ 752 $subject = $lang['mail_changed'].' '.$id; 753 $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true),$text); 754 require_once("inc/DifferenceEngine.php"); 755 $df = new Diff(split("\n",rawWiki($id,$rev)), 756 split("\n",rawWiki($id))); 757 $dformat = new UnifiedDiffFormatter(); 758 $diff = $dformat->format($df); 759 }else{ 760 $subject=$lang['mail_newpage'].' '.$id; 761 $text = str_replace('@OLDPAGE@','none',$text); 762 $diff = rawWiki($id); 763 } 764 $text = str_replace('@DIFF@',$diff,$text); 765 766 mail_send($conf['notify'],$subject,$text,$conf['mailfrom']); 767} 768 769/** 770 * Return a list of available page revisons 771 * 772 * @author Andreas Gohr <andi@splitbrain.org> 773 */ 774function getRevisions($id){ 775 $revd = dirname(wikiFN($id,'foo')); 776 $revs = array(); 777 $clid = cleanID($id); 778 if(strrpos($clid,':')) $clid = substr($clid,strrpos($clid,':')+1); //remove path 779 780 if (is_dir($revd) && $dh = opendir($revd)) { 781 while (($file = readdir($dh)) !== false) { 782 if (is_dir($revd.'/'.$file)) continue; 783 if (preg_match('/^'.$clid.'\.(\d+)\.txt(\.gz)?$/',$file,$match)){ 784 $revs[]=$match[1]; 785 } 786 } 787 closedir($dh); 788 } 789 rsort($revs); 790 return $revs; 791} 792 793/** 794 * downloads a file from the net and saves it to the given location 795 * 796 * @author Andreas Gohr <andi@splitbrain.org> 797 */ 798function download($url,$file){ 799 $fp = @fopen($url,"rb"); 800 if(!$fp) return false; 801 802 while(!feof($fp)){ 803 $cont.= fread($fp,1024); 804 } 805 fclose($fp); 806 807 $fp2 = @fopen($file,"w"); 808 if(!$fp2) return false; 809 fwrite($fp2,$cont); 810 fclose($fp2); 811 return true; 812} 813 814/** 815 * extracts the query from a google referer 816 * 817 * @todo should be more generic and support yahoo et al 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