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); 666 if(!$date) $date = time(); //use current time if none supplied 667 $remote = $_SERVER['REMOTE_ADDR']; 668 $user = $_SERVER['REMOTE_USER']; 669 670 $logline = join("\t",array($date,$remote,$id,$user,$summary))."\n"; 671 672 $fh = fopen($conf['changelog'],'a'); 673 if($fh){ 674 fwrite($fh,$logline); 675 fclose($fh); 676 } 677} 678 679/** 680 * returns an array of recently changed files using the 681 * changelog 682 * 683 * @author Andreas Gohr <andi@splitbrain.org> 684 */ 685function getRecents($num=0,$incdel=false){ 686 global $conf; 687 $recent = array(); 688 if(!$num) $num = $conf['recent']; 689 690 $loglines = file($conf['changelog']); 691 rsort($loglines); //reverse sort on timestamp 692 693 foreach ($loglines as $line){ 694 $line = rtrim($line); //remove newline 695 if(empty($line)) continue; //skip empty lines 696 $info = split("\t",$line); //split into parts 697 //add id if not in yet and file still exists and is allowed to read 698 if(!$recent[$info[2]] && 699 (@file_exists(wikiFN($info[2])) || $incdel) && 700 (auth_quickaclcheck($info[2]) >= AUTH_READ) 701 ){ 702 $recent[$info[2]]['date'] = $info[0]; 703 $recent[$info[2]]['ip'] = $info[1]; 704 $recent[$info[2]]['user'] = $info[3]; 705 $recent[$info[2]]['sum'] = $info[4]; 706 $recent[$info[2]]['del'] = !@file_exists(wikiFN($info[2])); 707 } 708 if(count($recent) >= $num){ 709 break; //finish if enough items found 710 } 711 } 712 return $recent; 713} 714 715/** 716 * gets additonal informations for a certain pagerevison 717 * from the changelog 718 * 719 * @author Andreas Gohr <andi@splitbrain.org> 720 */ 721function getRevisionInfo($id,$rev){ 722 global $conf; 723 $loglines = file($conf['changelog']); 724 $loglines = preg_grep("/$rev\t\d+\.\d+\.\d+\.\d+\t$id\t/",$loglines); 725 rsort($loglines); //reverse sort on timestamp (shouldn't be needed) 726 $line = split("\t",$loglines[0]); 727 $info['date'] = $line[0]; 728 $info['ip'] = $line[1]; 729 $info['user'] = $line[3]; 730 $info['sum'] = $line[4]; 731 return $info; 732} 733 734/** 735 * Saves a wikitext by calling io_saveFile 736 * 737 * @author Andreas Gohr <andi@splitbrain.org> 738 */ 739function saveWikiText($id,$text,$summary){ 740 global $conf; 741 global $lang; 742 umask($conf['umask']); 743 // ignore if no changes were made 744 if($text == rawWiki($id,'')){ 745 return; 746 } 747 748 $file = wikiFN($id); 749 $old = saveOldRevision($id); 750 751 if (empty($text)){ 752 // remove empty files 753 @unlink($file); 754 $del = true; 755 //autoset summary on deletion 756 if(empty($summary)) $summary = $lang['deleted']; 757 }else{ 758 // save file (datadir is created in io_saveFile) 759 io_saveFile($file,$text); 760 $del = false; 761 } 762 763 addLogEntry(@filemtime($file),$id,$summary); 764 notify($id,$old,$summary); 765 766 //purge cache on add by updating the purgefile 767 if($conf['purgeonadd'] && (!$old || $del)){ 768 io_saveFile($conf['datadir'].'/.cache/purgefile',time()); 769 } 770} 771 772/** 773 * moves the current version to the attic and returns its 774 * revision date 775 * 776 * @author Andreas Gohr <andi@splitbrain.org> 777 */ 778function saveOldRevision($id){ 779 global $conf; 780 umask($conf['umask']); 781 $oldf = wikiFN($id); 782 if(!@file_exists($oldf)) return ''; 783 $date = filemtime($oldf); 784 $newf = wikiFN($id,$date); 785 if(substr($newf,-3)=='.gz'){ 786 io_saveFile($newf,rawWiki($id)); 787 }else{ 788 io_makeFileDir($newf); 789 copy($oldf, $newf); 790 } 791 return $date; 792} 793 794/** 795 * Sends a notify mail to the wikiadmin when a page was 796 * changed 797 * 798 * @author Andreas Gohr <andi@splitbrain.org> 799 */ 800function notify($id,$rev="",$summary=""){ 801 global $lang; 802 global $conf; 803 $hdrs =''; 804 if(empty($conf['notify'])) return; //notify enabled? 805 806 $text = rawLocale('mailtext'); 807 $text = str_replace('@DATE@',date($conf['dformat']),$text); 808 $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text); 809 $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text); 810 $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text); 811 $text = str_replace('@NEWPAGE@',wl($id,'','doku.php',true),$text); 812 $text = str_replace('@DOKUWIKIURL@',getBaseURL(true),$text); 813 $text = str_replace('@SUMMARY@',$summary,$text); 814 $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text); 815 816 if($rev){ 817 $subject = $lang['mail_changed'].' '.$id; 818 $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",'doku.php',true),$text); 819 require_once("inc/DifferenceEngine.php"); 820 $df = new Diff(split("\n",rawWiki($id,$rev)), 821 split("\n",rawWiki($id))); 822 $dformat = new UnifiedDiffFormatter(); 823 $diff = $dformat->format($df); 824 }else{ 825 $subject=$lang['mail_newpage'].' '.$id; 826 $text = str_replace('@OLDPAGE@','none',$text); 827 $diff = rawWiki($id); 828 } 829 $text = str_replace('@DIFF@',$diff,$text); 830 831 mail_send($conf['notify'],$subject,$text,$conf['mailfrom']); 832} 833 834/** 835 * Return a list of available page revisons 836 * 837 * @author Andreas Gohr <andi@splitbrain.org> 838 */ 839function getRevisions($id){ 840 $revd = dirname(wikiFN($id,'foo')); 841 $revs = array(); 842 $clid = cleanID($id); 843 if(strrpos($clid,':')) $clid = substr($clid,strrpos($clid,':')+1); //remove path 844 845 if (is_dir($revd) && $dh = opendir($revd)) { 846 while (($file = readdir($dh)) !== false) { 847 if (is_dir($revd.'/'.$file)) continue; 848 if (preg_match('/^'.$clid.'\.(\d+)\.txt(\.gz)?$/',$file,$match)){ 849 $revs[]=$match[1]; 850 } 851 } 852 closedir($dh); 853 } 854 rsort($revs); 855 return $revs; 856} 857 858/** 859 * downloads a file from the net and saves it to the given location 860 * 861 * @author Andreas Gohr <andi@splitbrain.org> 862 */ 863function download($url,$file){ 864 $fp = @fopen($url,"rb"); 865 if(!$fp) return false; 866 867 while(!feof($fp)){ 868 $cont.= fread($fp,1024); 869 } 870 fclose($fp); 871 872 $fp2 = @fopen($file,"w"); 873 if(!$fp2) return false; 874 fwrite($fp2,$cont); 875 fclose($fp2); 876 return true; 877} 878 879/** 880 * extracts the query from a google referer 881 * 882 * @author Andreas Gohr <andi@splitbrain.org> 883 */ 884function getGoogleQuery(){ 885 $url = parse_url($_SERVER['HTTP_REFERER']); 886 887 if(!preg_match("#google\.#i",$url['host'])) return ''; 888 $query = array(); 889 parse_str($url['query'],$query); 890 891 return $query['q']; 892} 893 894/** 895 * Try to set correct locale 896 * 897 * @deprecated No longer used 898 * @author Andreas Gohr <andi@splitbrain.org> 899 */ 900function setCorrectLocale(){ 901 global $conf; 902 global $lang; 903 904 $enc = strtoupper($lang['encoding']); 905 foreach ($lang['locales'] as $loc){ 906 //try locale 907 if(@setlocale(LC_ALL,$loc)) return; 908 //try loceale with encoding 909 if(@setlocale(LC_ALL,"$loc.$enc")) return; 910 } 911 //still here? try to set from environment 912 @setlocale(LC_ALL,""); 913} 914 915/** 916 * Return the human readable size of a file 917 * 918 * @param int $size A file size 919 * @param int $dec A number of decimal places 920 * @author Martin Benjamin <b.martin@cybernet.ch> 921 * @author Aidan Lister <aidan@php.net> 922 * @version 1.0.0 923 */ 924function filesize_h($size, $dec = 1){ 925 $sizes = array('B', 'KB', 'MB', 'GB'); 926 $count = count($sizes); 927 $i = 0; 928 929 while ($size >= 1024 && ($i < $count - 1)) { 930 $size /= 1024; 931 $i++; 932 } 933 934 return round($size, $dec) . ' ' . $sizes[$i]; 935} 936 937/** 938 * Run a few sanity checks 939 * 940 * @author Andreas Gohr <andi@splitbrain.org> 941 */ 942function getVersion(){ 943 //import version string 944 if(@file_exists('VERSION')){ 945 //official release 946 return 'Release '.io_readfile('VERSION'); 947 }elseif(is_dir('_darcs')){ 948 //darcs checkout 949 $inv = file('_darcs/inventory'); 950 $inv = preg_grep('#andi@splitbrain\.org\*\*\d{14}#',$inv); 951 $cur = array_pop($inv); 952 preg_match('#\*\*(\d{4})(\d{2})(\d{2})#',$cur,$matches); 953 return 'Darcs '.$matches[1].'-'.$matches[2].'-'.$matches[3]; 954 }else{ 955 return 'snapshot?'; 956 } 957} 958 959/** 960 * Run a few sanity checks 961 * 962 * @author Andreas Gohr <andi@splitbrain.org> 963 */ 964function check(){ 965 global $conf; 966 global $INFO; 967 968 msg('DokuWiki version: '.getVersion(),1); 969 970 if(version_compare(phpversion(),'4.3.0','<')){ 971 msg('Your PHP version is too old ('.phpversion().' vs. 4.3.+ recommended)',-1); 972 }elseif(version_compare(phpversion(),'4.3.10','<')){ 973 msg('Consider upgrading PHP to 4.3.10 or higher for security reasons (your version: '.phpversion().')',0); 974 }else{ 975 msg('PHP version '.phpversion(),1); 976 } 977 978 if(is_writable($conf['changelog'])){ 979 msg('Changelog is writable',1); 980 }else{ 981 msg('Changelog is not writable',-1); 982 } 983 984 if(is_writable($conf['datadir'])){ 985 msg('Datadir is writable',1); 986 }else{ 987 msg('Datadir is not writable',-1); 988 } 989 990 if(is_writable($conf['olddir'])){ 991 msg('Attic is writable',1); 992 }else{ 993 msg('Attic is not writable',-1); 994 } 995 996 if(is_writable($conf['mediadir'])){ 997 msg('Mediadir is writable',1); 998 }else{ 999 msg('Mediadir is not writable',-1); 1000 } 1001 1002 if(is_writable('conf/users.auth')){ 1003 msg('conf/users.auth is writable',1); 1004 }else{ 1005 msg('conf/users.auth is not writable',0); 1006 } 1007 1008 if(function_exists('mb_strpos')){ 1009 if(defined('UTF8_NOMBSTRING')){ 1010 msg('mb_string extension is available but will not be used',0); 1011 }else{ 1012 msg('mb_string extension is available and will be used',1); 1013 } 1014 }else{ 1015 msg('mb_string extension not available - PHP only replacements will be used',0); 1016 } 1017 1018 msg('Your current permission for this page is '.$INFO['perm'],0); 1019 1020 if(is_writable($INFO['filepath'])){ 1021 msg('The current page is writable by the webserver',0); 1022 }else{ 1023 msg('The current page is not writable by the webserver',0); 1024 } 1025 1026 if($INFO['writable']){ 1027 msg('The current page is writable by you',0); 1028 }else{ 1029 msg('The current page is not writable you',0); 1030 } 1031} 1032?> 1033