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