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 $wordblocks = getWordblocks(); 416 //how many lines to read at once (to work around some PCRE limits) 417 if(version_compare(phpversion(),'4.3.0','<')){ 418 //old versions of PCRE define a maximum of parenthesises even if no 419 //backreferences are used - the maximum is 99 420 //this is very bad performancewise and may even be too high still 421 $chunksize = 40; 422 }else{ 423 //read file in chunks of 600 - this should work around the 424 //MAX_PATTERN_SIZE in modern PCRE 425 $chunksize = 400; 426 } 427 while($blocks = array_splice($wordblocks,0,$chunksize)){ 428 $re = array(); 429 #build regexp from blocks 430 foreach($blocks as $block){ 431 $block = preg_replace('/#.*$/','',$block); 432 $block = trim($block); 433 if(empty($block)) continue; 434 $re[] = $block; 435 } 436 if(preg_match('#('.join('|',$re).')#si',$TEXT, $match=array())) { 437 return true; 438 } 439 } 440 return false; 441} 442 443/** 444 * Return the IP of the client 445 * 446 * Honours X-Forwarded-For and X-Real-IP Proxy Headers 447 * 448 * It returns a comma separated list of IPs if the above mentioned 449 * headers are set. If the single parameter is set, it tries to return 450 * a routable public address, prefering the ones suplied in the X 451 * headers 452 * 453 * @param boolean $single If set only a single IP is returned 454 * @author Andreas Gohr <andi@splitbrain.org> 455 */ 456function clientIP($single=false){ 457 $ip = array(); 458 $ip[] = $_SERVER['REMOTE_ADDR']; 459 if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) 460 $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_FORWARDED_FOR'])); 461 if(!empty($_SERVER['HTTP_X_REAL_IP'])) 462 $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_REAL_IP'])); 463 464 // remove any non-IP stuff 465 $cnt = count($ip); 466 $match = array(); 467 for($i=0; $i<$cnt; $i++){ 468 if(preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/',$ip[$i],$match)) { 469 $ip[$i] = $match[0]; 470 } else { 471 $ip[$i] = ''; 472 } 473 if(empty($ip[$i])) unset($ip[$i]); 474 } 475 $ip = array_values(array_unique($ip)); 476 if(!$ip[0]) $ip[0] = '0.0.0.0'; // for some strange reason we don't have a IP 477 478 if(!$single) return join(',',$ip); 479 480 // decide which IP to use, trying to avoid local addresses 481 $ip = array_reverse($ip); 482 foreach($ip as $i){ 483 if(preg_match('/^(127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/',$i)){ 484 continue; 485 }else{ 486 return $i; 487 } 488 } 489 // still here? just use the first (last) address 490 return $ip[0]; 491} 492 493/** 494 * Checks if a given page is currently locked. 495 * 496 * removes stale lockfiles 497 * 498 * @author Andreas Gohr <andi@splitbrain.org> 499 */ 500function checklock($id){ 501 global $conf; 502 $lock = wikiLockFN($id); 503 504 //no lockfile 505 if(!@file_exists($lock)) return false; 506 507 //lockfile expired 508 if((time() - filemtime($lock)) > $conf['locktime']){ 509 @unlink($lock); 510 return false; 511 } 512 513 //my own lock 514 $ip = io_readFile($lock); 515 if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){ 516 return false; 517 } 518 519 return $ip; 520} 521 522/** 523 * Lock a page for editing 524 * 525 * @author Andreas Gohr <andi@splitbrain.org> 526 */ 527function lock($id){ 528 $lock = wikiLockFN($id); 529 if($_SERVER['REMOTE_USER']){ 530 io_saveFile($lock,$_SERVER['REMOTE_USER']); 531 }else{ 532 io_saveFile($lock,clientIP()); 533 } 534} 535 536/** 537 * Unlock a page if it was locked by the user 538 * 539 * @author Andreas Gohr <andi@splitbrain.org> 540 * @return bool true if a lock was removed 541 */ 542function unlock($id){ 543 $lock = wikiLockFN($id); 544 if(@file_exists($lock)){ 545 $ip = io_readFile($lock); 546 if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){ 547 @unlink($lock); 548 return true; 549 } 550 } 551 return false; 552} 553 554/** 555 * convert line ending to unix format 556 * 557 * @see formText() for 2crlf conversion 558 * @author Andreas Gohr <andi@splitbrain.org> 559 */ 560function cleanText($text){ 561 $text = preg_replace("/(\015\012)|(\015)/","\012",$text); 562 return $text; 563} 564 565/** 566 * Prepares text for print in Webforms by encoding special chars. 567 * It also converts line endings to Windows format which is 568 * pseudo standard for webforms. 569 * 570 * @see cleanText() for 2unix conversion 571 * @author Andreas Gohr <andi@splitbrain.org> 572 */ 573function formText($text){ 574 $text = preg_replace("/\012/","\015\012",$text); 575 return htmlspecialchars($text); 576} 577 578/** 579 * Returns the specified local text in raw format 580 * 581 * @author Andreas Gohr <andi@splitbrain.org> 582 */ 583function rawLocale($id){ 584 return io_readFile(localeFN($id)); 585} 586 587/** 588 * Returns the raw WikiText 589 * 590 * @author Andreas Gohr <andi@splitbrain.org> 591 */ 592function rawWiki($id,$rev=''){ 593 return io_readWikiPage(wikiFN($id, $rev), $id, $rev); 594} 595 596/** 597 * Returns the pagetemplate contents for the ID's namespace 598 * 599 * @author Andreas Gohr <andi@splitbrain.org> 600 */ 601function pageTemplate($data){ 602 $id = $data[0]; 603 global $conf; 604 global $INFO; 605 $tpl = io_readFile(dirname(wikiFN($id)).'/_template.txt'); 606 $tpl = str_replace('@ID@',$id,$tpl); 607 $tpl = str_replace('@NS@',getNS($id),$tpl); 608 $tpl = str_replace('@PAGE@',strtr(noNS($id),'_',' '),$tpl); 609 $tpl = str_replace('@USER@',$_SERVER['REMOTE_USER'],$tpl); 610 $tpl = str_replace('@NAME@',$INFO['userinfo']['name'],$tpl); 611 $tpl = str_replace('@MAIL@',$INFO['userinfo']['mail'],$tpl); 612 $tpl = str_replace('@DATE@',date($conf['dformat']),$tpl); 613 return $tpl; 614} 615 616 617/** 618 * Returns the raw Wiki Text in three slices. 619 * 620 * The range parameter needs to have the form "from-to" 621 * and gives the range of the section in bytes - no 622 * UTF-8 awareness is needed. 623 * The returned order is prefix, section and suffix. 624 * 625 * @author Andreas Gohr <andi@splitbrain.org> 626 */ 627function rawWikiSlices($range,$id,$rev=''){ 628 list($from,$to) = split('-',$range,2); 629 $text = io_readWikiPage(wikiFN($id, $rev), $id, $rev); 630 if(!$from) $from = 0; 631 if(!$to) $to = strlen($text)+1; 632 633 $slices[0] = substr($text,0,$from-1); 634 $slices[1] = substr($text,$from-1,$to-$from); 635 $slices[2] = substr($text,$to); 636 637 return $slices; 638} 639 640/** 641 * Joins wiki text slices 642 * 643 * function to join the text slices with correct lineendings again. 644 * When the pretty parameter is set to true it adds additional empty 645 * lines between sections if needed (used on saving). 646 * 647 * @author Andreas Gohr <andi@splitbrain.org> 648 */ 649function con($pre,$text,$suf,$pretty=false){ 650 651 if($pretty){ 652 if($pre && substr($pre,-1) != "\n") $pre .= "\n"; 653 if($suf && substr($text,-1) != "\n") $text .= "\n"; 654 } 655 656 if($pre) $pre .= "\n"; 657 if($suf) $text .= "\n"; 658 return $pre.$text.$suf; 659} 660 661/** 662 * Saves a wikitext by calling io_writeWikiPage 663 * 664 * @author Andreas Gohr <andi@splitbrain.org> 665 * @author Ben Coburn <btcoburn@silicodon.net> 666 */ 667function saveWikiText($id,$text,$summary,$minor=false){ 668 global $conf; 669 global $lang; 670 global $REV; 671 // ignore if no changes were made 672 if($text == rawWiki($id,'')){ 673 return; 674 } 675 676 $file = wikiFN($id); 677 $old = saveOldRevision($id); 678 $wasRemoved = empty($text); 679 $wasCreated = !@file_exists($file); 680 $wasReverted = ($REV==true); 681 $newRev = false; 682 683 if ($wasRemoved){ 684 // pre-save deleted revision 685 @touch($file); 686 $newRev = saveOldRevision($id); 687 // remove empty file 688 @unlink($file); 689 // remove old meta info... 690 $mfiles = metaFiles($id); 691 $changelog = metaFN($id, '.changes'); 692 foreach ($mfiles as $mfile) { 693 // but keep per-page changelog to preserve page history 694 if (@file_exists($mfile) && $mfile!==$changelog) { @unlink($mfile); } 695 } 696 $del = true; 697 // autoset summary on deletion 698 if(empty($summary)) $summary = $lang['deleted']; 699 // remove empty namespaces 700 io_sweepNS($id, 'datadir'); 701 io_sweepNS($id, 'mediadir'); 702 }else{ 703 // save file (namespace dir is created in io_writeWikiPage) 704 io_writeWikiPage($file, $text, $id); 705 $newRev = @filemtime($file); 706 $del = false; 707 } 708 709 // select changelog line type 710 $extra = ''; 711 $type = 'E'; 712 if ($wasReverted) { 713 $type = 'R'; 714 $extra = $REV; 715 } 716 else if ($wasCreated) { $type = 'C'; } 717 else if ($wasRemoved) { $type = 'D'; } 718 else if ($minor && $conf['useacl'] && $_SERVER['REMOTE_USER']) { $type = 'e'; } //minor edits only for logged in users 719 720 addLogEntry($newRev, $id, $type, $summary, $extra); 721 // send notify mails 722 notify($id,'admin',$old,$summary,$minor); 723 notify($id,'subscribers',$old,$summary,$minor); 724 725 //purge cache on add by updating the purgefile 726 if($conf['purgeonadd'] && (!$old || $del)){ 727 io_saveFile($conf['cachedir'].'/purgefile',time()); 728 } 729} 730 731/** 732 * moves the current version to the attic and returns its 733 * revision date 734 * 735 * @author Andreas Gohr <andi@splitbrain.org> 736 */ 737function saveOldRevision($id){ 738 global $conf; 739 $oldf = wikiFN($id); 740 if(!@file_exists($oldf)) return ''; 741 $date = filemtime($oldf); 742 $newf = wikiFN($id,$date); 743 io_writeWikiPage($newf, rawWiki($id), $id, $date); 744 return $date; 745} 746 747/** 748 * Sends a notify mail on page change 749 * 750 * @param string $id The changed page 751 * @param string $who Who to notify (admin|subscribers) 752 * @param int $rev Old page revision 753 * @param string $summary What changed 754 * @param boolean $minor Is this a minor edit? 755 * @param array $replace Additional string substitutions, @KEY@ to be replaced by value 756 * 757 * @author Andreas Gohr <andi@splitbrain.org> 758 */ 759function notify($id,$who,$rev='',$summary='',$minor=false,$replace=array()){ 760 global $lang; 761 global $conf; 762 763 // decide if there is something to do 764 if($who == 'admin'){ 765 if(empty($conf['notify'])) return; //notify enabled? 766 $text = rawLocale('mailtext'); 767 $to = $conf['notify']; 768 $bcc = ''; 769 }elseif($who == 'subscribers'){ 770 if(!$conf['subscribers']) return; //subscribers enabled? 771 if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return; //skip minors 772 $bcc = subscriber_addresslist($id); 773 if(empty($bcc)) return; 774 $to = ''; 775 $text = rawLocale('subscribermail'); 776 }elseif($who == 'register'){ 777 if(empty($conf['registernotify'])) return; 778 $text = rawLocale('registermail'); 779 $to = $conf['registernotify']; 780 $bcc = ''; 781 }else{ 782 return; //just to be safe 783 } 784 785 $text = str_replace('@DATE@',date($conf['dformat']),$text); 786 $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text); 787 $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text); 788 $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text); 789 $text = str_replace('@NEWPAGE@',wl($id,'',true),$text); 790 $text = str_replace('@PAGE@',$id,$text); 791 $text = str_replace('@TITLE@',$conf['title'],$text); 792 $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text); 793 $text = str_replace('@SUMMARY@',$summary,$text); 794 $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text); 795 796 foreach ($replace as $key => $substitution) { 797 $text = str_replace('@'.strtoupper($key).'@',$substitution, $text); 798 } 799 800 if($who == 'register'){ 801 $subject = $lang['mail_new_user'].' '.$summary; 802 }elseif($rev){ 803 $subject = $lang['mail_changed'].' '.$id; 804 $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true),$text); 805 require_once(DOKU_INC.'inc/DifferenceEngine.php'); 806 $df = new Diff(split("\n",rawWiki($id,$rev)), 807 split("\n",rawWiki($id))); 808 $dformat = new UnifiedDiffFormatter(); 809 $diff = $dformat->format($df); 810 }else{ 811 $subject=$lang['mail_newpage'].' '.$id; 812 $text = str_replace('@OLDPAGE@','none',$text); 813 $diff = rawWiki($id); 814 } 815 $text = str_replace('@DIFF@',$diff,$text); 816 $subject = '['.$conf['title'].'] '.$subject; 817 818 mail_send($to,$subject,$text,$conf['mailfrom'],'',$bcc); 819} 820 821/** 822 * extracts the query from a google referer 823 * 824 * @todo should be more generic and support yahoo et al 825 * @author Andreas Gohr <andi@splitbrain.org> 826 */ 827function getGoogleQuery(){ 828 $url = parse_url($_SERVER['HTTP_REFERER']); 829 if(!$url) return ''; 830 831 if(!preg_match("#google\.#i",$url['host'])) return ''; 832 $query = array(); 833 parse_str($url['query'],$query); 834 835 return $query['q']; 836} 837 838/** 839 * Try to set correct locale 840 * 841 * @deprecated No longer used 842 * @author Andreas Gohr <andi@splitbrain.org> 843 */ 844function setCorrectLocale(){ 845 global $conf; 846 global $lang; 847 848 $enc = strtoupper($lang['encoding']); 849 foreach ($lang['locales'] as $loc){ 850 //try locale 851 if(@setlocale(LC_ALL,$loc)) return; 852 //try loceale with encoding 853 if(@setlocale(LC_ALL,"$loc.$enc")) return; 854 } 855 //still here? try to set from environment 856 @setlocale(LC_ALL,""); 857} 858 859/** 860 * Return the human readable size of a file 861 * 862 * @param int $size A file size 863 * @param int $dec A number of decimal places 864 * @author Martin Benjamin <b.martin@cybernet.ch> 865 * @author Aidan Lister <aidan@php.net> 866 * @version 1.0.0 867 */ 868function filesize_h($size, $dec = 1){ 869 $sizes = array('B', 'KB', 'MB', 'GB'); 870 $count = count($sizes); 871 $i = 0; 872 873 while ($size >= 1024 && ($i < $count - 1)) { 874 $size /= 1024; 875 $i++; 876 } 877 878 return round($size, $dec) . ' ' . $sizes[$i]; 879} 880 881/** 882 * return an obfuscated email address in line with $conf['mailguard'] setting 883 * 884 * @author Harry Fuecks <hfuecks@gmail.com> 885 * @author Christopher Smith <chris@jalakai.co.uk> 886 */ 887function obfuscate($email) { 888 global $conf; 889 890 switch ($conf['mailguard']) { 891 case 'visible' : 892 $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] '); 893 return strtr($email, $obfuscate); 894 895 case 'hex' : 896 $encode = ''; 897 for ($x=0; $x < strlen($email); $x++) $encode .= '&#x' . bin2hex($email{$x}).';'; 898 return $encode; 899 900 case 'none' : 901 default : 902 return $email; 903 } 904} 905 906/** 907 * Let us know if a user is tracking a page 908 * 909 * @author Andreas Gohr <andi@splitbrain.org> 910 */ 911function is_subscribed($id,$uid){ 912 $file=metaFN($id,'.mlist'); 913 if (@file_exists($file)) { 914 $mlist = file($file); 915 $pos = array_search($uid."\n",$mlist); 916 return is_int($pos); 917 } 918 919 return false; 920} 921 922/** 923 * Return a string with the email addresses of all the 924 * users subscribed to a page 925 * 926 * @author Steven Danz <steven-danz@kc.rr.com> 927 */ 928function subscriber_addresslist($id){ 929 global $conf; 930 global $auth; 931 932 $emails = ''; 933 934 if (!$conf['subscribers']) return; 935 936 $mlist = array(); 937 $file=metaFN($id,'.mlist'); 938 if (@file_exists($file)) { 939 $mlist = file($file); 940 } 941 if(count($mlist) > 0) { 942 foreach ($mlist as $who) { 943 $who = rtrim($who); 944 $info = $auth->getUserData($who); 945 $level = auth_aclcheck($id,$who,$info['grps']); 946 if ($level >= AUTH_READ) { 947 if (strcasecmp($info['mail'],$conf['notify']) != 0) { 948 if (empty($emails)) { 949 $emails = $info['mail']; 950 } else { 951 $emails = "$emails,".$info['mail']; 952 } 953 } 954 } 955 } 956 } 957 958 return $emails; 959} 960 961/** 962 * Removes quoting backslashes 963 * 964 * @author Andreas Gohr <andi@splitbrain.org> 965 */ 966function unslash($string,$char="'"){ 967 return str_replace('\\'.$char,$char,$string); 968} 969 970//Setup VIM: ex: et ts=2 enc=utf-8 : 971