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