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