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 9if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../').'/'); 10require_once(DOKU_CONF.'dokuwiki.php'); 11require_once(DOKU_INC.'inc/io.php'); 12require_once(DOKU_INC.'inc/changelog.php'); 13require_once(DOKU_INC.'inc/utf8.php'); 14require_once(DOKU_INC.'inc/mail.php'); 15require_once(DOKU_INC.'inc/parserutils.php'); 16require_once(DOKU_INC.'inc/infoutils.php'); 17 18/** 19 * These constants are used with the recents function 20 */ 21define('RECENTS_SKIP_DELETED',2); 22define('RECENTS_SKIP_MINORS',4); 23define('RECENTS_SKIP_SUBSPACES',8); 24 25/** 26 * Wrapper around htmlspecialchars() 27 * 28 * @author Andreas Gohr <andi@splitbrain.org> 29 * @see htmlspecialchars() 30 */ 31function hsc($string){ 32 return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); 33} 34 35/** 36 * print a newline terminated string 37 * 38 * You can give an indention as optional parameter 39 * 40 * @author Andreas Gohr <andi@splitbrain.org> 41 */ 42function ptln($string,$intend=0){ 43 for($i=0; $i<$intend; $i++) print ' '; 44 print"$string\n"; 45} 46 47/** 48 * Return info about the current document as associative 49 * array. 50 * 51 * @author Andreas Gohr <andi@splitbrain.org> 52 */ 53function pageinfo(){ 54 global $ID; 55 global $REV; 56 global $USERINFO; 57 global $conf; 58 59 if($_SERVER['REMOTE_USER']){ 60 $info['userinfo'] = $USERINFO; 61 $info['perm'] = auth_quickaclcheck($ID); 62 $info['subscribed'] = is_subscribed($ID,$_SERVER['REMOTE_USER']); 63 $info['client'] = $_SERVER['REMOTE_USER']; 64 65 // if some outside auth were used only REMOTE_USER is set 66 if(!$info['userinfo']['name']){ 67 $info['userinfo']['name'] = $_SERVER['REMOTE_USER']; 68 } 69 70 }else{ 71 $info['perm'] = auth_aclcheck($ID,'',null); 72 $info['subscribed'] = false; 73 $info['client'] = clientIP(true); 74 } 75 76 $info['namespace'] = getNS($ID); 77 $info['locked'] = checklock($ID); 78 $info['filepath'] = realpath(wikiFN($ID,$REV)); 79 $info['exists'] = @file_exists($info['filepath']); 80 if($REV && !$info['exists']){ 81 //check if current revision was meant 82 $cur = wikiFN($ID); 83 if(@file_exists($cur) && (@filemtime($cur) == $REV)){ 84 $info['filepath'] = realpath($cur); 85 $info['exists'] = true; 86 $REV = ''; 87 } 88 } 89 $info['rev'] = $REV; 90 if($info['exists']){ 91 $info['writable'] = (is_writable($info['filepath']) && 92 ($info['perm'] >= AUTH_EDIT)); 93 }else{ 94 $info['writable'] = ($info['perm'] >= AUTH_CREATE); 95 } 96 $info['editable'] = ($info['writable'] && empty($info['lock'])); 97 $info['lastmod'] = @filemtime($info['filepath']); 98 99 //load page meta data 100 $info['meta'] = p_get_metadata($ID); 101 102 //who's the editor 103 if($REV){ 104 $revinfo = getRevisionInfo($ID, $REV, 1024); 105 }else{ 106 $revinfo = isset($info['meta']['last_change']) ? $info['meta']['last_change'] : getRevisionInfo($ID,$info['lastmod'],1024); 107 } 108 109 $info['ip'] = $revinfo['ip']; 110 $info['user'] = $revinfo['user']; 111 $info['sum'] = $revinfo['sum']; 112 // See also $INFO['meta']['last_change'] which is the most recent log line for page $ID. 113 // Use $INFO['meta']['last_change']['type']==='e' in place of $info['minor']. 114 115 if($revinfo['user']){ 116 $info['editor'] = $revinfo['user']; 117 }else{ 118 $info['editor'] = $revinfo['ip']; 119 } 120 121 // draft 122 $draft = getCacheName($info['client'].$ID,'.draft'); 123 if(@file_exists($draft)){ 124 if(@filemtime($draft) < @filemtime(wikiFN($ID))){ 125 // remove stale draft 126 @unlink($draft); 127 }else{ 128 $info['draft'] = $draft; 129 } 130 } 131 132 return $info; 133} 134 135/** 136 * Build an string of URL parameters 137 * 138 * @author Andreas Gohr 139 */ 140function buildURLparams($params, $sep='&'){ 141 $url = ''; 142 $amp = false; 143 foreach($params as $key => $val){ 144 if($amp) $url .= $sep; 145 146 $url .= $key.'='; 147 $url .= rawurlencode($val); 148 $amp = true; 149 } 150 return $url; 151} 152 153/** 154 * Build an string of html tag attributes 155 * 156 * Skips keys starting with '_', values get HTML encoded 157 * 158 * @author Andreas Gohr 159 */ 160function buildAttributes($params){ 161 $url = ''; 162 foreach($params as $key => $val){ 163 if($key{0} == '_') continue; 164 165 $url .= $key.'="'; 166 $url .= htmlspecialchars ($val); 167 $url .= '" '; 168 } 169 return $url; 170} 171 172 173/** 174 * This builds the breadcrumb trail and returns it as array 175 * 176 * @author Andreas Gohr <andi@splitbrain.org> 177 */ 178function breadcrumbs(){ 179 // we prepare the breadcrumbs early for quick session closing 180 static $crumbs = null; 181 if($crumbs != null) return $crumbs; 182 183 global $ID; 184 global $ACT; 185 global $conf; 186 $crumbs = $_SESSION[$conf['title']]['bc']; 187 188 //first visit? 189 if (!is_array($crumbs)){ 190 $crumbs = array(); 191 } 192 //we only save on show and existing wiki documents 193 $file = wikiFN($ID); 194 if($ACT != 'show' || !@file_exists($file)){ 195 $_SESSION[$conf['title']]['bc'] = $crumbs; 196 return $crumbs; 197 } 198 199 // page names 200 $name = noNS($ID); 201 if ($conf['useheading']) { 202 // get page title 203 $title = p_get_first_heading($ID); 204 if ($title) { 205 $name = $title; 206 } 207 } 208 209 //remove ID from array 210 if (isset($crumbs[$ID])) { 211 unset($crumbs[$ID]); 212 } 213 214 //add to array 215 $crumbs[$ID] = $name; 216 //reduce size 217 while(count($crumbs) > $conf['breadcrumbs']){ 218 array_shift($crumbs); 219 } 220 //save to session 221 $_SESSION[$conf['title']]['bc'] = $crumbs; 222 return $crumbs; 223} 224 225/** 226 * Filter for page IDs 227 * 228 * This is run on a ID before it is outputted somewhere 229 * currently used to replace the colon with something else 230 * on Windows systems and to have proper URL encoding 231 * 232 * Urlencoding is ommitted when the second parameter is false 233 * 234 * @author Andreas Gohr <andi@splitbrain.org> 235 */ 236function idfilter($id,$ue=true){ 237 global $conf; 238 if ($conf['useslash'] && $conf['userewrite']){ 239 $id = strtr($id,':','/'); 240 }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && 241 $conf['userewrite']) { 242 $id = strtr($id,':',';'); 243 } 244 if($ue){ 245 $id = rawurlencode($id); 246 $id = str_replace('%3A',':',$id); //keep as colon 247 $id = str_replace('%2F','/',$id); //keep as slash 248 } 249 return $id; 250} 251 252/** 253 * This builds a link to a wikipage 254 * 255 * It handles URL rewriting and adds additional parameter if 256 * given in $more 257 * 258 * @author Andreas Gohr <andi@splitbrain.org> 259 */ 260function wl($id='',$more='',$abs=false,$sep='&'){ 261 global $conf; 262 if(is_array($more)){ 263 $more = buildURLparams($more,$sep); 264 }else{ 265 $more = str_replace(',',$sep,$more); 266 } 267 268 $id = idfilter($id); 269 if($abs){ 270 $xlink = DOKU_URL; 271 }else{ 272 $xlink = DOKU_BASE; 273 } 274 275 if($conf['userewrite'] == 2){ 276 $xlink .= DOKU_SCRIPT.'/'.$id; 277 if($more) $xlink .= '?'.$more; 278 }elseif($conf['userewrite']){ 279 $xlink .= $id; 280 if($more) $xlink .= '?'.$more; 281 }else{ 282 $xlink .= DOKU_SCRIPT.'?id='.$id; 283 if($more) $xlink .= $sep.$more; 284 } 285 286 return $xlink; 287} 288 289/** 290 * This builds a link to an alternate page format 291 * 292 * Handles URL rewriting if enabled. Follows the style of wl(). 293 * 294 * @author Ben Coburn <btcoburn@silicodon.net> 295 */ 296function exportlink($id='',$format='raw',$more='',$abs=false,$sep='&'){ 297 global $conf; 298 if(is_array($more)){ 299 $more = buildURLparams($more,$sep); 300 }else{ 301 $more = str_replace(',',$sep,$more); 302 } 303 304 $format = rawurlencode($format); 305 $id = idfilter($id); 306 if($abs){ 307 $xlink = DOKU_URL; 308 }else{ 309 $xlink = DOKU_BASE; 310 } 311 312 if($conf['userewrite'] == 2){ 313 $xlink .= DOKU_SCRIPT.'/'.$id.'?do=export_'.$format; 314 if($more) $xlink .= $sep.$more; 315 }elseif($conf['userewrite'] == 1){ 316 $xlink .= '_export/'.$format.'/'.$id; 317 if($more) $xlink .= '?'.$more; 318 }else{ 319 $xlink .= DOKU_SCRIPT.'?do=export_'.$format.$sep.'id='.$id; 320 if($more) $xlink .= $sep.$more; 321 } 322 323 return $xlink; 324} 325 326/** 327 * Build a link to a media file 328 * 329 * Will return a link to the detail page if $direct is false 330 */ 331function ml($id='',$more='',$direct=true,$sep='&'){ 332 global $conf; 333 if(is_array($more)){ 334 $more = buildURLparams($more,$sep); 335 }else{ 336 $more = str_replace(',',$sep,$more); 337 } 338 339 $xlink = DOKU_BASE; 340 341 // external URLs are always direct without rewriting 342 if(preg_match('#^(https?|ftp)://#i',$id)){ 343 $xlink .= 'lib/exe/fetch.php'; 344 if($more){ 345 $xlink .= '?'.$more; 346 $xlink .= $sep.'media='.rawurlencode($id); 347 }else{ 348 $xlink .= '?media='.rawurlencode($id); 349 } 350 return $xlink; 351 } 352 353 $id = idfilter($id); 354 355 // decide on scriptname 356 if($direct){ 357 if($conf['userewrite'] == 1){ 358 $script = '_media'; 359 }else{ 360 $script = 'lib/exe/fetch.php'; 361 } 362 }else{ 363 if($conf['userewrite'] == 1){ 364 $script = '_detail'; 365 }else{ 366 $script = 'lib/exe/detail.php'; 367 } 368 } 369 370 // build URL based on rewrite mode 371 if($conf['userewrite']){ 372 $xlink .= $script.'/'.$id; 373 if($more) $xlink .= '?'.$more; 374 }else{ 375 if($more){ 376 $xlink .= $script.'?'.$more; 377 $xlink .= $sep.'media='.$id; 378 }else{ 379 $xlink .= $script.'?media='.$id; 380 } 381 } 382 383 return $xlink; 384} 385 386 387 388/** 389 * Just builds a link to a script 390 * 391 * @todo maybe obsolete 392 * @author Andreas Gohr <andi@splitbrain.org> 393 */ 394function script($script='doku.php'){ 395# $link = getBaseURL(); 396# $link .= $script; 397# return $link; 398 return DOKU_BASE.DOKU_SCRIPT; 399} 400 401/** 402 * Spamcheck against wordlist 403 * 404 * Checks the wikitext against a list of blocked expressions 405 * returns true if the text contains any bad words 406 * 407 * @author Andreas Gohr <andi@splitbrain.org> 408 */ 409function checkwordblock(){ 410 global $TEXT; 411 global $conf; 412 413 if(!$conf['usewordblock']) return false; 414 415 // we prepare the text a tiny bit to prevent spammers circumventing URL checks 416 $text = preg_replace('!(\b)(www\.[\w.:?\-;,]+?\.[\w.:?\-;,]+?[\w/\#~:.?+=&%@\!\-.:?\-;,]+?)([.:?\-;,]*[^\w/\#~:.?+=&%@\!\-.:?\-;,])!i','\1http://\2 \2\3',$TEXT); 417 418 $wordblocks = getWordblocks(); 419 //how many lines to read at once (to work around some PCRE limits) 420 if(version_compare(phpversion(),'4.3.0','<')){ 421 //old versions of PCRE define a maximum of parenthesises even if no 422 //backreferences are used - the maximum is 99 423 //this is very bad performancewise and may even be too high still 424 $chunksize = 40; 425 }else{ 426 //read file in chunks of 600 - this should work around the 427 //MAX_PATTERN_SIZE in modern PCRE 428 $chunksize = 400; 429 } 430 while($blocks = array_splice($wordblocks,0,$chunksize)){ 431 $re = array(); 432 #build regexp from blocks 433 foreach($blocks as $block){ 434 $block = preg_replace('/#.*$/','',$block); 435 $block = trim($block); 436 if(empty($block)) continue; 437 $re[] = $block; 438 } 439 if(preg_match('#('.join('|',$re).')#si',$text, $match=array())) { 440 return true; 441 } 442 } 443 return false; 444} 445 446/** 447 * Return the IP of the client 448 * 449 * Honours X-Forwarded-For and X-Real-IP Proxy Headers 450 * 451 * It returns a comma separated list of IPs if the above mentioned 452 * headers are set. If the single parameter is set, it tries to return 453 * a routable public address, prefering the ones suplied in the X 454 * headers 455 * 456 * @param boolean $single If set only a single IP is returned 457 * @author Andreas Gohr <andi@splitbrain.org> 458 */ 459function clientIP($single=false){ 460 $ip = array(); 461 $ip[] = $_SERVER['REMOTE_ADDR']; 462 if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) 463 $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_FORWARDED_FOR'])); 464 if(!empty($_SERVER['HTTP_X_REAL_IP'])) 465 $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_REAL_IP'])); 466 467 // remove any non-IP stuff 468 $cnt = count($ip); 469 $match = array(); 470 for($i=0; $i<$cnt; $i++){ 471 if(preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/',$ip[$i],$match)) { 472 $ip[$i] = $match[0]; 473 } else { 474 $ip[$i] = ''; 475 } 476 if(empty($ip[$i])) unset($ip[$i]); 477 } 478 $ip = array_values(array_unique($ip)); 479 if(!$ip[0]) $ip[0] = '0.0.0.0'; // for some strange reason we don't have a IP 480 481 if(!$single) return join(',',$ip); 482 483 // decide which IP to use, trying to avoid local addresses 484 $ip = array_reverse($ip); 485 foreach($ip as $i){ 486 if(preg_match('/^(127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/',$i)){ 487 continue; 488 }else{ 489 return $i; 490 } 491 } 492 // still here? just use the first (last) address 493 return $ip[0]; 494} 495 496/** 497 * Checks if a given page is currently locked. 498 * 499 * removes stale lockfiles 500 * 501 * @author Andreas Gohr <andi@splitbrain.org> 502 */ 503function checklock($id){ 504 global $conf; 505 $lock = wikiLockFN($id); 506 507 //no lockfile 508 if(!@file_exists($lock)) return false; 509 510 //lockfile expired 511 if((time() - filemtime($lock)) > $conf['locktime']){ 512 @unlink($lock); 513 return false; 514 } 515 516 //my own lock 517 $ip = io_readFile($lock); 518 if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){ 519 return false; 520 } 521 522 return $ip; 523} 524 525/** 526 * Lock a page for editing 527 * 528 * @author Andreas Gohr <andi@splitbrain.org> 529 */ 530function lock($id){ 531 $lock = wikiLockFN($id); 532 if($_SERVER['REMOTE_USER']){ 533 io_saveFile($lock,$_SERVER['REMOTE_USER']); 534 }else{ 535 io_saveFile($lock,clientIP()); 536 } 537} 538 539/** 540 * Unlock a page if it was locked by the user 541 * 542 * @author Andreas Gohr <andi@splitbrain.org> 543 * @return bool true if a lock was removed 544 */ 545function unlock($id){ 546 $lock = wikiLockFN($id); 547 if(@file_exists($lock)){ 548 $ip = io_readFile($lock); 549 if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){ 550 @unlink($lock); 551 return true; 552 } 553 } 554 return false; 555} 556 557/** 558 * convert line ending to unix format 559 * 560 * @see formText() for 2crlf conversion 561 * @author Andreas Gohr <andi@splitbrain.org> 562 */ 563function cleanText($text){ 564 $text = preg_replace("/(\015\012)|(\015)/","\012",$text); 565 return $text; 566} 567 568/** 569 * Prepares text for print in Webforms by encoding special chars. 570 * It also converts line endings to Windows format which is 571 * pseudo standard for webforms. 572 * 573 * @see cleanText() for 2unix conversion 574 * @author Andreas Gohr <andi@splitbrain.org> 575 */ 576function formText($text){ 577 $text = preg_replace("/\012/","\015\012",$text); 578 return htmlspecialchars($text); 579} 580 581/** 582 * Returns the specified local text in raw format 583 * 584 * @author Andreas Gohr <andi@splitbrain.org> 585 */ 586function rawLocale($id){ 587 return io_readFile(localeFN($id)); 588} 589 590/** 591 * Returns the raw WikiText 592 * 593 * @author Andreas Gohr <andi@splitbrain.org> 594 */ 595function rawWiki($id,$rev=''){ 596 return io_readWikiPage(wikiFN($id, $rev), $id, $rev); 597} 598 599/** 600 * Returns the pagetemplate contents for the ID's namespace 601 * 602 * @author Andreas Gohr <andi@splitbrain.org> 603 */ 604function pageTemplate($data){ 605 $id = $data[0]; 606 global $conf; 607 global $INFO; 608 $tpl = io_readFile(dirname(wikiFN($id)).'/_template.txt'); 609 $tpl = str_replace('@ID@',$id,$tpl); 610 $tpl = str_replace('@NS@',getNS($id),$tpl); 611 $tpl = str_replace('@PAGE@',strtr(noNS($id),'_',' '),$tpl); 612 $tpl = str_replace('@USER@',$_SERVER['REMOTE_USER'],$tpl); 613 $tpl = str_replace('@NAME@',$INFO['userinfo']['name'],$tpl); 614 $tpl = str_replace('@MAIL@',$INFO['userinfo']['mail'],$tpl); 615 $tpl = str_replace('@DATE@',date($conf['dformat']),$tpl); 616 return $tpl; 617} 618 619 620/** 621 * Returns the raw Wiki Text in three slices. 622 * 623 * The range parameter needs to have the form "from-to" 624 * and gives the range of the section in bytes - no 625 * UTF-8 awareness is needed. 626 * The returned order is prefix, section and suffix. 627 * 628 * @author Andreas Gohr <andi@splitbrain.org> 629 */ 630function rawWikiSlices($range,$id,$rev=''){ 631 list($from,$to) = split('-',$range,2); 632 $text = io_readWikiPage(wikiFN($id, $rev), $id, $rev); 633 if(!$from) $from = 0; 634 if(!$to) $to = strlen($text)+1; 635 636 $slices[0] = substr($text,0,$from-1); 637 $slices[1] = substr($text,$from-1,$to-$from); 638 $slices[2] = substr($text,$to); 639 640 return $slices; 641} 642 643/** 644 * Joins wiki text slices 645 * 646 * function to join the text slices with correct lineendings again. 647 * When the pretty parameter is set to true it adds additional empty 648 * lines between sections if needed (used on saving). 649 * 650 * @author Andreas Gohr <andi@splitbrain.org> 651 */ 652function con($pre,$text,$suf,$pretty=false){ 653 654 if($pretty){ 655 if($pre && substr($pre,-1) != "\n") $pre .= "\n"; 656 if($suf && substr($text,-1) != "\n") $text .= "\n"; 657 } 658 659 if($pre) $pre .= "\n"; 660 if($suf) $text .= "\n"; 661 return $pre.$text.$suf; 662} 663 664/** 665 * Saves a wikitext by calling io_writeWikiPage 666 * 667 * @author Andreas Gohr <andi@splitbrain.org> 668 * @author Ben Coburn <btcoburn@silicodon.net> 669 */ 670function saveWikiText($id,$text,$summary,$minor=false){ 671 global $conf; 672 global $lang; 673 global $REV; 674 // ignore if no changes were made 675 if($text == rawWiki($id,'')){ 676 return; 677 } 678 679 $file = wikiFN($id); 680 $old = saveOldRevision($id); 681 $wasRemoved = empty($text); 682 $wasCreated = !@file_exists($file); 683 $wasReverted = ($REV==true); 684 $newRev = false; 685 686 if ($wasRemoved){ 687 // pre-save deleted revision 688 @touch($file); 689 $newRev = saveOldRevision($id); 690 // remove empty file 691 @unlink($file); 692 // remove old meta info... 693 $mfiles = metaFiles($id); 694 $changelog = metaFN($id, '.changes'); 695 foreach ($mfiles as $mfile) { 696 // but keep per-page changelog to preserve page history 697 if (@file_exists($mfile) && $mfile!==$changelog) { @unlink($mfile); } 698 } 699 $del = true; 700 // autoset summary on deletion 701 if(empty($summary)) $summary = $lang['deleted']; 702 // remove empty namespaces 703 io_sweepNS($id, 'datadir'); 704 io_sweepNS($id, 'mediadir'); 705 }else{ 706 // save file (namespace dir is created in io_writeWikiPage) 707 io_writeWikiPage($file, $text, $id); 708 $newRev = @filemtime($file); 709 $del = false; 710 } 711 712 // select changelog line type 713 $extra = ''; 714 $type = 'E'; 715 if ($wasReverted) { 716 $type = 'R'; 717 $extra = $REV; 718 } 719 else if ($wasCreated) { $type = 'C'; } 720 else if ($wasRemoved) { $type = 'D'; } 721 else if ($minor && $conf['useacl'] && $_SERVER['REMOTE_USER']) { $type = 'e'; } //minor edits only for logged in users 722 723 addLogEntry($newRev, $id, $type, $summary, $extra); 724 // send notify mails 725 notify($id,'admin',$old,$summary,$minor); 726 notify($id,'subscribers',$old,$summary,$minor); 727 728 // update the purgefile (timestamp of the last time anything within the wiki was changed) 729 io_saveFile($conf['cachedir'].'/purgefile',time()); 730} 731 732/** 733 * moves the current version to the attic and returns its 734 * revision date 735 * 736 * @author Andreas Gohr <andi@splitbrain.org> 737 */ 738function saveOldRevision($id){ 739 global $conf; 740 $oldf = wikiFN($id); 741 if(!@file_exists($oldf)) return ''; 742 $date = filemtime($oldf); 743 $newf = wikiFN($id,$date); 744 io_writeWikiPage($newf, rawWiki($id), $id, $date); 745 return $date; 746} 747 748/** 749 * Sends a notify mail on page change 750 * 751 * @param string $id The changed page 752 * @param string $who Who to notify (admin|subscribers) 753 * @param int $rev Old page revision 754 * @param string $summary What changed 755 * @param boolean $minor Is this a minor edit? 756 * @param array $replace Additional string substitutions, @KEY@ to be replaced by value 757 * 758 * @author Andreas Gohr <andi@splitbrain.org> 759 */ 760function notify($id,$who,$rev='',$summary='',$minor=false,$replace=array()){ 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 if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return; //skip minors 773 $bcc = subscriber_addresslist($id); 774 if(empty($bcc)) return; 775 $to = ''; 776 $text = rawLocale('subscribermail'); 777 }elseif($who == 'register'){ 778 if(empty($conf['registernotify'])) return; 779 $text = rawLocale('registermail'); 780 $to = $conf['registernotify']; 781 $bcc = ''; 782 }else{ 783 return; //just to be safe 784 } 785 786 $text = str_replace('@DATE@',date($conf['dformat']),$text); 787 $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text); 788 $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text); 789 $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text); 790 $text = str_replace('@NEWPAGE@',wl($id,'',true),$text); 791 $text = str_replace('@PAGE@',$id,$text); 792 $text = str_replace('@TITLE@',$conf['title'],$text); 793 $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text); 794 $text = str_replace('@SUMMARY@',$summary,$text); 795 $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text); 796 797 foreach ($replace as $key => $substitution) { 798 $text = str_replace('@'.strtoupper($key).'@',$substitution, $text); 799 } 800 801 if($who == 'register'){ 802 $subject = $lang['mail_new_user'].' '.$summary; 803 }elseif($rev){ 804 $subject = $lang['mail_changed'].' '.$id; 805 $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true),$text); 806 require_once(DOKU_INC.'inc/DifferenceEngine.php'); 807 $df = new Diff(split("\n",rawWiki($id,$rev)), 808 split("\n",rawWiki($id))); 809 $dformat = new UnifiedDiffFormatter(); 810 $diff = $dformat->format($df); 811 }else{ 812 $subject=$lang['mail_newpage'].' '.$id; 813 $text = str_replace('@OLDPAGE@','none',$text); 814 $diff = rawWiki($id); 815 } 816 $text = str_replace('@DIFF@',$diff,$text); 817 $subject = '['.$conf['title'].'] '.$subject; 818 819 mail_send($to,$subject,$text,$conf['mailfrom'],'',$bcc); 820} 821 822/** 823 * extracts the query from a google referer 824 * 825 * @todo should be more generic and support yahoo et al 826 * @author Andreas Gohr <andi@splitbrain.org> 827 */ 828function getGoogleQuery(){ 829 $url = parse_url($_SERVER['HTTP_REFERER']); 830 if(!$url) return ''; 831 832 if(!preg_match("#google\.#i",$url['host'])) return ''; 833 $query = array(); 834 parse_str($url['query'],$query); 835 836 return $query['q']; 837} 838 839/** 840 * Try to set correct locale 841 * 842 * @deprecated No longer used 843 * @author Andreas Gohr <andi@splitbrain.org> 844 */ 845function setCorrectLocale(){ 846 global $conf; 847 global $lang; 848 849 $enc = strtoupper($lang['encoding']); 850 foreach ($lang['locales'] as $loc){ 851 //try locale 852 if(@setlocale(LC_ALL,$loc)) return; 853 //try loceale with encoding 854 if(@setlocale(LC_ALL,"$loc.$enc")) return; 855 } 856 //still here? try to set from environment 857 @setlocale(LC_ALL,""); 858} 859 860/** 861 * Return the human readable size of a file 862 * 863 * @param int $size A file size 864 * @param int $dec A number of decimal places 865 * @author Martin Benjamin <b.martin@cybernet.ch> 866 * @author Aidan Lister <aidan@php.net> 867 * @version 1.0.0 868 */ 869function filesize_h($size, $dec = 1){ 870 $sizes = array('B', 'KB', 'MB', 'GB'); 871 $count = count($sizes); 872 $i = 0; 873 874 while ($size >= 1024 && ($i < $count - 1)) { 875 $size /= 1024; 876 $i++; 877 } 878 879 return round($size, $dec) . ' ' . $sizes[$i]; 880} 881 882/** 883 * return an obfuscated email address in line with $conf['mailguard'] setting 884 * 885 * @author Harry Fuecks <hfuecks@gmail.com> 886 * @author Christopher Smith <chris@jalakai.co.uk> 887 */ 888function obfuscate($email) { 889 global $conf; 890 891 switch ($conf['mailguard']) { 892 case 'visible' : 893 $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] '); 894 return strtr($email, $obfuscate); 895 896 case 'hex' : 897 $encode = ''; 898 for ($x=0; $x < strlen($email); $x++) $encode .= '&#x' . bin2hex($email{$x}).';'; 899 return $encode; 900 901 case 'none' : 902 default : 903 return $email; 904 } 905} 906 907/** 908 * Let us know if a user is tracking a page 909 * 910 * @author Andreas Gohr <andi@splitbrain.org> 911 */ 912function is_subscribed($id,$uid){ 913 $file=metaFN($id,'.mlist'); 914 if (@file_exists($file)) { 915 $mlist = file($file); 916 $pos = array_search($uid."\n",$mlist); 917 return is_int($pos); 918 } 919 920 return false; 921} 922 923/** 924 * Return a string with the email addresses of all the 925 * users subscribed to a page 926 * 927 * @author Steven Danz <steven-danz@kc.rr.com> 928 */ 929function subscriber_addresslist($id){ 930 global $conf; 931 global $auth; 932 933 $emails = ''; 934 935 if (!$conf['subscribers']) return; 936 937 $mlist = array(); 938 $file=metaFN($id,'.mlist'); 939 if (@file_exists($file)) { 940 $mlist = file($file); 941 } 942 if(count($mlist) > 0) { 943 foreach ($mlist as $who) { 944 $who = rtrim($who); 945 $info = $auth->getUserData($who); 946 $level = auth_aclcheck($id,$who,$info['grps']); 947 if ($level >= AUTH_READ) { 948 if (strcasecmp($info['mail'],$conf['notify']) != 0) { 949 if (empty($emails)) { 950 $emails = $info['mail']; 951 } else { 952 $emails = "$emails,".$info['mail']; 953 } 954 } 955 } 956 } 957 } 958 959 return $emails; 960} 961 962/** 963 * Removes quoting backslashes 964 * 965 * @author Andreas Gohr <andi@splitbrain.org> 966 */ 967function unslash($string,$char="'"){ 968 return str_replace('\\'.$char,$char,$string); 969} 970 971//Setup VIM: ex: et ts=2 enc=utf-8 : 972