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