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