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')) die('meh.'); 10require_once(DOKU_INC.'inc/io.php'); 11require_once(DOKU_INC.'inc/changelog.php'); 12require_once(DOKU_INC.'inc/utf8.php'); 13require_once(DOKU_INC.'inc/mail.php'); 14require_once(DOKU_INC.'inc/parserutils.php'); 15require_once(DOKU_INC.'inc/infoutils.php'); 16 17/** 18 * These constants are used with the recents function 19 */ 20define('RECENTS_SKIP_DELETED',2); 21define('RECENTS_SKIP_MINORS',4); 22define('RECENTS_SKIP_SUBSPACES',8); 23define('RECENTS_MEDIA_CHANGES',16); 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,$indent=0){ 43 echo str_repeat(' ', $indent)."$string\n"; 44} 45 46/** 47 * strips control characters (<32) from the given string 48 * 49 * @author Andreas Gohr <andi@splitbrain.org> 50 */ 51function stripctl($string){ 52 return preg_replace('/[\x00-\x1F]+/s','',$string); 53} 54 55/** 56 * Return a secret token to be used for CSRF attack prevention 57 * 58 * @author Andreas Gohr <andi@splitbrain.org> 59 * @link http://en.wikipedia.org/wiki/Cross-site_request_forgery 60 * @link http://christ1an.blogspot.com/2007/04/preventing-csrf-efficiently.html 61 * @return string 62 */ 63function getSecurityToken(){ 64 return md5(auth_cookiesalt().session_id()); 65} 66 67/** 68 * Check the secret CSRF token 69 */ 70function checkSecurityToken($token=null){ 71 if(is_null($token)) $token = $_REQUEST['sectok']; 72 if(getSecurityToken() != $token){ 73 msg('Security Token did not match. Possible CSRF attack.',-1); 74 return false; 75 } 76 return true; 77} 78 79/** 80 * Print a hidden form field with a secret CSRF token 81 * 82 * @author Andreas Gohr <andi@splitbrain.org> 83 */ 84function formSecurityToken($print=true){ 85 $ret = '<div class="no"><input type="hidden" name="sectok" value="'.getSecurityToken().'" /></div>'."\n"; 86 if($print){ 87 echo $ret; 88 }else{ 89 return $ret; 90 } 91} 92 93/** 94 * Return info about the current document as associative 95 * array. 96 * 97 * @author Andreas Gohr <andi@splitbrain.org> 98 */ 99function pageinfo(){ 100 global $ID; 101 global $REV; 102 global $RANGE; 103 global $USERINFO; 104 global $conf; 105 global $lang; 106 107 // include ID & REV not redundant, as some parts of DokuWiki may temporarily change $ID, e.g. p_wiki_xhtml 108 // FIXME ... perhaps it would be better to ensure the temporary changes weren't necessary 109 $info['id'] = $ID; 110 $info['rev'] = $REV; 111 112 if($_SERVER['REMOTE_USER']){ 113 $info['userinfo'] = $USERINFO; 114 $info['perm'] = auth_quickaclcheck($ID); 115 $info['subscribed'] = is_subscribed($ID,$_SERVER['REMOTE_USER'],false); 116 $info['subscribedns'] = is_subscribed($ID,$_SERVER['REMOTE_USER'],true); 117 $info['client'] = $_SERVER['REMOTE_USER']; 118 119 // set info about manager/admin status 120 $info['isadmin'] = false; 121 $info['ismanager'] = false; 122 if($info['perm'] == AUTH_ADMIN){ 123 $info['isadmin'] = true; 124 $info['ismanager'] = true; 125 }elseif(auth_ismanager()){ 126 $info['ismanager'] = true; 127 } 128 129 // if some outside auth were used only REMOTE_USER is set 130 if(!$info['userinfo']['name']){ 131 $info['userinfo']['name'] = $_SERVER['REMOTE_USER']; 132 } 133 134 }else{ 135 $info['perm'] = auth_aclcheck($ID,'',null); 136 $info['subscribed'] = false; 137 $info['client'] = clientIP(true); 138 } 139 140 $info['namespace'] = getNS($ID); 141 $info['locked'] = checklock($ID); 142 $info['filepath'] = fullpath(wikiFN($ID)); 143 $info['exists'] = @file_exists($info['filepath']); 144 if($REV){ 145 //check if current revision was meant 146 if($info['exists'] && (@filemtime($info['filepath'])==$REV)){ 147 $REV = ''; 148 }elseif($RANGE){ 149 //section editing does not work with old revisions! 150 $REV = ''; 151 $RANGE = ''; 152 msg($lang['nosecedit'],0); 153 }else{ 154 //really use old revision 155 $info['filepath'] = fullpath(wikiFN($ID,$REV)); 156 $info['exists'] = @file_exists($info['filepath']); 157 } 158 } 159 $info['rev'] = $REV; 160 if($info['exists']){ 161 $info['writable'] = (is_writable($info['filepath']) && 162 ($info['perm'] >= AUTH_EDIT)); 163 }else{ 164 $info['writable'] = ($info['perm'] >= AUTH_CREATE); 165 } 166 $info['editable'] = ($info['writable'] && empty($info['lock'])); 167 $info['lastmod'] = @filemtime($info['filepath']); 168 169 //load page meta data 170 $info['meta'] = p_get_metadata($ID); 171 172 //who's the editor 173 if($REV){ 174 $revinfo = getRevisionInfo($ID, $REV, 1024); 175 }else{ 176 if (is_array($info['meta']['last_change'])) { 177 $revinfo = $info['meta']['last_change']; 178 } else { 179 $revinfo = getRevisionInfo($ID, $info['lastmod'], 1024); 180 // cache most recent changelog line in metadata if missing and still valid 181 if ($revinfo!==false) { 182 $info['meta']['last_change'] = $revinfo; 183 p_set_metadata($ID, array('last_change' => $revinfo)); 184 } 185 } 186 } 187 //and check for an external edit 188 if($revinfo!==false && $revinfo['date']!=$info['lastmod']){ 189 // cached changelog line no longer valid 190 $revinfo = false; 191 $info['meta']['last_change'] = $revinfo; 192 p_set_metadata($ID, array('last_change' => $revinfo)); 193 } 194 195 $info['ip'] = $revinfo['ip']; 196 $info['user'] = $revinfo['user']; 197 $info['sum'] = $revinfo['sum']; 198 // See also $INFO['meta']['last_change'] which is the most recent log line for page $ID. 199 // Use $INFO['meta']['last_change']['type']===DOKU_CHANGE_TYPE_MINOR_EDIT in place of $info['minor']. 200 201 if($revinfo['user']){ 202 $info['editor'] = $revinfo['user']; 203 }else{ 204 $info['editor'] = $revinfo['ip']; 205 } 206 207 // draft 208 $draft = getCacheName($info['client'].$ID,'.draft'); 209 if(@file_exists($draft)){ 210 if(@filemtime($draft) < @filemtime(wikiFN($ID))){ 211 // remove stale draft 212 @unlink($draft); 213 }else{ 214 $info['draft'] = $draft; 215 } 216 } 217 218 // mobile detection 219 $info['ismobile'] = clientismobile(); 220 221 return $info; 222} 223 224/** 225 * Build an string of URL parameters 226 * 227 * @author Andreas Gohr 228 */ 229function buildURLparams($params, $sep='&'){ 230 $url = ''; 231 $amp = false; 232 foreach($params as $key => $val){ 233 if($amp) $url .= $sep; 234 235 $url .= $key.'='; 236 $url .= rawurlencode((string)$val); 237 $amp = true; 238 } 239 return $url; 240} 241 242/** 243 * Build an string of html tag attributes 244 * 245 * Skips keys starting with '_', values get HTML encoded 246 * 247 * @author Andreas Gohr 248 */ 249function buildAttributes($params,$skipempty=false){ 250 $url = ''; 251 foreach($params as $key => $val){ 252 if($key{0} == '_') continue; 253 if($val === '' && $skipempty) continue; 254 255 $url .= $key.'="'; 256 $url .= htmlspecialchars ($val); 257 $url .= '" '; 258 } 259 return $url; 260} 261 262 263/** 264 * This builds the breadcrumb trail and returns it as array 265 * 266 * @author Andreas Gohr <andi@splitbrain.org> 267 */ 268function breadcrumbs(){ 269 // we prepare the breadcrumbs early for quick session closing 270 static $crumbs = null; 271 if($crumbs != null) return $crumbs; 272 273 global $ID; 274 global $ACT; 275 global $conf; 276 $crumbs = $_SESSION[DOKU_COOKIE]['bc']; 277 278 //first visit? 279 if (!is_array($crumbs)){ 280 $crumbs = array(); 281 } 282 //we only save on show and existing wiki documents 283 $file = wikiFN($ID); 284 if($ACT != 'show' || !@file_exists($file)){ 285 $_SESSION[DOKU_COOKIE]['bc'] = $crumbs; 286 return $crumbs; 287 } 288 289 // page names 290 $name = noNSorNS($ID); 291 if (useHeading('navigation')) { 292 // get page title 293 $title = p_get_first_heading($ID,true); 294 if ($title) { 295 $name = $title; 296 } 297 } 298 299 //remove ID from array 300 if (isset($crumbs[$ID])) { 301 unset($crumbs[$ID]); 302 } 303 304 //add to array 305 $crumbs[$ID] = $name; 306 //reduce size 307 while(count($crumbs) > $conf['breadcrumbs']){ 308 array_shift($crumbs); 309 } 310 //save to session 311 $_SESSION[DOKU_COOKIE]['bc'] = $crumbs; 312 return $crumbs; 313} 314 315/** 316 * Filter for page IDs 317 * 318 * This is run on a ID before it is outputted somewhere 319 * currently used to replace the colon with something else 320 * on Windows systems and to have proper URL encoding 321 * 322 * Urlencoding is ommitted when the second parameter is false 323 * 324 * @author Andreas Gohr <andi@splitbrain.org> 325 */ 326function idfilter($id,$ue=true){ 327 global $conf; 328 if ($conf['useslash'] && $conf['userewrite']){ 329 $id = strtr($id,':','/'); 330 }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && 331 $conf['userewrite']) { 332 $id = strtr($id,':',';'); 333 } 334 if($ue){ 335 $id = rawurlencode($id); 336 $id = str_replace('%3A',':',$id); //keep as colon 337 $id = str_replace('%2F','/',$id); //keep as slash 338 } 339 return $id; 340} 341 342/** 343 * This builds a link to a wikipage 344 * 345 * It handles URL rewriting and adds additional parameter if 346 * given in $more 347 * 348 * @author Andreas Gohr <andi@splitbrain.org> 349 */ 350function wl($id='',$more='',$abs=false,$sep='&'){ 351 global $conf; 352 if(is_array($more)){ 353 $more = buildURLparams($more,$sep); 354 }else{ 355 $more = str_replace(',',$sep,$more); 356 } 357 358 $id = idfilter($id); 359 if($abs){ 360 $xlink = DOKU_URL; 361 }else{ 362 $xlink = DOKU_BASE; 363 } 364 365 if($conf['userewrite'] == 2){ 366 $xlink .= DOKU_SCRIPT.'/'.$id; 367 if($more) $xlink .= '?'.$more; 368 }elseif($conf['userewrite']){ 369 $xlink .= $id; 370 if($more) $xlink .= '?'.$more; 371 }elseif($id){ 372 $xlink .= DOKU_SCRIPT.'?id='.$id; 373 if($more) $xlink .= $sep.$more; 374 }else{ 375 $xlink .= DOKU_SCRIPT; 376 if($more) $xlink .= '?'.$more; 377 } 378 379 return $xlink; 380} 381 382/** 383 * This builds a link to an alternate page format 384 * 385 * Handles URL rewriting if enabled. Follows the style of wl(). 386 * 387 * @author Ben Coburn <btcoburn@silicodon.net> 388 */ 389function exportlink($id='',$format='raw',$more='',$abs=false,$sep='&'){ 390 global $conf; 391 if(is_array($more)){ 392 $more = buildURLparams($more,$sep); 393 }else{ 394 $more = str_replace(',',$sep,$more); 395 } 396 397 $format = rawurlencode($format); 398 $id = idfilter($id); 399 if($abs){ 400 $xlink = DOKU_URL; 401 }else{ 402 $xlink = DOKU_BASE; 403 } 404 405 if($conf['userewrite'] == 2){ 406 $xlink .= DOKU_SCRIPT.'/'.$id.'?do=export_'.$format; 407 if($more) $xlink .= $sep.$more; 408 }elseif($conf['userewrite'] == 1){ 409 $xlink .= '_export/'.$format.'/'.$id; 410 if($more) $xlink .= '?'.$more; 411 }else{ 412 $xlink .= DOKU_SCRIPT.'?do=export_'.$format.$sep.'id='.$id; 413 if($more) $xlink .= $sep.$more; 414 } 415 416 return $xlink; 417} 418 419/** 420 * Build a link to a media file 421 * 422 * Will return a link to the detail page if $direct is false 423 * 424 * The $more parameter should always be given as array, the function then 425 * will strip default parameters to produce even cleaner URLs 426 * 427 * @param string $id - the media file id or URL 428 * @param mixed $more - string or array with additional parameters 429 * @param boolean $direct - link to detail page if false 430 * @param string $sep - URL parameter separator 431 * @param boolean $abs - Create an absolute URL 432 */ 433function ml($id='',$more='',$direct=true,$sep='&',$abs=false){ 434 global $conf; 435 if(is_array($more)){ 436 // strip defaults for shorter URLs 437 if(isset($more['cache']) && $more['cache'] == 'cache') unset($more['cache']); 438 if(!$more['w']) unset($more['w']); 439 if(!$more['h']) unset($more['h']); 440 if(isset($more['id']) && $direct) unset($more['id']); 441 $more = buildURLparams($more,$sep); 442 }else{ 443 $more = str_replace('cache=cache','',$more); //skip default 444 $more = str_replace(',,',',',$more); 445 $more = str_replace(',',$sep,$more); 446 } 447 448 if($abs){ 449 $xlink = DOKU_URL; 450 }else{ 451 $xlink = DOKU_BASE; 452 } 453 454 // external URLs are always direct without rewriting 455 if(preg_match('#^(https?|ftp)://#i',$id)){ 456 $xlink .= 'lib/exe/fetch.php'; 457 if($more){ 458 $xlink .= '?'.$more; 459 $xlink .= $sep.'media='.rawurlencode($id); 460 }else{ 461 $xlink .= '?media='.rawurlencode($id); 462 } 463 return $xlink; 464 } 465 466 $id = idfilter($id); 467 468 // decide on scriptname 469 if($direct){ 470 if($conf['userewrite'] == 1){ 471 $script = '_media'; 472 }else{ 473 $script = 'lib/exe/fetch.php'; 474 } 475 }else{ 476 if($conf['userewrite'] == 1){ 477 $script = '_detail'; 478 }else{ 479 $script = 'lib/exe/detail.php'; 480 } 481 } 482 483 // build URL based on rewrite mode 484 if($conf['userewrite']){ 485 $xlink .= $script.'/'.$id; 486 if($more) $xlink .= '?'.$more; 487 }else{ 488 if($more){ 489 $xlink .= $script.'?'.$more; 490 $xlink .= $sep.'media='.$id; 491 }else{ 492 $xlink .= $script.'?media='.$id; 493 } 494 } 495 496 return $xlink; 497} 498 499 500 501/** 502 * Just builds a link to a script 503 * 504 * @todo maybe obsolete 505 * @author Andreas Gohr <andi@splitbrain.org> 506 */ 507function script($script='doku.php'){ 508# $link = getBaseURL(); 509# $link .= $script; 510# return $link; 511 return DOKU_BASE.DOKU_SCRIPT; 512} 513 514/** 515 * Spamcheck against wordlist 516 * 517 * Checks the wikitext against a list of blocked expressions 518 * returns true if the text contains any bad words 519 * 520 * Triggers COMMON_WORDBLOCK_BLOCKED 521 * 522 * Action Plugins can use this event to inspect the blocked data 523 * and gain information about the user who was blocked. 524 * 525 * Event data: 526 * data['matches'] - array of matches 527 * data['userinfo'] - information about the blocked user 528 * [ip] - ip address 529 * [user] - username (if logged in) 530 * [mail] - mail address (if logged in) 531 * [name] - real name (if logged in) 532 * 533 * @author Andreas Gohr <andi@splitbrain.org> 534 * @author Michael Klier <chi@chimeric.de> 535 * @param string $text - optional text to check, if not given the globals are used 536 * @return bool - true if a spam word was found 537 */ 538function checkwordblock($text=''){ 539 global $TEXT; 540 global $PRE; 541 global $SUF; 542 global $conf; 543 global $INFO; 544 545 if(!$conf['usewordblock']) return false; 546 547 if(!$text) $text = "$PRE $TEXT $SUF"; 548 549 // we prepare the text a tiny bit to prevent spammers circumventing URL checks 550 $text = preg_replace('!(\b)(www\.[\w.:?\-;,]+?\.[\w.:?\-;,]+?[\w/\#~:.?+=&%@\!\-.:?\-;,]+?)([.:?\-;,]*[^\w/\#~:.?+=&%@\!\-.:?\-;,])!i','\1http://\2 \2\3',$text); 551 552 $wordblocks = getWordblocks(); 553 //how many lines to read at once (to work around some PCRE limits) 554 if(version_compare(phpversion(),'4.3.0','<')){ 555 //old versions of PCRE define a maximum of parenthesises even if no 556 //backreferences are used - the maximum is 99 557 //this is very bad performancewise and may even be too high still 558 $chunksize = 40; 559 }else{ 560 //read file in chunks of 200 - this should work around the 561 //MAX_PATTERN_SIZE in modern PCRE 562 $chunksize = 200; 563 } 564 while($blocks = array_splice($wordblocks,0,$chunksize)){ 565 $re = array(); 566 #build regexp from blocks 567 foreach($blocks as $block){ 568 $block = preg_replace('/#.*$/','',$block); 569 $block = trim($block); 570 if(empty($block)) continue; 571 $re[] = $block; 572 } 573 if(count($re) && preg_match('#('.join('|',$re).')#si',$text,$matches)) { 574 //prepare event data 575 $data['matches'] = $matches; 576 $data['userinfo']['ip'] = $_SERVER['REMOTE_ADDR']; 577 if($_SERVER['REMOTE_USER']) { 578 $data['userinfo']['user'] = $_SERVER['REMOTE_USER']; 579 $data['userinfo']['name'] = $INFO['userinfo']['name']; 580 $data['userinfo']['mail'] = $INFO['userinfo']['mail']; 581 } 582 $callback = create_function('', 'return true;'); 583 return trigger_event('COMMON_WORDBLOCK_BLOCKED', $data, $callback, true); 584 } 585 } 586 return false; 587} 588 589/** 590 * Return the IP of the client 591 * 592 * Honours X-Forwarded-For and X-Real-IP Proxy Headers 593 * 594 * It returns a comma separated list of IPs if the above mentioned 595 * headers are set. If the single parameter is set, it tries to return 596 * a routable public address, prefering the ones suplied in the X 597 * headers 598 * 599 * @param boolean $single If set only a single IP is returned 600 * @author Andreas Gohr <andi@splitbrain.org> 601 */ 602function clientIP($single=false){ 603 $ip = array(); 604 $ip[] = $_SERVER['REMOTE_ADDR']; 605 if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) 606 $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_FORWARDED_FOR'])); 607 if(!empty($_SERVER['HTTP_X_REAL_IP'])) 608 $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_REAL_IP'])); 609 610 // some IPv4/v6 regexps borrowed from Feyd 611 // see: http://forums.devnetwork.net/viewtopic.php?f=38&t=53479 612 $dec_octet = '(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|[0-9])'; 613 $hex_digit = '[A-Fa-f0-9]'; 614 $h16 = "{$hex_digit}{1,4}"; 615 $IPv4Address = "$dec_octet\\.$dec_octet\\.$dec_octet\\.$dec_octet"; 616 $ls32 = "(?:$h16:$h16|$IPv4Address)"; 617 $IPv6Address = 618 "(?:(?:{$IPv4Address})|(?:". 619 "(?:$h16:){6}$ls32" . 620 "|::(?:$h16:){5}$ls32" . 621 "|(?:$h16)?::(?:$h16:){4}$ls32" . 622 "|(?:(?:$h16:){0,1}$h16)?::(?:$h16:){3}$ls32" . 623 "|(?:(?:$h16:){0,2}$h16)?::(?:$h16:){2}$ls32" . 624 "|(?:(?:$h16:){0,3}$h16)?::(?:$h16:){1}$ls32" . 625 "|(?:(?:$h16:){0,4}$h16)?::$ls32" . 626 "|(?:(?:$h16:){0,5}$h16)?::$h16" . 627 "|(?:(?:$h16:){0,6}$h16)?::" . 628 ")(?:\\/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))?)"; 629 630 // remove any non-IP stuff 631 $cnt = count($ip); 632 $match = array(); 633 for($i=0; $i<$cnt; $i++){ 634 if(preg_match("/^$IPv4Address$/",$ip[$i],$match) || preg_match("/^$IPv6Address$/",$ip[$i],$match)) { 635 $ip[$i] = $match[0]; 636 } else { 637 $ip[$i] = ''; 638 } 639 if(empty($ip[$i])) unset($ip[$i]); 640 } 641 $ip = array_values(array_unique($ip)); 642 if(!$ip[0]) $ip[0] = '0.0.0.0'; // for some strange reason we don't have a IP 643 644 if(!$single) return join(',',$ip); 645 646 // decide which IP to use, trying to avoid local addresses 647 $ip = array_reverse($ip); 648 foreach($ip as $i){ 649 if(preg_match('/^(127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/',$i)){ 650 continue; 651 }else{ 652 return $i; 653 } 654 } 655 // still here? just use the first (last) address 656 return $ip[0]; 657} 658 659/** 660 * Check if the browser is on a mobile device 661 * 662 * Adapted from the example code at url below 663 * 664 * @link http://www.brainhandles.com/2007/10/15/detecting-mobile-browsers/#code 665 */ 666function clientismobile(){ 667 668 if(isset($_SERVER['HTTP_X_WAP_PROFILE'])) return true; 669 670 if(preg_match('/wap\.|\.wap/i',$_SERVER['HTTP_ACCEPT'])) return true; 671 672 if(!isset($_SERVER['HTTP_USER_AGENT'])) return false; 673 674 $uamatches = 'midp|j2me|avantg|docomo|novarra|palmos|palmsource|240x320|opwv|chtml|pda|windows ce|mmp\/|blackberry|mib\/|symbian|wireless|nokia|hand|mobi|phone|cdm|up\.b|audio|SIE\-|SEC\-|samsung|HTC|mot\-|mitsu|sagem|sony|alcatel|lg|erics|vx|NEC|philips|mmm|xx|panasonic|sharp|wap|sch|rover|pocket|benq|java|pt|pg|vox|amoi|bird|compal|kg|voda|sany|kdd|dbt|sendo|sgh|gradi|jb|\d\d\di|moto'; 675 676 if(preg_match("/$uamatches/i",$_SERVER['HTTP_USER_AGENT'])) return true; 677 678 return false; 679} 680 681 682/** 683 * Convert one or more comma separated IPs to hostnames 684 * 685 * @author Glen Harris <astfgl@iamnota.org> 686 * @returns a comma separated list of hostnames 687 */ 688function gethostsbyaddrs($ips){ 689 $hosts = array(); 690 $ips = explode(',',$ips); 691 692 if(is_array($ips)) { 693 foreach($ips as $ip){ 694 $hosts[] = gethostbyaddr(trim($ip)); 695 } 696 return join(',',$hosts); 697 } else { 698 return gethostbyaddr(trim($ips)); 699 } 700} 701 702/** 703 * Checks if a given page is currently locked. 704 * 705 * removes stale lockfiles 706 * 707 * @author Andreas Gohr <andi@splitbrain.org> 708 */ 709function checklock($id){ 710 global $conf; 711 $lock = wikiLockFN($id); 712 713 //no lockfile 714 if(!@file_exists($lock)) return false; 715 716 //lockfile expired 717 if((time() - filemtime($lock)) > $conf['locktime']){ 718 @unlink($lock); 719 return false; 720 } 721 722 //my own lock 723 $ip = io_readFile($lock); 724 if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){ 725 return false; 726 } 727 728 return $ip; 729} 730 731/** 732 * Lock a page for editing 733 * 734 * @author Andreas Gohr <andi@splitbrain.org> 735 */ 736function lock($id){ 737 $lock = wikiLockFN($id); 738 if($_SERVER['REMOTE_USER']){ 739 io_saveFile($lock,$_SERVER['REMOTE_USER']); 740 }else{ 741 io_saveFile($lock,clientIP()); 742 } 743} 744 745/** 746 * Unlock a page if it was locked by the user 747 * 748 * @author Andreas Gohr <andi@splitbrain.org> 749 * @return bool true if a lock was removed 750 */ 751function unlock($id){ 752 $lock = wikiLockFN($id); 753 if(@file_exists($lock)){ 754 $ip = io_readFile($lock); 755 if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){ 756 @unlink($lock); 757 return true; 758 } 759 } 760 return false; 761} 762 763/** 764 * convert line ending to unix format 765 * 766 * @see formText() for 2crlf conversion 767 * @author Andreas Gohr <andi@splitbrain.org> 768 */ 769function cleanText($text){ 770 $text = preg_replace("/(\015\012)|(\015)/","\012",$text); 771 return $text; 772} 773 774/** 775 * Prepares text for print in Webforms by encoding special chars. 776 * It also converts line endings to Windows format which is 777 * pseudo standard for webforms. 778 * 779 * @see cleanText() for 2unix conversion 780 * @author Andreas Gohr <andi@splitbrain.org> 781 */ 782function formText($text){ 783 $text = str_replace("\012","\015\012",$text); 784 return htmlspecialchars($text); 785} 786 787/** 788 * Returns the specified local text in raw format 789 * 790 * @author Andreas Gohr <andi@splitbrain.org> 791 */ 792function rawLocale($id){ 793 return io_readFile(localeFN($id)); 794} 795 796/** 797 * Returns the raw WikiText 798 * 799 * @author Andreas Gohr <andi@splitbrain.org> 800 */ 801function rawWiki($id,$rev=''){ 802 return io_readWikiPage(wikiFN($id, $rev), $id, $rev); 803} 804 805/** 806 * Returns the pagetemplate contents for the ID's namespace 807 * 808 * @author Andreas Gohr <andi@splitbrain.org> 809 */ 810function pageTemplate($data){ 811 $id = $data[0]; 812 global $conf; 813 global $INFO; 814 815 $path = dirname(wikiFN($id)); 816 817 if(@file_exists($path.'/_template.txt')){ 818 $tpl = io_readFile($path.'/_template.txt'); 819 }else{ 820 // search upper namespaces for templates 821 $len = strlen(rtrim($conf['datadir'],'/')); 822 while (strlen($path) >= $len){ 823 if(@file_exists($path.'/__template.txt')){ 824 $tpl = io_readFile($path.'/__template.txt'); 825 break; 826 } 827 $path = substr($path, 0, strrpos($path, '/')); 828 } 829 } 830 if(!$tpl) return ''; 831 832 // replace placeholders 833 $file = noNS($id); 834 $page = strtr($file,'_',' '); 835 836 $tpl = str_replace(array( 837 '@ID@', 838 '@NS@', 839 '@FILE@', 840 '@!FILE@', 841 '@!FILE!@', 842 '@PAGE@', 843 '@!PAGE@', 844 '@!!PAGE@', 845 '@!PAGE!@', 846 '@USER@', 847 '@NAME@', 848 '@MAIL@', 849 '@DATE@', 850 ), 851 array( 852 $id, 853 getNS($id), 854 $file, 855 utf8_ucfirst($file), 856 utf8_strtoupper($file), 857 $page, 858 utf8_ucfirst($page), 859 utf8_ucwords($page), 860 utf8_strtoupper($page), 861 $_SERVER['REMOTE_USER'], 862 $INFO['userinfo']['name'], 863 $INFO['userinfo']['mail'], 864 $conf['dformat'], 865 ), $tpl); 866 867 // we need the callback to work around strftime's char limit 868 $tpl = preg_replace_callback('/%./',create_function('$m','return strftime($m[0]);'),$tpl); 869 870 return $tpl; 871} 872 873 874/** 875 * Returns the raw Wiki Text in three slices. 876 * 877 * The range parameter needs to have the form "from-to" 878 * and gives the range of the section in bytes - no 879 * UTF-8 awareness is needed. 880 * The returned order is prefix, section and suffix. 881 * 882 * @author Andreas Gohr <andi@splitbrain.org> 883 */ 884function rawWikiSlices($range,$id,$rev=''){ 885 list($from,$to) = split('-',$range,2); 886 $text = io_readWikiPage(wikiFN($id, $rev), $id, $rev); 887 if(!$from) $from = 0; 888 if(!$to) $to = strlen($text)+1; 889 890 $slices[0] = substr($text,0,$from-1); 891 $slices[1] = substr($text,$from-1,$to-$from); 892 $slices[2] = substr($text,$to); 893 894 return $slices; 895} 896 897/** 898 * Joins wiki text slices 899 * 900 * function to join the text slices with correct lineendings again. 901 * When the pretty parameter is set to true it adds additional empty 902 * lines between sections if needed (used on saving). 903 * 904 * @author Andreas Gohr <andi@splitbrain.org> 905 */ 906function con($pre,$text,$suf,$pretty=false){ 907 908 if($pretty){ 909 if($pre && substr($pre,-1) != "\n") $pre .= "\n"; 910 if($suf && substr($text,-1) != "\n") $text .= "\n"; 911 } 912 913 // Avoid double newline above section when saving section edit 914 //if($pre) $pre .= "\n"; 915 if($suf) $text .= "\n"; 916 return $pre.$text.$suf; 917} 918 919/** 920 * Saves a wikitext by calling io_writeWikiPage. 921 * Also directs changelog and attic updates. 922 * 923 * @author Andreas Gohr <andi@splitbrain.org> 924 * @author Ben Coburn <btcoburn@silicodon.net> 925 */ 926function saveWikiText($id,$text,$summary,$minor=false){ 927 /* Note to developers: 928 This code is subtle and delicate. Test the behavior of 929 the attic and changelog with dokuwiki and external edits 930 after any changes. External edits change the wiki page 931 directly without using php or dokuwiki. 932 */ 933 global $conf; 934 global $lang; 935 global $REV; 936 // ignore if no changes were made 937 if($text == rawWiki($id,'')){ 938 return; 939 } 940 941 $file = wikiFN($id); 942 $old = @filemtime($file); // from page 943 $wasRemoved = empty($text); 944 $wasCreated = !@file_exists($file); 945 $wasReverted = ($REV==true); 946 $newRev = false; 947 $oldRev = getRevisions($id, -1, 1, 1024); // from changelog 948 $oldRev = (int)(empty($oldRev)?0:$oldRev[0]); 949 if(!@file_exists(wikiFN($id, $old)) && @file_exists($file) && $old>=$oldRev) { 950 // add old revision to the attic if missing 951 saveOldRevision($id); 952 // add a changelog entry if this edit came from outside dokuwiki 953 if ($old>$oldRev) { 954 addLogEntry($old, $id, DOKU_CHANGE_TYPE_EDIT, $lang['external_edit'], '', array('ExternalEdit'=>true)); 955 // remove soon to be stale instructions 956 $cache = new cache_instructions($id, $file); 957 $cache->removeCache(); 958 } 959 } 960 961 if ($wasRemoved){ 962 // Send "update" event with empty data, so plugins can react to page deletion 963 $data = array(array($file, '', false), getNS($id), noNS($id), false); 964 trigger_event('IO_WIKIPAGE_WRITE', $data); 965 // pre-save deleted revision 966 @touch($file); 967 clearstatcache(); 968 $newRev = saveOldRevision($id); 969 // remove empty file 970 @unlink($file); 971 // remove old meta info... 972 $mfiles = metaFiles($id); 973 $changelog = metaFN($id, '.changes'); 974 $metadata = metaFN($id, '.meta'); 975 foreach ($mfiles as $mfile) { 976 // but keep per-page changelog to preserve page history and keep meta data 977 if (@file_exists($mfile) && $mfile!==$changelog && $mfile!==$metadata) { @unlink($mfile); } 978 } 979 // purge meta data 980 p_purge_metadata($id); 981 $del = true; 982 // autoset summary on deletion 983 if(empty($summary)) $summary = $lang['deleted']; 984 // remove empty namespaces 985 io_sweepNS($id, 'datadir'); 986 io_sweepNS($id, 'mediadir'); 987 }else{ 988 // save file (namespace dir is created in io_writeWikiPage) 989 io_writeWikiPage($file, $text, $id); 990 // pre-save the revision, to keep the attic in sync 991 $newRev = saveOldRevision($id); 992 $del = false; 993 } 994 995 // select changelog line type 996 $extra = ''; 997 $type = DOKU_CHANGE_TYPE_EDIT; 998 if ($wasReverted) { 999 $type = DOKU_CHANGE_TYPE_REVERT; 1000 $extra = $REV; 1001 } 1002 else if ($wasCreated) { $type = DOKU_CHANGE_TYPE_CREATE; } 1003 else if ($wasRemoved) { $type = DOKU_CHANGE_TYPE_DELETE; } 1004 else if ($minor && $conf['useacl'] && $_SERVER['REMOTE_USER']) { $type = DOKU_CHANGE_TYPE_MINOR_EDIT; } //minor edits only for logged in users 1005 1006 addLogEntry($newRev, $id, $type, $summary, $extra); 1007 // send notify mails 1008 notify($id,'admin',$old,$summary,$minor); 1009 notify($id,'subscribers',$old,$summary,$minor); 1010 1011 // update the purgefile (timestamp of the last time anything within the wiki was changed) 1012 io_saveFile($conf['cachedir'].'/purgefile',time()); 1013 1014 // if useheading is enabled, purge the cache of all linking pages 1015 if(useHeading('content')){ 1016 require_once(DOKU_INC.'inc/fulltext.php'); 1017 $pages = ft_backlinks($id); 1018 foreach ($pages as $page) { 1019 $cache = new cache_renderer($page, wikiFN($page), 'xhtml'); 1020 $cache->removeCache(); 1021 } 1022 } 1023} 1024 1025/** 1026 * moves the current version to the attic and returns its 1027 * revision date 1028 * 1029 * @author Andreas Gohr <andi@splitbrain.org> 1030 */ 1031function saveOldRevision($id){ 1032 global $conf; 1033 $oldf = wikiFN($id); 1034 if(!@file_exists($oldf)) return ''; 1035 $date = filemtime($oldf); 1036 $newf = wikiFN($id,$date); 1037 io_writeWikiPage($newf, rawWiki($id), $id, $date); 1038 return $date; 1039} 1040 1041/** 1042 * Sends a notify mail on page change 1043 * 1044 * @param string $id The changed page 1045 * @param string $who Who to notify (admin|subscribers) 1046 * @param int $rev Old page revision 1047 * @param string $summary What changed 1048 * @param boolean $minor Is this a minor edit? 1049 * @param array $replace Additional string substitutions, @KEY@ to be replaced by value 1050 * 1051 * @author Andreas Gohr <andi@splitbrain.org> 1052 */ 1053function notify($id,$who,$rev='',$summary='',$minor=false,$replace=array()){ 1054 global $lang; 1055 global $conf; 1056 global $INFO; 1057 1058 // decide if there is something to do 1059 if($who == 'admin'){ 1060 if(empty($conf['notify'])) return; //notify enabled? 1061 $text = rawLocale('mailtext'); 1062 $to = $conf['notify']; 1063 $bcc = ''; 1064 }elseif($who == 'subscribers'){ 1065 if(!$conf['subscribers']) return; //subscribers enabled? 1066 if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return; //skip minors 1067 $bcc = subscriber_addresslist($id,false); 1068 if(empty($bcc)) return; 1069 $to = ''; 1070 $text = rawLocale('subscribermail'); 1071 }elseif($who == 'register'){ 1072 if(empty($conf['registernotify'])) return; 1073 $text = rawLocale('registermail'); 1074 $to = $conf['registernotify']; 1075 $bcc = ''; 1076 }else{ 1077 return; //just to be safe 1078 } 1079 1080 $ip = clientIP(); 1081 $text = str_replace('@DATE@',strftime($conf['dformat']),$text); 1082 $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text); 1083 $text = str_replace('@IPADDRESS@',$ip,$text); 1084 $text = str_replace('@HOSTNAME@',gethostsbyaddrs($ip),$text); 1085 $text = str_replace('@NEWPAGE@',wl($id,'',true,'&'),$text); 1086 $text = str_replace('@PAGE@',$id,$text); 1087 $text = str_replace('@TITLE@',$conf['title'],$text); 1088 $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text); 1089 $text = str_replace('@SUMMARY@',$summary,$text); 1090 $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text); 1091 1092 foreach ($replace as $key => $substitution) { 1093 $text = str_replace('@'.strtoupper($key).'@',$substitution, $text); 1094 } 1095 1096 if($who == 'register'){ 1097 $subject = $lang['mail_new_user'].' '.$summary; 1098 }elseif($rev){ 1099 $subject = $lang['mail_changed'].' '.$id; 1100 $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true,'&'),$text); 1101 require_once(DOKU_INC.'inc/DifferenceEngine.php'); 1102 $df = new Diff(split("\n",rawWiki($id,$rev)), 1103 split("\n",rawWiki($id))); 1104 $dformat = new UnifiedDiffFormatter(); 1105 $diff = $dformat->format($df); 1106 }else{ 1107 $subject=$lang['mail_newpage'].' '.$id; 1108 $text = str_replace('@OLDPAGE@','none',$text); 1109 $diff = rawWiki($id); 1110 } 1111 $text = str_replace('@DIFF@',$diff,$text); 1112 $subject = '['.$conf['title'].'] '.$subject; 1113 1114 $from = $conf['mailfrom']; 1115 $from = str_replace('@USER@',$_SERVER['REMOTE_USER'],$from); 1116 $from = str_replace('@NAME@',$INFO['userinfo']['name'],$from); 1117 $from = str_replace('@MAIL@',$INFO['userinfo']['mail'],$from); 1118 1119 mail_send($to,$subject,$text,$from,'',$bcc); 1120} 1121 1122/** 1123 * extracts the query from a search engine referrer 1124 * 1125 * @author Andreas Gohr <andi@splitbrain.org> 1126 * @author Todd Augsburger <todd@rollerorgans.com> 1127 */ 1128function getGoogleQuery(){ 1129 $url = parse_url($_SERVER['HTTP_REFERER']); 1130 if(!$url) return ''; 1131 1132 $query = array(); 1133 parse_str($url['query'],$query); 1134 if(isset($query['q'])) 1135 $q = $query['q']; // google, live/msn, aol, ask, altavista, alltheweb, gigablast 1136 elseif(isset($query['p'])) 1137 $q = $query['p']; // yahoo 1138 elseif(isset($query['query'])) 1139 $q = $query['query']; // lycos, netscape, clusty, hotbot 1140 elseif(preg_match("#a9\.com#i",$url['host'])) // a9 1141 $q = urldecode(ltrim($url['path'],'/')); 1142 1143 if(!$q) return ''; 1144 $q = preg_split('/[\s\'"\\\\`()\]\[?:!\.{};,#+*<>\\/]+/',$q,-1,PREG_SPLIT_NO_EMPTY); 1145 return $q; 1146} 1147 1148/** 1149 * Try to set correct locale 1150 * 1151 * @deprecated No longer used 1152 * @author Andreas Gohr <andi@splitbrain.org> 1153 */ 1154function setCorrectLocale(){ 1155 global $conf; 1156 global $lang; 1157 1158 $enc = strtoupper($lang['encoding']); 1159 foreach ($lang['locales'] as $loc){ 1160 //try locale 1161 if(@setlocale(LC_ALL,$loc)) return; 1162 //try loceale with encoding 1163 if(@setlocale(LC_ALL,"$loc.$enc")) return; 1164 } 1165 //still here? try to set from environment 1166 @setlocale(LC_ALL,""); 1167} 1168 1169/** 1170 * Return the human readable size of a file 1171 * 1172 * @param int $size A file size 1173 * @param int $dec A number of decimal places 1174 * @author Martin Benjamin <b.martin@cybernet.ch> 1175 * @author Aidan Lister <aidan@php.net> 1176 * @version 1.0.0 1177 */ 1178function filesize_h($size, $dec = 1){ 1179 $sizes = array('B', 'KB', 'MB', 'GB'); 1180 $count = count($sizes); 1181 $i = 0; 1182 1183 while ($size >= 1024 && ($i < $count - 1)) { 1184 $size /= 1024; 1185 $i++; 1186 } 1187 1188 return round($size, $dec) . ' ' . $sizes[$i]; 1189} 1190 1191/** 1192 * return an obfuscated email address in line with $conf['mailguard'] setting 1193 * 1194 * @author Harry Fuecks <hfuecks@gmail.com> 1195 * @author Christopher Smith <chris@jalakai.co.uk> 1196 */ 1197function obfuscate($email) { 1198 global $conf; 1199 1200 switch ($conf['mailguard']) { 1201 case 'visible' : 1202 $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] '); 1203 return strtr($email, $obfuscate); 1204 1205 case 'hex' : 1206 $encode = ''; 1207 for ($x=0; $x < strlen($email); $x++) $encode .= '&#x' . bin2hex($email{$x}).';'; 1208 return $encode; 1209 1210 case 'none' : 1211 default : 1212 return $email; 1213 } 1214} 1215 1216/** 1217 * Let us know if a user is tracking a page or a namespace 1218 * 1219 * @author Andreas Gohr <andi@splitbrain.org> 1220 */ 1221function is_subscribed($id,$uid,$ns=false){ 1222 if(!$ns) { 1223 $file=metaFN($id,'.mlist'); 1224 } else { 1225 if(!getNS($id)) { 1226 $file = metaFN(getNS($id),'.mlist'); 1227 } else { 1228 $file = metaFN(getNS($id),'/.mlist'); 1229 } 1230 } 1231 if (@file_exists($file)) { 1232 $mlist = file($file); 1233 $pos = array_search($uid."\n",$mlist); 1234 return is_int($pos); 1235 } 1236 1237 return false; 1238} 1239 1240/** 1241 * Return a string with the email addresses of all the 1242 * users subscribed to a page 1243 * 1244 * @author Steven Danz <steven-danz@kc.rr.com> 1245 */ 1246function subscriber_addresslist($id,$self=true){ 1247 global $conf; 1248 global $auth; 1249 1250 if (!$conf['subscribers']) return ''; 1251 1252 $users = array(); 1253 $emails = array(); 1254 1255 // load the page mlist file content 1256 $mlist = array(); 1257 $file=metaFN($id,'.mlist'); 1258 if (@file_exists($file)) { 1259 $mlist = file($file); 1260 foreach ($mlist as $who) { 1261 $who = rtrim($who); 1262 if(!$self && $who == $_SERVER['REMOTE_USER']) continue; 1263 $users[$who] = true; 1264 } 1265 } 1266 1267 // load also the namespace mlist file content 1268 $ns = getNS($id); 1269 while ($ns) { 1270 $nsfile = metaFN($ns,'/.mlist'); 1271 if (@file_exists($nsfile)) { 1272 $mlist = file($nsfile); 1273 foreach ($mlist as $who) { 1274 $who = rtrim($who); 1275 if(!$self && $who == $_SERVER['REMOTE_USER']) continue; 1276 $users[$who] = true; 1277 } 1278 } 1279 $ns = getNS($ns); 1280 } 1281 // root namespace 1282 $nsfile = metaFN('','.mlist'); 1283 if (@file_exists($nsfile)) { 1284 $mlist = file($nsfile); 1285 foreach ($mlist as $who) { 1286 $who = rtrim($who); 1287 if(!$self && $who == $_SERVER['REMOTE_USER']) continue; 1288 $users[$who] = true; 1289 } 1290 } 1291 if(!empty($users)) { 1292 foreach (array_keys($users) as $who) { 1293 $info = $auth->getUserData($who); 1294 if($info === false) continue; 1295 $level = auth_aclcheck($id,$who,$info['grps']); 1296 if ($level >= AUTH_READ) { 1297 if (strcasecmp($info['mail'],$conf['notify']) != 0) { 1298 $emails[] = $info['mail']; 1299 } 1300 } 1301 } 1302 } 1303 1304 return implode(',',$emails); 1305} 1306 1307/** 1308 * Removes quoting backslashes 1309 * 1310 * @author Andreas Gohr <andi@splitbrain.org> 1311 */ 1312function unslash($string,$char="'"){ 1313 return str_replace('\\'.$char,$char,$string); 1314} 1315 1316/** 1317 * Convert php.ini shorthands to byte 1318 * 1319 * @author <gilthans dot NO dot SPAM at gmail dot com> 1320 * @link http://de3.php.net/manual/en/ini.core.php#79564 1321 */ 1322function php_to_byte($v){ 1323 $l = substr($v, -1); 1324 $ret = substr($v, 0, -1); 1325 switch(strtoupper($l)){ 1326 case 'P': 1327 $ret *= 1024; 1328 case 'T': 1329 $ret *= 1024; 1330 case 'G': 1331 $ret *= 1024; 1332 case 'M': 1333 $ret *= 1024; 1334 case 'K': 1335 $ret *= 1024; 1336 break; 1337 } 1338 return $ret; 1339} 1340 1341/** 1342 * Wrapper around preg_quote adding the default delimiter 1343 */ 1344function preg_quote_cb($string){ 1345 return preg_quote($string,'/'); 1346} 1347 1348/** 1349 * Shorten a given string by removing data from the middle 1350 * 1351 * You can give the string in two parts, teh first part $keep 1352 * will never be shortened. The second part $short will be cut 1353 * in the middle to shorten but only if at least $min chars are 1354 * left to display it. Otherwise it will be left off. 1355 * 1356 * @param string $keep the part to keep 1357 * @param string $short the part to shorten 1358 * @param int $max maximum chars you want for the whole string 1359 * @param int $min minimum number of chars to have left for middle shortening 1360 * @param string $char the shortening character to use 1361 */ 1362function shorten($keep,$short,$max,$min=9,$char='⌇'){ 1363 $max = $max - utf8_strlen($keep); 1364 if($max < $min) return $keep; 1365 $len = utf8_strlen($short); 1366 if($len <= $max) return $keep.$short; 1367 $half = floor($max/2); 1368 return $keep.utf8_substr($short,0,$half-1).$char.utf8_substr($short,$len-$half); 1369} 1370 1371/** 1372 * Return the users realname or e-mail address for use 1373 * in page footer and recent changes pages 1374 * 1375 * @author Andy Webber <dokuwiki AT andywebber DOT com> 1376 */ 1377function editorinfo($username){ 1378 global $conf; 1379 global $auth; 1380 1381 switch($conf['showuseras']){ 1382 case 'username': 1383 case 'email': 1384 case 'email_link': 1385 if($auth) $info = $auth->getUserData($username); 1386 break; 1387 default: 1388 return hsc($username); 1389 } 1390 1391 if(isset($info) && $info) { 1392 switch($conf['showuseras']){ 1393 case 'username': 1394 return hsc($info['name']); 1395 case 'email': 1396 return obfuscate($info['mail']); 1397 case 'email_link': 1398 $mail=obfuscate($info['mail']); 1399 return '<a href="mailto:'.$mail.'">'.$mail.'</a>'; 1400 default: 1401 return hsc($username); 1402 } 1403 } else { 1404 return hsc($username); 1405 } 1406} 1407 1408/** 1409 * Returns the path to a image file for the currently chosen license. 1410 * When no image exists, returns an empty string 1411 * 1412 * @author Andreas Gohr <andi@splitbrain.org> 1413 * @param string $type - type of image 'badge' or 'button' 1414 */ 1415function license_img($type){ 1416 global $license; 1417 global $conf; 1418 if(!$conf['license']) return ''; 1419 if(!is_array($license[$conf['license']])) return ''; 1420 $lic = $license[$conf['license']]; 1421 $try = array(); 1422 $try[] = 'lib/images/license/'.$type.'/'.$conf['license'].'.png'; 1423 $try[] = 'lib/images/license/'.$type.'/'.$conf['license'].'.gif'; 1424 if(substr($conf['license'],0,3) == 'cc-'){ 1425 $try[] = 'lib/images/license/'.$type.'/cc.png'; 1426 } 1427 foreach($try as $src){ 1428 if(@file_exists(DOKU_INC.$src)) return $src; 1429 } 1430 return ''; 1431} 1432 1433/** 1434 * Checks if the given amount of memory is available 1435 * 1436 * If the memory_get_usage() function is not available the 1437 * function just assumes $bytes of already allocated memory 1438 * 1439 * @param int $mem Size of memory you want to allocate in bytes 1440 * @param int $used already allocated memory (see above) 1441 * @author Filip Oscadal <webmaster@illusionsoftworks.cz> 1442 * @author Andreas Gohr <andi@splitbrain.org> 1443 */ 1444function is_mem_available($mem,$bytes=1048576){ 1445 $limit = trim(ini_get('memory_limit')); 1446 if(empty($limit)) return true; // no limit set! 1447 1448 // parse limit to bytes 1449 $limit = php_to_byte($limit); 1450 1451 // get used memory if possible 1452 if(function_exists('memory_get_usage')){ 1453 $used = memory_get_usage(); 1454 } 1455 1456 if($used+$mem > $limit){ 1457 return false; 1458 } 1459 1460 return true; 1461} 1462 1463/** 1464 * Send a HTTP redirect to the browser 1465 * 1466 * Works arround Microsoft IIS cookie sending bug. Exits the script. 1467 * 1468 * @link http://support.microsoft.com/kb/q176113/ 1469 * @author Andreas Gohr <andi@splitbrain.org> 1470 */ 1471function send_redirect($url){ 1472 // always close the session 1473 session_write_close(); 1474 1475 // check if running on IIS < 6 with CGI-PHP 1476 if( isset($_SERVER['SERVER_SOFTWARE']) && isset($_SERVER['GATEWAY_INTERFACE']) && 1477 (strpos($_SERVER['GATEWAY_INTERFACE'],'CGI') !== false) && 1478 (preg_match('|^Microsoft-IIS/(\d)\.\d$|', trim($_SERVER['SERVER_SOFTWARE']), $matches)) && 1479 $matches[1] < 6 ){ 1480 header('Refresh: 0;url='.$url); 1481 }else{ 1482 header('Location: '.$url); 1483 } 1484 exit; 1485} 1486 1487//Setup VIM: ex: et ts=2 enc=utf-8 : 1488