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