| Textpattern | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /* 4 $HeadURL: https://textpattern.googlecode.com/svn/releases/4.5.4/source/textpattern/lib/txplib_misc.php $ 5 $LastChangedRevision: 4816 $ 6 */ 7 8 // ------------------------------------------------------------- 9 function deNull($in) 10 { 11 return (is_array($in) ? doArray($in, 'deNull') : strtr($in, array("\0" => ''))); 12 } 13 14 // ------------------------------------------------------------- 15 function deCRLF($in) 16 { 17 return (is_array($in) ? doArray($in, 'deCRLF') : strtr($in, array("\n" => '', "\r" => ''))); 18 } 19 20 // ------------------------------------------------------------- 21 function doArray($in,$function) 22 { 23 if(is_array($in)) 24 { 25 return array_map($function, $in); 26 } 27 28 if(is_array($function)) 29 { 30 return call_user_func($function, $in); 31 } 32 33 return $function($in); 34 } 35 36 // ------------------------------------------------------------- 37 function doStrip($in) 38 { 39 return is_array($in) ? doArray($in, 'doStrip') : doArray($in, 'stripslashes'); 40 } 41 42 // ------------------------------------------------------------- 43 function doStripTags($in) 44 { 45 return is_array($in) ? doArray($in, 'doStripTags') : doArray($in,'strip_tags'); 46 } 47 48 // ------------------------------------------------------------- 49 function doDeEnt($in) 50 { 51 return doArray($in,'deEntBrackets'); 52 } 53 54 // ------------------------------------------------------------- 55 function deEntBrackets($in) 56 { 57 $array = array( 58 '<' => '<', 59 '<' => '<', 60 '<' => '<', 61 '>' => '>', 62 '>' => '>', 63 '>' => '>' 64 ); 65 66 foreach($array as $k=>$v){ 67 $in = preg_replace("/".preg_quote($k)."/i",$v, $in); 68 } 69 return $in; 70 } 71 72 // ------------------------------------------------------------- 73 function doSlash($in) 74 { 75 return doArray($in,'safe_escape'); 76 } 77 78 /** 79 * A shell for htmlspecialchars() with $flags defaulting to ENT_QUOTES 80 * 81 * @param string $string The string being converted. 82 * @param int $flags A bitmask of one or more flags. The default is ENT_QUOTES 83 * @param string $encoding Defines encoding used in conversion. The default is UTF-8. 84 * @param bool $double_encode When double_encode is turned off PHP will not encode existing html entities, the default is to convert everything. 85 * @return string 86 * @see http://www.php.net/manual/function.htmlspecialchars.php 87 * @since 4.5.0 88 */ 89 function txpspecialchars($string, $flags = ENT_QUOTES, $encoding = 'UTF-8', $double_encode = true) 90 { 91 // Ignore ENT_HTML5 and ENT_XHTML for now. 92 // ENT_HTML5 and ENT_XHTML are defined in PHP 5.4+ but we consistently encode single quotes as ' in any doctype. 93 // global $prefs; 94 // static $h5 = null; 95 // if (defined(ENT_HTML5)) { 96 // if ($h5 === null) { 97 // $h5 = ($prefs['doctype'] == 'html5' && txpinterface == 'public'); 98 // } 99 // if ($h5) { 100 // $flags = ($flags | ENT_HTML5) & ~ENT_HTML401; 101 // } 102 // } 103 return htmlspecialchars($string, $flags, $encoding, $double_encode); 104 } 105 106 // ------------------------------------------------------------- 107 function doSpecial($in) 108 { 109 return doArray($in,'txpspecialchars'); 110 } 111 112 // ------------------------------------------------------------- 113 function _null($a) 114 { 115 return NULL; 116 } 117 // ------------------------------------------------------------- 118 function array_null($in) 119 { 120 return array_map('_null', $in); 121 } 122 123 // ------------------------------------------------------------- 124 function escape_title($title) 125 { 126 return strtr($title, 127 array( 128 '<' => '<', 129 '>' => '>', 130 "'" => ''', 131 '"' => '"', 132 ) 133 ); 134 } 135 136 /** 137 * Escape special string characters like \n or \\ for JavaScript. 138 * 139 * @param string $js JavaScript input 140 * @return string Escaped JavaScript 141 * @since 4.4 142 */ 143 144 function escape_js($js) 145 { 146 return addcslashes($js, "\\\'\"\n\r"); 147 } 148 149 // ------------------------------------------------------------- 150 // deprecated in 4.2.0 151 function escape_output($str) 152 { 153 trigger_error(gTxt('deprecated_function_with', array('{name}' => __FUNCTION__, '{with}' => 'txpspecialchars')), E_USER_NOTICE); 154 return txpspecialchars($str); 155 } 156 157 // ------------------------------------------------------------- 158 // deprecated in 4.2.0 159 function escape_tags($str) 160 { 161 trigger_error(gTxt('deprecated_function', array('{name}' => __FUNCTION__)), E_USER_NOTICE); 162 return strtr($str, 163 array( 164 '<' => '<', 165 '>' => '>', 166 ) 167 ); 168 } 169 170 // ------------------------------------------------------------- 171 function escape_cdata($str) 172 { 173 return '<![CDATA['.str_replace(']]>', ']]]><![CDATA[]>', $str).']]>'; 174 } 175 176 //------------------------------------------------------------- 177 function gTxt($var, $atts=array(), $escape='html') 178 { 179 global $textarray; 180 181 if (!is_array($atts)) { 182 $atts = array(); 183 } 184 185 if ($escape == 'html') 186 { 187 foreach ($atts as $key => $value) 188 { 189 $atts[$key] = txpspecialchars($value); 190 } 191 } 192 193 $v = strtolower($var); 194 if (isset($textarray[$v])) { 195 $out = $textarray[$v]; 196 if ($out !== '') return strtr($out, $atts); 197 } 198 199 if ($atts) 200 return $var.': '.join(', ', $atts); 201 return $var; 202 } 203 204 //------------------------------------------------------------- 205 /** 206 * Localize client scripts 207 * 208 * @param string|array $var scalar or array of string keys 209 * @param array $atts array or array of arrays of variable substitution pairs 210 * @since 4.5.0 211 */ 212 function gTxtScript($var, $atts = array()) 213 { 214 global $textarray_script; 215 216 if (!is_array($textarray_script)) { 217 $textarray_script = array(); 218 } 219 220 $data = (is_array($var) ? array_map('gTxt', $var, $atts) : (array)gTxt($var, $atts)); 221 $textarray_script = $textarray_script + array_combine((array)$var, $data); 222 } 223 224 //------------------------------------------------------------- 225 function gTime($timestamp) 226 { 227 return safe_strftime('%d %b %Y %X', $timestamp); 228 } 229 230 // ------------------------------------------------------------- 231 function dmp() 232 { 233 static $f = FALSE; 234 235 if(defined('txpdmpfile')) 236 { 237 global $prefs; 238 239 if(!$f) $f = fopen($prefs['tempdir'].'/'.txpdmpfile, 'a'); 240 241 $stack = get_caller(); 242 fwrite($f, "\n[".$stack[0].t.safe_strftime('iso8601')."]\n"); 243 } 244 245 $a = func_get_args(); 246 247 if(!$f) echo "<pre>".n; 248 249 foreach ($a as $thing) 250 { 251 $out = is_scalar($thing) ? strval($thing) : var_export($thing, true); 252 253 if ($f) 254 { 255 fwrite($f, $out."\n"); 256 } 257 else 258 { 259 echo txpspecialchars($out), n; 260 } 261 } 262 263 if(!$f) echo "</pre>".n; 264 } 265 266 // ------------------------------------------------------------- 267 function load_lang($lang) 268 { 269 foreach(array($lang, 'en-gb') as $lang_code) 270 { 271 $rs = (txpinterface == 'admin') 272 ? safe_rows('name, data','txp_lang',"lang='".doSlash($lang_code)."'") 273 : safe_rows('name, data','txp_lang',"lang='".doSlash($lang_code)."' AND ( event='public' OR event='common')"); 274 275 if (!empty($rs)) break; 276 } 277 278 $out = array(); 279 280 if (!empty($rs)) 281 { 282 foreach ($rs as $a) 283 { 284 if (!empty($a['name'])) { 285 $out[$a['name']] = $a['data']; 286 } 287 } 288 }else{ 289 #backward compatibility stuff. Remove when necessary. 290 $filename = is_file(txpath.'/lang/'.$lang.'.txt') 291 ? txpath.'/lang/'.$lang.'.txt' 292 : txpath.'/lang/en-gb.txt'; 293 294 $file = @fopen($filename, "r"); 295 if ($file) { 296 while (!feof($file)) { 297 $line = fgets($file, 4096); 298 if($line[0]=='#') continue; 299 @list($name,$val) = explode(' => ',trim($line)); 300 $out[$name] = $val; 301 } 302 @fclose($filename); 303 } 304 } 305 306 return $out; 307 } 308 309 // ------------------------------------------------------------- 310 function load_lang_dates($lang) 311 { 312 $filename = is_file(txpath.'/lang/'.$lang.'_dates.txt')? 313 txpath.'/lang/'.$lang.'_dates.txt': 314 txpath.'/lang/en-gb_dates.txt'; 315 $file = @file(txpath.'/lang/'.$lang.'_dates.txt','r'); 316 if(is_array($file)) { 317 foreach($file as $line) { 318 if($line[0]=='#' || strlen($line) < 2) continue; 319 list($name,$val) = explode('=>',$line,2); 320 $out[trim($name)] = trim($val); 321 } 322 return $out; 323 } 324 return false; 325 } 326 // ------------------------------------------------------------- 327 328 function load_lang_event($event) 329 { 330 $lang = LANG; 331 332 $installed = (false !== safe_field('name', 'txp_lang',"lang='".doSlash($lang)."' limit 1")); 333 334 $lang_code = ($installed)? $lang : 'en-gb'; 335 336 $rs = safe_rows_start('name, data','txp_lang',"lang='".doSlash($lang_code)."' AND event='".doSlash($event)."'"); 337 338 $out = array(); 339 340 if ($rs && !empty($rs)) 341 { 342 while ($a = nextRow($rs)) 343 { 344 $out[$a['name']] = $a['data']; 345 } 346 } 347 return ($out) ? $out : ''; 348 } 349 350 // ------------------------------------------------------------- 351 // deprecated in 4.3.0 352 function check_privs() 353 { 354 trigger_error(gTxt('deprecated_function_with', array('{name}' => __FUNCTION__, '{with}' => 'require_privs')), E_USER_NOTICE); 355 global $txp_user; 356 $privs = safe_field("privs", "txp_users", "name='".doSlash($txp_user)."'"); 357 $args = func_get_args(); 358 if(!in_array($privs,$args)) { 359 exit(pageTop('Restricted').'<p style="margin-top:3em;text-align:center">'. 360 gTxt('restricted_area').'</p>'); 361 } 362 } 363 364 // ------------------------------------------------------------- 365 function add_privs($res, $perm = '1') // perm = '1,2,3' 366 { 367 global $txp_permissions; 368 // Don't let them override privs that exist 369 if (!isset($txp_permissions[$res])) 370 $txp_permissions[$res] = $perm; 371 } 372 373 // ------------------------------------------------------------- 374 function has_privs($res, $user='') 375 { 376 global $txp_user, $txp_permissions; 377 static $privs; 378 379 // If no user name is supplied, assume the current login name 380 if (empty($user)) 381 $user = $txp_user; 382 383 if (!isset($privs[$user])) 384 { 385 $privs[$user] = safe_field("privs", "txp_users", "name='".doSlash($user)."'"); 386 } 387 388 if (isset($txp_permissions[$res])) 389 { 390 return in_array($privs[$user], explode(',', $txp_permissions[$res])); 391 } 392 393 else 394 { 395 return false; 396 } 397 } 398 399 // ------------------------------------------------------------- 400 function require_privs($res, $user='') 401 { 402 if (!has_privs($res, $user)) 403 exit(pageTop('Restricted').'<p class="restricted-area">'. 404 gTxt('restricted_area').'</p>'); 405 } 406 407 /** 408 * Get list of users having access to a resource 409 * 410 * @param string $res resource, e.g. 'article.edit.published' 411 * @return array 412 * @since 4.5.0 413 */ 414 function the_privileged($res) 415 { 416 global $txp_permissions; 417 if (isset($txp_permissions[$res])) { 418 return safe_column('name', 'txp_users', "FIND_IN_SET(privs, '{$txp_permissions[$res]}') order by name asc"); 419 } else { 420 return array(); 421 } 422 } 423 424 // ------------------------------------------------------------- 425 function get_groups() 426 { 427 global $txp_groups; 428 return doArray($txp_groups, 'gTxt'); 429 } 430 431 // ------------------------------------------------------------- 432 function sizeImage($name) 433 { 434 $size = @getimagesize($name); 435 return(is_array($size)) ? $size[3] : false; 436 } 437 438 // ------------------------------------------------------------- 439 function gps($thing) // checks GET and POST for a named variable, or creates it blank 440 { 441 $out = ''; 442 if (isset($_GET[$thing])) { 443 if (MAGIC_QUOTES_GPC) { 444 $out = doStrip($_GET[$thing]); 445 } else { 446 $out = $_GET[$thing]; 447 } 448 449 $out = doArray( $out, 'deCRLF' ); # Remove CRLF from Get parameters 450 } elseif (isset($_POST[$thing])) { 451 if (MAGIC_QUOTES_GPC) { 452 $out = doStrip($_POST[$thing]); 453 } else { 454 $out = $_POST[$thing]; # CRLF ok in posted vars 455 } 456 } 457 458 $out = doArray($out, 'deNull'); # Remove Nulls to avoid string truncations in C calls (ie. All the filesystem routines) 459 460 return $out; 461 } 462 463 // ------------------------------------------------------------- 464 function gpsa($array) // performs gps() on an array of variable names 465 { 466 if(is_array($array)) { 467 $out = array(); 468 foreach($array as $a) { 469 $out[$a] = gps($a); 470 } 471 return $out; 472 } 473 return false; 474 } 475 476 // ------------------------------------------------------------- 477 function ps($thing) // checks POST for a named variable, or creates it blank 478 { 479 $out = ''; 480 if (isset($_POST[$thing])) { 481 if (MAGIC_QUOTES_GPC) { 482 $out = doStrip($_POST[$thing]); 483 } else { 484 $out = $_POST[$thing]; 485 } 486 } 487 488 $out = doArray($out, 'deNull'); 489 490 return $out; 491 } 492 493 // ------------------------------------------------------------- 494 function psa($array) // performs ps on an array of variable names 495 { 496 foreach($array as $a) { 497 $out[$a] = ps($a); 498 } 499 return $out; 500 } 501 502 // ------------------------------------------------------------- 503 function psas($array) // same as above, but does strip_tags on post values 504 { 505 foreach($array as $a) { 506 $out[$a] = doStripTags(ps($a)); 507 } 508 return $out; 509 } 510 511 // ------------------------------------------------------------- 512 function stripPost() 513 { 514 if (isset($_POST)) { 515 if (MAGIC_QUOTES_GPC) { 516 return doStrip($_POST); 517 } else { 518 return $_POST; 519 } 520 } 521 return ''; 522 } 523 524 // ------------------------------------------------------------- 525 function serverSet($thing) // Get a var from $_SERVER global array, or create it 526 { 527 return (isset($_SERVER[$thing])) ? $_SERVER[$thing] : ''; 528 } 529 530 // ------------------------------------------------------------- 531 function remote_addr() 532 { 533 $ip = serverSet('REMOTE_ADDR'); 534 if (($ip == '127.0.0.1' || $ip == '::1' || $ip == '::ffff:127.0.0.1' || $ip == serverSet('SERVER_ADDR')) && serverSet('HTTP_X_FORWARDED_FOR')) { 535 $ips = explode(', ', serverSet('HTTP_X_FORWARDED_FOR')); 536 $ip = $ips[0]; 537 } 538 return $ip; 539 } 540 541 // ------------------------------------------------------------- 542 function pcs($thing) // Get a var from POST or COOKIE; if not, create it 543 { 544 if (isset($_COOKIE["txp_".$thing])) { 545 if (MAGIC_QUOTES_GPC) { 546 return doStrip($_COOKIE["txp_".$thing]); 547 } else return $_COOKIE["txp_".$thing]; 548 } elseif (isset($_POST[$thing])) { 549 if (MAGIC_QUOTES_GPC) { 550 return doStrip($_POST[$thing]); 551 } else return $_POST[$thing]; 552 } 553 return ''; 554 } 555 556 // ------------------------------------------------------------- 557 function cs($thing) // Get a var from COOKIE; if not, create it 558 { 559 if (isset($_COOKIE[$thing])) { 560 if (MAGIC_QUOTES_GPC) { 561 return doStrip($_COOKIE[$thing]); 562 } else return $_COOKIE[$thing]; 563 } 564 return ''; 565 } 566 567 // ------------------------------------------------------------- 568 function yes_no($status) 569 { 570 return ($status==0) ? (gTxt('no')) : (gTxt('yes')); 571 } 572 573 // ------------------------------------------------------------- 574 function getmicrotime() 575 { 576 list($usec, $sec) = explode(" ",microtime()); 577 return ((float)$usec + (float)$sec); 578 } 579 580 // ------------------------------------------------------------- 581 function load_plugin($name, $force=false) 582 { 583 global $plugins, $plugins_ver, $prefs, $txp_current_plugin; 584 585 if (is_array($plugins) and in_array($name,$plugins)) { 586 return true; 587 } 588 589 if (!empty($prefs['plugin_cache_dir'])) { 590 $dir = rtrim($prefs['plugin_cache_dir'], '/') . '/'; 591 # in case it's a relative path 592 if (!is_dir($dir)) 593 $dir = rtrim(realpath(txpath.'/'.$dir), '/') . '/'; 594 if (is_file($dir . $name . '.php')) { 595 $plugins[] = $name; 596 set_error_handler("pluginErrorHandler"); 597 if (isset($txp_current_plugin)) $txp_parent_plugin = $txp_current_plugin; 598 $txp_current_plugin = $name; 599 include($dir . $name . '.php'); 600 $txp_current_plugin = (isset($txp_parent_plugin) ? $txp_parent_plugin : NULL); 601 $plugins_ver[$name] = @$plugin['version']; 602 restore_error_handler(); 603 return true; 604 } 605 } 606 607 $rs = safe_row("name,code,version","txp_plugin", ($force ? '' : 'status = 1 AND '). "name='".doSlash($name)."'"); 608 if ($rs) { 609 $plugins[] = $rs['name']; 610 $plugins_ver[$rs['name']] = $rs['version']; 611 612 set_error_handler("pluginErrorHandler"); 613 if (isset($txp_current_plugin)) $txp_parent_plugin = $txp_current_plugin; 614 $txp_current_plugin = $rs['name']; 615 eval($rs['code']); 616 $txp_current_plugin = (isset($txp_parent_plugin) ? $txp_parent_plugin : NULL); 617 restore_error_handler(); 618 619 return true; 620 } 621 622 return false; 623 } 624 625 // ------------------------------------------------------------- 626 function require_plugin($name) 627 { 628 if (!load_plugin($name)) { 629 trigger_error("Unable to include required plugin \"{$name}\"",E_USER_ERROR); 630 return false; 631 } 632 return true; 633 } 634 635 // ------------------------------------------------------------- 636 function include_plugin($name) 637 { 638 if (!load_plugin($name)) { 639 trigger_error("Unable to include plugin \"{$name}\"",E_USER_WARNING); 640 return false; 641 } 642 return true; 643 } 644 645 // ------------------------------------------------------------- 646 function pluginErrorHandler($errno, $errstr, $errfile, $errline) 647 { 648 global $production_status; 649 650 $error = array( E_WARNING => "Warning", E_NOTICE => "Notice", E_RECOVERABLE_ERROR => "Catchable fatal error", 651 E_USER_ERROR => "User_Error", E_USER_WARNING => "User_Warning", E_USER_NOTICE => "User_Notice"); 652 653 if (!($errno & error_reporting())) return; 654 if ($production_status == 'live' || ($production_status != 'debug' && $errno == E_USER_NOTICE)) return; 655 656 global $txp_current_plugin, $production_status; 657 printf ("<pre>".gTxt('plugin_load_error').' <b>%s</b> -> <b>%s: %s on line %s</b></pre>', 658 $txp_current_plugin, $error[$errno], $errstr, $errline); 659 if ($production_status == 'debug') 660 print "\n<pre style=\"padding-left: 2em;\" class=\"backtrace\"><code>".txpspecialchars(join("\n", get_caller(10)))."</code></pre>"; 661 } 662 663 // ------------------------------------------------------------- 664 function tagErrorHandler($errno, $errstr, $errfile, $errline) 665 { 666 global $production_status; 667 668 $error = array( E_WARNING => "Warning", E_NOTICE => "Notice", E_RECOVERABLE_ERROR => "Textpattern Catchable fatal error", 669 E_USER_ERROR => "Textpattern Error", E_USER_WARNING => "Textpattern Warning", E_USER_NOTICE => "Textpattern Notice"); 670 671 if (!($errno & error_reporting())) return; 672 if ($production_status == 'live') return; 673 674 global $txp_current_tag, $txp_current_form, $pretext; 675 $page = (empty($pretext['page']) ? gTxt('none') : $pretext['page']); 676 if (!isset($txp_current_form)) $txp_current_form = gTxt('none'); 677 $locus = gTxt('while_parsing_page_form', array('{page}' => txpspecialchars($page), '{form}' => txpspecialchars($txp_current_form))); 678 679 printf ("<pre>".gTxt('tag_error').' <b>%s</b> -> <b> %s: %s %s</b></pre>', 680 txpspecialchars($txp_current_tag), $error[$errno], $errstr, $locus ); 681 if ($production_status == 'debug') 682 { 683 print "\n<pre style=\"padding-left: 2em;\" class=\"backtrace\"><code>".txpspecialchars(join("\n", get_caller(10)))."</code></pre>"; 684 685 $trace_msg = gTxt('tag_error').' '.$txp_current_tag.' -> '.$error[$errno].': '.$errstr.' '.$locus; 686 trace_add( $trace_msg ); 687 } 688 } 689 690 // ------------------------------------------------------------- 691 function feedErrorHandler($errno, $errstr, $errfile, $errline) 692 { 693 global $production_status; 694 695 if ($production_status != 'debug') return; 696 697 return tagErrorHandler($errno, $errstr, $errfile, $errline); 698 } 699 700 // ------------------------------------------------------------- 701 function adminErrorHandler($errno, $errstr, $errfile, $errline) 702 { 703 global $production_status, $theme, $event, $step; 704 705 if (!error_reporting()) return; 706 707 // When even a minimum environment is missing... 708 if (!isset($production_status)) { 709 echo '<pre>'.gTxt('internal_error').' "'.$errstr.'"'.n."in $errfile at line $errline".'</pre>'; 710 return; 711 } 712 713 if ($production_status == 'live' || ($production_status != 'debug' && $errno == E_USER_NOTICE)) { 714 $backtrace = $msg = ''; 715 } else { 716 $backtrace = ''; 717 $msg = gTxt('internal_error'); 718 719 if (has_privs('debug.verbose')) { 720 $msg .= ' "'.$errstr.'"'; 721 }; 722 723 if (($production_status == 'debug')) { 724 if (has_privs('debug.backtrace')) { 725 $msg .= n."in $errfile at line $errline"; 726 $backtrace = join(n, get_caller(5,1)); 727 } 728 } 729 } 730 731 $httpstatus = in_array($errno, array(E_ERROR, E_USER_ERROR)) ? '500' : '200'; 732 $out = "$msg.\n$backtrace"; 733 734 if (http_accept_format('html')) { 735 if (!empty($backtrace)) { 736 echo "<pre>$msg.</pre>". 737 n.'<pre style="padding-left: 2em;" class="backtrace"><code>'. 738 txpspecialchars($backtrace).'</code></pre>'; 739 } elseif (!empty($msg)) { 740 echo is_object($theme) ? $theme->announce(array($out, E_ERROR), true) : "<pre>$out</pre>"; 741 } 742 $c = array('in' => '', 'out' => ''); 743 } elseif (http_accept_format('js')) { 744 send_script_response( 745 is_object($theme) && !empty($msg) ? 746 $theme->announce_async(array($out, E_ERROR), true) : 747 "/* $out */" 748 ); 749 $c = array('in' => '/* ', 'out' => ' */'); 750 } elseif (http_accept_format('xml')) { 751 send_xml_response(array('http-status' => $httpstatus, 'internal_error' => "$out")); 752 $c = array('in' => '<!-- ', 'out' => ' -->'); 753 } else { 754 txp_die($msg, 500); 755 } 756 757 if ($production_status != 'live' && in_array($errno, array(E_ERROR, E_USER_ERROR))) { 758 die($c['in'].gTxt('get_off_my_lawn', array('{event}' => $event, '{step}' => $step)).$c['out']); 759 } 760 } 761 762 // ------------------------------------------------------------- 763 function publicErrorHandler($errno, $errstr, $errfile, $errline) 764 { 765 global $production_status; 766 767 $error = array( E_WARNING => "Warning", E_NOTICE => "Notice", E_USER_ERROR => "Textpattern Error", 768 E_USER_WARNING => "Textpattern Warning", E_USER_NOTICE => "Textpattern Notice"); 769 770 if (!($errno & error_reporting())) return; 771 if ($production_status == 'live' || ($production_status != 'debug' && $errno == E_USER_NOTICE)) return; 772 773 global $production_status; 774 printf ("<pre>".gTxt('general_error').' <b>%s: %s on line %s</b></pre>', 775 $error[$errno], $errstr, $errline); 776 if ($production_status == 'debug') 777 print "\n<pre style=\"padding-left: 2em;\" class=\"backtrace\"><code>".txpspecialchars(join("\n", get_caller(10)))."</code></pre>"; 778 } 779 780 // ------------------------------------------------------------- 781 function load_plugins($type=0) 782 { 783 global $prefs, $plugins, $plugins_ver, $app_mode; 784 785 if (!is_array($plugins)) $plugins = array(); 786 787 if (!empty($prefs['plugin_cache_dir'])) { 788 $dir = rtrim($prefs['plugin_cache_dir'], '/') . '/'; 789 // in case it's a relative path 790 if (!is_dir($dir)) 791 $dir = rtrim(realpath(txpath.'/'.$dir), '/') . '/'; 792 $files = glob($dir.'*.php'); 793 if ($files) { 794 natsort($files); 795 foreach ($files as $f) { 796 load_plugin(basename($f, '.php')); 797 } 798 } 799 } 800 801 $admin = ($app_mode == 'async' && !AJAXALLY_CHALLENGED ? '4,5' : '1,3,4,5'); 802 $where = 'status = 1 AND type IN ('.($type ? $admin : '0,1,5').')'; 803 804 $rs = safe_rows("name, code, version", "txp_plugin", $where.' order by load_order'); 805 if ($rs) { 806 $old_error_handler = set_error_handler("pluginErrorHandler"); 807 foreach($rs as $a) { 808 if (!in_array($a['name'],$plugins)) { 809 $plugins[] = $a['name']; 810 $plugins_ver[$a['name']] = $a['version']; 811 $GLOBALS['txp_current_plugin'] = $a['name']; 812 $eval_ok = eval($a['code']); 813 if ($eval_ok === FALSE) 814 echo gTxt('plugin_load_error_above').strong($a['name']).n.br; 815 unset($GLOBALS['txp_current_plugin']); 816 } 817 } 818 restore_error_handler(); 819 } 820 } 821 822 // ------------------------------------------------------------- 823 function register_callback($func, $event, $step='', $pre=0) 824 { 825 global $plugin_callback; 826 827 $plugin_callback[] = array('function'=>$func, 'event'=>$event, 'step'=>$step, 'pre'=>$pre); 828 } 829 830 // ------------------------------------------------------------- 831 function register_page_extension($func, $event, $step='', $top=0) 832 { 833 # For now this just does the same as register_callback 834 register_callback($func, $event, $step, $top); 835 } 836 837 // ------------------------------------------------------------- 838 function callback_event($event, $step='', $pre=0) 839 { 840 global $plugin_callback, $production_status; 841 842 if (!is_array($plugin_callback)) 843 return ''; 844 845 $return_value = ''; 846 847 // any payload parameters? 848 $argv = func_get_args(); 849 $argv = (count($argv) > 3) ? array_slice($argv, 3) : array(); 850 851 foreach ($plugin_callback as $c) { 852 if ($c['event'] == $event and (empty($c['step']) or $c['step'] == $step) and $c['pre'] == $pre) { 853 if (is_callable($c['function'])) { 854 $return_value .= call_user_func_array($c['function'], array('event' => $event, 'step' => $step) + $argv); 855 } elseif ($production_status == 'debug') { 856 trigger_error(gTxt('unknown_callback_function', array('{function}' => callback_tostring($c['function']))), E_USER_WARNING); 857 } 858 } 859 } 860 return $return_value; 861 } 862 863 864 // ------------------------------------------------------------- 865 /** 866 * Call an event's callback with two optional byref parameters 867 * @param string $event 868 * @param string $step 869 * @param boolean $pre 0|1 870 * @param mixed $data optional arguments for event handlers 871 * @param mixed $options optional arguments for event handlers 872 * @return array collection of return values from event handlers 873 * @since 4.5.0 874 */ 875 function callback_event_ref($event, $step='', $pre=0, &$data=null, &$options=null) 876 { 877 global $plugin_callback, $production_status; 878 879 if (!is_array($plugin_callback)) 880 return array(); 881 882 $return_value = array(); 883 884 foreach ($plugin_callback as $c) { 885 if ($c['event'] == $event and (empty($c['step']) or $c['step'] == $step) and $c['pre'] == $pre) { 886 if (is_callable($c['function'])) { 887 // cannot call event handler via call_user_func() as this would dereference all arguments. 888 // side effect: callback handler *must* be ordinary function, *must not* be class method in PHP <5.4 (@see https://bugs.php.net/bug.php?id=47160) 889 $return_value[] = $c['function']($event, $step, $data, $options); 890 } elseif ($production_status == 'debug') { 891 trigger_error(gTxt('unknown_callback_function', array('{function}' => callback_tostring($c['function']))), E_USER_WARNING); 892 } 893 } 894 } 895 return $return_value; 896 } 897 898 /** 899 * @param string|array $callback a PHP "callback" 900 * @return string $callback as a human-readable string 901 * @since 4.5.0 902 */ 903 function callback_tostring($callback) 904 { 905 if (is_array($callback)) { 906 return join('::', array_filter($callback, 'is_scalar')); 907 } 908 elseif (!is_scalar($callback)) { 909 return ''; 910 } 911 return $callback; 912 } 913 914 // ------------------------------------------------------------- 915 function register_tab($area, $event, $title) 916 { 917 global $plugin_areas; 918 919 if (!isset($GLOBALS['event']) || ($GLOBALS['event'] !== 'plugin')) 920 { 921 $plugin_areas[$area][$title] = $event; 922 } 923 } 924 925 // ------------------------------------------------------------- 926 function pluggable_ui($event, $element, $default='') 927 { 928 $argv = func_get_args(); 929 $argv = array_slice($argv, 2); 930 // custom user interface, anyone? 931 // signature for called functions: 932 // string my_called_func(string $event, string $step, string $default_markup[, mixed $context_data...]) 933 $ui = call_user_func_array('callback_event', array('event' => $event, 'step' => $element, 'pre' => 0) + $argv); 934 // either plugins provided a user interface, or we render our own 935 return ($ui === '')? $default : $ui; 936 } 937 938 // ------------------------------------------------------------- 939 // deprecated in 4.2.0 940 function getAtt($name, $default=NULL) 941 { 942 trigger_error(gTxt('deprecated_function_with', array('{name}' => __FUNCTION__, '{with}' => 'lAtts')), E_USER_NOTICE); 943 global $theseatts; 944 return isset($theseatts[$name]) ? $theseatts[$name] : $default; 945 } 946 947 // ------------------------------------------------------------- 948 // deprecated in 4.2.0 949 function gAtt(&$atts, $name, $default=NULL) 950 { 951 trigger_error(gTxt('deprecated_function_with', array('{name}' => __FUNCTION__, '{with}' => 'lAtts')), E_USER_NOTICE); 952 return isset($atts[$name]) ? $atts[$name] : $default; 953 } 954 955 // ------------------------------------------------------------- 956 function lAtts($pairs, $atts, $warn=1) 957 { 958 global $production_status; 959 960 foreach($atts as $name => $value) 961 { 962 if (array_key_exists($name, $pairs)) 963 { 964 $pairs[$name] = $value; 965 } 966 elseif ($warn and $production_status != 'live') 967 { 968 trigger_error(gTxt('unknown_attribute', array('{att}' => $name))); 969 } 970 } 971 972 return ($pairs) ? $pairs : false; 973 } 974 975 // ------------------------------------------------------------- 976 // deprecated in 4.5.0 977 function select_buttons() 978 { 979 return 980 gTxt('select'). 981 n.fInput('button','selall',gTxt('all'),'','select all','selectall();'). 982 n.fInput('button','selnone',gTxt('none'),'','select none','deselectall();'). 983 n.fInput('button','selrange',gTxt('range'),'','select range','selectrange();'); 984 } 985 986 // ------------------------------------------------------------- 987 function stripSpace($text, $force=0) 988 { 989 global $prefs; 990 if ($force or !empty($prefs['attach_titles_to_permalinks'])) 991 { 992 $text = trim(sanitizeForUrl($text), '-'); 993 if ($prefs['permalink_title_format']) { 994 return (function_exists('mb_strtolower') ? mb_strtolower($text, 'UTF-8') : strtolower($text)); 995 } else { 996 return str_replace('-','',$text); 997 } 998 } 999 } 1000 1001 // ------------------------------------------------------------- 1002 function sanitizeForUrl($text) 1003 { 1004 // any overrides? 1005 $out = callback_event('sanitize_for_url', '', 0, $text); 1006 if ($out !== '') return $out; 1007 1008 $in = $text; 1009 // Remove names entities and tags 1010 $text = preg_replace("/(^|&\S+;)|(<[^>]*>)/U","",dumbDown($text)); 1011 // Dashify high-order chars leftover from dumbDown() 1012 $text = preg_replace("/[\x80-\xff]/","-",$text); 1013 // Collapse spaces, minuses, (back-)slashes and non-words 1014 $text = preg_replace('/[\s\-\/\\\\]+/', '-', trim(preg_replace('/[^\w\s\-\/\\\\]/', '', $text))); 1015 // Remove all non-whitelisted characters 1016 $text = preg_replace("/[^A-Za-z0-9\-_]/","",$text); 1017 // Sanitizing shouldn't leave us with plain nothing to show. 1018 // Fall back on percent-encoded URLs as a last resort for RFC 1738 conformance. 1019 if (empty($text) || $text == '-') 1020 { 1021 $text = rawurlencode($in); 1022 } 1023 return $text; 1024 } 1025 1026 // ------------------------------------------------------------- 1027 function sanitizeForFile($text) 1028 { 1029 // any overrides? 1030 $out = callback_event('sanitize_for_file', '', 0, $text); 1031 if ($out !== '') return $out; 1032 1033 // Remove control characters and " * \ : < > ? / | 1034 $text = preg_replace('/[\x00-\x1f\x22\x2a\x2f\x3a\x3c\x3e\x3f\x5c\x7c\x7f]+/', '', $text); 1035 // Remove duplicate dots and any leading or trailing dots/spaces 1036 $text = preg_replace('/[.]{2,}/', '.', trim($text, '. ')); 1037 return $text; 1038 } 1039 1040 // ------------------------------------------------------------- 1041 function sanitizeForPage($text) 1042 { 1043 // any overrides? 1044 $out = callback_event('sanitize_for_page', '', 0, $text); 1045 if ($out !== '') return $out; 1046 1047 return trim(preg_replace('/[<>&"\']/', '', $text)); 1048 } 1049 1050 // ------------------------------------------------------------- 1051 function dumbDown($str, $lang=LANG) 1052 { 1053 static $array; 1054 if (empty($array[$lang])) { 1055 $array[$lang] = array( // nasty, huh?. 1056 'À'=>'A','À'=>'A','Á'=>'A','Á'=>'A','Â'=>'A','Â'=>'A', 1057 'Ã'=>'A','Ã'=>'A','Ä'=>'Ae','Ä'=>'A','Å'=>'A','Å'=>'A', 1058 'Æ'=>'Ae','Æ'=>'AE', 1059 'Ā'=>'A','Ą'=>'A','Ă'=>'A', 1060 'Ç'=>'C','Ç'=>'C','Ć'=>'C','Č'=>'C','Ĉ'=>'C','Ċ'=>'C', 1061 'Ď'=>'D','Đ'=>'D','Ð'=>'D','Ð'=>'D', 1062 'È'=>'E','È'=>'E','É'=>'E','É'=>'E','Ê'=>'E','Ê'=>'E','Ë'=>'E','Ë'=>'E', 1063 'Ē'=>'E','Ę'=>'E','Ě'=>'E','Ĕ'=>'E','Ė'=>'E', 1064 'Ĝ'=>'G','Ğ'=>'G','Ġ'=>'G','Ģ'=>'G', 1065 'Ĥ'=>'H','Ħ'=>'H', 1066 'Ì'=>'I','Ì'=>'I','Í'=>'I','Í'=>'I','Î'=>'I','Î'=>'I','Ï'=>'I','Ï'=>'I', 1067 'Ī'=>'I','Ĩ'=>'I','Ĭ'=>'I','Į'=>'I','İ'=>'I', 1068 'IJ'=>'IJ', 1069 'Ĵ'=>'J', 1070 'Ķ'=>'K', 1071 'Ł'=>'K','Ľ'=>'K','Ĺ'=>'K','Ļ'=>'K','Ŀ'=>'K', 1072 'Ñ'=>'N','Ñ'=>'N','Ń'=>'N','Ň'=>'N','Ņ'=>'N','Ŋ'=>'N', 1073 'Ò'=>'O','Ò'=>'O','Ó'=>'O','Ó'=>'O','Ô'=>'O','Ô'=>'O','Õ'=>'O','Õ'=>'O', 1074 'Ö'=>'Oe','Ö'=>'Oe', 1075 'Ø'=>'O','Ø'=>'O','Ō'=>'O','Ő'=>'O','Ŏ'=>'O', 1076 'Œ'=>'OE', 1077 'Ŕ'=>'R','Ř'=>'R','Ŗ'=>'R', 1078 'Ś'=>'S','Š'=>'S','Ş'=>'S','Ŝ'=>'S','Ș'=>'S', 1079 'Ť'=>'T','Ţ'=>'T','Ŧ'=>'T','Ț'=>'T', 1080 'Ù'=>'U','Ù'=>'U','Ú'=>'U','Ú'=>'U','Û'=>'U','Û'=>'U', 1081 'Ü'=>'Ue','Ū'=>'U','Ü'=>'Ue', 1082 'Ů'=>'U','Ű'=>'U','Ŭ'=>'U','Ũ'=>'U','Ų'=>'U', 1083 'Ŵ'=>'W', 1084 'Ý'=>'Y','Ý'=>'Y','Ŷ'=>'Y','Ÿ'=>'Y', 1085 'Ź'=>'Z','Ž'=>'Z','Ż'=>'Z', 1086 'Þ'=>'T','Þ'=>'T', 1087 'à'=>'a','á'=>'a','â'=>'a','ã'=>'a','ä'=>'ae', 1088 'ä'=>'ae', 1089 'å'=>'a','ā'=>'a','ą'=>'a','ă'=>'a','å'=>'a', 1090 'æ'=>'ae', 1091 'ç'=>'c','ć'=>'c','č'=>'c','ĉ'=>'c','ċ'=>'c', 1092 'ď'=>'d','đ'=>'d','ð'=>'d', 1093 'è'=>'e','é'=>'e','ê'=>'e','ë'=>'e','ē'=>'e', 1094 'ę'=>'e','ě'=>'e','ĕ'=>'e','ė'=>'e', 1095 'ƒ'=>'f', 1096 'ĝ'=>'g','ğ'=>'g','ġ'=>'g','ģ'=>'g', 1097 'ĥ'=>'h','ħ'=>'h', 1098 'ì'=>'i','í'=>'i','î'=>'i','ï'=>'i','ī'=>'i', 1099 'ĩ'=>'i','ĭ'=>'i','į'=>'i','ı'=>'i', 1100 'ij'=>'ij', 1101 'ĵ'=>'j', 1102 'ķ'=>'k','ĸ'=>'k', 1103 'ł'=>'l','ľ'=>'l','ĺ'=>'l','ļ'=>'l','ŀ'=>'l', 1104 'ñ'=>'n','ń'=>'n','ň'=>'n','ņ'=>'n','ʼn'=>'n', 1105 'ŋ'=>'n', 1106 'ò'=>'o','ó'=>'o','ô'=>'o','õ'=>'o','ö'=>'oe', 1107 'ö'=>'oe', 1108 'ø'=>'o','ō'=>'o','ő'=>'o','ŏ'=>'o', 1109 'œ'=>'oe', 1110 'ŕ'=>'r','ř'=>'r','ŗ'=>'r', 1111 'š'=>'s', 1112 'ù'=>'u','ú'=>'u','û'=>'u','ü'=>'ue','ū'=>'u', 1113 'ü'=>'ue', 1114 'ů'=>'u','ű'=>'u','ŭ'=>'u','ũ'=>'u','ų'=>'u', 1115 'ŵ'=>'w', 1116 'ý'=>'y','ÿ'=>'y','ŷ'=>'y', 1117 'ž'=>'z','ż'=>'z','ź'=>'z', 1118 'þ'=>'t', 1119 'ß'=>'ss', 1120 'ſ'=>'ss', 1121 'à'=>'a','á'=>'a','â'=>'a','ã'=>'a','ä'=>'ae', 1122 'å'=>'a','æ'=>'ae','ç'=>'c','ð'=>'d', 1123 'è'=>'e','é'=>'e','ê'=>'e','ë'=>'e', 1124 'ì'=>'i','í'=>'i','î'=>'i','ï'=>'i', 1125 'ñ'=>'n', 1126 'ò'=>'o','ó'=>'o','ô'=>'o','õ'=>'o','ö'=>'oe', 1127 'ø'=>'o', 1128 'ù'=>'u','ú'=>'u','û'=>'u','ü'=>'ue', 1129 'ý'=>'y','ÿ'=>'y', 1130 'þ'=>'t', 1131 'ß'=>'ss' 1132 ); 1133 1134 1135 if (is_file(txpath.'/lib/i18n-ascii.txt')) { 1136 $i18n = parse_ini_file(txpath.'/lib/i18n-ascii.txt', true); 1137 # load the global map 1138 if (isset($i18n['default']) && is_array($i18n['default'])) { 1139 $array[$lang] = array_merge($array[$lang], $i18n['default']); 1140 # base language overrides: 'de-AT' applies the 'de' section 1141 if (preg_match('/([a-zA-Z]+)-.+/', $lang, $m)) { 1142 if (isset($i18n[$m[1]]) && is_array($i18n[$m[1]])) 1143 $array[$lang] = array_merge($array[$lang], $i18n[$m[1]]); 1144 }; 1145 # regional language overrides: 'de-AT' applies the 'de-AT' section 1146 if (isset($i18n[$lang]) && is_array($i18n[$lang])) 1147 $array[$lang] = array_merge($array[$lang], $i18n[$lang]); 1148 } 1149 # load an old file (no sections) just in case 1150 else 1151 $array[$lang] = array_merge($array[$lang], $i18n); 1152 } 1153 } 1154 1155 return strtr($str, $array[$lang]); 1156 } 1157 1158 // ------------------------------------------------------------- 1159 function clean_url($url) 1160 { 1161 return preg_replace("/\"|'|(?:\s.*$)/",'',$url); 1162 } 1163 1164 // ------------------------------------------------------------- 1165 function noWidow($str) 1166 { 1167 // replace the last space with a nbsp 1168 if (REGEXP_UTF8 == 1) 1169 return preg_replace('@[ ]+([[:punct:]]?[\p{L}\p{N}\p{Pc}]+[[:punct:]]?)$@u', ' $1', rtrim($str)); 1170 return preg_replace('@[ ]+([[:punct:]]?\w+[[:punct:]]?)$@', ' $1', rtrim($str)); 1171 } 1172 1173 // ------------------------------------------------------------- 1174 function is_blacklisted($ip, $checks = '') 1175 { 1176 global $prefs; 1177 1178 if (!$checks) 1179 { 1180 $checks = do_list($prefs['spam_blacklists']); 1181 } 1182 1183 $rip = join('.', array_reverse(explode('.', $ip))); 1184 1185 foreach ($checks as $a) 1186 { 1187 $parts = explode(':', $a, 2); 1188 $rbl = $parts[0]; 1189 1190 if (isset($parts[1])) 1191 { 1192 foreach (explode(':', $parts[1]) as $code) 1193 { 1194 $codes[] = strpos($code, '.') ? $code : '127.0.0.'.$code; 1195 } 1196 } 1197 1198 $hosts = $rbl ? @gethostbynamel($rip.'.'.trim($rbl, '. ').'.') : FALSE; 1199 1200 if ($hosts and (!isset($codes) or array_intersect($hosts, $codes))) 1201 { 1202 $listed[] = $rbl; 1203 } 1204 } 1205 1206 return (!empty($listed)) ? join(', ', $listed) : false; 1207 } 1208 1209 // ------------------------------------------------------------- 1210 function is_logged_in($user = '') 1211 { 1212 $name = substr(cs('txp_login_public'), 10); 1213 1214 if (!strlen($name) or strlen($user) and $user !== $name) 1215 { 1216 return FALSE; 1217 } 1218 1219 $rs = safe_row('nonce, name, RealName, email, privs', 'txp_users', "name = '".doSlash($name)."'"); 1220 1221 if ($rs and substr(md5($rs['nonce']), -10) === substr(cs('txp_login_public'), 0, 10)) 1222 { 1223 unset($rs['nonce']); 1224 return $rs; 1225 } 1226 else 1227 { 1228 return FALSE; 1229 } 1230 } 1231 1232 // ------------------------------------------------------------- 1233 function updateSitePath($here) 1234 { 1235 $here = doSlash($here); 1236 $rs = safe_field ("name",'txp_prefs',"name = 'path_to_site'"); 1237 if (!$rs) { 1238 safe_insert("txp_prefs","prefs_id=1,name='path_to_site',val='$here'"); 1239 } else { 1240 safe_update('txp_prefs',"val='$here'","name='path_to_site'"); 1241 } 1242 } 1243 1244 // ------------------------------------------------------------- 1245 function splat($text) 1246 { 1247 $atts = array(); 1248 1249 if (preg_match_all('@(\w+)\s*=\s*(?:"((?:[^"]|"")*)"|\'((?:[^\']|\'\')*)\'|([^\s\'"/>]+))@s', $text, $match, PREG_SET_ORDER)) 1250 { 1251 foreach ($match as $m) 1252 { 1253 switch (count($m)) 1254 { 1255 case 3: 1256 $val = str_replace('""', '"', $m[2]); 1257 break; 1258 case 4: 1259 $val = str_replace("''", "'", $m[3]); 1260 1261 if (strpos($m[3], '<txp:') !== FALSE) 1262 { 1263 trace_add("[attribute '".$m[1]."']"); 1264 $val = parse($val); 1265 trace_add("[/attribute]"); 1266 } 1267 1268 break; 1269 case 5: 1270 $val = $m[4]; 1271 trigger_error(gTxt('attribute_values_must_be_quoted'), E_USER_WARNING); 1272 break; 1273 } 1274 1275 $atts[strtolower($m[1])] = $val; 1276 } 1277 1278 } 1279 1280 return $atts; 1281 } 1282 1283 // ------------------------------------------------------------- 1284 function maxMemUsage($message = 'none', $returnit = 0) 1285 { 1286 static $memory_top = 0; 1287 static $memory_message; 1288 1289 if (is_callable('memory_get_usage')) 1290 { 1291 $memory_now = memory_get_usage(); 1292 if ($memory_now > $memory_top) 1293 { 1294 $memory_top = $memory_now; 1295 $memory_message = $message; 1296 } 1297 } 1298 1299 if ($returnit != 0) 1300 { 1301 if (is_callable('memory_get_usage')) 1302 return n.comment(sprintf('Memory: %sKb, %s', 1303 ceil($memory_top/1024),$memory_message)); 1304 else 1305 return n.comment('Memory: no info available'); 1306 } 1307 } 1308 1309 // ------------------------------------------------------------- 1310 function strip_rn($str) 1311 { 1312 return strtr($str, "\r\n", ' '); 1313 } 1314 1315 // ------------------------------------------------------------- 1316 1317 function is_valid_email($address) 1318 { 1319 return preg_match('/^[a-z0-9](\.?[a-z0-9_+%-])*@([a-z0-9](-*[a-z0-9])*\.)+[a-z]{2,6}$/i', $address); 1320 } 1321 1322 // ------------------------------------------------------------- 1323 1324 function txpMail($to_address, $subject, $body, $reply_to = null) 1325 { 1326 global $txp_user, $prefs; 1327 1328 // if mailing isn't possible, don't even try 1329 if (is_disabled('mail')) 1330 { 1331 return false; 1332 } 1333 1334 // Likely sending passwords 1335 if (isset($txp_user)) 1336 { 1337 if (is_valid_email($prefs['publisher_email'])) { 1338 // explicit publisher email address preferred 1339 $RealName = safe_field('RealName', 'txp_users', "name = '".doSlash($txp_user)."'"); 1340 $email = $prefs['publisher_email']; 1341 } else { 1342 // default: current user invites new users using her personal email address 1343 extract(safe_row('RealName, email', 'txp_users', "name = '".doSlash($txp_user)."'")); 1344 } 1345 } 1346 1347 // Likely sending comments -> "to" equals "from" 1348 else 1349 { 1350 extract(safe_row('RealName, email', 'txp_users', "email = '".doSlash($to_address)."'")); 1351 } 1352 1353 if ($prefs['override_emailcharset'] and is_callable('utf8_decode')) 1354 { 1355 $charset = 'ISO-8859-1'; 1356 1357 $RealName = utf8_decode($RealName); 1358 $subject = utf8_decode($subject); 1359 $body = utf8_decode($body); 1360 } 1361 1362 else 1363 { 1364 $charset = 'UTF-8'; 1365 } 1366 1367 $RealName = encode_mailheader(strip_rn($RealName), 'phrase'); 1368 $subject = encode_mailheader(strip_rn($subject), 'text'); 1369 $email = strip_rn($email); 1370 1371 if (!is_null($reply_to)) 1372 { 1373 $reply_to = strip_rn($reply_to); 1374 } 1375 1376 $sep = !IS_WIN ? "\n" : "\r\n"; 1377 1378 $body = str_replace("\r\n", "\n", $body); 1379 $body = str_replace("\r", "\n", $body); 1380 $body = str_replace("\n", $sep, $body); 1381 1382 $headers = "From: $RealName <$email>". 1383 $sep.'Reply-To: '.( isset($reply_to) ? $reply_to : "$RealName <$email>" ). 1384 $sep.'X-Mailer: Textpattern'. 1385 $sep.'Content-Transfer-Encoding: 8bit'. 1386 $sep.'Content-Type: text/plain; charset="'.$charset.'"'. 1387 $sep; 1388 1389 if (is_valid_email($prefs['smtp_from'])) 1390 { 1391 if (IS_WIN) 1392 { 1393 ini_set('sendmail_from', $prefs['smtp_from']); 1394 } 1395 elseif (!ini_get('safe_mode')) 1396 { 1397 return mail($to_address, $subject, $body, $headers, '-f'.$prefs['smtp_from']); 1398 } 1399 } 1400 1401 return mail($to_address, $subject, $body, $headers); 1402 } 1403 1404 // ------------------------------------------------------------- 1405 function encode_mailheader($string, $type) 1406 { 1407 global $prefs; 1408 if (!strstr($string,'=?') and !preg_match('/[\x00-\x1F\x7F-\xFF]/', $string)) { 1409 if ("phrase" == $type) { 1410 if (preg_match('/[][()<>@,;:".\x5C]/', $string)) { 1411 $string = '"'. strtr($string, array("\\" => "\\\\", '"' => '\"')) . '"'; 1412 } 1413 } 1414 elseif ( "text" != $type) { 1415 trigger_error( 'Unknown encode_mailheader type', E_USER_WARNING); 1416 } 1417 return $string; 1418 } 1419 if ($prefs['override_emailcharset'] and is_callable('utf8_decode')) { 1420 $start = '=?ISO-8859-1?B?'; 1421 $pcre = '/.{1,42}/s'; 1422 } 1423 else { 1424 $start = '=?UTF-8?B?'; 1425 $pcre = '/.{1,45}(?=[\x00-\x7F\xC0-\xFF]|$)/s'; 1426 } 1427 $end = '?='; 1428 $sep = IS_WIN ? "\r\n" : "\n"; 1429 preg_match_all($pcre, $string, $matches); 1430 return $start . join($end.$sep.' '.$start, array_map('base64_encode',$matches[0])) . $end; 1431 } 1432 1433 // ------------------------------------------------------------- 1434 function stripPHP($in) 1435 { 1436 return preg_replace("/".chr(60)."\?(?:php)?|\?".chr(62)."/i",'',$in); 1437 } 1438 1439 // ------------------------------------------------------------- 1440 1441 /** 1442 * PEDRO: 1443 * Helper functions for common textpattern event files actions. 1444 * Code refactoring from original files. Intended to do easy and less error 1445 * prone the future build of new textpattern extensions, and to add new 1446 * events to multiedit forms. 1447 */ 1448 1449 function event_category_popup($name, $cat = '', $id = '') 1450 { 1451 $arr = array(''); 1452 $rs = getTree('root', $name); 1453 1454 if ($rs) 1455 { 1456 return treeSelectInput('category', $rs, $cat, $id); 1457 } 1458 1459 return false; 1460 } 1461 1462 // ------------------------------------------------------------- 1463 function event_change_pageby($name) 1464 { 1465 global $event; 1466 $qty = gps('qty'); 1467 assert_int($qty); 1468 $pageby = $name.'_list_pageby'; 1469 $GLOBALS[$pageby] = $qty; 1470 1471 set_pref($pageby, $qty, $event, PREF_HIDDEN, 'text_input', 0, PREF_PRIVATE); 1472 1473 return; 1474 } 1475 1476 // ------------------------------------------------------------- 1477 // DEPRECATED in v4.5.0: use multi_edit() instead 1478 function event_multiedit_form($name, $methods = null, $page, $sort, $dir, $crit, $search_method) 1479 { 1480 $method = ps('edit_method'); 1481 1482 if ($methods === NULL) 1483 { 1484 $methods = array( 1485 'delete' => gTxt('delete') 1486 ); 1487 } 1488 1489 return '<label for="withselected">'.gTxt('with_selected').'</label>'. 1490 n.selectInput('edit_method', $methods, $method, 1, ' id="withselected" onchange="poweredit(this); return false;"'). 1491 n.eInput($name). 1492 n.sInput($name.'_multi_edit'). 1493 n.hInput('page', $page). 1494 ( $sort ? n.hInput('sort', $sort).n.hInput('dir', $dir) : '' ). 1495 ( ($crit != '') ? n.hInput('crit', $crit).n.hInput('search_method', $search_method) : '' ). 1496 n.fInput('submit', '', gTxt('go')); 1497 } 1498 1499 // ------------------------------------------------------------- 1500 1501 function event_multi_edit($table, $id_key) 1502 { 1503 $method = ps('edit_method'); 1504 $selected = ps('selected'); 1505 1506 if ($selected) 1507 { 1508 if ($method == 'delete') 1509 { 1510 foreach ($selected as $id) 1511 { 1512 $id = assert_int($id); 1513 1514 if (safe_delete($table, "$id_key = $id")) 1515 { 1516 $ids[] = $id; 1517 } 1518 } 1519 1520 return join(', ', $ids); 1521 } 1522 } 1523 1524 return ''; 1525 } 1526 1527 // ------------------------------------------------------------- 1528 function since($stamp) 1529 { 1530 $diff = (time() - $stamp); 1531 if ($diff <= 3600) { 1532 $mins = round($diff / 60); 1533 $since = ($mins <= 1) 1534 ? ($mins==1) 1535 ? '1 '.gTxt('minute') 1536 : gTxt('a_few_seconds') 1537 : "$mins ".gTxt('minutes'); 1538 } else if (($diff <= 86400) && ($diff > 3600)) { 1539 $hours = round($diff / 3600); 1540 $since = ($hours <= 1) ? '1 '.gTxt('hour') : "$hours ".gTxt('hours'); 1541 } else if ($diff >= 86400) { 1542 $days = round($diff / 86400); 1543 $since = ($days <= 1) ? "1 ".gTxt('day') : "$days ".gTxt('days'); 1544 } 1545 return $since.' '.gTxt('ago'); // sorry, this needs to be hacked until a truly multilingual version is done 1546 } 1547 1548 // ------------------------------------------------------------- 1549 // Calculate the offset between the server local time and the 1550 // user's selected time zone at a given point in time 1551 function tz_offset($timestamp = NULL) 1552 { 1553 global $gmtoffset, $timezone_key; 1554 1555 if (is_null($timestamp)) $timestamp = time(); 1556 1557 extract(getdate($timestamp)); 1558 $serveroffset = gmmktime($hours,$minutes,0,$mon,$mday,$year) - mktime($hours,$minutes,0,$mon,$mday,$year); 1559 1560 $real_dst = timezone::is_dst($timestamp, $timezone_key); 1561 return $gmtoffset - $serveroffset + ($real_dst ? 3600 : 0); 1562 } 1563 1564 // ------------------------------------------------------------- 1565 // Format a time, respecting the locale and local time zone, 1566 // and make sure the output string is safe for UTF-8 1567 function safe_strftime($format, $time='', $gmt=0, $override_locale='') 1568 { 1569 global $locale; 1570 $old_locale = $locale; 1571 1572 if (!$time) 1573 $time = time(); 1574 1575 # we could add some other formats here 1576 if ($format == 'iso8601' or $format == 'w3cdtf') { 1577 $format = '%Y-%m-%dT%H:%M:%SZ'; 1578 $gmt = 1; 1579 } 1580 elseif ($format == 'rfc822') { 1581 $format = '%a, %d %b %Y %H:%M:%S GMT'; 1582 $gmt = 1; 1583 $override_locale = 'en-gb'; 1584 } 1585 1586 if ($override_locale) 1587 getlocale($override_locale); 1588 1589 if ($format == 'since') 1590 $str = since($time); 1591 elseif ($gmt) 1592 $str = gmstrftime($format, $time); 1593 else 1594 $str = strftime($format, $time + tz_offset($time)); 1595 1596 @list($lang, $charset) = explode('.', $locale); 1597 if (empty($charset)) 1598 $charset = 'ISO-8859-1'; 1599 elseif (IS_WIN and is_numeric($charset)) 1600 // Score -1 for consistent naming conventions 1601 $charset = 'Windows-'.$charset; 1602 1603 if ($charset != 'UTF-8' and $format != 'since') { 1604 $new = ''; 1605 if (is_callable('iconv')) 1606 $new = @iconv($charset, 'UTF-8', $str); 1607 1608 if ($new) 1609 $str = $new; 1610 elseif (is_callable('utf8_encode')) 1611 $str = utf8_encode($str); 1612 } 1613 1614 # revert to the old locale 1615 if ($override_locale) 1616 $locale = setlocale(LC_ALL, $old_locale); 1617 1618 return $str; 1619 } 1620 1621 // ------------------------------------------------------------- 1622 // Convert a time string from the Textpattern time zone to GMT 1623 function safe_strtotime($time_str) 1624 { 1625 $ts = strtotime($time_str); 1626 return strtotime($time_str, time() + tz_offset($ts)) - tz_offset($ts); 1627 } 1628 1629 // ------------------------------------------------------------- 1630 function myErrorHandler($errno, $errstr, $errfile, $errline) 1631 { 1632 # error_reporting() returns 0 when the '@' suppression 1633 # operator is used 1634 if (!error_reporting()) 1635 return; 1636 1637 echo '<pre>'.n.n."$errno: $errstr in $errfile at line $errline\n"; 1638 # Requires PHP 4.3 1639 if (is_callable('debug_backtrace')) { 1640 echo "Backtrace:\n"; 1641 $trace = debug_backtrace(); 1642 foreach($trace as $ent) { 1643 if(isset($ent['file'])) echo $ent['file'].':'; 1644 if(isset($ent['function'])) { 1645 echo $ent['function'].'('; 1646 if(isset($ent['args'])) { 1647 $args=''; 1648 foreach($ent['args'] as $arg) { $args.=$arg.','; } 1649 echo rtrim($args,','); 1650 } 1651 echo ') '; 1652 } 1653 if(isset($ent['line'])) echo 'at line '.$ent['line'].' '; 1654 if(isset($ent['file'])) echo 'in '.$ent['file']; 1655 echo "\n"; 1656 } 1657 } 1658 echo "</pre>"; 1659 } 1660 1661 // ------------------------------------------------------------- 1662 function find_temp_dir() 1663 { 1664 global $path_to_site, $img_dir; 1665 1666 if (IS_WIN) { 1667 $guess = array(txpath.DS.'tmp', getenv('TMP'), getenv('TEMP'), getenv('SystemRoot').DS.'Temp', 'C:'.DS.'Temp', $path_to_site.DS.$img_dir); 1668 foreach ($guess as $k=>$v) 1669 if (empty($v)) unset($guess[$k]); 1670 } 1671 else 1672 $guess = array(txpath.DS.'tmp', '', DS.'tmp', $path_to_site.DS.$img_dir); 1673 1674 foreach ($guess as $dir) { 1675 $tf = @tempnam($dir, 'txp_'); 1676 if ($tf) $tf = realpath($tf); 1677 if ($tf and file_exists($tf)) { 1678 unlink($tf); 1679 return dirname($tf); 1680 } 1681 } 1682 1683 return false; 1684 } 1685 1686 // ------------------------------------------------------------- 1687 function get_uploaded_file($f, $dest='') 1688 { 1689 global $tempdir; 1690 1691 if (!is_uploaded_file($f)) 1692 return false; 1693 1694 if ($dest) { 1695 $newfile = $dest; 1696 } 1697 else { 1698 $newfile = tempnam($tempdir, 'txp_'); 1699 if (!$newfile) 1700 return false; 1701 } 1702 1703 # $newfile is created by tempnam(), but move_uploaded_file 1704 # will overwrite it 1705 if (move_uploaded_file($f, $newfile)) 1706 return $newfile; 1707 } 1708 1709 // -------------------------------------------------------------- 1710 function set_error_level($level) 1711 { 1712 1713 if ($level == 'debug') { 1714 // We need to violate/disable E_STRICT for PHP 4.x compatibility 1715 // E_STRICT bitmask calculation stems from the variations for E_ALL in PHP 4.x, 5.{0,1,2,3}, and 5.4+ 1716 // E_STRICT is defined since PHP 5.x and is set in E_ALL in PHP 5.4 1717 error_reporting(E_ALL & ~(defined('E_STRICT') ? E_STRICT : 0)); 1718 } 1719 elseif ($level == 'live') { 1720 // don't show errors on screen 1721 $suppress = E_WARNING | E_NOTICE; 1722 if (defined('E_STRICT') && (E_ALL & E_STRICT)) $suppress |= E_STRICT; 1723 if (defined('E_DEPRECATED')) $suppress |= E_DEPRECATED; 1724 error_reporting(E_ALL ^ $suppress); 1725 @ini_set("display_errors","1"); 1726 } 1727 else { 1728 // default is 'testing': display everything except notices and strict 1729 error_reporting((E_ALL ^ E_NOTICE) & ~(defined('E_STRICT') ? E_STRICT : 0)); 1730 } 1731 } 1732 1733 1734 // ------------------------------------------------------------- 1735 function shift_uploaded_file($f, $dest) 1736 { 1737 // Rename might not work, but it's worth a try 1738 if (@rename($f, $dest)) 1739 return true; 1740 1741 if (@copy($f, $dest)) { 1742 unlink($f); 1743 return true; 1744 } 1745 } 1746 // ------------------------------------------------------------- 1747 function upload_get_errormsg($err_code) 1748 { 1749 $msg = ''; 1750 switch ($err_code) 1751 { 1752 // Value: 0; There is no error, the file uploaded with success. 1753 case UPLOAD_ERR_OK : $msg = '';break; 1754 // Value: 1; The uploaded file exceeds the upload_max_filesize directive in php.ini. 1755 case UPLOAD_ERR_INI_SIZE : $msg = gTxt('upload_err_ini_size');break; 1756 // Value: 2; The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form. 1757 case UPLOAD_ERR_FORM_SIZE : $msg = gTxt('upload_err_form_size');break; 1758 // Value: 3; The uploaded file was only partially uploaded. 1759 case UPLOAD_ERR_PARTIAL : $msg = gTxt('upload_err_partial');break; 1760 // Value: 4; No file was uploaded. 1761 case UPLOAD_ERR_NO_FILE : $msg = gTxt('upload_err_no_file');break; 1762 // Value: 6; Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3. 1763 case UPLOAD_ERR_NO_TMP_DIR : $msg = gTxt('upload_err_tmp_dir');break; 1764 // Value: 7; Failed to write file to disk. Introduced in PHP 5.1.0. 1765 case UPLOAD_ERR_CANT_WRITE : $msg = gTxt('upload_err_cant_write');break; 1766 // Value: 8; File upload stopped by extension. Introduced in PHP 5.2.0. 1767 case UPLOAD_ERR_EXTENSION : $msg = gTxt('upload_err_extension');break; 1768 } 1769 return $msg; 1770 } 1771 1772 /** 1773 * Formats a file size 1774 * 1775 * @param int $bytes Size in bytes 1776 * @param int $decimals Number of decimals 1777 * @param string $format The format the size is represented 1778 * @return string Formatted file size 1779 */ 1780 1781 function format_filesize($bytes, $decimals=2, $format='') 1782 { 1783 $units = array('b', 'k', 'm', 'g', 't', 'p', 'e', 'z', 'y'); 1784 1785 if (in_array($format, $units)) 1786 { 1787 $pow = array_search($format, $units); 1788 } 1789 else 1790 { 1791 $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); 1792 $pow = min($pow, count($units) - 1); 1793 } 1794 1795 $bytes /= pow(1024, $pow); 1796 1797 $separators = localeconv(); 1798 $sep_dec = isset($separators['decimal_point']) ? $separators['decimal_point'] : '.'; 1799 $sep_thous = isset($separators['thousands_sep']) ? $separators['thousands_sep'] : ','; 1800 1801 return number_format($bytes, $decimals, $sep_dec, $sep_thous) . gTxt('units_' . $units[$pow]); 1802 } 1803 1804 // ------------------------------------------------------------- 1805 // for b/c only 1806 function is_windows() 1807 { 1808 return IS_WIN; 1809 } 1810 1811 // ------------------------------------------------------------- 1812 function is_cgi() 1813 { 1814 return IS_CGI; 1815 } 1816 1817 // ------------------------------------------------------------- 1818 function is_mod_php() 1819 { 1820 return IS_APACHE; 1821 } 1822 1823 // ------------------------------------------------------------- 1824 1825 function is_disabled($function) 1826 { 1827 static $disabled; 1828 1829 if (!isset($disabled)) 1830 { 1831 $disabled = do_list(ini_get('disable_functions')); 1832 } 1833 1834 return in_array($function, $disabled); 1835 } 1836 1837 // -------------------------------------------------------------- 1838 function build_file_path($base,$path) 1839 { 1840 $base = rtrim($base,'/\\'); 1841 $path = ltrim($path,'/\\'); 1842 1843 return $base.DIRECTORY_SEPARATOR.$path; 1844 } 1845 1846 // -------------------------------------------------------------- 1847 function get_author_name($name) 1848 { 1849 static $authors = array(); 1850 1851 if (isset($authors[$name])) 1852 return $authors[$name]; 1853 1854 $realname = fetch('RealName','txp_users','name',doSlash($name)); 1855 $authors[$name] = $realname; 1856 return ($realname) ? $realname : $name; 1857 } 1858 1859 // -------------------------------------------------------------- 1860 function get_author_email($name) 1861 { 1862 static $authors = array(); 1863 1864 if (isset($authors[$name])) 1865 return $authors[$name]; 1866 1867 $email = fetch('email','txp_users','name',doSlash($name)); 1868 $authors[$name] = $email; 1869 return $email; 1870 } 1871 1872 // -------------------------------------------------------------- 1873 function has_single_author($table, $col='author') 1874 { 1875 return (safe_field('COUNT(name)', 'txp_users', '1=1') <= 1) && 1876 (safe_field('COUNT(DISTINCT('.doSlash($col).'))', doSlash($table), '1=1') <= 1); 1877 } 1878 1879 // -------------------------------------------------------------- 1880 function EvalElse($thing, $condition) 1881 { 1882 global $txp_current_tag; 1883 static $gTxtTrue = NULL, $gTxtFalse; 1884 1885 if (empty($gTxtTrue)) 1886 { 1887 $gTxtTrue = gTxt('true'); 1888 $gTxtFalse = gTxt('false'); 1889 } 1890 1891 trace_add("[$txp_current_tag: ".($condition ? $gTxtTrue : $gTxtFalse)."]"); 1892 1893 $els = strpos($thing, '<txp:else'); 1894 1895 if ($els === FALSE) 1896 { 1897 return $condition ? $thing : ''; 1898 } 1899 elseif ($els === strpos($thing, '<txp:')) 1900 { 1901 return $condition 1902 ? substr($thing, 0, $els) 1903 : substr($thing, strpos($thing, '>', $els) + 1); 1904 } 1905 1906 $tag = FALSE; 1907 $level = 0; 1908 $str = ''; 1909 $regex = '@(</?txp:\w+(?:\s+\w+\s*=\s*(?:"(?:[^"]|"")*"|\'(?:[^\']|\'\')*\'|[^\s\'"/>]+))*\s*/?'.chr(62).')@s'; 1910 $parsed = preg_split($regex, $thing, -1, PREG_SPLIT_DELIM_CAPTURE); 1911 1912 foreach ($parsed as $chunk) 1913 { 1914 if ($tag) 1915 { 1916 if ($level === 0 and strpos($chunk, 'else') === 5 and substr($chunk, -2, 1) === '/') 1917 { 1918 return $condition 1919 ? $str 1920 : substr($thing, strlen($str)+strlen($chunk)); 1921 } 1922 elseif (substr($chunk, 1, 1) === '/') 1923 { 1924 $level--; 1925 } 1926 elseif (substr($chunk, -2, 1) !== '/') 1927 { 1928 $level++; 1929 } 1930 } 1931 1932 $tag = !$tag; 1933 $str .= $chunk; 1934 } 1935 1936 return $condition ? $thing : ''; 1937 } 1938 1939 // -------------------------------------------------------------- 1940 function fetch_form($name) 1941 { 1942 static $forms = array(); 1943 1944 if (isset($forms[$name])) 1945 $f = $forms[$name]; 1946 else { 1947 $row = safe_row('Form', 'txp_form',"name='".doSlash($name)."'"); 1948 if (!$row) { 1949 trigger_error(gTxt('form_not_found').': '.$name); 1950 return; 1951 } 1952 $f = $row['Form']; 1953 $forms[$name] = $f; 1954 } 1955 1956 trace_add('['.gTxt('form').': '.$name.']'); 1957 return $f; 1958 } 1959 1960 // -------------------------------------------------------------- 1961 function parse_form($name) 1962 { 1963 global $txp_current_form; 1964 static $stack = array(); 1965 1966 $f = fetch_form($name); 1967 if ($f) { 1968 if (in_array($name, $stack)) { 1969 trigger_error(gTxt('form_circular_reference', array('{name}' => $name))); 1970 return; 1971 } 1972 $old_form = $txp_current_form; 1973 $txp_current_form = $stack[] = $name; 1974 $out = parse($f); 1975 $txp_current_form = $old_form; 1976 array_pop($stack); 1977 return $out; 1978 } 1979 } 1980 1981 // -------------------------------------------------------------- 1982 function fetch_category_title($name, $type='article') 1983 { 1984 static $cattitles = array(); 1985 global $thiscategory; 1986 1987 if (isset($cattitles[$type][$name])) 1988 return $cattitles[$type][$name]; 1989 1990 if(!empty($thiscategory['title']) && $thiscategory['name'] == $name && $thiscategory['type'] == $type) 1991 { 1992 $cattitles[$type][$name] = $thiscategory['title']; 1993 return $thiscategory['title']; 1994 } 1995 1996 $f = safe_field('title','txp_category',"name='".doSlash($name)."' and type='".doSlash($type)."'"); 1997 $cattitles[$type][$name] = $f; 1998 return $f; 1999 } 2000 2001 // ------------------------------------------------------------- 2002 function fetch_section_title($name) 2003 { 2004 static $sectitles = array(); 2005 global $thissection; 2006 2007 // try cache 2008 if (isset($sectitles[$name])) 2009 return $sectitles[$name]; 2010 2011 // try global set by section_list() 2012 if(!empty($thissection['title']) && $thissection['name'] == $name) 2013 { 2014 $sectitles[$name] = $thissection['title']; 2015 return $thissection['title']; 2016 } 2017 2018 if($name == 'default' or empty($name)) 2019 return ''; 2020 2021 $f = safe_field('title','txp_section',"name='".doSlash($name)."'"); 2022 $sectitles[$name] = $f; 2023 return $f; 2024 } 2025 2026 // ------------------------------------------------------------- 2027 function update_comments_count($id) 2028 { 2029 $id = assert_int($id); 2030 $thecount = safe_field('count(*)','txp_discuss','parentid='.$id.' and visible='.VISIBLE); 2031 $thecount = assert_int($thecount); 2032 $updated = safe_update('textpattern','comments_count='.$thecount,'ID='.$id); 2033 return ($updated) ? true : false; 2034 } 2035 2036 // ------------------------------------------------------------- 2037 function clean_comment_counts($parentids) 2038 { 2039 $parentids = array_map('assert_int',$parentids); 2040 $rs = safe_rows_start('parentid, count(*) as thecount','txp_discuss','parentid IN ('.implode(',',$parentids).') AND visible='.VISIBLE.' group by parentid'); 2041 if (!$rs) return; 2042 2043 $updated = array(); 2044 while($a = nextRow($rs)) { 2045 safe_update('textpattern',"comments_count=".$a['thecount'],"ID=".$a['parentid']); 2046 $updated[] = $a['parentid']; 2047 } 2048 // We still need to update all those, that have zero comments left. 2049 $leftover = array_diff($parentids, $updated); 2050 if ($leftover) 2051 safe_update('textpattern',"comments_count=0","ID IN (".implode(',',$leftover).")"); 2052 } 2053 2054 // ------------------------------------------------------------- 2055 2056 function markup_comment($msg) 2057 { 2058 global $prefs; 2059 2060 $disallow_images = !empty($prefs['comments_disallow_images']); 2061 $lite = empty($prefs['comments_use_fat_textile']); 2062 2063 $rel = !empty($prefs['comment_nofollow']) ? 'nofollow' : ''; 2064 2065 include_once txpath.'/lib/classTextile.php'; 2066 2067 $textile = new Textile($prefs['doctype']); 2068 2069 return $textile->TextileRestricted($msg, $lite, $disallow_images, $rel); 2070 } 2071 2072 //------------------------------------------------------------- 2073 function update_lastmod() { 2074 safe_upsert("txp_prefs", "val = now()", "name = 'lastmod'"); 2075 } 2076 2077 //------------------------------------------------------------- 2078 function get_lastmod($unix_ts=NULL) { 2079 global $prefs; 2080 2081 if ($unix_ts === NULL) 2082 $unix_ts = @strtotime($prefs['lastmod']); 2083 2084 # check for future articles that are now visible 2085 if ($max_article = safe_field('unix_timestamp(Posted)', 'textpattern', "Posted <= now() and Status >= 4 order by Posted desc limit 1")) { 2086 $unix_ts = max($unix_ts, $max_article); 2087 } 2088 2089 return $unix_ts; 2090 } 2091 2092 //------------------------------------------------------------- 2093 function handle_lastmod($unix_ts=NULL, $exit=1) { 2094 global $prefs; 2095 extract($prefs); 2096 2097 if($send_lastmod and $production_status == 'live') { 2098 $unix_ts = get_lastmod($unix_ts); 2099 2100 # make sure lastmod isn't in the future 2101 $unix_ts = min($unix_ts, time()); 2102 # or too far in the past (7 days) 2103 $unix_ts = max($unix_ts, time() - 3600*24*7); 2104 2105 $last = safe_strftime('rfc822', $unix_ts, 1); 2106 header("Last-Modified: $last"); 2107 header('Cache-Control: no-cache'); 2108 2109 $hims = serverset('HTTP_IF_MODIFIED_SINCE'); 2110 if ($hims and @strtotime($hims) >= $unix_ts) { 2111 log_hit('304'); 2112 if (!$exit) 2113 return array('304', $last); 2114 txp_status_header('304 Not Modified'); 2115 # some mod_deflate versions have a bug that breaks subsequent 2116 # requests when keepalive is used. dropping the connection 2117 # is the only reliable way to fix this. 2118 if (empty($lastmod_keepalive)) 2119 header('Connection: close'); 2120 header('Content-Length: 0'); 2121 # discard all output 2122 while (@ob_end_clean()); 2123 exit; 2124 } 2125 2126 if (!$exit) 2127 return array('200', $last); 2128 } 2129 } 2130 2131 //------------------------------------------------------------- 2132 function set_pref($name, $val, $event='publish', $type=0, $html='text_input', $position=0, $is_private=PREF_GLOBAL) 2133 { 2134 global $txp_user; 2135 extract(doSlash(func_get_args())); 2136 2137 $user_name = ''; 2138 if ($is_private == PREF_PRIVATE) { 2139 if (empty($txp_user)) 2140 return false; 2141 2142 $user_name = 'user_name = \''.doSlash($txp_user).'\''; 2143 } 2144 2145 if (!safe_row('name', 'txp_prefs', "name = '$name'" . ($user_name ? " AND $user_name" : ''))) { 2146 $user_name = ($user_name ? "$user_name," : ''); 2147 return safe_insert('txp_prefs', " 2148 name = '$name', 2149 val = '$val', 2150 event = '$event', 2151 html = '$html', 2152 type = '$type', 2153 position = '$position', 2154 $user_name 2155 prefs_id = 1" 2156 ); 2157 } else { 2158 return safe_update('txp_prefs', "val = '$val'","name like '$name'" . ($user_name ? " AND $user_name" : '')); 2159 } 2160 } 2161 2162 //------------------------------------------------------------- 2163 function get_pref($thing, $default='', $from_db=0) // checks $prefs for a named variable, or creates a default 2164 { 2165 global $prefs, $txp_user; 2166 2167 if ($from_db) 2168 { 2169 $name = doSlash($thing); 2170 $user_name = doSlash($txp_user); 2171 // prefer system prefs over user's prefs 2172 $field = safe_field('val', 'txp_prefs', 2173 "name='$name' AND (user_name='' OR user_name='$user_name') order by user_name limit 1"); 2174 if ($field !== false) 2175 { 2176 $prefs[$thing] = $field; 2177 } 2178 } 2179 return (isset($prefs[$thing])) ? $prefs[$thing] : $default; 2180 } 2181 2182 // ------------------------------------------------------------- 2183 function getCustomFields() 2184 { 2185 global $prefs; 2186 static $out = NULL; 2187 2188 // have cache? 2189 if (!is_array($out)) 2190 { 2191 $cfs = preg_grep('/^custom_\d+_set/', array_keys($prefs)); 2192 2193 $out = array(); 2194 foreach ($cfs as $name) { 2195 preg_match('/(\d+)/', $name, $match); 2196 if (!empty($prefs[$name])) { 2197 $out[$match[1]] = strtolower($prefs[$name]); 2198 } 2199 } 2200 } 2201 return $out; 2202 } 2203 2204 /** 2205 * Build a query qualifier to filter non-matching custom fields from the result set 2206 * 2207 * @param array $custom An array of 'custom_field_name' => field_number tupels 2208 * @param array $pairs Filter criteria: An array of 'name' => value tupels 2209 * @return bool|string A SQL qualifier for a querys 'WHERE' part 2210 */ 2211 // ------------------------------------------------------------- 2212 function buildCustomSql($custom,$pairs) 2213 { 2214 if ($pairs) { 2215 $pairs = doSlash($pairs); 2216 foreach($pairs as $k => $v) { 2217 if(in_array($k,$custom)) { 2218 $no = array_keys($custom,$k); 2219 # nb - use 'like' here to allow substring matches 2220 $out[] = "and custom_".$no[0]." like '$v'"; 2221 } 2222 } 2223 } 2224 return (!empty($out)) ? ' '.join(' ',$out).' ' : false; 2225 } 2226 2227 // ------------------------------------------------------------- 2228 function txp_status_header($status='200 OK') 2229 { 2230 if (IS_FASTCGI) 2231 header("Status: $status"); 2232 elseif ($_SERVER['SERVER_PROTOCOL'] == 'HTTP/1.0') 2233 header("HTTP/1.0 $status"); 2234 else 2235 header("HTTP/1.1 $status"); 2236 } 2237 2238 // ------------------------------------------------------------- 2239 function txp_die($msg, $status='503', $url='') 2240 { 2241 // 503 status might discourage search engines from indexing or caching the error message 2242 2243 //Make it possible to call this function as a tag, e.g. in an article <txp:txp_die status="410" /> 2244 if (is_array($msg)) 2245 extract(lAtts(array('msg' => '', 'status' => '503', 'url' => ''),$msg)); 2246 2247 // Intentionally incomplete - just the ones we're likely to use 2248 $codes = array( 2249 '200' => 'OK', 2250 '301' => 'Moved Permanently', 2251 '302' => 'Found', 2252 '304' => 'Not Modified', 2253 '307' => 'Temporary Redirect', 2254 '401' => 'Unauthorized', 2255 '403' => 'Forbidden', 2256 '404' => 'Not Found', 2257 '410' => 'Gone', 2258 '414' => 'Request-URI Too Long', 2259 '500' => 'Internal Server Error', 2260 '501' => 'Not Implemented', 2261 '503' => 'Service Unavailable', 2262 ); 2263 2264 if ($status) { 2265 if (isset($codes[strval($status)])) 2266 $status = strval($status) . ' ' . $codes[$status]; 2267 2268 txp_status_header($status); 2269 } 2270 2271 $code = ''; 2272 if ($status and $parts = @explode(' ', $status, 2)) { 2273 $code = @$parts[0]; 2274 } 2275 2276 callback_event('txp_die', $code); 2277 2278 // redirect with status 2279 if ($url && in_array($code, array(301, 302, 307))) { 2280 ob_end_clean(); 2281 header("Location: $url", true, $code); 2282 die('<html><head><meta http-equiv="refresh" content="0;URL='.txpspecialchars($url).'"></head><body></body></html>'); 2283 } 2284 2285 if (@$GLOBALS['connected'] && @txpinterface == 'public') { 2286 $out = safe_field('user_html','txp_page',"name='error_".doSlash($code)."'"); 2287 if ($out === false) 2288 $out = safe_field('user_html','txp_page',"name='error_default'"); 2289 } 2290 2291 if (!isset($out)) 2292 $out = <<<eod 2293 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 2294 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2295 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 2296 <head> 2297 <meta http-equiv="content-type" content="text/html; charset=utf-8" /> 2298 <title>Textpattern Error: <txp:error_status /></title> 2299 </head> 2300 <body> 2301 <p align="center" style="margin-top:4em"><txp:error_message /></p> 2302 </body> 2303 </html> 2304 eod; 2305 2306 header("Content-type: text/html; charset=utf-8"); 2307 2308 if (is_callable('parse')) { 2309 2310 $GLOBALS['txp_error_message'] = $msg; 2311 $GLOBALS['txp_error_status'] = $status; 2312 $GLOBALS['txp_error_code'] = $code; 2313 2314 set_error_handler("tagErrorHandler"); 2315 die(parse($out)); 2316 } 2317 else { 2318 $out = preg_replace(array('@<txp:error_status[^>]*/>@', '@<txp:error_message[^>]*/>@'), 2319 array($status, $msg), 2320 $out); 2321 die($out); 2322 } 2323 } 2324 2325 // ------------------------------------------------------------- 2326 function join_qs($q) 2327 { 2328 $qs = array(); 2329 foreach ($q as $k=>$v) 2330 { 2331 if (is_array($v)) 2332 { 2333 $v = join(',', $v); 2334 } 2335 if ($v) 2336 { 2337 $qs[] = urlencode($k) . '=' . urlencode($v); 2338 } 2339 } 2340 2341 $str = join('&', $qs); 2342 return ($str ? '?'.$str : ''); 2343 } 2344 2345 // ------------------------------------------------------------- 2346 2347 function pagelinkurl($parts, $inherit = array()) 2348 { 2349 global $permlink_mode, $prefs; 2350 2351 // $inherit can be used to add parameters to an existing url, e.g: 2352 // $url = pagelinkurl(array('pg'=>2), $pretext); 2353 $keys = array_merge($inherit, $parts); 2354 2355 if (isset($prefs['custom_url_func']) 2356 and is_callable($prefs['custom_url_func']) 2357 and ($url = call_user_func($prefs['custom_url_func'], $keys, PAGELINKURL)) !== FALSE) 2358 { 2359 return $url; 2360 } 2361 2362 // can't use this to link to an article 2363 if (isset($keys['id'])) 2364 { 2365 unset($keys['id']); 2366 } 2367 2368 if (isset($keys['s']) && $keys['s'] == 'default') 2369 { 2370 unset($keys['s']); 2371 } 2372 2373 // 'article' context is implicit, no need to add it to the page URL 2374 if (isset($keys['context']) && $keys['context'] == 'article') 2375 { 2376 unset($keys['context']); 2377 } 2378 2379 if ($permlink_mode == 'messy') 2380 { 2381 if (!empty($keys['context'])) { 2382 $keys['context'] = gTxt($keys['context'].'_context'); 2383 } 2384 return hu.'index.php'.join_qs($keys); 2385 } 2386 2387 else 2388 { 2389 // all clean URL modes use the same schemes for list pages 2390 $url = ''; 2391 2392 if (!empty($keys['rss'])) 2393 { 2394 $url = hu.'rss/'; 2395 unset($keys['rss']); 2396 return $url.join_qs($keys); 2397 } 2398 2399 elseif (!empty($keys['atom'])) 2400 { 2401 $url = hu.'atom/'; 2402 unset($keys['atom']); 2403 return $url.join_qs($keys); 2404 } 2405 2406 elseif (!empty($keys['s'])) 2407 { 2408 if (!empty($keys['context'])) { 2409 $keys['context'] = gTxt($keys['context'].'_context'); 2410 } 2411 $url = hu.urlencode($keys['s']).'/'; 2412 unset($keys['s']); 2413 return $url.join_qs($keys); 2414 } 2415 2416 elseif (!empty($keys['author'])) 2417 { 2418 $ct = empty($keys['context']) ? '' : strtolower(urlencode(gTxt($keys['context'].'_context'))).'/'; 2419 $url = hu.strtolower(urlencode(gTxt('author'))).'/'.$ct.urlencode($keys['author']).'/'; 2420 unset($keys['author'], $keys['context']); 2421 return $url.join_qs($keys); 2422 } 2423 2424 elseif (!empty($keys['c'])) 2425 { 2426 $ct = empty($keys['context']) ? '' : strtolower(urlencode(gTxt($keys['context'].'_context'))).'/'; 2427 $url = hu.strtolower(urlencode(gTxt('category'))).'/'.$ct.urlencode($keys['c']).'/'; 2428 unset($keys['c'], $keys['context']); 2429 return $url.join_qs($keys); 2430 } 2431 2432 return hu.join_qs($keys); 2433 } 2434 } 2435 2436 // ------------------------------------------------------------- 2437 function filedownloadurl($id, $filename='') 2438 { 2439 global $permlink_mode; 2440 2441 $filename = urlencode($filename); 2442 #FIXME: work around yet another mod_deflate problem (double compression) 2443 # http://blogs.msdn.com/wndp/archive/2006/08/21/Content-Encoding-not-equal-Content-Type.aspx 2444 if (preg_match('/gz$/i', $filename)) 2445 $filename .= a; 2446 return ($permlink_mode == 'messy') ? 2447 hu.'index.php?s=file_download'.a.'id='.$id : 2448 hu.gTxt('file_download').'/'.$id.($filename ? '/'.$filename : ''); 2449 } 2450 2451 // ------------------------------------------------------------- 2452 function imagesrcurl($id, $ext, $thumbnail = false) 2453 { 2454 global $img_dir; 2455 $thumbnail = ($thumbnail ? 't' : ''); 2456 return ihu.$img_dir.'/'.$id.$thumbnail.$ext; 2457 } 2458 2459 // ------------------------------------------------------------- 2460 2461 function in_list($val, $list, $delim = ',') 2462 { 2463 $args = do_list($list, $delim); 2464 2465 return in_array($val, $args); 2466 } 2467 2468 // ------------------------------------------------------------- 2469 2470 function do_list($list, $delim = ',') 2471 { 2472 return array_map('trim', explode($delim, $list)); 2473 } 2474 2475 // ------------------------------------------------------------- 2476 function doQuote($val) 2477 { 2478 return "'$val'"; 2479 } 2480 2481 // ------------------------------------------------------------- 2482 function quote_list($in) 2483 { 2484 $out = doSlash($in); 2485 return doArray($out, 'doQuote'); 2486 } 2487 2488 // ------------------------------------------------------------- 2489 function trace_add($msg) 2490 { 2491 global $production_status; 2492 2493 if ($production_status === 'debug') 2494 { 2495 global $txptrace,$txptracelevel; 2496 2497 $txptrace[] = str_repeat("\t", $txptracelevel).$msg; 2498 } 2499 } 2500 2501 //------------------------------------------------------------- 2502 function article_push() { 2503 global $thisarticle, $stack_article; 2504 $stack_article[] = @$thisarticle; 2505 } 2506 2507 //------------------------------------------------------------- 2508 function article_pop() { 2509 global $thisarticle, $stack_article; 2510 $thisarticle = array_pop($stack_article); 2511 } 2512 // ------------------------------------------------------------- 2513 2514 function relative_path($path, $pfx=NULL) 2515 { 2516 if ($pfx === NULL) 2517 $pfx = dirname(txpath); 2518 return preg_replace('@^/'.preg_quote(ltrim($pfx, '/'), '@').'/?@', '', $path); 2519 } 2520 2521 // ------------------------------------------------------------- 2522 function get_caller($num=1,$start=2) 2523 { 2524 $out = array(); 2525 if (!is_callable('debug_backtrace')) 2526 return $out; 2527 2528 $bt = debug_backtrace(); 2529 for ($i=$start; $i< $num+$start; $i++) { 2530 if (!empty($bt[$i])) { 2531 $t = ''; 2532 if (!empty($bt[$i]['file'])) 2533 $t .= relative_path($bt[$i]['file']); 2534 if (!empty($bt[$i]['line'])) 2535 $t .= ':'.$bt[$i]['line']; 2536 if ($t) 2537 $t .= ' '; 2538 if (!empty($bt[$i]['class'])) 2539 $t .= $bt[$i]['class']; 2540 if (!empty($bt[$i]['type'])) 2541 $t .= $bt[$i]['type']; 2542 if (!empty($bt[$i]['function'])) { 2543 $t .= $bt[$i]['function']; 2544 2545 $t .= '()'; 2546 } 2547 2548 2549 $out[] = $t; 2550 } 2551 } 2552 return $out; 2553 } 2554 2555 //------------------------------------------------------------- 2556 // function name is misleading but remains for legacy reasons 2557 // this actually sets the locale 2558 function getlocale($lang) { 2559 global $locale; 2560 2561 if (empty($locale)) 2562 $locale = @setlocale(LC_TIME, '0'); 2563 2564 // Locale identifiers vary from system to system. The 2565 // following code will attempt to discover which identifiers 2566 // are available. We'll need to expand these lists to 2567 // improve support. 2568 // ISO identifiers: http://www.w3.org/WAI/ER/IG/ert/iso639.htm 2569 // Windows: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt_language_strings.asp 2570 $guesses = array( 2571 'ar-dz' => array('ar_DZ.UTF-8', 'ar_DZ', 'ara', 'ar', 'arabic', 'ar_DZ.ISO_8859-6'), 2572 'bg-bg' => array('bg_BG.UTF-8', 'bg_BG', 'bg', 'bul', 'bulgarian', 'bg_BG.ISO8859-5'), 2573 'ca-es' => array('ca_ES.UTF-8', 'ca_ES', 'cat', 'ca', 'catalan', 'ca_ES.ISO_8859-1'), 2574 'cs-cz' => array('cs_CZ.UTF-8', 'cs_CZ', 'ces', 'cze', 'cs', 'csy', 'czech', 'cs_CZ.cs_CZ.ISO_8859-2'), 2575 'da-dk' => array('da_DK.UTF-8', 'da_DK'), 2576 'de-de' => array('de_DE.UTF-8', 'de_DE', 'de', 'deu', 'german', 'de_DE.ISO_8859-1'), 2577 'en-gb' => array('en_GB.UTF-8', 'en_GB', 'en_UK', 'eng', 'en', 'english-uk', 'english', 'en_GB.ISO_8859-1','C'), 2578 'en-us' => array('en_US.UTF-8', 'en_US', 'english-us', 'en_US.ISO_8859-1'), 2579 'es-es' => array('es_ES.UTF-8', 'es_ES', 'esp', 'spanish', 'es_ES.ISO_8859-1'), 2580 'et-ee' => array('et_EE.UTF-8', 'et_EE'), 2581 'el-gr' => array('el_GR.UTF-8', 'el_GR', 'el', 'gre', 'greek', 'el_GR.ISO_8859-7'), 2582 'fi-fi' => array('fi_FI.UTF-8', 'fi_FI', 'fin', 'fi', 'finnish', 'fi_FI.ISO_8859-1'), 2583 'fr-fr' => array('fr_FR.UTF-8', 'fr_FR', 'fra', 'fre', 'fr', 'french', 'fr_FR.ISO_8859-1'), 2584 'gl-gz' => array('gl_GZ.UTF-8', 'gl_GZ', 'glg', 'gl', '', ''), 2585 'he_il' => array('he_IL.UTF-8', 'he_IL', 'heb', 'he', 'hebrew', 'he_IL.ISO_8859-8'), 2586 'hr-hr' => array('hr_HR.UTF-8', 'hr_HR', 'hr'), 2587 'hu-hu' => array('hu_HU.UTF-8', 'hu_HU', 'hun', 'hu', 'hungarian', 'hu_HU.ISO8859-2'), 2588 'id-id' => array('id_ID.UTF-8', 'id_ID', 'id', 'ind', 'indonesian','id_ID.ISO_8859-1'), 2589 'is-is' => array('is_IS.UTF-8', 'is_IS'), 2590 'it-it' => array('it_IT.UTF-8', 'it_IT', 'it', 'ita', 'italian', 'it_IT.ISO_8859-1'), 2591 'ja-jp' => array('ja_JP.UTF-8', 'ja_JP', 'ja', 'jpn', 'japanese', 'ja_JP.ISO_8859-1'), 2592 'ko-kr' => array('ko_KR.UTF-8', 'ko_KR', 'ko', 'kor', 'korean'), 2593 'lv-lv' => array('lv_LV.UTF-8', 'lv_LV', 'lv', 'lav'), 2594 'nl-nl' => array('nl_NL.UTF-8', 'nl_NL', 'dut', 'nla', 'nl', 'nld', 'dutch', 'nl_NL.ISO_8859-1'), 2595 'no-no' => array('no_NO.UTF-8', 'no_NO', 'no', 'nor', 'norwegian', 'no_NO.ISO_8859-1'), 2596 'pl-pl' => array('pl_PL.UTF-8', 'pl_PL', 'pl', 'pol', 'polish', ''), 2597 'pt-br' => array('pt_BR.UTF-8', 'pt_BR', 'pt', 'ptb', 'portuguese-brazil', ''), 2598 'pt-pt' => array('pt_PT.UTF-8', 'pt_PT', 'por', 'portuguese', 'pt_PT.ISO_8859-1'), 2599 'ro-ro' => array('ro_RO.UTF-8', 'ro_RO', 'ron', 'rum', 'ro', 'romanian', 'ro_RO.ISO8859-2'), 2600 'ru-ru' => array('ru_RU.UTF-8', 'ru_RU', 'ru', 'rus', 'russian', 'ru_RU.ISO8859-5'), 2601 'sk-sk' => array('sk_SK.UTF-8', 'sk_SK', 'sk', 'slo', 'slk', 'sky', 'slovak', 'sk_SK.ISO_8859-1'), 2602 'sv-se' => array('sv_SE.UTF-8', 'sv_SE', 'sv', 'swe', 'sve', 'swedish', 'sv_SE.ISO_8859-1'), 2603 'th-th' => array('th_TH.UTF-8', 'th_TH', 'th', 'tha', 'thai', 'th_TH.ISO_8859-11'), 2604 'uk-ua' => array('uk_UA.UTF-8', 'uk_UA', 'uk', 'ukr'), 2605 'vi-vn' => array('vi_VN.UTF-8', 'vi_VN', 'vi', 'vie'), 2606 'zh-cn' => array('zh_CN.UTF-8', 'zh_CN'), 2607 'zh-tw' => array('zh_TW.UTF-8', 'zh_TW'), 2608 ); 2609 2610 if (!empty($guesses[$lang])) { 2611 $l = @setlocale(LC_TIME, $guesses[$lang]); 2612 if ($l !== false) 2613 $locale = $l; 2614 } 2615 @setlocale(LC_TIME, $locale); 2616 2617 return $locale; 2618 } 2619 2620 //------------------------------------------------------------- 2621 function assert_article() { 2622 global $thisarticle; 2623 if (empty($thisarticle)) 2624 trigger_error(gTxt('error_article_context')); 2625 } 2626 2627 //------------------------------------------------------------- 2628 function assert_comment() { 2629 global $thiscomment; 2630 if (empty($thiscomment)) 2631 trigger_error(gTxt('error_comment_context')); 2632 } 2633 2634 //------------------------------------------------------------- 2635 function assert_file() { 2636 global $thisfile; 2637 if (empty($thisfile)) 2638 trigger_error(gTxt('error_file_context')); 2639 } 2640 2641 //------------------------------------------------------------- 2642 function assert_image() { 2643 global $thisimage; 2644 if (empty($thisimage)) 2645 trigger_error(gTxt('error_image_context')); 2646 } 2647 2648 //------------------------------------------------------------- 2649 function assert_link() { 2650 global $thislink; 2651 if (empty($thislink)) 2652 trigger_error(gTxt('error_link_context')); 2653 } 2654 2655 //------------------------------------------------------------- 2656 function assert_section() { 2657 global $thissection; 2658 if (empty($thissection)) 2659 trigger_error(gTxt('error_section_context')); 2660 } 2661 2662 //------------------------------------------------------------- 2663 function assert_category() { 2664 global $thiscategory; 2665 if (empty($thiscategory)) 2666 trigger_error(gTxt('error_category_context')); 2667 } 2668 2669 //------------------------------------------------------------- 2670 function assert_int($myvar) { 2671 if (is_numeric($myvar) and $myvar == intval($myvar)) { 2672 return (int) $myvar; 2673 } 2674 trigger_error("'".txpspecialchars((string)$myvar)."' is not an integer", E_USER_ERROR); 2675 return false; 2676 } 2677 2678 //------------------------------------------------------------- 2679 function assert_string($myvar) { 2680 if (is_string($myvar)) { 2681 return $myvar; 2682 } 2683 trigger_error("'".txpspecialchars((string)$myvar)."' is not a string", E_USER_ERROR); 2684 return false; 2685 } 2686 2687 //------------------------------------------------------------- 2688 function assert_array($myvar) { 2689 if (is_array($myvar)) { 2690 return $myvar; 2691 } 2692 trigger_error("'".txpspecialchars((string)$myvar)."' is not an array", E_USER_ERROR); 2693 return false; 2694 } 2695 2696 //------------------------------------------------------------- 2697 function replace_relative_urls($html, $permalink='') { 2698 2699 global $siteurl; 2700 2701 # urls like "/foo/bar" - relative to the domain 2702 if (serverSet('HTTP_HOST')) { 2703 $html = preg_replace('@(<a[^>]+href=")/@','$1'.PROTOCOL.serverSet('HTTP_HOST').'/',$html); 2704 $html = preg_replace('@(<img[^>]+src=")/@','$1'.PROTOCOL.serverSet('HTTP_HOST').'/',$html); 2705 } 2706 # "foo/bar" - relative to the textpattern root 2707 # leave "http:", "mailto:" et al. as absolute urls 2708 $html = preg_replace('@(<a[^>]+href=")(?!\w+:)@','$1'.PROTOCOL.$siteurl.'/$2',$html); 2709 $html = preg_replace('@(<img[^>]+src=")(?!\w+:)@','$1'.PROTOCOL.$siteurl.'/$2',$html); 2710 2711 if ($permalink) 2712 $html = preg_replace("/href=\\\"#(.*)\"/","href=\"".$permalink."#\\1\"",$html); 2713 return ($html); 2714 } 2715 2716 //------------------------------------------------------------- 2717 function show_clean_test($pretext) { 2718 echo md5(@$pretext['req']).n; 2719 if (serverSet('SERVER_ADDR') == serverSet('REMOTE_ADDR')) 2720 { 2721 var_export($pretext); 2722 } 2723 } 2724 2725 //------------------------------------------------------------- 2726 2727 function pager($total, $limit, $page) { 2728 $total = (int) $total; 2729 $limit = (int) $limit; 2730 $page = (int) $page; 2731 2732 $num_pages = ceil($total / $limit); 2733 2734 $page = min(max($page, 1), $num_pages); 2735 2736 $offset = max(($page - 1) * $limit, 0); 2737 2738 return array($page, $offset, $num_pages); 2739 } 2740 2741 //------------------------------------------------------------- 2742 // word-wrap a string using a zero width space 2743 function soft_wrap($text, $width, $break='​') 2744 { 2745 $wbr = chr(226).chr(128).chr(139); 2746 $words = explode(' ', $text); 2747 foreach($words as $wordnr => $word) { 2748 $word = preg_replace('|([,./\\>?!:;@-]+)(?=.)|', '$1 ', $word); 2749 $parts = explode(' ', $word); 2750 foreach($parts as $partnr => $part) { 2751 $len = strlen(utf8_decode($part)); 2752 if (!$len) continue; 2753 $parts[$partnr] = preg_replace('/(.{'.ceil($len/ceil($len/$width)).'})(?=.)/u', '$1'.$wbr, $part); 2754 } 2755 $words[$wordnr] = join($wbr, $parts); 2756 } 2757 return join(' ', $words); 2758 } 2759 2760 //------------------------------------------------------------- 2761 function strip_prefix($str, $pfx) { 2762 return preg_replace('/^'.preg_quote($pfx, '/').'/', '', $str); 2763 } 2764 2765 //------------------------------------------------------------- 2766 // wrap an array of name => value tupels into an XML envelope, 2767 // supports one level of nested arrays at most. 2768 function send_xml_response($response=array()) 2769 { 2770 static $headers_sent = false; 2771 2772 if (!$headers_sent) { 2773 ob_clean(); 2774 header('Content-Type: text/xml; charset=utf-8'); 2775 $out[] = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>'; 2776 $headers_sent = true; 2777 } 2778 2779 $default_response = array ( 2780 'http-status' => '200 OK', 2781 ); 2782 2783 // backfill default response properties 2784 $response = $response + $default_response; 2785 2786 txp_status_header($response['http-status']); 2787 $out[] = '<textpattern>'; 2788 foreach ($response as $element => $value) 2789 { 2790 if (is_array($value)) 2791 { 2792 $out[] = t."<$element>".n; 2793 foreach ($value as $e => $v) 2794 { 2795 // Character escaping in values; @see http://www.w3.org/TR/2000/WD-xml-c14n-20000119.html#charescaping 2796 $v = str_replace(array("\t", "\n", "\r"), array("	", "
", "
"), htmlentities($v, ENT_QUOTES, 'UTF-8')); 2797 $out[] = t.t."<$e value='$v' />".n; 2798 } 2799 $out[] = t."</$element>".n; 2800 } 2801 else 2802 { 2803 $value = str_replace(array("\t", "\n", "\r"), array("	", "
", "
"), htmlentities($value, ENT_QUOTES, 'UTF-8')); 2804 $out[] = t."<$element value='$value' />".n; 2805 } 2806 } 2807 $out[] = '</textpattern>'; 2808 echo join(n, $out); 2809 } 2810 2811 /** 2812 * Send a text/javascript response 2813 * 2814 * @param string $out 2815 * @since 4.4 2816 */ 2817 function send_script_response($out = '') 2818 { 2819 static $headers_sent = false; 2820 if (!$headers_sent) { 2821 ob_clean(); 2822 header('Content-Type: text/javascript; charset=utf-8'); 2823 txp_status_header('200 OK'); 2824 $headers_sent = true; 2825 } 2826 echo ";\n".$out.";\n"; 2827 } 2828 2829 /** 2830 * Display a modal client message in response to an AJAX request and halt execution. 2831 * 2832 * @param array $message string|array: $message[0] is the message's text; $message[1] is the message's type (one of E_ERROR or E_WARNING, anything else meaning "success"; not used) 2833 * @since 4.5 2834 */ 2835 function modal_halt($thing) 2836 { 2837 global $app_mode, $theme; 2838 if ($app_mode == 'async') 2839 { 2840 send_script_response($theme->announce_async($thing, true)); 2841 die(); 2842 } 2843 } 2844 2845 // ------------------------------------------------------------- 2846 // Perform regular housekeeping. 2847 // Might evolve into some kind of pseudo-cron later... 2848 function janitor() 2849 { 2850 global $prefs; 2851 2852 // update DST setting 2853 global $auto_dst, $timezone_key, $is_dst; 2854 if ($auto_dst && $timezone_key) 2855 { 2856 $is_dst = timezone::is_dst(time(), $timezone_key); 2857 if ($is_dst != $prefs['is_dst']) 2858 { 2859 $prefs['is_dst'] = $is_dst; 2860 set_pref('is_dst', $is_dst, 'publish', 2); 2861 } 2862 } 2863 2864 // deprecation nags 2865 if (AJAXALLY_CHALLENGED) 2866 { 2867 trigger_error(gTxt('deprecated_configuration', array('{name}' => 'AJAXALLY_CHALLENGED')), E_USER_NOTICE); 2868 } 2869 } 2870 2871 // ------------------------------------------------------------- 2872 // Dealing with timezones. 2873 class timezone 2874 { 2875 private $_details; 2876 private $_offsets; 2877 2878 function __construct() 2879 { 2880 if (!timezone::is_supported()) 2881 { 2882 // Standard time zones as compiled by H.M. Nautical Almanac Office, June 2004 2883 // http://aa.usno.navy.mil/faq/docs/world_tzones.html 2884 $timezones = array( 2885 -12, -11, -10, -9.5, -9, -8.5, -8, -7, -6, -5, -4, -3.5, -3, -2, -1, 2886 0, 2887 +1, +2, +3, +3.5, +4, +4.5, +5, +5.5, +6, +6.5, +7, +8, +9, +9.5, +10, +10.5, +11, +11.5, +12, +13, +14, 2888 ); 2889 2890 foreach ($timezones as $tz) 2891 { 2892 // Fake timezone id 2893 $timezone_id = 'GMT'.sprintf('%+05.1f', $tz); 2894 $sign = ($tz >= 0 ? '+' : ''); 2895 $label = sprintf("GMT %s%02d:%02d", $sign, $tz, abs($tz - (int)$tz) * 60); 2896 $this->_details[$timezone_id]['continent'] = gTxt('timezone_gmt'); 2897 $this->_details[$timezone_id]['city'] = $label; 2898 $this->_details[$timezone_id]['offset'] = $tz * 3600; 2899 $this->_offsets[$tz * 3600] = $timezone_id; 2900 } 2901 } 2902 else 2903 { 2904 $continents = array('Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 2905 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific'); 2906 2907 $server_tz = @date_default_timezone_get(); 2908 $tzlist = timezone_abbreviations_list(); 2909 foreach ($tzlist as $abbr => $timezones) 2910 { 2911 foreach ($timezones as $tz) 2912 { 2913 $timezone_id = $tz['timezone_id']; 2914 // $timezone_ids are not unique among abbreviations 2915 if ($timezone_id && !isset($this->_details[$timezone_id])) 2916 { 2917 $parts = explode('/', $timezone_id); 2918 if (in_array($parts[0], $continents)) 2919 { 2920 if (!empty($server_tz)) 2921 { 2922 if (date_default_timezone_set($timezone_id)) 2923 { 2924 $is_dst = date('I', time()); 2925 } 2926 } 2927 2928 $this->_details[$timezone_id]['continent'] = $parts[0]; 2929 $this->_details[$timezone_id]['city'] = (isset($parts[1])) ? $parts[1] : ''; 2930 $this->_details[$timezone_id]['subcity'] = (isset($parts[2])) ? $parts[2] : ''; 2931 $this->_details[$timezone_id]['offset'] = date_offset_get(date_create()) - ($is_dst ? 3600 : 0); 2932 $this->_details[$timezone_id]['dst'] = $tz['dst']; 2933 $this->_details[$timezone_id]['abbr'] = strtoupper($abbr); 2934 2935 // Guesstimate a timezone key for a given GMT offset 2936 $this->_offsets[$tz['offset']] = $timezone_id; 2937 } 2938 } 2939 } 2940 } 2941 } 2942 2943 if (!empty($server_tz)) 2944 { 2945 date_default_timezone_set($server_tz); 2946 } 2947 } 2948 2949 /** 2950 * Render HTML SELECT element for choosing a timezone 2951 * @param string $name Element name 2952 * @param string $value Selected timezone 2953 * @param boolean $blank_first Add empty first option 2954 * @param boolean|string $onchange n/a 2955 * @param string $select_id HTML id attribute 2956 * @return string HTML markup 2957 */ 2958 function selectInput($name = '', $value = '', $blank_first = '', $onchange = '', $select_id = '') 2959 { 2960 if (!empty($this->_details)) 2961 { 2962 $thiscontinent = ''; 2963 $selected = false; 2964 2965 ksort($this->_details); 2966 foreach ($this->_details as $timezone_id => $tz) 2967 { 2968 extract($tz); 2969 if ($value == $timezone_id) $selected = true; 2970 if ($continent !== $thiscontinent) 2971 { 2972 if ($thiscontinent !== '') $out[] = n.t.'</optgroup>'; 2973 $out[] = n.t.'<optgroup label="'.gTxt($continent).'">'; 2974 $thiscontinent = $continent; 2975 } 2976 2977 $where = gTxt(str_replace('_', ' ', $city)) 2978 .(!empty($subcity) ? '/'.gTxt(str_replace('_', ' ', $subcity)) : '').t 2979 /*."($abbr)"*/; 2980 $out[] = n.t.t.'<option value="'.txpspecialchars($timezone_id).'"'.($value == $timezone_id ? ' selected="selected"' : '').'>'.$where.'</option>'; 2981 } 2982 $out[] = n.t.'</optgroup>'; 2983 return n.'<select'.( $select_id ? ' id="'.$select_id.'"' : '' ).' name="'.$name.'"'. 2984 ($onchange == 1 ? ' onchange="submit(this.form);"' : $onchange). 2985 '>'. 2986 ($blank_first ? n.t.'<option value=""'.($selected == false ? ' selected="selected"' : '').'></option>' : ''). 2987 join('', $out). 2988 n.'</select>'; 2989 } 2990 return ''; 2991 } 2992 2993 /** 2994 * Build a matrix of timezone details 2995 * @return array Array of timezone details indexed by timezone key 2996 */ 2997 function details() 2998 { 2999 return $this->_details; 3000 } 3001 3002 /** 3003 * Find a timezone key matching a given GMT offset. 3004 * NB: More than one key might fit any given GMT offset, 3005 * thus the returned value is ambiguous and merely useful for presentation purposes. 3006 * @param integer $gmtoffset 3007 * @return string timezone key 3008 */ 3009 function key($gmtoffset) 3010 { 3011 return isset($this->_offsets[$gmtoffset]) ? $this->_offsets[$gmtoffset] : ''; 3012 } 3013 3014 /** 3015 * Is DST in effect? 3016 * @param integer $timestamp When? 3017 * @param string $timezone_key Where? 3018 * @return boolean Yes, they are saving time, actually. 3019 */ 3020 static function is_dst($timestamp, $timezone_key) 3021 { 3022 global $is_dst, $auto_dst; 3023 3024 $out = $is_dst; 3025 if ($auto_dst && $timezone_key && timezone::is_supported()) 3026 { 3027 $server_tz = @date_default_timezone_get(); 3028 if ($server_tz) 3029 { 3030 // switch to client time zone 3031 if (date_default_timezone_set($timezone_key)) 3032 { 3033 $out = date('I', $timestamp); 3034 // restore server time zone 3035 date_default_timezone_set($server_tz); 3036 } 3037 } 3038 } 3039 return $out; 3040 } 3041 3042 /** 3043 * Check for run-time timezone support 3044 * @return boolean Timezone feature is enabled 3045 */ 3046 static function is_supported() 3047 { 3048 return !defined('NO_TIMEZONE_SUPPORT'); // user-definable emergency brake 3049 } 3050 } 3051 3052 //------------------------------------------------------------- 3053 function install_textpack($textpack, $add_new_langs = false) 3054 { 3055 global $prefs; 3056 3057 $textpack = explode(n, $textpack); 3058 if (empty($textpack)) return 0; 3059 3060 // presume site language equals textpack language 3061 $language = get_pref('language', 'en-gb'); 3062 3063 $installed_langs = safe_column('lang', 'txp_lang', "1 = 1 group by lang"); 3064 $doit = true; 3065 3066 $done = 0; 3067 foreach ($textpack as $line) 3068 { 3069 $line = trim($line); 3070 // A line starting with #, not followed by @ is a simple comment 3071 if (preg_match('/^#[^@]/', $line, $m)) 3072 { 3073 continue; 3074 } 3075 3076 // A line matching "#@language xx-xx" establishes the designated language for all subsequent lines 3077 if (preg_match('/^#@language\s+(.+)$/', $line, $m)) 3078 { 3079 $language = doSlash($m[1]); 3080 // May this Textpack introduce texts for this language? 3081 $doit = ($add_new_langs || in_array($language, $installed_langs)); 3082 continue; 3083 } 3084 3085 // A line matching "#@event_name" establishes the event value for all subsequent lines 3086 if (preg_match('/^#@([a-zA-Z0-9_-]+)$/', $line, $m)) 3087 { 3088 $event = doSlash($m[1]); 3089 continue; 3090 } 3091 3092 // Data lines match a "name => value" pattern. Some white space allowed. 3093 if ($doit && preg_match('/^(\w+)\s*=>\s*(.+)$/', $line, $m)) 3094 { 3095 if (!empty($m[1]) && !empty($m[2])) 3096 { 3097 $name = doSlash($m[1]); 3098 $value = doSlash($m[2]); 3099 $where = "lang='$language' AND name='$name'"; 3100 // Store text; do *not* tamper with last modification date from RPC but use a well-known date in the past 3101 if (safe_count('txp_lang', $where)) 3102 { 3103 safe_update('txp_lang', "lastmod='2005-08-14', data='$value', event='$event'", $where); 3104 } 3105 else 3106 { 3107 safe_insert('txp_lang', "lastmod='2005-08-14', data='$value', event='$event', lang='$language', name='$name'"); 3108 } 3109 ++$done; 3110 } 3111 } 3112 } 3113 return $done; 3114 } 3115 3116 /** 3117 * Generate a ciphered token. 3118 * 3119 * The token is reproducable, unique among sites and users, expires later. 3120 * 3121 * @return string The token. 3122 */ 3123 //------------------------------------------------------------- 3124 function form_token() 3125 { 3126 static $token; 3127 global $txp_user; 3128 3129 // Generate a ciphered token from the current user's nonce (thus valid for login time plus 30 days) 3130 // and a pinch of salt from the blog UID. 3131 if (empty($token)) { 3132 $nonce = safe_field('nonce', 'txp_users', "name='".doSlash($txp_user)."'"); 3133 $token = md5($nonce . get_pref('blog_uid')); 3134 } 3135 return $token; 3136 } 3137 3138 /** 3139 * Assert system requirements 3140 */ 3141 //------------------------------------------------------------- 3142 function assert_system_requirements() 3143 { 3144 if (version_compare(REQUIRED_PHP_VERSION, PHP_VERSION) > 0) { 3145 txp_die('This server runs PHP version '.PHP_VERSION.'. Textpattern needs PHP version '. REQUIRED_PHP_VERSION. ' or better.'); 3146 } 3147 } 3148 3149 3150 /** 3151 * Validate admin steps. Protect against CSRF attempts. 3152 * 3153 * @param string $step Requested admin step. 3154 * @param array $steps An array of valid steps with flag indicating CSRF needs, e.g. array('savething' => true, 'listthings' => false) 3155 * @return boolean $step is valid, proceed. Dies on CSRF attempt. 3156 */ 3157 //------------------------------------------------------------- 3158 function bouncer($step, $steps) 3159 { 3160 global $event; 3161 3162 if (empty($step)) return true; 3163 3164 // Validate step 3165 if (!array_key_exists($step, $steps)) { 3166 return false; 3167 } 3168 3169 // Does this step require a token? 3170 if (!$steps[$step]) { 3171 return true; 3172 } 3173 3174 // Validate token 3175 if (gps('_txp_token') == form_token()) { 3176 return true; 3177 } 3178 3179 // This place ain't no good for you, son. 3180 die(gTxt('get_off_my_lawn', array('{event}' => $event, '{step}' => $step))); 3181 } 3182 3183 /** 3184 * Test whether the client accepts a certain response format. 3185 * 3186 * Discards formats with a quality factor below 0.1 3187 * 3188 * @param string $format One of 'html', 'txt', 'js', 'css', 'json', 'xml', 'rdf', 'atom', 'rss' 3189 * @return boolean $format TRUE if accepted 3190 * @since 4.5.0 3191 * @package Network 3192 */ 3193 3194 function http_accept_format($format) 3195 { 3196 static $formats = array( 3197 'html' => array('text/html', 'application/xhtml+xml', '*/*'), 3198 'txt' => array('text/plain', '*/*'), 3199 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript', 'application/ecmascript', 'application/x-ecmascript', '*/*'), 3200 'css' => array('text/css', '*/*'), 3201 'json' => array('application/json', 'application/x-json', '*/*'), 3202 'xml' => array('text/xml', 'application/xml', 'application/x-xml', '*/*'), 3203 'rdf' => array('application/rdf+xml', '*/*'), 3204 'atom' => array('application/atom+xml', '*/*'), 3205 'rss' => array('application/rss+xml', '*/*'), 3206 ); 3207 static $accepts = array(); 3208 static $q = array(); 3209 3210 if (empty($accepts)) 3211 { 3212 // Build cache of accepted formats. 3213 $accepts = preg_split('/\s*,\s*/', serverSet('HTTP_ACCEPT'), null, PREG_SPLIT_NO_EMPTY); 3214 foreach ($accepts as $i => &$a) 3215 { 3216 // Sniff out quality factors if present. 3217 if (preg_match('/(.*)\s*;\s*q=([.0-9]*)/', $a, $m)) 3218 { 3219 $a = $m[1]; 3220 $q[$a] = floatval($m[2]); 3221 } 3222 else 3223 { 3224 $q[$a] = 1.0; 3225 } 3226 // Discard formats with quality factors below an arbitrary threshold 3227 // as jQuery adds a wildcard '*/*; q=0.01' to the 'Accepts' header for XHR requests. 3228 if ($q[$a] < 0.1) 3229 { 3230 unset($q[$a]); 3231 unset($accepts[$i]); 3232 } 3233 } 3234 } 3235 return isset($formats[$format]) ? count(array_intersect($formats[$format], $accepts)) > 0 : false; 3236 } 3237 3238 /** 3239 * Translate article status names into numerical status codes 3240 * 3241 * @param string $name Named status {'draft', 'hidden', 'pending', 'live', 'sticky'} 3242 * @return int Numerical status [1..5] 3243 */ 3244 function getStatusNum($name) 3245 { 3246 $labels = array('draft' => 1, 'hidden' => 2, 'pending' => 3, 'live' => 4, 'sticky' => 5); 3247 $status = strtolower($name); 3248 $num = empty($labels[$status]) ? 4 : $labels[$status]; 3249 return $num; 3250 } 3251 3252 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title