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 // update the purgefile (timestamp of the last time anything within the wiki was changed) 726 io_saveFile($conf['cachedir'].'/purgefile',time()); 727} 728 729/** 730 * moves the current version to the attic and returns its 731 * revision date 732 * 733 * @author Andreas Gohr <andi@splitbrain.org> 734 */ 735function saveOldRevision($id){ 736 global $conf; 737 $oldf = wikiFN($id); 738 if(!@file_exists($oldf)) return ''; 739 $date = filemtime($oldf); 740 $newf = wikiFN($id,$date); 741 io_writeWikiPage($newf, rawWiki($id), $id, $date); 742 return $date; 743} 744 745/** 746 * Sends a notify mail on page change 747 * 748 * @param string $id The changed page 749 * @param string $who Who to notify (admin|subscribers) 750 * @param int $rev Old page revision 751 * @param string $summary What changed 752 * @param boolean $minor Is this a minor edit? 753 * @param array $replace Additional string substitutions, @KEY@ to be replaced by value 754 * 755 * @author Andreas Gohr <andi@splitbrain.org> 756 */ 757function notify($id,$who,$rev='',$summary='',$minor=false,$replace=array()){ 758 global $lang; 759 global $conf; 760 761 // decide if there is something to do 762 if($who == 'admin'){ 763 if(empty($conf['notify'])) return; //notify enabled? 764 $text = rawLocale('mailtext'); 765 $to = $conf['notify']; 766 $bcc = ''; 767 }elseif($who == 'subscribers'){ 768 if(!$conf['subscribers']) return; //subscribers enabled? 769 if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return; //skip minors 770 $bcc = subscriber_addresslist($id); 771 if(empty($bcc)) return; 772 $to = ''; 773 $text = rawLocale('subscribermail'); 774 }elseif($who == 'register'){ 775 if(empty($conf['registernotify'])) return; 776 $text = rawLocale('registermail'); 777 $to = $conf['registernotify']; 778 $bcc = ''; 779 }else{ 780 return; //just to be safe 781 } 782 783 $text = str_replace('@DATE@',date($conf['dformat']),$text); 784 $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text); 785 $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text); 786 $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text); 787 $text = str_replace('@NEWPAGE@',wl($id,'',true),$text); 788 $text = str_replace('@PAGE@',$id,$text); 789 $text = str_replace('@TITLE@',$conf['title'],$text); 790 $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text); 791 $text = str_replace('@SUMMARY@',$summary,$text); 792 $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text); 793 794 foreach ($replace as $key => $substitution) { 795 $text = str_replace('@'.strtoupper($key).'@',$substitution, $text); 796 } 797 798 if($who == 'register'){ 799 $subject = $lang['mail_new_user'].' '.$summary; 800 }elseif($rev){ 801 $subject = $lang['mail_changed'].' '.$id; 802 $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true),$text); 803 require_once(DOKU_INC.'inc/DifferenceEngine.php'); 804 $df = new Diff(split("\n",rawWiki($id,$rev)), 805 split("\n",rawWiki($id))); 806 $dformat = new UnifiedDiffFormatter(); 807 $diff = $dformat->format($df); 808 }else{ 809 $subject=$lang['mail_newpage'].' '.$id; 810 $text = str_replace('@OLDPAGE@','none',$text); 811 $diff = rawWiki($id); 812 } 813 $text = str_replace('@DIFF@',$diff,$text); 814 $subject = '['.$conf['title'].'] '.$subject; 815 816 mail_send($to,$subject,$text,$conf['mailfrom'],'',$bcc); 817} 818 819/** 820 * extracts the query from a google referer 821 * 822 * @todo should be more generic and support yahoo et al 823 * @author Andreas Gohr <andi@splitbrain.org> 824 */ 825function getGoogleQuery(){ 826 $url = parse_url($_SERVER['HTTP_REFERER']); 827 if(!$url) return ''; 828 829 if(!preg_match("#google\.#i",$url['host'])) return ''; 830 $query = array(); 831 parse_str($url['query'],$query); 832 833 return $query['q']; 834} 835 836/** 837 * Try to set correct locale 838 * 839 * @deprecated No longer used 840 * @author Andreas Gohr <andi@splitbrain.org> 841 */ 842function setCorrectLocale(){ 843 global $conf; 844 global $lang; 845 846 $enc = strtoupper($lang['encoding']); 847 foreach ($lang['locales'] as $loc){ 848 //try locale 849 if(@setlocale(LC_ALL,$loc)) return; 850 //try loceale with encoding 851 if(@setlocale(LC_ALL,"$loc.$enc")) return; 852 } 853 //still here? try to set from environment 854 @setlocale(LC_ALL,""); 855} 856 857/** 858 * Return the human readable size of a file 859 * 860 * @param int $size A file size 861 * @param int $dec A number of decimal places 862 * @author Martin Benjamin <b.martin@cybernet.ch> 863 * @author Aidan Lister <aidan@php.net> 864 * @version 1.0.0 865 */ 866function filesize_h($size, $dec = 1){ 867 $sizes = array('B', 'KB', 'MB', 'GB'); 868 $count = count($sizes); 869 $i = 0; 870 871 while ($size >= 1024 && ($i < $count - 1)) { 872 $size /= 1024; 873 $i++; 874 } 875 876 return round($size, $dec) . ' ' . $sizes[$i]; 877} 878 879/** 880 * return an obfuscated email address in line with $conf['mailguard'] setting 881 * 882 * @author Harry Fuecks <hfuecks@gmail.com> 883 * @author Christopher Smith <chris@jalakai.co.uk> 884 */ 885function obfuscate($email) { 886 global $conf; 887 888 switch ($conf['mailguard']) { 889 case 'visible' : 890 $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] '); 891 return strtr($email, $obfuscate); 892 893 case 'hex' : 894 $encode = ''; 895 for ($x=0; $x < strlen($email); $x++) $encode .= '&#x' . bin2hex($email{$x}).';'; 896 return $encode; 897 898 case 'none' : 899 default : 900 return $email; 901 } 902} 903 904/** 905 * Let us know if a user is tracking a page 906 * 907 * @author Andreas Gohr <andi@splitbrain.org> 908 */ 909function is_subscribed($id,$uid){ 910 $file=metaFN($id,'.mlist'); 911 if (@file_exists($file)) { 912 $mlist = file($file); 913 $pos = array_search($uid."\n",$mlist); 914 return is_int($pos); 915 } 916 917 return false; 918} 919 920/** 921 * Return a string with the email addresses of all the 922 * users subscribed to a page 923 * 924 * @author Steven Danz <steven-danz@kc.rr.com> 925 */ 926function subscriber_addresslist($id){ 927 global $conf; 928 global $auth; 929 930 $emails = ''; 931 932 if (!$conf['subscribers']) return; 933 934 $mlist = array(); 935 $file=metaFN($id,'.mlist'); 936 if (@file_exists($file)) { 937 $mlist = file($file); 938 } 939 if(count($mlist) > 0) { 940 foreach ($mlist as $who) { 941 $who = rtrim($who); 942 $info = $auth->getUserData($who); 943 $level = auth_aclcheck($id,$who,$info['grps']); 944 if ($level >= AUTH_READ) { 945 if (strcasecmp($info['mail'],$conf['notify']) != 0) { 946 if (empty($emails)) { 947 $emails = $info['mail']; 948 } else { 949 $emails = "$emails,".$info['mail']; 950 } 951 } 952 } 953 } 954 } 955 956 return $emails; 957} 958 959/** 960 * Removes quoting backslashes 961 * 962 * @author Andreas Gohr <andi@splitbrain.org> 963 */ 964function unslash($string,$char="'"){ 965 return str_replace('\\'.$char,$char,$string); 966} 967 968//Setup VIM: ex: et ts=2 enc=utf-8 : 969