[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * @package phpBB3 5 * @version $Id$ 6 * @copyright (c) 2005 phpBB Group 7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License 8 * 9 */ 10 11 /** 12 * @ignore 13 */ 14 if (!defined('IN_PHPBB')) 15 { 16 exit; 17 } 18 19 // Common global functions 20 21 /** 22 * set_var 23 * 24 * Set variable, used by {@link request_var the request_var function} 25 * 26 * @access private 27 */ 28 function set_var(&$result, $var, $type, $multibyte = false) 29 { 30 settype($var, $type); 31 $result = $var; 32 33 if ($type == 'string') 34 { 35 $result = trim(htmlspecialchars(str_replace(array("\r\n", "\r", "\0"), array("\n", "\n", ''), $result), ENT_COMPAT, 'UTF-8')); 36 37 if (!empty($result)) 38 { 39 // Make sure multibyte characters are wellformed 40 if ($multibyte) 41 { 42 if (!preg_match('/^./u', $result)) 43 { 44 $result = ''; 45 } 46 } 47 else 48 { 49 // no multibyte, allow only ASCII (0-127) 50 $result = preg_replace('/[\x80-\xFF]/', '?', $result); 51 } 52 } 53 54 $result = (STRIP) ? stripslashes($result) : $result; 55 } 56 } 57 58 /** 59 * request_var 60 * 61 * Used to get passed variable 62 */ 63 function request_var($var_name, $default, $multibyte = false, $cookie = false) 64 { 65 if (!$cookie && isset($_COOKIE[$var_name])) 66 { 67 if (!isset($_GET[$var_name]) && !isset($_POST[$var_name])) 68 { 69 return (is_array($default)) ? array() : $default; 70 } 71 $_REQUEST[$var_name] = isset($_POST[$var_name]) ? $_POST[$var_name] : $_GET[$var_name]; 72 } 73 74 $super_global = ($cookie) ? '_COOKIE' : '_REQUEST'; 75 if (!isset($GLOBALS[$super_global][$var_name]) || is_array($GLOBALS[$super_global][$var_name]) != is_array($default)) 76 { 77 return (is_array($default)) ? array() : $default; 78 } 79 80 $var = $GLOBALS[$super_global][$var_name]; 81 if (!is_array($default)) 82 { 83 $type = gettype($default); 84 } 85 else 86 { 87 list($key_type, $type) = each($default); 88 $type = gettype($type); 89 $key_type = gettype($key_type); 90 if ($type == 'array') 91 { 92 reset($default); 93 $default = current($default); 94 list($sub_key_type, $sub_type) = each($default); 95 $sub_type = gettype($sub_type); 96 $sub_type = ($sub_type == 'array') ? 'NULL' : $sub_type; 97 $sub_key_type = gettype($sub_key_type); 98 } 99 } 100 101 if (is_array($var)) 102 { 103 $_var = $var; 104 $var = array(); 105 106 foreach ($_var as $k => $v) 107 { 108 set_var($k, $k, $key_type); 109 if ($type == 'array' && is_array($v)) 110 { 111 foreach ($v as $_k => $_v) 112 { 113 if (is_array($_v)) 114 { 115 $_v = null; 116 } 117 set_var($_k, $_k, $sub_key_type, $multibyte); 118 set_var($var[$k][$_k], $_v, $sub_type, $multibyte); 119 } 120 } 121 else 122 { 123 if ($type == 'array' || is_array($v)) 124 { 125 $v = null; 126 } 127 set_var($var[$k], $v, $type, $multibyte); 128 } 129 } 130 } 131 else 132 { 133 set_var($var, $var, $type, $multibyte); 134 } 135 136 return $var; 137 } 138 139 /** 140 * Sets a configuration option's value. 141 * 142 * Please note that this function does not update the is_dynamic value for 143 * an already existing config option. 144 * 145 * @param string $config_name The configuration option's name 146 * @param string $config_value New configuration value 147 * @param bool $is_dynamic Whether this variable should be cached (false) or 148 * if it changes too frequently (true) to be 149 * efficiently cached. 150 * 151 * @return null 152 */ 153 function set_config($config_name, $config_value, $is_dynamic = false) 154 { 155 global $db, $cache, $config; 156 157 $sql = 'UPDATE ' . CONFIG_TABLE . " 158 SET config_value = '" . $db->sql_escape($config_value) . "' 159 WHERE config_name = '" . $db->sql_escape($config_name) . "'"; 160 $db->sql_query($sql); 161 162 if (!$db->sql_affectedrows() && !isset($config[$config_name])) 163 { 164 $sql = 'INSERT INTO ' . CONFIG_TABLE . ' ' . $db->sql_build_array('INSERT', array( 165 'config_name' => $config_name, 166 'config_value' => $config_value, 167 'is_dynamic' => ($is_dynamic) ? 1 : 0)); 168 $db->sql_query($sql); 169 } 170 171 $config[$config_name] = $config_value; 172 173 if (!$is_dynamic) 174 { 175 $cache->destroy('config'); 176 } 177 } 178 179 /** 180 * Increments an integer config value directly in the database. 181 * 182 * @param string $config_name The configuration option's name 183 * @param int $increment Amount to increment by 184 * @param bool $is_dynamic Whether this variable should be cached (false) or 185 * if it changes too frequently (true) to be 186 * efficiently cached. 187 * 188 * @return null 189 */ 190 function set_config_count($config_name, $increment, $is_dynamic = false) 191 { 192 global $db, $cache; 193 194 switch ($db->sql_layer) 195 { 196 case 'firebird': 197 // Precision must be from 1 to 18 198 $sql_update = 'CAST(CAST(config_value as DECIMAL(18, 0)) + ' . (int) $increment . ' as VARCHAR(255))'; 199 break; 200 201 case 'postgres': 202 // Need to cast to text first for PostgreSQL 7.x 203 $sql_update = 'CAST(CAST(config_value::text as DECIMAL(255, 0)) + ' . (int) $increment . ' as VARCHAR(255))'; 204 break; 205 206 // MySQL, SQlite, mssql, mssql_odbc, oracle 207 default: 208 $sql_update = 'config_value + ' . (int) $increment; 209 break; 210 } 211 212 $db->sql_query('UPDATE ' . CONFIG_TABLE . ' SET config_value = ' . $sql_update . " WHERE config_name = '" . $db->sql_escape($config_name) . "'"); 213 214 if (!$is_dynamic) 215 { 216 $cache->destroy('config'); 217 } 218 } 219 220 /** 221 * Generates an alphanumeric random string of given length 222 * 223 * @return string 224 */ 225 function gen_rand_string($num_chars = 8) 226 { 227 // [a, z] + [0, 9] = 36 228 return substr(strtoupper(base_convert(unique_id(), 16, 36)), 0, $num_chars); 229 } 230 231 /** 232 * Generates a user-friendly alphanumeric random string of given length 233 * We remove 0 and O so users cannot confuse those in passwords etc. 234 * 235 * @return string 236 */ 237 function gen_rand_string_friendly($num_chars = 8) 238 { 239 $rand_str = unique_id(); 240 241 // Remove Z and Y from the base_convert(), replace 0 with Z and O with Y 242 // [a, z] + [0, 9] - {z, y} = [a, z] + [0, 9] - {0, o} = 34 243 $rand_str = str_replace(array('0', 'O'), array('Z', 'Y'), strtoupper(base_convert($rand_str, 16, 34))); 244 245 return substr($rand_str, 0, $num_chars); 246 } 247 248 /** 249 * Return unique id 250 * @param string $extra additional entropy 251 */ 252 function unique_id($extra = 'c') 253 { 254 static $dss_seeded = false; 255 global $config; 256 257 $val = $config['rand_seed'] . microtime(); 258 $val = md5($val); 259 $config['rand_seed'] = md5($config['rand_seed'] . $val . $extra); 260 261 if ($dss_seeded !== true && ($config['rand_seed_last_update'] < time() - rand(1,10))) 262 { 263 set_config('rand_seed_last_update', time(), true); 264 set_config('rand_seed', $config['rand_seed'], true); 265 $dss_seeded = true; 266 } 267 268 return substr($val, 4, 16); 269 } 270 271 /** 272 * Wrapper for mt_rand() which allows swapping $min and $max parameters. 273 * 274 * PHP does not allow us to swap the order of the arguments for mt_rand() anymore. 275 * (since PHP 5.3.4, see http://bugs.php.net/46587) 276 * 277 * @param int $min Lowest value to be returned 278 * @param int $max Highest value to be returned 279 * 280 * @return int Random integer between $min and $max (or $max and $min) 281 */ 282 function phpbb_mt_rand($min, $max) 283 { 284 return ($min > $max) ? mt_rand($max, $min) : mt_rand($min, $max); 285 } 286 287 /** 288 * Wrapper for getdate() which returns the equivalent array for UTC timestamps. 289 * 290 * @param int $time Unix timestamp (optional) 291 * 292 * @return array Returns an associative array of information related to the timestamp. 293 * See http://www.php.net/manual/en/function.getdate.php 294 */ 295 function phpbb_gmgetdate($time = false) 296 { 297 if ($time === false) 298 { 299 $time = time(); 300 } 301 302 // getdate() interprets timestamps in local time. 303 // What follows uses the fact that getdate() and 304 // date('Z') balance each other out. 305 return getdate($time - date('Z')); 306 } 307 308 /** 309 * Return formatted string for filesizes 310 * 311 * @param mixed $value filesize in bytes 312 * (non-negative number; int, float or string) 313 * @param bool $string_only true if language string should be returned 314 * @param array $allowed_units only allow these units (data array indexes) 315 * 316 * @return mixed data array if $string_only is false 317 * @author bantu 318 */ 319 function get_formatted_filesize($value, $string_only = true, $allowed_units = false) 320 { 321 global $user; 322 323 $available_units = array( 324 'tb' => array( 325 'min' => 1099511627776, // pow(2, 40) 326 'index' => 4, 327 'si_unit' => 'TB', 328 'iec_unit' => 'TIB', 329 ), 330 'gb' => array( 331 'min' => 1073741824, // pow(2, 30) 332 'index' => 3, 333 'si_unit' => 'GB', 334 'iec_unit' => 'GIB', 335 ), 336 'mb' => array( 337 'min' => 1048576, // pow(2, 20) 338 'index' => 2, 339 'si_unit' => 'MB', 340 'iec_unit' => 'MIB', 341 ), 342 'kb' => array( 343 'min' => 1024, // pow(2, 10) 344 'index' => 1, 345 'si_unit' => 'KB', 346 'iec_unit' => 'KIB', 347 ), 348 'b' => array( 349 'min' => 0, 350 'index' => 0, 351 'si_unit' => 'BYTES', // Language index 352 'iec_unit' => 'BYTES', // Language index 353 ), 354 ); 355 356 foreach ($available_units as $si_identifier => $unit_info) 357 { 358 if (!empty($allowed_units) && $si_identifier != 'b' && !in_array($si_identifier, $allowed_units)) 359 { 360 continue; 361 } 362 363 if ($value >= $unit_info['min']) 364 { 365 $unit_info['si_identifier'] = $si_identifier; 366 367 break; 368 } 369 } 370 unset($available_units); 371 372 for ($i = 0; $i < $unit_info['index']; $i++) 373 { 374 $value /= 1024; 375 } 376 $value = round($value, 2); 377 378 // Lookup units in language dictionary 379 $unit_info['si_unit'] = (isset($user->lang[$unit_info['si_unit']])) ? $user->lang[$unit_info['si_unit']] : $unit_info['si_unit']; 380 $unit_info['iec_unit'] = (isset($user->lang[$unit_info['iec_unit']])) ? $user->lang[$unit_info['iec_unit']] : $unit_info['iec_unit']; 381 382 // Default to IEC 383 $unit_info['unit'] = $unit_info['iec_unit']; 384 385 if (!$string_only) 386 { 387 $unit_info['value'] = $value; 388 389 return $unit_info; 390 } 391 392 return $value . ' ' . $unit_info['unit']; 393 } 394 395 /** 396 * Determine whether we are approaching the maximum execution time. Should be called once 397 * at the beginning of the script in which it's used. 398 * @return bool Either true if the maximum execution time is nearly reached, or false 399 * if some time is still left. 400 */ 401 function still_on_time($extra_time = 15) 402 { 403 static $max_execution_time, $start_time; 404 405 $time = explode(' ', microtime()); 406 $current_time = $time[0] + $time[1]; 407 408 if (empty($max_execution_time)) 409 { 410 $max_execution_time = (function_exists('ini_get')) ? (int) @ini_get('max_execution_time') : (int) @get_cfg_var('max_execution_time'); 411 412 // If zero, then set to something higher to not let the user catch the ten seconds barrier. 413 if ($max_execution_time === 0) 414 { 415 $max_execution_time = 50 + $extra_time; 416 } 417 418 $max_execution_time = min(max(10, ($max_execution_time - $extra_time)), 50); 419 420 // For debugging purposes 421 // $max_execution_time = 10; 422 423 global $starttime; 424 $start_time = (empty($starttime)) ? $current_time : $starttime; 425 } 426 427 return (ceil($current_time - $start_time) < $max_execution_time) ? true : false; 428 } 429 430 /** 431 * 432 * @version Version 0.1 / slightly modified for phpBB 3.0.x (using $H$ as hash type identifier) 433 * 434 * Portable PHP password hashing framework. 435 * 436 * Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in 437 * the public domain. 438 * 439 * There's absolutely no warranty. 440 * 441 * The homepage URL for this framework is: 442 * 443 * http://www.openwall.com/phpass/ 444 * 445 * Please be sure to update the Version line if you edit this file in any way. 446 * It is suggested that you leave the main version number intact, but indicate 447 * your project name (after the slash) and add your own revision information. 448 * 449 * Please do not change the "private" password hashing method implemented in 450 * here, thereby making your hashes incompatible. However, if you must, please 451 * change the hash type identifier (the "$P$") to something different. 452 * 453 * Obviously, since this code is in the public domain, the above are not 454 * requirements (there can be none), but merely suggestions. 455 * 456 * 457 * Hash the password 458 */ 459 function phpbb_hash($password) 460 { 461 $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; 462 463 $random_state = unique_id(); 464 $random = ''; 465 $count = 6; 466 467 if (($fh = @fopen('/dev/urandom', 'rb'))) 468 { 469 $random = fread($fh, $count); 470 fclose($fh); 471 } 472 473 if (strlen($random) < $count) 474 { 475 $random = ''; 476 477 for ($i = 0; $i < $count; $i += 16) 478 { 479 $random_state = md5(unique_id() . $random_state); 480 $random .= pack('H*', md5($random_state)); 481 } 482 $random = substr($random, 0, $count); 483 } 484 485 $hash = _hash_crypt_private($password, _hash_gensalt_private($random, $itoa64), $itoa64); 486 487 if (strlen($hash) == 34) 488 { 489 return $hash; 490 } 491 492 return md5($password); 493 } 494 495 /** 496 * Check for correct password 497 * 498 * @param string $password The password in plain text 499 * @param string $hash The stored password hash 500 * 501 * @return bool Returns true if the password is correct, false if not. 502 */ 503 function phpbb_check_hash($password, $hash) 504 { 505 if (strlen($password) > 4096) 506 { 507 // If the password is too huge, we will simply reject it 508 // and not let the server try to hash it. 509 return false; 510 } 511 512 $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; 513 if (strlen($hash) == 34) 514 { 515 return (_hash_crypt_private($password, $hash, $itoa64) === $hash) ? true : false; 516 } 517 518 return (md5($password) === $hash) ? true : false; 519 } 520 521 /** 522 * Generate salt for hash generation 523 */ 524 function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6) 525 { 526 if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) 527 { 528 $iteration_count_log2 = 8; 529 } 530 531 $output = '$H$'; 532 $output .= $itoa64[min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)]; 533 $output .= _hash_encode64($input, 6, $itoa64); 534 535 return $output; 536 } 537 538 /** 539 * Encode hash 540 */ 541 function _hash_encode64($input, $count, &$itoa64) 542 { 543 $output = ''; 544 $i = 0; 545 546 do 547 { 548 $value = ord($input[$i++]); 549 $output .= $itoa64[$value & 0x3f]; 550 551 if ($i < $count) 552 { 553 $value |= ord($input[$i]) << 8; 554 } 555 556 $output .= $itoa64[($value >> 6) & 0x3f]; 557 558 if ($i++ >= $count) 559 { 560 break; 561 } 562 563 if ($i < $count) 564 { 565 $value |= ord($input[$i]) << 16; 566 } 567 568 $output .= $itoa64[($value >> 12) & 0x3f]; 569 570 if ($i++ >= $count) 571 { 572 break; 573 } 574 575 $output .= $itoa64[($value >> 18) & 0x3f]; 576 } 577 while ($i < $count); 578 579 return $output; 580 } 581 582 /** 583 * The crypt function/replacement 584 */ 585 function _hash_crypt_private($password, $setting, &$itoa64) 586 { 587 $output = '*'; 588 589 // Check for correct hash 590 if (substr($setting, 0, 3) != '$H$' && substr($setting, 0, 3) != '$P$') 591 { 592 return $output; 593 } 594 595 $count_log2 = strpos($itoa64, $setting[3]); 596 597 if ($count_log2 < 7 || $count_log2 > 30) 598 { 599 return $output; 600 } 601 602 $count = 1 << $count_log2; 603 $salt = substr($setting, 4, 8); 604 605 if (strlen($salt) != 8) 606 { 607 return $output; 608 } 609 610 /** 611 * We're kind of forced to use MD5 here since it's the only 612 * cryptographic primitive available in all versions of PHP 613 * currently in use. To implement our own low-level crypto 614 * in PHP would result in much worse performance and 615 * consequently in lower iteration counts and hashes that are 616 * quicker to crack (by non-PHP code). 617 */ 618 if (PHP_VERSION >= 5) 619 { 620 $hash = md5($salt . $password, true); 621 do 622 { 623 $hash = md5($hash . $password, true); 624 } 625 while (--$count); 626 } 627 else 628 { 629 $hash = pack('H*', md5($salt . $password)); 630 do 631 { 632 $hash = pack('H*', md5($hash . $password)); 633 } 634 while (--$count); 635 } 636 637 $output = substr($setting, 0, 12); 638 $output .= _hash_encode64($hash, 16, $itoa64); 639 640 return $output; 641 } 642 643 /** 644 * Hashes an email address to a big integer 645 * 646 * @param string $email Email address 647 * 648 * @return string Unsigned Big Integer 649 */ 650 function phpbb_email_hash($email) 651 { 652 return sprintf('%u', crc32(strtolower($email))) . strlen($email); 653 } 654 655 /** 656 * Wrapper for version_compare() that allows using uppercase A and B 657 * for alpha and beta releases. 658 * 659 * See http://www.php.net/manual/en/function.version-compare.php 660 * 661 * @param string $version1 First version number 662 * @param string $version2 Second version number 663 * @param string $operator Comparison operator (optional) 664 * 665 * @return mixed Boolean (true, false) if comparison operator is specified. 666 * Integer (-1, 0, 1) otherwise. 667 */ 668 function phpbb_version_compare($version1, $version2, $operator = null) 669 { 670 $version1 = strtolower($version1); 671 $version2 = strtolower($version2); 672 673 if (is_null($operator)) 674 { 675 return version_compare($version1, $version2); 676 } 677 else 678 { 679 return version_compare($version1, $version2, $operator); 680 } 681 } 682 683 /** 684 * Global function for chmodding directories and files for internal use 685 * 686 * This function determines owner and group whom the file belongs to and user and group of PHP and then set safest possible file permissions. 687 * The function determines owner and group from common.php file and sets the same to the provided file. 688 * The function uses bit fields to build the permissions. 689 * The function sets the appropiate execute bit on directories. 690 * 691 * Supported constants representing bit fields are: 692 * 693 * CHMOD_ALL - all permissions (7) 694 * CHMOD_READ - read permission (4) 695 * CHMOD_WRITE - write permission (2) 696 * CHMOD_EXECUTE - execute permission (1) 697 * 698 * NOTE: The function uses POSIX extension and fileowner()/filegroup() functions. If any of them is disabled, this function tries to build proper permissions, by calling is_readable() and is_writable() functions. 699 * 700 * @param string $filename The file/directory to be chmodded 701 * @param int $perms Permissions to set 702 * 703 * @return bool true on success, otherwise false 704 * @author faw, phpBB Group 705 */ 706 function phpbb_chmod($filename, $perms = CHMOD_READ) 707 { 708 static $_chmod_info; 709 710 // Return if the file no longer exists. 711 if (!file_exists($filename)) 712 { 713 return false; 714 } 715 716 // Determine some common vars 717 if (empty($_chmod_info)) 718 { 719 if (!function_exists('fileowner') || !function_exists('filegroup')) 720 { 721 // No need to further determine owner/group - it is unknown 722 $_chmod_info['process'] = false; 723 } 724 else 725 { 726 global $phpbb_root_path, $phpEx; 727 728 // Determine owner/group of common.php file and the filename we want to change here 729 $common_php_owner = @fileowner($phpbb_root_path . 'common.' . $phpEx); 730 $common_php_group = @filegroup($phpbb_root_path . 'common.' . $phpEx); 731 732 // And the owner and the groups PHP is running under. 733 $php_uid = (function_exists('posix_getuid')) ? @posix_getuid() : false; 734 $php_gids = (function_exists('posix_getgroups')) ? @posix_getgroups() : false; 735 736 // If we are unable to get owner/group, then do not try to set them by guessing 737 if (!$php_uid || empty($php_gids) || !$common_php_owner || !$common_php_group) 738 { 739 $_chmod_info['process'] = false; 740 } 741 else 742 { 743 $_chmod_info = array( 744 'process' => true, 745 'common_owner' => $common_php_owner, 746 'common_group' => $common_php_group, 747 'php_uid' => $php_uid, 748 'php_gids' => $php_gids, 749 ); 750 } 751 } 752 } 753 754 if ($_chmod_info['process']) 755 { 756 $file_uid = @fileowner($filename); 757 $file_gid = @filegroup($filename); 758 759 // Change owner 760 if (@chown($filename, $_chmod_info['common_owner'])) 761 { 762 clearstatcache(); 763 $file_uid = @fileowner($filename); 764 } 765 766 // Change group 767 if (@chgrp($filename, $_chmod_info['common_group'])) 768 { 769 clearstatcache(); 770 $file_gid = @filegroup($filename); 771 } 772 773 // If the file_uid/gid now match the one from common.php we can process further, else we are not able to change something 774 if ($file_uid != $_chmod_info['common_owner'] || $file_gid != $_chmod_info['common_group']) 775 { 776 $_chmod_info['process'] = false; 777 } 778 } 779 780 // Still able to process? 781 if ($_chmod_info['process']) 782 { 783 if ($file_uid == $_chmod_info['php_uid']) 784 { 785 $php = 'owner'; 786 } 787 else if (in_array($file_gid, $_chmod_info['php_gids'])) 788 { 789 $php = 'group'; 790 } 791 else 792 { 793 // Since we are setting the everyone bit anyway, no need to do expensive operations 794 $_chmod_info['process'] = false; 795 } 796 } 797 798 // We are not able to determine or change something 799 if (!$_chmod_info['process']) 800 { 801 $php = 'other'; 802 } 803 804 // Owner always has read/write permission 805 $owner = CHMOD_READ | CHMOD_WRITE; 806 if (is_dir($filename)) 807 { 808 $owner |= CHMOD_EXECUTE; 809 810 // Only add execute bit to the permission if the dir needs to be readable 811 if ($perms & CHMOD_READ) 812 { 813 $perms |= CHMOD_EXECUTE; 814 } 815 } 816 817 switch ($php) 818 { 819 case 'owner': 820 $result = @chmod($filename, ($owner << 6) + (0 << 3) + (0 << 0)); 821 822 clearstatcache(); 823 824 if (is_readable($filename) && phpbb_is_writable($filename)) 825 { 826 break; 827 } 828 829 case 'group': 830 $result = @chmod($filename, ($owner << 6) + ($perms << 3) + (0 << 0)); 831 832 clearstatcache(); 833 834 if ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || phpbb_is_writable($filename))) 835 { 836 break; 837 } 838 839 case 'other': 840 $result = @chmod($filename, ($owner << 6) + ($perms << 3) + ($perms << 0)); 841 842 clearstatcache(); 843 844 if ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || phpbb_is_writable($filename))) 845 { 846 break; 847 } 848 849 default: 850 return false; 851 break; 852 } 853 854 return $result; 855 } 856 857 /** 858 * Test if a file/directory is writable 859 * 860 * This function calls the native is_writable() when not running under 861 * Windows and it is not disabled. 862 * 863 * @param string $file Path to perform write test on 864 * @return bool True when the path is writable, otherwise false. 865 */ 866 function phpbb_is_writable($file) 867 { 868 if (strtolower(substr(PHP_OS, 0, 3)) === 'win' || !function_exists('is_writable')) 869 { 870 if (file_exists($file)) 871 { 872 // Canonicalise path to absolute path 873 $file = phpbb_realpath($file); 874 875 if (is_dir($file)) 876 { 877 // Test directory by creating a file inside the directory 878 $result = @tempnam($file, 'i_w'); 879 880 if (is_string($result) && file_exists($result)) 881 { 882 unlink($result); 883 884 // Ensure the file is actually in the directory (returned realpathed) 885 return (strpos($result, $file) === 0) ? true : false; 886 } 887 } 888 else 889 { 890 $handle = @fopen($file, 'r+'); 891 892 if (is_resource($handle)) 893 { 894 fclose($handle); 895 return true; 896 } 897 } 898 } 899 else 900 { 901 // file does not exist test if we can write to the directory 902 $dir = dirname($file); 903 904 if (file_exists($dir) && is_dir($dir) && phpbb_is_writable($dir)) 905 { 906 return true; 907 } 908 } 909 910 return false; 911 } 912 else 913 { 914 return is_writable($file); 915 } 916 } 917 918 // Compatibility functions 919 920 if (!function_exists('array_combine')) 921 { 922 /** 923 * A wrapper for the PHP5 function array_combine() 924 * @param array $keys contains keys for the resulting array 925 * @param array $values contains values for the resulting array 926 * 927 * @return Returns an array by using the values from the keys array as keys and the 928 * values from the values array as the corresponding values. Returns false if the 929 * number of elements for each array isn't equal or if the arrays are empty. 930 */ 931 function array_combine($keys, $values) 932 { 933 $keys = array_values($keys); 934 $values = array_values($values); 935 936 $n = sizeof($keys); 937 $m = sizeof($values); 938 if (!$n || !$m || ($n != $m)) 939 { 940 return false; 941 } 942 943 $combined = array(); 944 for ($i = 0; $i < $n; $i++) 945 { 946 $combined[$keys[$i]] = $values[$i]; 947 } 948 return $combined; 949 } 950 } 951 952 if (!function_exists('str_split')) 953 { 954 /** 955 * A wrapper for the PHP5 function str_split() 956 * @param array $string contains the string to be converted 957 * @param array $split_length contains the length of each chunk 958 * 959 * @return Converts a string to an array. If the optional split_length parameter is specified, 960 * the returned array will be broken down into chunks with each being split_length in length, 961 * otherwise each chunk will be one character in length. FALSE is returned if split_length is 962 * less than 1. If the split_length length exceeds the length of string, the entire string is 963 * returned as the first (and only) array element. 964 */ 965 function str_split($string, $split_length = 1) 966 { 967 if ($split_length < 1) 968 { 969 return false; 970 } 971 else if ($split_length >= strlen($string)) 972 { 973 return array($string); 974 } 975 else 976 { 977 preg_match_all('#.{1,' . $split_length . '}#s', $string, $matches); 978 return $matches[0]; 979 } 980 } 981 } 982 983 if (!function_exists('stripos')) 984 { 985 /** 986 * A wrapper for the PHP5 function stripos 987 * Find position of first occurrence of a case-insensitive string 988 * 989 * @param string $haystack is the string to search in 990 * @param string $needle is the string to search for 991 * 992 * @return mixed Returns the numeric position of the first occurrence of needle in the haystack string. Unlike strpos(), stripos() is case-insensitive. 993 * Note that the needle may be a string of one or more characters. 994 * If needle is not found, stripos() will return boolean FALSE. 995 */ 996 function stripos($haystack, $needle) 997 { 998 if (preg_match('#' . preg_quote($needle, '#') . '#i', $haystack, $m)) 999 { 1000 return strpos($haystack, $m[0]); 1001 } 1002 1003 return false; 1004 } 1005 } 1006 1007 /** 1008 * Checks if a path ($path) is absolute or relative 1009 * 1010 * @param string $path Path to check absoluteness of 1011 * @return boolean 1012 */ 1013 function is_absolute($path) 1014 { 1015 return (isset($path[0]) && $path[0] == '/' || preg_match('#^[a-z]:[/\\\]#i', $path)) ? true : false; 1016 } 1017 1018 /** 1019 * @author Chris Smith <chris@project-minerva.org> 1020 * @copyright 2006 Project Minerva Team 1021 * @param string $path The path which we should attempt to resolve. 1022 * @return mixed 1023 */ 1024 function phpbb_own_realpath($path) 1025 { 1026 // Now to perform funky shizzle 1027 1028 // Switch to use UNIX slashes 1029 $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); 1030 $path_prefix = ''; 1031 1032 // Determine what sort of path we have 1033 if (is_absolute($path)) 1034 { 1035 $absolute = true; 1036 1037 if ($path[0] == '/') 1038 { 1039 // Absolute path, *NIX style 1040 $path_prefix = ''; 1041 } 1042 else 1043 { 1044 // Absolute path, Windows style 1045 // Remove the drive letter and colon 1046 $path_prefix = $path[0] . ':'; 1047 $path = substr($path, 2); 1048 } 1049 } 1050 else 1051 { 1052 // Relative Path 1053 // Prepend the current working directory 1054 if (function_exists('getcwd')) 1055 { 1056 // This is the best method, hopefully it is enabled! 1057 $path = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . '/' . $path; 1058 $absolute = true; 1059 if (preg_match('#^[a-z]:#i', $path)) 1060 { 1061 $path_prefix = $path[0] . ':'; 1062 $path = substr($path, 2); 1063 } 1064 else 1065 { 1066 $path_prefix = ''; 1067 } 1068 } 1069 else if (isset($_SERVER['SCRIPT_FILENAME']) && !empty($_SERVER['SCRIPT_FILENAME'])) 1070 { 1071 // Warning: If chdir() has been used this will lie! 1072 // Warning: This has some problems sometime (CLI can create them easily) 1073 $path = str_replace(DIRECTORY_SEPARATOR, '/', dirname($_SERVER['SCRIPT_FILENAME'])) . '/' . $path; 1074 $absolute = true; 1075 $path_prefix = ''; 1076 } 1077 else 1078 { 1079 // We have no way of getting the absolute path, just run on using relative ones. 1080 $absolute = false; 1081 $path_prefix = '.'; 1082 } 1083 } 1084 1085 // Remove any repeated slashes 1086 $path = preg_replace('#/{2,}#', '/', $path); 1087 1088 // Remove the slashes from the start and end of the path 1089 $path = trim($path, '/'); 1090 1091 // Break the string into little bits for us to nibble on 1092 $bits = explode('/', $path); 1093 1094 // Remove any . in the path, renumber array for the loop below 1095 $bits = array_values(array_diff($bits, array('.'))); 1096 1097 // Lets get looping, run over and resolve any .. (up directory) 1098 for ($i = 0, $max = sizeof($bits); $i < $max; $i++) 1099 { 1100 // @todo Optimise 1101 if ($bits[$i] == '..' ) 1102 { 1103 if (isset($bits[$i - 1])) 1104 { 1105 if ($bits[$i - 1] != '..') 1106 { 1107 // We found a .. and we are able to traverse upwards, lets do it! 1108 unset($bits[$i]); 1109 unset($bits[$i - 1]); 1110 $i -= 2; 1111 $max -= 2; 1112 $bits = array_values($bits); 1113 } 1114 } 1115 else if ($absolute) // ie. !isset($bits[$i - 1]) && $absolute 1116 { 1117 // We have an absolute path trying to descend above the root of the filesystem 1118 // ... Error! 1119 return false; 1120 } 1121 } 1122 } 1123 1124 // Prepend the path prefix 1125 array_unshift($bits, $path_prefix); 1126 1127 $resolved = ''; 1128 1129 $max = sizeof($bits) - 1; 1130 1131 // Check if we are able to resolve symlinks, Windows cannot. 1132 $symlink_resolve = (function_exists('readlink')) ? true : false; 1133 1134 foreach ($bits as $i => $bit) 1135 { 1136 if (@is_dir("$resolved/$bit") || ($i == $max && @is_file("$resolved/$bit"))) 1137 { 1138 // Path Exists 1139 if ($symlink_resolve && is_link("$resolved/$bit") && ($link = readlink("$resolved/$bit"))) 1140 { 1141 // Resolved a symlink. 1142 $resolved = $link . (($i == $max) ? '' : '/'); 1143 continue; 1144 } 1145 } 1146 else 1147 { 1148 // Something doesn't exist here! 1149 // This is correct realpath() behaviour but sadly open_basedir and safe_mode make this problematic 1150 // return false; 1151 } 1152 $resolved .= $bit . (($i == $max) ? '' : '/'); 1153 } 1154 1155 // @todo If the file exists fine and open_basedir only has one path we should be able to prepend it 1156 // because we must be inside that basedir, the question is where... 1157 // @internal The slash in is_dir() gets around an open_basedir restriction 1158 if (!@file_exists($resolved) || (!@is_dir($resolved . '/') && !is_file($resolved))) 1159 { 1160 return false; 1161 } 1162 1163 // Put the slashes back to the native operating systems slashes 1164 $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved); 1165 1166 // Check for DIRECTORY_SEPARATOR at the end (and remove it!) 1167 if (substr($resolved, -1) == DIRECTORY_SEPARATOR) 1168 { 1169 return substr($resolved, 0, -1); 1170 } 1171 1172 return $resolved; // We got here, in the end! 1173 } 1174 1175 if (!function_exists('realpath')) 1176 { 1177 /** 1178 * A wrapper for realpath 1179 * @ignore 1180 */ 1181 function phpbb_realpath($path) 1182 { 1183 return phpbb_own_realpath($path); 1184 } 1185 } 1186 else 1187 { 1188 /** 1189 * A wrapper for realpath 1190 */ 1191 function phpbb_realpath($path) 1192 { 1193 $realpath = realpath($path); 1194 1195 // Strangely there are provider not disabling realpath but returning strange values. :o 1196 // We at least try to cope with them. 1197 if ($realpath === $path || $realpath === false) 1198 { 1199 return phpbb_own_realpath($path); 1200 } 1201 1202 // Check for DIRECTORY_SEPARATOR at the end (and remove it!) 1203 if (substr($realpath, -1) == DIRECTORY_SEPARATOR) 1204 { 1205 $realpath = substr($realpath, 0, -1); 1206 } 1207 1208 return $realpath; 1209 } 1210 } 1211 1212 /** 1213 * Eliminates useless . and .. components from specified path. 1214 * 1215 * @param string $path Path to clean 1216 * @return string Cleaned path 1217 */ 1218 function phpbb_clean_path($path) 1219 { 1220 $exploded = explode('/', $path); 1221 $filtered = array(); 1222 foreach ($exploded as $part) 1223 { 1224 if ($part === '.' && !empty($filtered)) 1225 { 1226 continue; 1227 } 1228 1229 if ($part === '..' && !empty($filtered) && $filtered[sizeof($filtered) - 1] !== '..') 1230 { 1231 array_pop($filtered); 1232 } 1233 else 1234 { 1235 $filtered[] = $part; 1236 } 1237 } 1238 $path = implode('/', $filtered); 1239 return $path; 1240 } 1241 1242 if (!function_exists('htmlspecialchars_decode')) 1243 { 1244 /** 1245 * A wrapper for htmlspecialchars_decode 1246 * @ignore 1247 */ 1248 function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT) 1249 { 1250 return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style))); 1251 } 1252 } 1253 1254 // functions used for building option fields 1255 1256 /** 1257 * Pick a language, any language ... 1258 */ 1259 function language_select($default = '') 1260 { 1261 global $db; 1262 1263 $sql = 'SELECT lang_iso, lang_local_name 1264 FROM ' . LANG_TABLE . ' 1265 ORDER BY lang_english_name'; 1266 $result = $db->sql_query($sql); 1267 1268 $lang_options = ''; 1269 while ($row = $db->sql_fetchrow($result)) 1270 { 1271 $selected = ($row['lang_iso'] == $default) ? ' selected="selected"' : ''; 1272 $lang_options .= '<option value="' . $row['lang_iso'] . '"' . $selected . '>' . $row['lang_local_name'] . '</option>'; 1273 } 1274 $db->sql_freeresult($result); 1275 1276 return $lang_options; 1277 } 1278 1279 /** 1280 * Pick a template/theme combo, 1281 */ 1282 function style_select($default = '', $all = false) 1283 { 1284 global $db; 1285 1286 $sql_where = (!$all) ? 'WHERE style_active = 1 ' : ''; 1287 $sql = 'SELECT style_id, style_name 1288 FROM ' . STYLES_TABLE . " 1289 $sql_where 1290 ORDER BY style_name"; 1291 $result = $db->sql_query($sql); 1292 1293 $style_options = ''; 1294 while ($row = $db->sql_fetchrow($result)) 1295 { 1296 $selected = ($row['style_id'] == $default) ? ' selected="selected"' : ''; 1297 $style_options .= '<option value="' . $row['style_id'] . '"' . $selected . '>' . $row['style_name'] . '</option>'; 1298 } 1299 $db->sql_freeresult($result); 1300 1301 return $style_options; 1302 } 1303 1304 /** 1305 * Pick a timezone 1306 */ 1307 function tz_select($default = '', $truncate = false) 1308 { 1309 global $user; 1310 1311 $tz_select = ''; 1312 foreach ($user->lang['tz_zones'] as $offset => $zone) 1313 { 1314 if ($truncate) 1315 { 1316 $zone_trunc = truncate_string($zone, 50, 255, false, '...'); 1317 } 1318 else 1319 { 1320 $zone_trunc = $zone; 1321 } 1322 1323 if (is_numeric($offset)) 1324 { 1325 $selected = ($offset == $default) ? ' selected="selected"' : ''; 1326 $tz_select .= '<option title="' . $zone . '" value="' . $offset . '"' . $selected . '>' . $zone_trunc . '</option>'; 1327 } 1328 } 1329 1330 return $tz_select; 1331 } 1332 1333 // Functions handling topic/post tracking/marking 1334 1335 /** 1336 * Marks a topic/forum as read 1337 * Marks a topic as posted to 1338 * 1339 * @param int $user_id can only be used with $mode == 'post' 1340 */ 1341 function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $user_id = 0) 1342 { 1343 global $db, $user, $config; 1344 1345 if ($mode == 'all') 1346 { 1347 if ($forum_id === false || !sizeof($forum_id)) 1348 { 1349 if ($config['load_db_lastread'] && $user->data['is_registered']) 1350 { 1351 // Mark all forums read (index page) 1352 $db->sql_query('DELETE FROM ' . TOPICS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}"); 1353 $db->sql_query('DELETE FROM ' . FORUMS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}"); 1354 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}"); 1355 } 1356 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 1357 { 1358 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : ''; 1359 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array(); 1360 1361 unset($tracking_topics['tf']); 1362 unset($tracking_topics['t']); 1363 unset($tracking_topics['f']); 1364 $tracking_topics['l'] = base_convert(time() - $config['board_startdate'], 10, 36); 1365 1366 $user->set_cookie('track', tracking_serialize($tracking_topics), time() + 31536000); 1367 $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking_topics)) : tracking_serialize($tracking_topics); 1368 1369 unset($tracking_topics); 1370 1371 if ($user->data['is_registered']) 1372 { 1373 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}"); 1374 } 1375 } 1376 } 1377 1378 return; 1379 } 1380 else if ($mode == 'topics') 1381 { 1382 // Mark all topics in forums read 1383 if (!is_array($forum_id)) 1384 { 1385 $forum_id = array($forum_id); 1386 } 1387 1388 // Add 0 to forums array to mark global announcements correctly 1389 // $forum_id[] = 0; 1390 1391 if ($config['load_db_lastread'] && $user->data['is_registered']) 1392 { 1393 $sql = 'DELETE FROM ' . TOPICS_TRACK_TABLE . " 1394 WHERE user_id = {$user->data['user_id']} 1395 AND " . $db->sql_in_set('forum_id', $forum_id); 1396 $db->sql_query($sql); 1397 1398 $sql = 'SELECT forum_id 1399 FROM ' . FORUMS_TRACK_TABLE . " 1400 WHERE user_id = {$user->data['user_id']} 1401 AND " . $db->sql_in_set('forum_id', $forum_id); 1402 $result = $db->sql_query($sql); 1403 1404 $sql_update = array(); 1405 while ($row = $db->sql_fetchrow($result)) 1406 { 1407 $sql_update[] = (int) $row['forum_id']; 1408 } 1409 $db->sql_freeresult($result); 1410 1411 if (sizeof($sql_update)) 1412 { 1413 $sql = 'UPDATE ' . FORUMS_TRACK_TABLE . ' 1414 SET mark_time = ' . time() . " 1415 WHERE user_id = {$user->data['user_id']} 1416 AND " . $db->sql_in_set('forum_id', $sql_update); 1417 $db->sql_query($sql); 1418 } 1419 1420 if ($sql_insert = array_diff($forum_id, $sql_update)) 1421 { 1422 $sql_ary = array(); 1423 foreach ($sql_insert as $f_id) 1424 { 1425 $sql_ary[] = array( 1426 'user_id' => (int) $user->data['user_id'], 1427 'forum_id' => (int) $f_id, 1428 'mark_time' => time() 1429 ); 1430 } 1431 1432 $db->sql_multi_insert(FORUMS_TRACK_TABLE, $sql_ary); 1433 } 1434 } 1435 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 1436 { 1437 $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : ''; 1438 $tracking = ($tracking) ? tracking_unserialize($tracking) : array(); 1439 1440 foreach ($forum_id as $f_id) 1441 { 1442 $topic_ids36 = (isset($tracking['tf'][$f_id])) ? $tracking['tf'][$f_id] : array(); 1443 1444 if (isset($tracking['tf'][$f_id])) 1445 { 1446 unset($tracking['tf'][$f_id]); 1447 } 1448 1449 foreach ($topic_ids36 as $topic_id36) 1450 { 1451 unset($tracking['t'][$topic_id36]); 1452 } 1453 1454 if (isset($tracking['f'][$f_id])) 1455 { 1456 unset($tracking['f'][$f_id]); 1457 } 1458 1459 $tracking['f'][$f_id] = base_convert(time() - $config['board_startdate'], 10, 36); 1460 } 1461 1462 if (isset($tracking['tf']) && empty($tracking['tf'])) 1463 { 1464 unset($tracking['tf']); 1465 } 1466 1467 $user->set_cookie('track', tracking_serialize($tracking), time() + 31536000); 1468 $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking); 1469 1470 unset($tracking); 1471 } 1472 1473 return; 1474 } 1475 else if ($mode == 'topic') 1476 { 1477 if ($topic_id === false || $forum_id === false) 1478 { 1479 return; 1480 } 1481 1482 if ($config['load_db_lastread'] && $user->data['is_registered']) 1483 { 1484 $sql = 'UPDATE ' . TOPICS_TRACK_TABLE . ' 1485 SET mark_time = ' . (($post_time) ? $post_time : time()) . " 1486 WHERE user_id = {$user->data['user_id']} 1487 AND topic_id = $topic_id"; 1488 $db->sql_query($sql); 1489 1490 // insert row 1491 if (!$db->sql_affectedrows()) 1492 { 1493 $db->sql_return_on_error(true); 1494 1495 $sql_ary = array( 1496 'user_id' => (int) $user->data['user_id'], 1497 'topic_id' => (int) $topic_id, 1498 'forum_id' => (int) $forum_id, 1499 'mark_time' => ($post_time) ? (int) $post_time : time(), 1500 ); 1501 1502 $db->sql_query('INSERT INTO ' . TOPICS_TRACK_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); 1503 1504 $db->sql_return_on_error(false); 1505 } 1506 } 1507 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 1508 { 1509 $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : ''; 1510 $tracking = ($tracking) ? tracking_unserialize($tracking) : array(); 1511 1512 $topic_id36 = base_convert($topic_id, 10, 36); 1513 1514 if (!isset($tracking['t'][$topic_id36])) 1515 { 1516 $tracking['tf'][$forum_id][$topic_id36] = true; 1517 } 1518 1519 $post_time = ($post_time) ? $post_time : time(); 1520 $tracking['t'][$topic_id36] = base_convert($post_time - $config['board_startdate'], 10, 36); 1521 1522 // If the cookie grows larger than 10000 characters we will remove the smallest value 1523 // This can result in old topics being unread - but most of the time it should be accurate... 1524 if (isset($_COOKIE[$config['cookie_name'] . '_track']) && strlen($_COOKIE[$config['cookie_name'] . '_track']) > 10000) 1525 { 1526 //echo 'Cookie grown too large' . print_r($tracking, true); 1527 1528 // We get the ten most minimum stored time offsets and its associated topic ids 1529 $time_keys = array(); 1530 for ($i = 0; $i < 10 && sizeof($tracking['t']); $i++) 1531 { 1532 $min_value = min($tracking['t']); 1533 $m_tkey = array_search($min_value, $tracking['t']); 1534 unset($tracking['t'][$m_tkey]); 1535 1536 $time_keys[$m_tkey] = $min_value; 1537 } 1538 1539 // Now remove the topic ids from the array... 1540 foreach ($tracking['tf'] as $f_id => $topic_id_ary) 1541 { 1542 foreach ($time_keys as $m_tkey => $min_value) 1543 { 1544 if (isset($topic_id_ary[$m_tkey])) 1545 { 1546 $tracking['f'][$f_id] = $min_value; 1547 unset($tracking['tf'][$f_id][$m_tkey]); 1548 } 1549 } 1550 } 1551 1552 if ($user->data['is_registered']) 1553 { 1554 $user->data['user_lastmark'] = intval(base_convert(max($time_keys) + $config['board_startdate'], 36, 10)); 1555 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . $user->data['user_lastmark'] . " WHERE user_id = {$user->data['user_id']}"); 1556 } 1557 else 1558 { 1559 $tracking['l'] = max($time_keys); 1560 } 1561 } 1562 1563 $user->set_cookie('track', tracking_serialize($tracking), time() + 31536000); 1564 $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking); 1565 } 1566 1567 return; 1568 } 1569 else if ($mode == 'post') 1570 { 1571 if ($topic_id === false) 1572 { 1573 return; 1574 } 1575 1576 $use_user_id = (!$user_id) ? $user->data['user_id'] : $user_id; 1577 1578 if ($config['load_db_track'] && $use_user_id != ANONYMOUS) 1579 { 1580 $db->sql_return_on_error(true); 1581 1582 $sql_ary = array( 1583 'user_id' => (int) $use_user_id, 1584 'topic_id' => (int) $topic_id, 1585 'topic_posted' => 1 1586 ); 1587 1588 $db->sql_query('INSERT INTO ' . TOPICS_POSTED_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); 1589 1590 $db->sql_return_on_error(false); 1591 } 1592 1593 return; 1594 } 1595 } 1596 1597 /** 1598 * Get topic tracking info by using already fetched info 1599 */ 1600 function get_topic_tracking($forum_id, $topic_ids, &$rowset, $forum_mark_time, $global_announce_list = false) 1601 { 1602 global $config, $user; 1603 1604 $last_read = array(); 1605 1606 if (!is_array($topic_ids)) 1607 { 1608 $topic_ids = array($topic_ids); 1609 } 1610 1611 foreach ($topic_ids as $topic_id) 1612 { 1613 if (!empty($rowset[$topic_id]['mark_time'])) 1614 { 1615 $last_read[$topic_id] = $rowset[$topic_id]['mark_time']; 1616 } 1617 } 1618 1619 $topic_ids = array_diff($topic_ids, array_keys($last_read)); 1620 1621 if (sizeof($topic_ids)) 1622 { 1623 $mark_time = array(); 1624 1625 // Get global announcement info 1626 if ($global_announce_list && sizeof($global_announce_list)) 1627 { 1628 if (!isset($forum_mark_time[0])) 1629 { 1630 global $db; 1631 1632 $sql = 'SELECT mark_time 1633 FROM ' . FORUMS_TRACK_TABLE . " 1634 WHERE user_id = {$user->data['user_id']} 1635 AND forum_id = 0"; 1636 $result = $db->sql_query($sql); 1637 $row = $db->sql_fetchrow($result); 1638 $db->sql_freeresult($result); 1639 1640 if ($row) 1641 { 1642 $mark_time[0] = $row['mark_time']; 1643 } 1644 } 1645 else 1646 { 1647 if ($forum_mark_time[0] !== false) 1648 { 1649 $mark_time[0] = $forum_mark_time[0]; 1650 } 1651 } 1652 } 1653 1654 if (!empty($forum_mark_time[$forum_id]) && $forum_mark_time[$forum_id] !== false) 1655 { 1656 $mark_time[$forum_id] = $forum_mark_time[$forum_id]; 1657 } 1658 1659 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark']; 1660 1661 foreach ($topic_ids as $topic_id) 1662 { 1663 if ($global_announce_list && isset($global_announce_list[$topic_id])) 1664 { 1665 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark; 1666 } 1667 else 1668 { 1669 $last_read[$topic_id] = $user_lastmark; 1670 } 1671 } 1672 } 1673 1674 return $last_read; 1675 } 1676 1677 /** 1678 * Get topic tracking info from db (for cookie based tracking only this function is used) 1679 */ 1680 function get_complete_topic_tracking($forum_id, $topic_ids, $global_announce_list = false) 1681 { 1682 global $config, $user; 1683 1684 $last_read = array(); 1685 1686 if (!is_array($topic_ids)) 1687 { 1688 $topic_ids = array($topic_ids); 1689 } 1690 1691 if ($config['load_db_lastread'] && $user->data['is_registered']) 1692 { 1693 global $db; 1694 1695 $sql = 'SELECT topic_id, mark_time 1696 FROM ' . TOPICS_TRACK_TABLE . " 1697 WHERE user_id = {$user->data['user_id']} 1698 AND " . $db->sql_in_set('topic_id', $topic_ids); 1699 $result = $db->sql_query($sql); 1700 1701 while ($row = $db->sql_fetchrow($result)) 1702 { 1703 $last_read[$row['topic_id']] = $row['mark_time']; 1704 } 1705 $db->sql_freeresult($result); 1706 1707 $topic_ids = array_diff($topic_ids, array_keys($last_read)); 1708 1709 if (sizeof($topic_ids)) 1710 { 1711 $sql = 'SELECT forum_id, mark_time 1712 FROM ' . FORUMS_TRACK_TABLE . " 1713 WHERE user_id = {$user->data['user_id']} 1714 AND forum_id " . 1715 (($global_announce_list && sizeof($global_announce_list)) ? "IN (0, $forum_id)" : "= $forum_id"); 1716 $result = $db->sql_query($sql); 1717 1718 $mark_time = array(); 1719 while ($row = $db->sql_fetchrow($result)) 1720 { 1721 $mark_time[$row['forum_id']] = $row['mark_time']; 1722 } 1723 $db->sql_freeresult($result); 1724 1725 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark']; 1726 1727 foreach ($topic_ids as $topic_id) 1728 { 1729 if ($global_announce_list && isset($global_announce_list[$topic_id])) 1730 { 1731 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark; 1732 } 1733 else 1734 { 1735 $last_read[$topic_id] = $user_lastmark; 1736 } 1737 } 1738 } 1739 } 1740 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 1741 { 1742 global $tracking_topics; 1743 1744 if (!isset($tracking_topics) || !sizeof($tracking_topics)) 1745 { 1746 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : ''; 1747 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array(); 1748 } 1749 1750 if (!$user->data['is_registered']) 1751 { 1752 $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0; 1753 } 1754 else 1755 { 1756 $user_lastmark = $user->data['user_lastmark']; 1757 } 1758 1759 foreach ($topic_ids as $topic_id) 1760 { 1761 $topic_id36 = base_convert($topic_id, 10, 36); 1762 1763 if (isset($tracking_topics['t'][$topic_id36])) 1764 { 1765 $last_read[$topic_id] = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate']; 1766 } 1767 } 1768 1769 $topic_ids = array_diff($topic_ids, array_keys($last_read)); 1770 1771 if (sizeof($topic_ids)) 1772 { 1773 $mark_time = array(); 1774 if ($global_announce_list && sizeof($global_announce_list)) 1775 { 1776 if (isset($tracking_topics['f'][0])) 1777 { 1778 $mark_time[0] = base_convert($tracking_topics['f'][0], 36, 10) + $config['board_startdate']; 1779 } 1780 } 1781 1782 if (isset($tracking_topics['f'][$forum_id])) 1783 { 1784 $mark_time[$forum_id] = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate']; 1785 } 1786 1787 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user_lastmark; 1788 1789 foreach ($topic_ids as $topic_id) 1790 { 1791 if ($global_announce_list && isset($global_announce_list[$topic_id])) 1792 { 1793 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark; 1794 } 1795 else 1796 { 1797 $last_read[$topic_id] = $user_lastmark; 1798 } 1799 } 1800 } 1801 } 1802 1803 return $last_read; 1804 } 1805 1806 /** 1807 * Get list of unread topics 1808 * 1809 * @param int $user_id User ID (or false for current user) 1810 * @param string $sql_extra Extra WHERE SQL statement 1811 * @param string $sql_sort ORDER BY SQL sorting statement 1812 * @param string $sql_limit Limits the size of unread topics list, 0 for unlimited query 1813 * @param string $sql_limit_offset Sets the offset of the first row to search, 0 to search from the start 1814 * 1815 * @return array[int][int] Topic ids as keys, mark_time of topic as value 1816 */ 1817 function get_unread_topics($user_id = false, $sql_extra = '', $sql_sort = '', $sql_limit = 1001, $sql_limit_offset = 0) 1818 { 1819 global $config, $db, $user; 1820 1821 $user_id = ($user_id === false) ? (int) $user->data['user_id'] : (int) $user_id; 1822 1823 // Data array we're going to return 1824 $unread_topics = array(); 1825 1826 if (empty($sql_sort)) 1827 { 1828 $sql_sort = 'ORDER BY t.topic_last_post_time DESC'; 1829 } 1830 1831 if ($config['load_db_lastread'] && $user->data['is_registered']) 1832 { 1833 // Get list of the unread topics 1834 $last_mark = (int) $user->data['user_lastmark']; 1835 1836 $sql_array = array( 1837 'SELECT' => 't.topic_id, t.topic_last_post_time, tt.mark_time as topic_mark_time, ft.mark_time as forum_mark_time', 1838 1839 'FROM' => array(TOPICS_TABLE => 't'), 1840 1841 'LEFT_JOIN' => array( 1842 array( 1843 'FROM' => array(TOPICS_TRACK_TABLE => 'tt'), 1844 'ON' => "tt.user_id = $user_id AND t.topic_id = tt.topic_id", 1845 ), 1846 array( 1847 'FROM' => array(FORUMS_TRACK_TABLE => 'ft'), 1848 'ON' => "ft.user_id = $user_id AND t.forum_id = ft.forum_id", 1849 ), 1850 ), 1851 1852 'WHERE' => " 1853 t.topic_last_post_time > $last_mark AND 1854 ( 1855 (tt.mark_time IS NOT NULL AND t.topic_last_post_time > tt.mark_time) OR 1856 (tt.mark_time IS NULL AND ft.mark_time IS NOT NULL AND t.topic_last_post_time > ft.mark_time) OR 1857 (tt.mark_time IS NULL AND ft.mark_time IS NULL) 1858 ) 1859 $sql_extra 1860 $sql_sort", 1861 ); 1862 1863 $sql = $db->sql_build_query('SELECT', $sql_array); 1864 $result = $db->sql_query_limit($sql, $sql_limit, $sql_limit_offset); 1865 1866 while ($row = $db->sql_fetchrow($result)) 1867 { 1868 $topic_id = (int) $row['topic_id']; 1869 $unread_topics[$topic_id] = ($row['topic_mark_time']) ? (int) $row['topic_mark_time'] : (($row['forum_mark_time']) ? (int) $row['forum_mark_time'] : $last_mark); 1870 } 1871 $db->sql_freeresult($result); 1872 } 1873 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 1874 { 1875 global $tracking_topics; 1876 1877 if (empty($tracking_topics)) 1878 { 1879 $tracking_topics = request_var($config['cookie_name'] . '_track', '', false, true); 1880 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array(); 1881 } 1882 1883 if (!$user->data['is_registered']) 1884 { 1885 $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0; 1886 } 1887 else 1888 { 1889 $user_lastmark = (int) $user->data['user_lastmark']; 1890 } 1891 1892 $sql = 'SELECT t.topic_id, t.forum_id, t.topic_last_post_time 1893 FROM ' . TOPICS_TABLE . ' t 1894 WHERE t.topic_last_post_time > ' . $user_lastmark . " 1895 $sql_extra 1896 $sql_sort"; 1897 $result = $db->sql_query_limit($sql, $sql_limit, $sql_limit_offset); 1898 1899 while ($row = $db->sql_fetchrow($result)) 1900 { 1901 $forum_id = (int) $row['forum_id']; 1902 $topic_id = (int) $row['topic_id']; 1903 $topic_id36 = base_convert($topic_id, 10, 36); 1904 1905 if (isset($tracking_topics['t'][$topic_id36])) 1906 { 1907 $last_read = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate']; 1908 1909 if ($row['topic_last_post_time'] > $last_read) 1910 { 1911 $unread_topics[$topic_id] = $last_read; 1912 } 1913 } 1914 else if (isset($tracking_topics['f'][$forum_id])) 1915 { 1916 $mark_time = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate']; 1917 1918 if ($row['topic_last_post_time'] > $mark_time) 1919 { 1920 $unread_topics[$topic_id] = $mark_time; 1921 } 1922 } 1923 else 1924 { 1925 $unread_topics[$topic_id] = $user_lastmark; 1926 } 1927 } 1928 $db->sql_freeresult($result); 1929 } 1930 1931 return $unread_topics; 1932 } 1933 1934 /** 1935 * Check for read forums and update topic tracking info accordingly 1936 * 1937 * @param int $forum_id the forum id to check 1938 * @param int $forum_last_post_time the forums last post time 1939 * @param int $f_mark_time the forums last mark time if user is registered and load_db_lastread enabled 1940 * @param int $mark_time_forum false if the mark time needs to be obtained, else the last users forum mark time 1941 * 1942 * @return true if complete forum got marked read, else false. 1943 */ 1944 function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_time = false, $mark_time_forum = false) 1945 { 1946 global $db, $tracking_topics, $user, $config, $auth; 1947 1948 // Determine the users last forum mark time if not given. 1949 if ($mark_time_forum === false) 1950 { 1951 if ($config['load_db_lastread'] && $user->data['is_registered']) 1952 { 1953 $mark_time_forum = (!empty($f_mark_time)) ? $f_mark_time : $user->data['user_lastmark']; 1954 } 1955 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 1956 { 1957 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : ''; 1958 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array(); 1959 1960 if (!$user->data['is_registered']) 1961 { 1962 $user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0; 1963 } 1964 1965 $mark_time_forum = (isset($tracking_topics['f'][$forum_id])) ? (int) (base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate']) : $user->data['user_lastmark']; 1966 } 1967 } 1968 1969 // Handle update of unapproved topics info. 1970 // Only update for moderators having m_approve permission for the forum. 1971 $sql_update_unapproved = ($auth->acl_get('m_approve', $forum_id)) ? '': 'AND t.topic_approved = 1'; 1972 1973 // Check the forum for any left unread topics. 1974 // If there are none, we mark the forum as read. 1975 if ($config['load_db_lastread'] && $user->data['is_registered']) 1976 { 1977 if ($mark_time_forum >= $forum_last_post_time) 1978 { 1979 // We do not need to mark read, this happened before. Therefore setting this to true 1980 $row = true; 1981 } 1982 else 1983 { 1984 $sql = 'SELECT t.forum_id 1985 FROM ' . TOPICS_TABLE . ' t 1986 LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt 1987 ON (tt.topic_id = t.topic_id 1988 AND tt.user_id = ' . $user->data['user_id'] . ') 1989 WHERE t.forum_id = ' . $forum_id . ' 1990 AND t.topic_last_post_time > ' . $mark_time_forum . ' 1991 AND t.topic_moved_id = 0 ' . 1992 $sql_update_unapproved . ' 1993 AND (tt.topic_id IS NULL 1994 OR tt.mark_time < t.topic_last_post_time)'; 1995 $result = $db->sql_query_limit($sql, 1); 1996 $row = $db->sql_fetchrow($result); 1997 $db->sql_freeresult($result); 1998 } 1999 } 2000 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 2001 { 2002 // Get information from cookie 2003 $row = false; 2004 2005 if (!isset($tracking_topics['tf'][$forum_id])) 2006 { 2007 // We do not need to mark read, this happened before. Therefore setting this to true 2008 $row = true; 2009 } 2010 else 2011 { 2012 $sql = 'SELECT t.topic_id 2013 FROM ' . TOPICS_TABLE . ' t 2014 WHERE t.forum_id = ' . $forum_id . ' 2015 AND t.topic_last_post_time > ' . $mark_time_forum . ' 2016 AND t.topic_moved_id = 0 ' . 2017 $sql_update_unapproved; 2018 $result = $db->sql_query($sql); 2019 2020 $check_forum = $tracking_topics['tf'][$forum_id]; 2021 $unread = false; 2022 2023 while ($row = $db->sql_fetchrow($result)) 2024 { 2025 if (!isset($check_forum[base_convert($row['topic_id'], 10, 36)])) 2026 { 2027 $unread = true; 2028 break; 2029 } 2030 } 2031 $db->sql_freeresult($result); 2032 2033 $row = $unread; 2034 } 2035 } 2036 else 2037 { 2038 $row = true; 2039 } 2040 2041 if (!$row) 2042 { 2043 markread('topics', $forum_id); 2044 return true; 2045 } 2046 2047 return false; 2048 } 2049 2050 /** 2051 * Transform an array into a serialized format 2052 */ 2053 function tracking_serialize($input) 2054 { 2055 $out = ''; 2056 foreach ($input as $key => $value) 2057 { 2058 if (is_array($value)) 2059 { 2060 $out .= $key . ':(' . tracking_serialize($value) . ');'; 2061 } 2062 else 2063 { 2064 $out .= $key . ':' . $value . ';'; 2065 } 2066 } 2067 return $out; 2068 } 2069 2070 /** 2071 * Transform a serialized array into an actual array 2072 */ 2073 function tracking_unserialize($string, $max_depth = 3) 2074 { 2075 $n = strlen($string); 2076 if ($n > 10010) 2077 { 2078 die('Invalid data supplied'); 2079 } 2080 $data = $stack = array(); 2081 $key = ''; 2082 $mode = 0; 2083 $level = &$data; 2084 for ($i = 0; $i < $n; ++$i) 2085 { 2086 switch ($mode) 2087 { 2088 case 0: 2089 switch ($string[$i]) 2090 { 2091 case ':': 2092 $level[$key] = 0; 2093 $mode = 1; 2094 break; 2095 case ')': 2096 unset($level); 2097 $level = array_pop($stack); 2098 $mode = 3; 2099 break; 2100 default: 2101 $key .= $string[$i]; 2102 } 2103 break; 2104 2105 case 1: 2106 switch ($string[$i]) 2107 { 2108 case '(': 2109 if (sizeof($stack) >= $max_depth) 2110 { 2111 die('Invalid data supplied'); 2112 } 2113 $stack[] = &$level; 2114 $level[$key] = array(); 2115 $level = &$level[$key]; 2116 $key = ''; 2117 $mode = 0; 2118 break; 2119 default: 2120 $level[$key] = $string[$i]; 2121 $mode = 2; 2122 break; 2123 } 2124 break; 2125 2126 case 2: 2127 switch ($string[$i]) 2128 { 2129 case ')': 2130 unset($level); 2131 $level = array_pop($stack); 2132 $mode = 3; 2133 break; 2134 case ';': 2135 $key = ''; 2136 $mode = 0; 2137 break; 2138 default: 2139 $level[$key] .= $string[$i]; 2140 break; 2141 } 2142 break; 2143 2144 case 3: 2145 switch ($string[$i]) 2146 { 2147 case ')': 2148 unset($level); 2149 $level = array_pop($stack); 2150 break; 2151 case ';': 2152 $key = ''; 2153 $mode = 0; 2154 break; 2155 default: 2156 die('Invalid data supplied'); 2157 break; 2158 } 2159 break; 2160 } 2161 } 2162 2163 if (sizeof($stack) != 0 || ($mode != 0 && $mode != 3)) 2164 { 2165 die('Invalid data supplied'); 2166 } 2167 2168 return $level; 2169 } 2170 2171 // Pagination functions 2172 2173 /** 2174 * Pagination routine, generates page number sequence 2175 * tpl_prefix is for using different pagination blocks at one page 2176 */ 2177 function generate_pagination($base_url, $num_items, $per_page, $start_item, $add_prevnext_text = false, $tpl_prefix = '') 2178 { 2179 global $template, $user; 2180 2181 // Make sure $per_page is a valid value 2182 $per_page = ($per_page <= 0) ? 1 : $per_page; 2183 2184 $seperator = '<span class="page-sep">' . $user->lang['COMMA_SEPARATOR'] . '</span>'; 2185 $total_pages = ceil($num_items / $per_page); 2186 2187 if ($total_pages == 1 || !$num_items) 2188 { 2189 return false; 2190 } 2191 2192 $on_page = floor($start_item / $per_page) + 1; 2193 $url_delim = (strpos($base_url, '?') === false) ? '?' : ((strpos($base_url, '?') === strlen($base_url) - 1) ? '' : '&'); 2194 2195 $page_string = ($on_page == 1) ? '<strong>1</strong>' : '<a href="' . $base_url . '">1</a>'; 2196 2197 if ($total_pages > 5) 2198 { 2199 $start_cnt = min(max(1, $on_page - 4), $total_pages - 5); 2200 $end_cnt = max(min($total_pages, $on_page + 4), 6); 2201 2202 $page_string .= ($start_cnt > 1) ? '<span class="page-dots"> ... </span>' : $seperator; 2203 2204 for ($i = $start_cnt + 1; $i < $end_cnt; $i++) 2205 { 2206 $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($i - 1) * $per_page) . '">' . $i . '</a>'; 2207 if ($i < $end_cnt - 1) 2208 { 2209 $page_string .= $seperator; 2210 } 2211 } 2212 2213 $page_string .= ($end_cnt < $total_pages) ? '<span class="page-dots"> ... </span>' : $seperator; 2214 } 2215 else 2216 { 2217 $page_string .= $seperator; 2218 2219 for ($i = 2; $i < $total_pages; $i++) 2220 { 2221 $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($i - 1) * $per_page) . '">' . $i . '</a>'; 2222 if ($i < $total_pages) 2223 { 2224 $page_string .= $seperator; 2225 } 2226 } 2227 } 2228 2229 $page_string .= ($on_page == $total_pages) ? '<strong>' . $total_pages . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($total_pages - 1) * $per_page) . '">' . $total_pages . '</a>'; 2230 2231 if ($add_prevnext_text) 2232 { 2233 if ($on_page != 1) 2234 { 2235 $page_string = '<a href="' . $base_url . "{$url_delim}start=" . (($on_page - 2) * $per_page) . '">' . $user->lang['PREVIOUS'] . '</a> ' . $page_string; 2236 } 2237 2238 if ($on_page != $total_pages) 2239 { 2240 $page_string .= ' <a href="' . $base_url . "{$url_delim}start=" . ($on_page * $per_page) . '">' . $user->lang['NEXT'] . '</a>'; 2241 } 2242 } 2243 2244 $template->assign_vars(array( 2245 $tpl_prefix . 'BASE_URL' => $base_url, 2246 'A_' . $tpl_prefix . 'BASE_URL' => addslashes($base_url), 2247 $tpl_prefix . 'PER_PAGE' => $per_page, 2248 2249 $tpl_prefix . 'PREVIOUS_PAGE' => ($on_page == 1) ? '' : $base_url . "{$url_delim}start=" . (($on_page - 2) * $per_page), 2250 $tpl_prefix . 'NEXT_PAGE' => ($on_page == $total_pages) ? '' : $base_url . "{$url_delim}start=" . ($on_page * $per_page), 2251 $tpl_prefix . 'TOTAL_PAGES' => $total_pages, 2252 )); 2253 2254 return $page_string; 2255 } 2256 2257 /** 2258 * Return current page (pagination) 2259 */ 2260 function on_page($num_items, $per_page, $start) 2261 { 2262 global $template, $user; 2263 2264 // Make sure $per_page is a valid value 2265 $per_page = ($per_page <= 0) ? 1 : $per_page; 2266 2267 $on_page = floor($start / $per_page) + 1; 2268 2269 $template->assign_vars(array( 2270 'ON_PAGE' => $on_page) 2271 ); 2272 2273 return sprintf($user->lang['PAGE_OF'], $on_page, max(ceil($num_items / $per_page), 1)); 2274 } 2275 2276 // Server functions (building urls, redirecting...) 2277 2278 /** 2279 * Append session id to url. 2280 * This function supports hooks. 2281 * 2282 * @param string $url The url the session id needs to be appended to (can have params) 2283 * @param mixed $params String or array of additional url parameters 2284 * @param bool $is_amp Is url using & (true) or & (false) 2285 * @param string $session_id Possibility to use a custom session id instead of the global one 2286 * 2287 * Examples: 2288 * <code> 2289 * append_sid("{$phpbb_root_path}viewtopic.$phpEx?t=1&f=2"); 2290 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2'); 2291 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2', false); 2292 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", array('t' => 1, 'f' => 2)); 2293 * </code> 2294 * 2295 */ 2296 function append_sid($url, $params = false, $is_amp = true, $session_id = false) 2297 { 2298 global $_SID, $_EXTRA_URL, $phpbb_hook; 2299 2300 if ($params === '' || (is_array($params) && empty($params))) 2301 { 2302 // Do not append the ? if the param-list is empty anyway. 2303 $params = false; 2304 } 2305 2306 // Developers using the hook function need to globalise the $_SID and $_EXTRA_URL on their own and also handle it appropriately. 2307 // They could mimic most of what is within this function 2308 if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__, $url, $params, $is_amp, $session_id)) 2309 { 2310 if ($phpbb_hook->hook_return(__FUNCTION__)) 2311 { 2312 return $phpbb_hook->hook_return_result(__FUNCTION__); 2313 } 2314 } 2315 2316 $params_is_array = is_array($params); 2317 2318 // Get anchor 2319 $anchor = ''; 2320 if (strpos($url, '#') !== false) 2321 { 2322 list($url, $anchor) = explode('#', $url, 2); 2323 $anchor = '#' . $anchor; 2324 } 2325 else if (!$params_is_array && strpos($params, '#') !== false) 2326 { 2327 list($params, $anchor) = explode('#', $params, 2); 2328 $anchor = '#' . $anchor; 2329 } 2330 2331 // Handle really simple cases quickly 2332 if ($_SID == '' && $session_id === false && empty($_EXTRA_URL) && !$params_is_array && !$anchor) 2333 { 2334 if ($params === false) 2335 { 2336 return $url; 2337 } 2338 2339 $url_delim = (strpos($url, '?') === false) ? '?' : (($is_amp) ? '&' : '&'); 2340 return $url . ($params !== false ? $url_delim. $params : ''); 2341 } 2342 2343 // Assign sid if session id is not specified 2344 if ($session_id === false) 2345 { 2346 $session_id = $_SID; 2347 } 2348 2349 $amp_delim = ($is_amp) ? '&' : '&'; 2350 $url_delim = (strpos($url, '?') === false) ? '?' : $amp_delim; 2351 2352 // Appending custom url parameter? 2353 $append_url = (!empty($_EXTRA_URL)) ? implode($amp_delim, $_EXTRA_URL) : ''; 2354 2355 // Use the short variant if possible ;) 2356 if ($params === false) 2357 { 2358 // Append session id 2359 if (!$session_id) 2360 { 2361 return $url . (($append_url) ? $url_delim . $append_url : '') . $anchor; 2362 } 2363 else 2364 { 2365 return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . 'sid=' . $session_id . $anchor; 2366 } 2367 } 2368 2369 // Build string if parameters are specified as array 2370 if (is_array($params)) 2371 { 2372 $output = array(); 2373 2374 foreach ($params as $key => $item) 2375 { 2376 if ($item === NULL) 2377 { 2378 continue; 2379 } 2380 2381 if ($key == '#') 2382 { 2383 $anchor = '#' . $item; 2384 continue; 2385 } 2386 2387 $output[] = $key . '=' . $item; 2388 } 2389 2390 $params = implode($amp_delim, $output); 2391 } 2392 2393 // Append session id and parameters (even if they are empty) 2394 // If parameters are empty, the developer can still append his/her parameters without caring about the delimiter 2395 return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . $params . ((!$session_id) ? '' : $amp_delim . 'sid=' . $session_id) . $anchor; 2396 } 2397 2398 /** 2399 * Generate board url (example: http://www.example.com/phpBB) 2400 * 2401 * @param bool $without_script_path if set to true the script path gets not appended (example: http://www.example.com) 2402 * 2403 * @return string the generated board url 2404 */ 2405 function generate_board_url($without_script_path = false) 2406 { 2407 global $config, $user; 2408 2409 $server_name = $user->host; 2410 $server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT'); 2411 2412 // Forcing server vars is the only way to specify/override the protocol 2413 if ($config['force_server_vars'] || !$server_name) 2414 { 2415 $server_protocol = ($config['server_protocol']) ? $config['server_protocol'] : (($config['cookie_secure']) ? 'https://' : 'http://'); 2416 $server_name = $config['server_name']; 2417 $server_port = (int) $config['server_port']; 2418 $script_path = $config['script_path']; 2419 2420 $url = $server_protocol . $server_name; 2421 $cookie_secure = $config['cookie_secure']; 2422 } 2423 else 2424 { 2425 // Do not rely on cookie_secure, users seem to think that it means a secured cookie instead of an encrypted connection 2426 $cookie_secure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 1 : 0; 2427 $url = (($cookie_secure) ? 'https://' : 'http://') . $server_name; 2428 2429 $script_path = $user->page['root_script_path']; 2430 } 2431 2432 if ($server_port && (($cookie_secure && $server_port <> 443) || (!$cookie_secure && $server_port <> 80))) 2433 { 2434 // HTTP HOST can carry a port number (we fetch $user->host, but for old versions this may be true) 2435 if (strpos($server_name, ':') === false) 2436 { 2437 $url .= ':' . $server_port; 2438 } 2439 } 2440 2441 if (!$without_script_path) 2442 { 2443 $url .= $script_path; 2444 } 2445 2446 // Strip / from the end 2447 if (substr($url, -1, 1) == '/') 2448 { 2449 $url = substr($url, 0, -1); 2450 } 2451 2452 return $url; 2453 } 2454 2455 /** 2456 * Redirects the user to another page then exits the script nicely 2457 * This function is intended for urls within the board. It's not meant to redirect to cross-domains. 2458 * 2459 * @param string $url The url to redirect to 2460 * @param bool $return If true, do not redirect but return the sanitized URL. Default is no return. 2461 * @param bool $disable_cd_check If true, redirect() will redirect to an external domain. If false, the redirect point to the boards url if it does not match the current domain. Default is false. 2462 */ 2463 function redirect($url, $return = false, $disable_cd_check = false) 2464 { 2465 global $db, $cache, $config, $user, $phpbb_root_path; 2466 2467 $failover_flag = false; 2468 2469 if (empty($user->lang)) 2470 { 2471 $user->add_lang('common'); 2472 } 2473 2474 if (!$return) 2475 { 2476 garbage_collection(); 2477 } 2478 2479 // Make sure no &'s are in, this will break the redirect 2480 $url = str_replace('&', '&', $url); 2481 2482 // Determine which type of redirect we need to handle... 2483 $url_parts = @parse_url($url); 2484 2485 if ($url_parts === false) 2486 { 2487 // Malformed url, redirect to current page... 2488 $url = generate_board_url() . '/' . $user->page['page']; 2489 } 2490 else if (!empty($url_parts['scheme']) && !empty($url_parts['host'])) 2491 { 2492 // Attention: only able to redirect within the same domain if $disable_cd_check is false (yourdomain.com -> www.yourdomain.com will not work) 2493 if (!$disable_cd_check && $url_parts['host'] !== $user->host) 2494 { 2495 $url = generate_board_url(); 2496 } 2497 } 2498 else if ($url[0] == '/') 2499 { 2500 // Absolute uri, prepend direct url... 2501 $url = generate_board_url(true) . $url; 2502 } 2503 else 2504 { 2505 // Relative uri 2506 $pathinfo = pathinfo($url); 2507 2508 if (!$disable_cd_check && !file_exists($pathinfo['dirname'] . '/')) 2509 { 2510 $url = str_replace('../', '', $url); 2511 $pathinfo = pathinfo($url); 2512 2513 if (!file_exists($pathinfo['dirname'] . '/')) 2514 { 2515 // fallback to "last known user page" 2516 // at least this way we know the user does not leave the phpBB root 2517 $url = generate_board_url() . '/' . $user->page['page']; 2518 $failover_flag = true; 2519 } 2520 } 2521 2522 if (!$failover_flag) 2523 { 2524 // Is the uri pointing to the current directory? 2525 if ($pathinfo['dirname'] == '.') 2526 { 2527 $url = str_replace('./', '', $url); 2528 2529 // Strip / from the beginning 2530 if ($url && substr($url, 0, 1) == '/') 2531 { 2532 $url = substr($url, 1); 2533 } 2534 2535 if ($user->page['page_dir']) 2536 { 2537 $url = generate_board_url() . '/' . $user->page['page_dir'] . '/' . $url; 2538 } 2539 else 2540 { 2541 $url = generate_board_url() . '/' . $url; 2542 } 2543 } 2544 else 2545 { 2546 // Used ./ before, but $phpbb_root_path is working better with urls within another root path 2547 $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($phpbb_root_path))); 2548 $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($pathinfo['dirname']))); 2549 $intersection = array_intersect_assoc($root_dirs, $page_dirs); 2550 2551 $root_dirs = array_diff_assoc($root_dirs, $intersection); 2552 $page_dirs = array_diff_assoc($page_dirs, $intersection); 2553 2554 $dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs); 2555 2556 // Strip / from the end 2557 if ($dir && substr($dir, -1, 1) == '/') 2558 { 2559 $dir = substr($dir, 0, -1); 2560 } 2561 2562 // Strip / from the beginning 2563 if ($dir && substr($dir, 0, 1) == '/') 2564 { 2565 $dir = substr($dir, 1); 2566 } 2567 2568 $url = str_replace($pathinfo['dirname'] . '/', '', $url); 2569 2570 // Strip / from the beginning 2571 if (substr($url, 0, 1) == '/') 2572 { 2573 $url = substr($url, 1); 2574 } 2575 2576 $url = (!empty($dir) ? $dir . '/' : '') . $url; 2577 $url = generate_board_url() . '/' . $url; 2578 } 2579 } 2580 } 2581 2582 // Make sure no linebreaks are there... to prevent http response splitting for PHP < 4.4.2 2583 if (strpos(urldecode($url), "\n") !== false || strpos(urldecode($url), "\r") !== false || strpos($url, ';') !== false) 2584 { 2585 trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR); 2586 } 2587 2588 // Now, also check the protocol and for a valid url the last time... 2589 $allowed_protocols = array('http', 'https', 'ftp', 'ftps'); 2590 $url_parts = parse_url($url); 2591 2592 if ($url_parts === false || empty($url_parts['scheme']) || !in_array($url_parts['scheme'], $allowed_protocols)) 2593 { 2594 trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR); 2595 } 2596 2597 if ($return) 2598 { 2599 return $url; 2600 } 2601 2602 // Redirect via an HTML form for PITA webservers 2603 if (@preg_match('#Microsoft|WebSTAR|Xitami#', getenv('SERVER_SOFTWARE'))) 2604 { 2605 header('Refresh: 0; URL=' . $url); 2606 2607 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'; 2608 echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="' . $user->lang['DIRECTION'] . '" lang="' . $user->lang['USER_LANG'] . '" xml:lang="' . $user->lang['USER_LANG'] . '">'; 2609 echo '<head>'; 2610 echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />'; 2611 echo '<meta http-equiv="refresh" content="0; url=' . str_replace('&', '&', $url) . '" />'; 2612 echo '<title>' . $user->lang['REDIRECT'] . '</title>'; 2613 echo '</head>'; 2614 echo '<body>'; 2615 echo '<div style="text-align: center;">' . sprintf($user->lang['URL_REDIRECT'], '<a href="' . str_replace('&', '&', $url) . '">', '</a>') . '</div>'; 2616 echo '</body>'; 2617 echo '</html>'; 2618 2619 exit; 2620 } 2621 2622 // Behave as per HTTP/1.1 spec for others 2623 header('Location: ' . $url); 2624 exit; 2625 } 2626 2627 /** 2628 * Re-Apply session id after page reloads 2629 */ 2630 function reapply_sid($url) 2631 { 2632 global $phpEx, $phpbb_root_path; 2633 2634 if ($url === "index.$phpEx") 2635 { 2636 return append_sid("index.$phpEx"); 2637 } 2638 else if ($url === "{$phpbb_root_path}index.$phpEx") 2639 { 2640 return append_sid("{$phpbb_root_path}index.$phpEx"); 2641 } 2642 2643 // Remove previously added sid 2644 if (strpos($url, 'sid=') !== false) 2645 { 2646 // All kind of links 2647 $url = preg_replace('/(\?)?(&|&)?sid=[a-z0-9]+/', '', $url); 2648 // if the sid was the first param, make the old second as first ones 2649 $url = preg_replace("/$phpEx(&|&)+?/", "$phpEx?", $url); 2650 } 2651 2652 return append_sid($url); 2653 } 2654 2655 /** 2656 * Returns url from the session/current page with an re-appended SID with optionally stripping vars from the url 2657 */ 2658 function build_url($strip_vars = false) 2659 { 2660 global $user, $phpbb_root_path; 2661 2662 // Append SID 2663 $redirect = append_sid($user->page['page'], false, false); 2664 2665 // Add delimiter if not there... 2666 if (strpos($redirect, '?') === false) 2667 { 2668 $redirect .= '?'; 2669 } 2670 2671 // Strip vars... 2672 if ($strip_vars !== false && strpos($redirect, '?') !== false) 2673 { 2674 if (!is_array($strip_vars)) 2675 { 2676 $strip_vars = array($strip_vars); 2677 } 2678 2679 $query = $_query = array(); 2680 2681 $args = substr($redirect, strpos($redirect, '?') + 1); 2682 $args = ($args) ? explode('&', $args) : array(); 2683 $redirect = substr($redirect, 0, strpos($redirect, '?')); 2684 2685 foreach ($args as $argument) 2686 { 2687 $arguments = explode('=', $argument); 2688 $key = $arguments[0]; 2689 unset($arguments[0]); 2690 2691 if ($key === '') 2692 { 2693 continue; 2694 } 2695 2696 $query[$key] = implode('=', $arguments); 2697 } 2698 2699 // Strip the vars off 2700 foreach ($strip_vars as $strip) 2701 { 2702 if (isset($query[$strip])) 2703 { 2704 unset($query[$strip]); 2705 } 2706 } 2707 2708 // Glue the remaining parts together... already urlencoded 2709 foreach ($query as $key => $value) 2710 { 2711 $_query[] = $key . '=' . $value; 2712 } 2713 $query = implode('&', $_query); 2714 2715 $redirect .= ($query) ? '?' . $query : ''; 2716 } 2717 2718 // We need to be cautious here. 2719 // On some situations, the redirect path is an absolute URL, sometimes a relative path 2720 // For a relative path, let's prefix it with $phpbb_root_path to point to the correct location, 2721 // else we use the URL directly. 2722 $url_parts = @parse_url($redirect); 2723 2724 // URL 2725 if ($url_parts !== false && !empty($url_parts['scheme']) && !empty($url_parts['host'])) 2726 { 2727 return str_replace('&', '&', $redirect); 2728 } 2729 2730 return $phpbb_root_path . str_replace('&', '&', $redirect); 2731 } 2732 2733 /** 2734 * Meta refresh assignment 2735 * Adds META template variable with meta http tag. 2736 * 2737 * @param int $time Time in seconds for meta refresh tag 2738 * @param string $url URL to redirect to. The url will go through redirect() first before the template variable is assigned 2739 * @param bool $disable_cd_check If true, meta_refresh() will redirect to an external domain. If false, the redirect point to the boards url if it does not match the current domain. Default is false. 2740 */ 2741 function meta_refresh($time, $url, $disable_cd_check = false) 2742 { 2743 global $template; 2744 2745 $url = redirect($url, true, $disable_cd_check); 2746 $url = str_replace('&', '&', $url); 2747 2748 // For XHTML compatibility we change back & to & 2749 $template->assign_vars(array( 2750 'META' => '<meta http-equiv="refresh" content="' . $time . '; url=' . $url . '" />') 2751 ); 2752 2753 return $url; 2754 } 2755 2756 /** 2757 * Outputs correct status line header. 2758 * 2759 * Depending on php sapi one of the two following forms is used: 2760 * 2761 * Status: 404 Not Found 2762 * 2763 * HTTP/1.x 404 Not Found 2764 * 2765 * HTTP version is taken from HTTP_VERSION environment variable, 2766 * and defaults to 1.0. 2767 * 2768 * Sample usage: 2769 * 2770 * send_status_line(404, 'Not Found'); 2771 * 2772 * @param int $code HTTP status code 2773 * @param string $message Message for the status code 2774 * @return null 2775 */ 2776 function send_status_line($code, $message) 2777 { 2778 if (substr(strtolower(@php_sapi_name()), 0, 3) === 'cgi') 2779 { 2780 // in theory, we shouldn't need that due to php doing it. Reality offers a differing opinion, though 2781 header("Status: $code $message", true, $code); 2782 } 2783 else 2784 { 2785 if (!empty($_SERVER['SERVER_PROTOCOL'])) 2786 { 2787 $version = $_SERVER['SERVER_PROTOCOL']; 2788 } 2789 else 2790 { 2791 $version = 'HTTP/1.0'; 2792 } 2793 header("$version $code $message", true, $code); 2794 } 2795 } 2796 2797 //Form validation 2798 2799 2800 /** 2801 * Add a secret hash for use in links/GET requests 2802 * @param string $link_name The name of the link; has to match the name used in check_link_hash, otherwise no restrictions apply 2803 * @return string the hash 2804 2805 */ 2806 function generate_link_hash($link_name) 2807 { 2808 global $user; 2809 2810 if (!isset($user->data["hash_$link_name"])) 2811 { 2812 $user->data["hash_$link_name"] = substr(sha1($user->data['user_form_salt'] . $link_name), 0, 8); 2813 } 2814 2815 return $user->data["hash_$link_name"]; 2816 } 2817 2818 2819 /** 2820 * checks a link hash - for GET requests 2821 * @param string $token the submitted token 2822 * @param string $link_name The name of the link 2823 * @return boolean true if all is fine 2824 */ 2825 function check_link_hash($token, $link_name) 2826 { 2827 return $token === generate_link_hash($link_name); 2828 } 2829 2830 /** 2831 * Add a secret token to the form (requires the S_FORM_TOKEN template variable) 2832 * @param string $form_name The name of the form; has to match the name used in check_form_key, otherwise no restrictions apply 2833 */ 2834 function add_form_key($form_name) 2835 { 2836 global $config, $template, $user; 2837 2838 $now = time(); 2839 $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : ''; 2840 $token = sha1($now . $user->data['user_form_salt'] . $form_name . $token_sid); 2841 2842 $s_fields = build_hidden_fields(array( 2843 'creation_time' => $now, 2844 'form_token' => $token, 2845 )); 2846 2847 $template->assign_vars(array( 2848 'S_FORM_TOKEN' => $s_fields, 2849 )); 2850 } 2851 2852 /** 2853 * Check the form key. Required for all altering actions not secured by confirm_box 2854 * @param string $form_name The name of the form; has to match the name used in add_form_key, otherwise no restrictions apply 2855 * @param int $timespan The maximum acceptable age for a submitted form in seconds. Defaults to the config setting. 2856 * @param string $return_page The address for the return link 2857 * @param bool $trigger If true, the function will triger an error when encountering an invalid form 2858 */ 2859 function check_form_key($form_name, $timespan = false, $return_page = '', $trigger = false) 2860 { 2861 global $config, $user; 2862 2863 if ($timespan === false) 2864 { 2865 // we enforce a minimum value of half a minute here. 2866 $timespan = ($config['form_token_lifetime'] == -1) ? -1 : max(30, $config['form_token_lifetime']); 2867 } 2868 2869 if (isset($_POST['creation_time']) && isset($_POST['form_token'])) 2870 { 2871 $creation_time = abs(request_var('creation_time', 0)); 2872 $token = request_var('form_token', ''); 2873 2874 $diff = time() - $creation_time; 2875 2876 // If creation_time and the time() now is zero we can assume it was not a human doing this (the check for if ($diff)... 2877 if (defined('DEBUG_TEST') || $diff && ($diff <= $timespan || $timespan === -1)) 2878 { 2879 $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : ''; 2880 $key = sha1($creation_time . $user->data['user_form_salt'] . $form_name . $token_sid); 2881 2882 if ($key === $token) 2883 { 2884 return true; 2885 } 2886 } 2887 } 2888 2889 if ($trigger) 2890 { 2891 trigger_error($user->lang['FORM_INVALID'] . $return_page); 2892 } 2893 2894 return false; 2895 } 2896 2897 // Message/Login boxes 2898 2899 /** 2900 * Build Confirm box 2901 * @param boolean $check True for checking if confirmed (without any additional parameters) and false for displaying the confirm box 2902 * @param string $title Title/Message used for confirm box. 2903 * message text is _CONFIRM appended to title. 2904 * If title cannot be found in user->lang a default one is displayed 2905 * If title_CONFIRM cannot be found in user->lang the text given is used. 2906 * @param string $hidden Hidden variables 2907 * @param string $html_body Template used for confirm box 2908 * @param string $u_action Custom form action 2909 */ 2910 function confirm_box($check, $title = '', $hidden = '', $html_body = 'confirm_body.html', $u_action = '') 2911 { 2912 global $user, $template, $db; 2913 global $phpEx, $phpbb_root_path; 2914 2915 if (isset($_POST['cancel'])) 2916 { 2917 return false; 2918 } 2919 2920 $confirm = false; 2921 if (isset($_POST['confirm'])) 2922 { 2923 // language frontier 2924 if ($_POST['confirm'] === $user->lang['YES']) 2925 { 2926 $confirm = true; 2927 } 2928 } 2929 2930 if ($check && $confirm) 2931 { 2932 $user_id = request_var('confirm_uid', 0); 2933 $session_id = request_var('sess', ''); 2934 $confirm_key = request_var('confirm_key', ''); 2935 2936 if ($user_id != $user->data['user_id'] || $session_id != $user->session_id || !$confirm_key || !$user->data['user_last_confirm_key'] || $confirm_key != $user->data['user_last_confirm_key']) 2937 { 2938 return false; 2939 } 2940 2941 // Reset user_last_confirm_key 2942 $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = '' 2943 WHERE user_id = " . $user->data['user_id']; 2944 $db->sql_query($sql); 2945 2946 return true; 2947 } 2948 else if ($check) 2949 { 2950 return false; 2951 } 2952 2953 $s_hidden_fields = build_hidden_fields(array( 2954 'confirm_uid' => $user->data['user_id'], 2955 'sess' => $user->session_id, 2956 'sid' => $user->session_id, 2957 )); 2958 2959 // generate activation key 2960 $confirm_key = gen_rand_string(10); 2961 2962 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin']) 2963 { 2964 adm_page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]); 2965 } 2966 else 2967 { 2968 page_header(((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]), false); 2969 } 2970 2971 $template->set_filenames(array( 2972 'body' => $html_body) 2973 ); 2974 2975 // If activation key already exist, we better do not re-use the key (something very strange is going on...) 2976 if (request_var('confirm_key', '')) 2977 { 2978 // This should not occur, therefore we cancel the operation to safe the user 2979 return false; 2980 } 2981 2982 // re-add sid / transform & to & for user->page (user->page is always using &) 2983 $use_page = ($u_action) ? $phpbb_root_path . $u_action : $phpbb_root_path . str_replace('&', '&', $user->page['page']); 2984 $u_action = reapply_sid($use_page); 2985 $u_action .= ((strpos($u_action, '?') === false) ? '?' : '&') . 'confirm_key=' . $confirm_key; 2986 2987 $template->assign_vars(array( 2988 'MESSAGE_TITLE' => (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title], 2989 'MESSAGE_TEXT' => (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'], 2990 2991 'YES_VALUE' => $user->lang['YES'], 2992 'S_CONFIRM_ACTION' => $u_action, 2993 'S_HIDDEN_FIELDS' => $hidden . $s_hidden_fields) 2994 ); 2995 2996 $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = '" . $db->sql_escape($confirm_key) . "' 2997 WHERE user_id = " . $user->data['user_id']; 2998 $db->sql_query($sql); 2999 3000 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin']) 3001 { 3002 adm_page_footer(); 3003 } 3004 else 3005 { 3006 page_footer(); 3007 } 3008 } 3009 3010 /** 3011 * Generate login box or verify password 3012 */ 3013 function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = false, $s_display = true) 3014 { 3015 global $db, $user, $template, $auth, $phpEx, $phpbb_root_path, $config; 3016 3017 if (!class_exists('phpbb_captcha_factory')) 3018 { 3019 include($phpbb_root_path . 'includes/captcha/captcha_factory.' . $phpEx); 3020 } 3021 3022 $err = ''; 3023 3024 // Make sure user->setup() has been called 3025 if (empty($user->lang)) 3026 { 3027 $user->setup(); 3028 } 3029 3030 // Print out error if user tries to authenticate as an administrator without having the privileges... 3031 if ($admin && !$auth->acl_get('a_')) 3032 { 3033 // Not authd 3034 // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions 3035 if ($user->data['is_registered']) 3036 { 3037 add_log('admin', 'LOG_ADMIN_AUTH_FAIL'); 3038 } 3039 trigger_error('NO_AUTH_ADMIN'); 3040 } 3041 3042 if (isset($_POST['login'])) 3043 { 3044 // Get credential 3045 if ($admin) 3046 { 3047 $credential = request_var('credential', ''); 3048 3049 if (strspn($credential, 'abcdef0123456789') !== strlen($credential) || strlen($credential) != 32) 3050 { 3051 if ($user->data['is_registered']) 3052 { 3053 add_log('admin', 'LOG_ADMIN_AUTH_FAIL'); 3054 } 3055 trigger_error('NO_AUTH_ADMIN'); 3056 } 3057 3058 $password = request_var('password_' . $credential, '', true); 3059 } 3060 else 3061 { 3062 $password = request_var('password', '', true); 3063 } 3064 3065 $username = request_var('username', '', true); 3066 $autologin = (!empty($_POST['autologin'])) ? true : false; 3067 $viewonline = (!empty($_POST['viewonline'])) ? 0 : 1; 3068 $admin = ($admin) ? 1 : 0; 3069 $viewonline = ($admin) ? $user->data['session_viewonline'] : $viewonline; 3070 3071 // Check if the supplied username is equal to the one stored within the database if re-authenticating 3072 if ($admin && utf8_clean_string($username) != utf8_clean_string($user->data['username'])) 3073 { 3074 // We log the attempt to use a different username... 3075 add_log('admin', 'LOG_ADMIN_AUTH_FAIL'); 3076 trigger_error('NO_AUTH_ADMIN_USER_DIFFER'); 3077 } 3078 3079 // If authentication is successful we redirect user to previous page 3080 $result = $auth->login($username, $password, $autologin, $viewonline, $admin); 3081 3082 // If admin authentication and login, we will log if it was a success or not... 3083 // We also break the operation on the first non-success login - it could be argued that the user already knows 3084 if ($admin) 3085 { 3086 if ($result['status'] == LOGIN_SUCCESS) 3087 { 3088 add_log('admin', 'LOG_ADMIN_AUTH_SUCCESS'); 3089 } 3090 else 3091 { 3092 // Only log the failed attempt if a real user tried to. 3093 // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions 3094 if ($user->data['is_registered']) 3095 { 3096 add_log('admin', 'LOG_ADMIN_AUTH_FAIL'); 3097 } 3098 } 3099 } 3100 3101 // The result parameter is always an array, holding the relevant information... 3102 if ($result['status'] == LOGIN_SUCCESS) 3103 { 3104 $redirect = request_var('redirect', "{$phpbb_root_path}index.$phpEx"); 3105 $message = ($l_success) ? $l_success : $user->lang['LOGIN_REDIRECT']; 3106 $l_redirect = ($admin) ? $user->lang['PROCEED_TO_ACP'] : (($redirect === "{$phpbb_root_path}index.$phpEx" || $redirect === "index.$phpEx") ? $user->lang['RETURN_INDEX'] : $user->lang['RETURN_PAGE']); 3107 3108 // append/replace SID (may change during the session for AOL users) 3109 $redirect = reapply_sid($redirect); 3110 3111 // Special case... the user is effectively banned, but we allow founders to login 3112 if (defined('IN_CHECK_BAN') && $result['user_row']['user_type'] != USER_FOUNDER) 3113 { 3114 return; 3115 } 3116 3117 $redirect = meta_refresh(3, $redirect); 3118 trigger_error($message . '<br /><br />' . sprintf($l_redirect, '<a href="' . $redirect . '">', '</a>')); 3119 } 3120 3121 // Something failed, determine what... 3122 if ($result['status'] == LOGIN_BREAK) 3123 { 3124 trigger_error($result['error_msg']); 3125 } 3126 3127 // Special cases... determine 3128 switch ($result['status']) 3129 { 3130 case LOGIN_ERROR_ATTEMPTS: 3131 3132 $captcha = phpbb_captcha_factory::get_instance($config['captcha_plugin']); 3133 $captcha->init(CONFIRM_LOGIN); 3134 // $captcha->reset(); 3135 3136 $template->assign_vars(array( 3137 'CAPTCHA_TEMPLATE' => $captcha->get_template(), 3138 )); 3139 3140 $err = $user->lang[$result['error_msg']]; 3141 break; 3142 3143 case LOGIN_ERROR_PASSWORD_CONVERT: 3144 $err = sprintf( 3145 $user->lang[$result['error_msg']], 3146 ($config['email_enable']) ? '<a href="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') . '">' : '', 3147 ($config['email_enable']) ? '</a>' : '', 3148 ($config['board_contact']) ? '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">' : '', 3149 ($config['board_contact']) ? '</a>' : '' 3150 ); 3151 break; 3152 3153 // Username, password, etc... 3154 default: 3155 $err = $user->lang[$result['error_msg']]; 3156 3157 // Assign admin contact to some error messages 3158 if ($result['error_msg'] == 'LOGIN_ERROR_USERNAME' || $result['error_msg'] == 'LOGIN_ERROR_PASSWORD') 3159 { 3160 $err = (!$config['board_contact']) ? sprintf($user->lang[$result['error_msg']], '', '') : sprintf($user->lang[$result['error_msg']], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>'); 3161 } 3162 3163 break; 3164 } 3165 } 3166 3167 // Assign credential for username/password pair 3168 $credential = ($admin) ? md5(unique_id()) : false; 3169 3170 $s_hidden_fields = array( 3171 'sid' => $user->session_id, 3172 ); 3173 3174 if ($redirect) 3175 { 3176 $s_hidden_fields['redirect'] = $redirect; 3177 } 3178 3179 if ($admin) 3180 { 3181 $s_hidden_fields['credential'] = $credential; 3182 } 3183 3184 $s_hidden_fields = build_hidden_fields($s_hidden_fields); 3185 3186 $template->assign_vars(array( 3187 'LOGIN_ERROR' => $err, 3188 'LOGIN_EXPLAIN' => $l_explain, 3189 3190 'U_SEND_PASSWORD' => ($config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') : '', 3191 'U_RESEND_ACTIVATION' => ($config['require_activation'] == USER_ACTIVATION_SELF && $config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=resend_act') : '', 3192 'U_TERMS_USE' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'), 3193 'U_PRIVACY' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'), 3194 3195 'S_DISPLAY_FULL_LOGIN' => ($s_display) ? true : false, 3196 'S_HIDDEN_FIELDS' => $s_hidden_fields, 3197 3198 'S_ADMIN_AUTH' => $admin, 3199 'USERNAME' => ($admin) ? $user->data['username'] : '', 3200 3201 'USERNAME_CREDENTIAL' => 'username', 3202 'PASSWORD_CREDENTIAL' => ($admin) ? 'password_' . $credential : 'password', 3203 )); 3204 3205 page_header($user->lang['LOGIN'], false); 3206 3207 $template->set_filenames(array( 3208 'body' => 'login_body.html') 3209 ); 3210 make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx")); 3211 3212 page_footer(); 3213 } 3214 3215 /** 3216 * Generate forum login box 3217 */ 3218 function login_forum_box($forum_data) 3219 { 3220 global $db, $config, $user, $template, $phpEx; 3221 3222 $password = request_var('password', '', true); 3223 3224 $sql = 'SELECT forum_id 3225 FROM ' . FORUMS_ACCESS_TABLE . ' 3226 WHERE forum_id = ' . $forum_data['forum_id'] . ' 3227 AND user_id = ' . $user->data['user_id'] . " 3228 AND session_id = '" . $db->sql_escape($user->session_id) . "'"; 3229 $result = $db->sql_query($sql); 3230 $row = $db->sql_fetchrow($result); 3231 $db->sql_freeresult($result); 3232 3233 if ($row) 3234 { 3235 return true; 3236 } 3237 3238 if ($password) 3239 { 3240 // Remove expired authorised sessions 3241 $sql = 'SELECT f.session_id 3242 FROM ' . FORUMS_ACCESS_TABLE . ' f 3243 LEFT JOIN ' . SESSIONS_TABLE . ' s ON (f.session_id = s.session_id) 3244 WHERE s.session_id IS NULL'; 3245 $result = $db->sql_query($sql); 3246 3247 if ($row = $db->sql_fetchrow($result)) 3248 { 3249 $sql_in = array(); 3250 do 3251 { 3252 $sql_in[] = (string) $row['session_id']; 3253 } 3254 while ($row = $db->sql_fetchrow($result)); 3255 3256 // Remove expired sessions 3257 $sql = 'DELETE FROM ' . FORUMS_ACCESS_TABLE . ' 3258 WHERE ' . $db->sql_in_set('session_id', $sql_in); 3259 $db->sql_query($sql); 3260 } 3261 $db->sql_freeresult($result); 3262 3263 if (phpbb_check_hash($password, $forum_data['forum_password'])) 3264 { 3265 $sql_ary = array( 3266 'forum_id' => (int) $forum_data['forum_id'], 3267 'user_id' => (int) $user->data['user_id'], 3268 'session_id' => (string) $user->session_id, 3269 ); 3270 3271 $db->sql_query('INSERT INTO ' . FORUMS_ACCESS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); 3272 3273 return true; 3274 } 3275 3276 $template->assign_var('LOGIN_ERROR', $user->lang['WRONG_PASSWORD']); 3277 } 3278 3279 page_header($user->lang['LOGIN'], false); 3280 3281 $template->assign_vars(array( 3282 'FORUM_NAME' => isset($forum_data['forum_name']) ? $forum_data['forum_name'] : '', 3283 'S_LOGIN_ACTION' => build_url(array('f')), 3284 'S_HIDDEN_FIELDS' => build_hidden_fields(array('f' => $forum_data['forum_id']))) 3285 ); 3286 3287 $template->set_filenames(array( 3288 'body' => 'login_forum.html') 3289 ); 3290 3291 page_footer(); 3292 } 3293 3294 // Little helpers 3295 3296 /** 3297 * Little helper for the build_hidden_fields function 3298 */ 3299 function _build_hidden_fields($key, $value, $specialchar, $stripslashes) 3300 { 3301 $hidden_fields = ''; 3302 3303 if (!is_array($value)) 3304 { 3305 $value = ($stripslashes) ? stripslashes($value) : $value; 3306 $value = ($specialchar) ? htmlspecialchars($value, ENT_COMPAT, 'UTF-8') : $value; 3307 3308 $hidden_fields .= '<input type="hidden" name="' . $key . '" value="' . $value . '" />' . "\n"; 3309 } 3310 else 3311 { 3312 foreach ($value as $_key => $_value) 3313 { 3314 $_key = ($stripslashes) ? stripslashes($_key) : $_key; 3315 $_key = ($specialchar) ? htmlspecialchars($_key, ENT_COMPAT, 'UTF-8') : $_key; 3316 3317 $hidden_fields .= _build_hidden_fields($key . '[' . $_key . ']', $_value, $specialchar, $stripslashes); 3318 } 3319 } 3320 3321 return $hidden_fields; 3322 } 3323 3324 /** 3325 * Build simple hidden fields from array 3326 * 3327 * @param array $field_ary an array of values to build the hidden field from 3328 * @param bool $specialchar if true, keys and values get specialchared 3329 * @param bool $stripslashes if true, keys and values get stripslashed 3330 * 3331 * @return string the hidden fields 3332 */ 3333 function build_hidden_fields($field_ary, $specialchar = false, $stripslashes = false) 3334 { 3335 $s_hidden_fields = ''; 3336 3337 foreach ($field_ary as $name => $vars) 3338 { 3339 $name = ($stripslashes) ? stripslashes($name) : $name; 3340 $name = ($specialchar) ? htmlspecialchars($name, ENT_COMPAT, 'UTF-8') : $name; 3341 3342 $s_hidden_fields .= _build_hidden_fields($name, $vars, $specialchar, $stripslashes); 3343 } 3344 3345 return $s_hidden_fields; 3346 } 3347 3348 /** 3349 * Parse cfg file 3350 */ 3351 function parse_cfg_file($filename, $lines = false) 3352 { 3353 $parsed_items = array(); 3354 3355 if ($lines === false) 3356 { 3357 $lines = file($filename); 3358 } 3359 3360 foreach ($lines as $line) 3361 { 3362 $line = trim($line); 3363 3364 if (!$line || $line[0] == '#' || ($delim_pos = strpos($line, '=')) === false) 3365 { 3366 continue; 3367 } 3368 3369 // Determine first occurrence, since in values the equal sign is allowed 3370 $key = strtolower(trim(substr($line, 0, $delim_pos))); 3371 $value = trim(substr($line, $delim_pos + 1)); 3372 3373 if (in_array($value, array('off', 'false', '0'))) 3374 { 3375 $value = false; 3376 } 3377 else if (in_array($value, array('on', 'true', '1'))) 3378 { 3379 $value = true; 3380 } 3381 else if (!trim($value)) 3382 { 3383 $value = ''; 3384 } 3385 else if (($value[0] == "'" && $value[sizeof($value) - 1] == "'") || ($value[0] == '"' && $value[sizeof($value) - 1] == '"')) 3386 { 3387 $value = substr($value, 1, sizeof($value)-2); 3388 } 3389 3390 $parsed_items[$key] = $value; 3391 } 3392 3393 if (isset($parsed_items['inherit_from']) && isset($parsed_items['name']) && $parsed_items['inherit_from'] == $parsed_items['name']) 3394 { 3395 unset($parsed_items['inherit_from']); 3396 } 3397 3398 return $parsed_items; 3399 } 3400 3401 /** 3402 * Add log event 3403 */ 3404 function add_log() 3405 { 3406 global $db, $user; 3407 3408 // In phpBB 3.1.x i want to have logging in a class to be able to control it 3409 // For now, we need a quite hakish approach to circumvent logging for some actions 3410 // @todo implement cleanly 3411 if (!empty($GLOBALS['skip_add_log'])) 3412 { 3413 return false; 3414 } 3415 3416 $args = func_get_args(); 3417 3418 $mode = array_shift($args); 3419 $reportee_id = ($mode == 'user') ? intval(array_shift($args)) : ''; 3420 $forum_id = ($mode == 'mod') ? intval(array_shift($args)) : ''; 3421 $topic_id = ($mode == 'mod') ? intval(array_shift($args)) : ''; 3422 $action = array_shift($args); 3423 $data = (!sizeof($args)) ? '' : serialize($args); 3424 3425 $sql_ary = array( 3426 'user_id' => (empty($user->data)) ? ANONYMOUS : $user->data['user_id'], 3427 'log_ip' => $user->ip, 3428 'log_time' => time(), 3429 'log_operation' => $action, 3430 'log_data' => $data, 3431 ); 3432 3433 switch ($mode) 3434 { 3435 case 'admin': 3436 $sql_ary['log_type'] = LOG_ADMIN; 3437 break; 3438 3439 case 'mod': 3440 $sql_ary += array( 3441 'log_type' => LOG_MOD, 3442 'forum_id' => $forum_id, 3443 'topic_id' => $topic_id 3444 ); 3445 break; 3446 3447 case 'user': 3448 $sql_ary += array( 3449 'log_type' => LOG_USERS, 3450 'reportee_id' => $reportee_id 3451 ); 3452 break; 3453 3454 case 'critical': 3455 $sql_ary['log_type'] = LOG_CRITICAL; 3456 break; 3457 3458 default: 3459 return false; 3460 } 3461 3462 $db->sql_query('INSERT INTO ' . LOG_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); 3463 3464 return $db->sql_nextid(); 3465 } 3466 3467 /** 3468 * Return a nicely formatted backtrace. 3469 * 3470 * Turns the array returned by debug_backtrace() into HTML markup. 3471 * Also filters out absolute paths to phpBB root. 3472 * 3473 * @return string HTML markup 3474 */ 3475 function get_backtrace() 3476 { 3477 $output = '<div style="font-family: monospace;">'; 3478 $backtrace = debug_backtrace(); 3479 3480 // We skip the first one, because it only shows this file/function 3481 unset($backtrace[0]); 3482 3483 foreach ($backtrace as $trace) 3484 { 3485 // Strip the current directory from path 3486 $trace['file'] = (empty($trace['file'])) ? '(not given by php)' : htmlspecialchars(phpbb_filter_root_path($trace['file'])); 3487 $trace['line'] = (empty($trace['line'])) ? '(not given by php)' : $trace['line']; 3488 3489 // Only show function arguments for include etc. 3490 // Other parameters may contain sensible information 3491 $argument = ''; 3492 if (!empty($trace['args'][0]) && in_array($trace['function'], array('include', 'require', 'include_once', 'require_once'))) 3493 { 3494 $argument = htmlspecialchars(phpbb_filter_root_path($trace['args'][0])); 3495 } 3496 3497 $trace['class'] = (!isset($trace['class'])) ? '' : $trace['class']; 3498 $trace['type'] = (!isset($trace['type'])) ? '' : $trace['type']; 3499 3500 $output .= '<br />'; 3501 $output .= '<b>FILE:</b> ' . $trace['file'] . '<br />'; 3502 $output .= '<b>LINE:</b> ' . ((!empty($trace['line'])) ? $trace['line'] : '') . '<br />'; 3503 3504 $output .= '<b>CALL:</b> ' . htmlspecialchars($trace['class'] . $trace['type'] . $trace['function']); 3505 $output .= '(' . (($argument !== '') ? "'$argument'" : '') . ')<br />'; 3506 } 3507 $output .= '</div>'; 3508 return $output; 3509 } 3510 3511 /** 3512 * This function returns a regular expression pattern for commonly used expressions 3513 * Use with / as delimiter for email mode and # for url modes 3514 * mode can be: email|bbcode_htm|url|url_inline|www_url|www_url_inline|relative_url|relative_url_inline|ipv4|ipv6 3515 */ 3516 function get_preg_expression($mode) 3517 { 3518 switch ($mode) 3519 { 3520 case 'email': 3521 // Regex written by James Watts and Francisco Jose Martin Moreno 3522 // http://fightingforalostcause.net/misc/2006/compare-email-regex.php 3523 return '([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*(?:[\w\!\#$\%\'\*\+\-\/\=\?\^\`{\|\}\~]|&)+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,63})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)'; 3524 break; 3525 3526 case 'bbcode_htm': 3527 return array( 3528 '#<!\-\- e \-\-><a href="mailto:(.*?)">.*?</a><!\-\- e \-\->#', 3529 '#<!\-\- l \-\-><a (?:class="[\w-]+" )?href="(.*?)(?:(&|\?)sid=[0-9a-f]{32})?">.*?</a><!\-\- l \-\->#', 3530 '#<!\-\- ([mw]) \-\-><a (?:class="[\w-]+" )?href="(.*?)">.*?</a><!\-\- \1 \-\->#', 3531 '#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#', 3532 '#<!\-\- .*? \-\->#s', 3533 '#<.*?>#s', 3534 ); 3535 break; 3536 3537 // Whoa these look impressive! 3538 // The code to generate the following two regular expressions which match valid IPv4/IPv6 addresses 3539 // can be found in the develop directory 3540 case 'ipv4': 3541 return '#^(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$#'; 3542 break; 3543 3544 case 'ipv6': 3545 return '#^(?:(?:(?:[\dA-F]{1,4}:){6}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:::(?:[\dA-F]{1,4}:){0,5}(?:[\dA-F]{1,4}(?::[\dA-F]{1,4})?|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:):(?:[\dA-F]{1,4}:){4}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,2}:(?:[\dA-F]{1,4}:){3}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,3}:(?:[\dA-F]{1,4}:){2}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,4}:(?:[\dA-F]{1,4}:)(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,5}:(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,6}:[\dA-F]{1,4})|(?:(?:[\dA-F]{1,4}:){1,7}:)|(?:::))$#i'; 3546 break; 3547 3548 case 'url': 3549 case 'url_inline': 3550 $inline = ($mode == 'url') ? ')' : ''; 3551 $scheme = ($mode == 'url') ? '[a-z\d+\-.]' : '[a-z\d+]'; // avoid automatic parsing of "word" in "last word.http://..." 3552 // generated with regex generation file in the develop folder 3553 return "[a-z]$scheme*:/{2}(?:(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})+|[0-9.]+|\[[a-z0-9.]+:[a-z0-9.]+:[a-z0-9.:]+\])(?::\d*)?(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?"; 3554 break; 3555 3556 case 'www_url': 3557 case 'www_url_inline': 3558 $inline = ($mode == 'www_url') ? ')' : ''; 3559 return "www\.(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})+(?::\d*)?(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?"; 3560 break; 3561 3562 case 'relative_url': 3563 case 'relative_url_inline': 3564 $inline = ($mode == 'relative_url') ? ')' : ''; 3565 return "(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?"; 3566 break; 3567 3568 case 'table_prefix': 3569 return '#^[a-zA-Z][a-zA-Z0-9_]*$#'; 3570 break; 3571 } 3572 3573 return ''; 3574 } 3575 3576 /** 3577 * Generate regexp for naughty words censoring 3578 * Depends on whether installed PHP version supports unicode properties 3579 * 3580 * @param string $word word template to be replaced 3581 * @param bool $use_unicode whether or not to take advantage of PCRE supporting unicode 3582 * 3583 * @return string $preg_expr regex to use with word censor 3584 */ 3585 function get_censor_preg_expression($word, $use_unicode = true) 3586 { 3587 static $unicode_support = null; 3588 3589 // Check whether PHP version supports unicode properties 3590 if (is_null($unicode_support)) 3591 { 3592 $unicode_support = ((version_compare(PHP_VERSION, '5.1.0', '>=') || (version_compare(PHP_VERSION, '5.0.0-dev', '<=') && version_compare(PHP_VERSION, '4.4.0', '>='))) && @preg_match('/\p{L}/u', 'a') !== false) ? true : false; 3593 } 3594 3595 // Unescape the asterisk to simplify further conversions 3596 $word = str_replace('\*', '*', preg_quote($word, '#')); 3597 3598 if ($use_unicode && $unicode_support) 3599 { 3600 // Replace asterisk(s) inside the pattern, at the start and at the end of it with regexes 3601 $word = preg_replace(array('#(?<=[\p{Nd}\p{L}_])\*+(?=[\p{Nd}\p{L}_])#iu', '#^\*+#', '#\*+$#'), array('([\x20]*?|[\p{Nd}\p{L}_-]*?)', '[\p{Nd}\p{L}_-]*?', '[\p{Nd}\p{L}_-]*?'), $word); 3602 3603 // Generate the final substitution 3604 $preg_expr = '#(?<![\p{Nd}\p{L}_-])(' . $word . ')(?![\p{Nd}\p{L}_-])#iu'; 3605 } 3606 else 3607 { 3608 // Replace the asterisk inside the pattern, at the start and at the end of it with regexes 3609 $word = preg_replace(array('#(?<=\S)\*+(?=\S)#iu', '#^\*+#', '#\*+$#'), array('(\x20*?\S*?)', '\S*?', '\S*?'), $word); 3610 3611 // Generate the final substitution 3612 $preg_expr = '#(?<!\S)(' . $word . ')(?!\S)#iu'; 3613 } 3614 3615 return $preg_expr; 3616 } 3617 3618 /** 3619 * Returns the first block of the specified IPv6 address and as many additional 3620 * ones as specified in the length paramater. 3621 * If length is zero, then an empty string is returned. 3622 * If length is greater than 3 the complete IP will be returned 3623 */ 3624 function short_ipv6($ip, $length) 3625 { 3626 if ($length < 1) 3627 { 3628 return ''; 3629 } 3630 3631 // extend IPv6 addresses 3632 $blocks = substr_count($ip, ':') + 1; 3633 if ($blocks < 9) 3634 { 3635 $ip = str_replace('::', ':' . str_repeat('0000:', 9 - $blocks), $ip); 3636 } 3637 if ($ip[0] == ':') 3638 { 3639 $ip = '0000' . $ip; 3640 } 3641 if ($length < 4) 3642 { 3643 $ip = implode(':', array_slice(explode(':', $ip), 0, 1 + $length)); 3644 } 3645 3646 return $ip; 3647 } 3648 3649 /** 3650 * Wrapper for php's checkdnsrr function. 3651 * 3652 * @param string $host Fully-Qualified Domain Name 3653 * @param string $type Resource record type to lookup 3654 * Supported types are: MX (default), A, AAAA, NS, TXT, CNAME 3655 * Other types may work or may not work 3656 * 3657 * @return mixed true if entry found, 3658 * false if entry not found, 3659 * null if this function is not supported by this environment 3660 * 3661 * Since null can also be returned, you probably want to compare the result 3662 * with === true or === false, 3663 * 3664 * @author bantu 3665 */ 3666 function phpbb_checkdnsrr($host, $type = 'MX') 3667 { 3668 // The dot indicates to search the DNS root (helps those having DNS prefixes on the same domain) 3669 if (substr($host, -1) == '.') 3670 { 3671 $host_fqdn = $host; 3672 $host = substr($host, 0, -1); 3673 } 3674 else 3675 { 3676 $host_fqdn = $host . '.'; 3677 } 3678 // $host has format some.host.example.com 3679 // $host_fqdn has format some.host.example.com. 3680 3681 // If we're looking for an A record we can use gethostbyname() 3682 if ($type == 'A' && function_exists('gethostbyname')) 3683 { 3684 return (@gethostbyname($host_fqdn) == $host_fqdn) ? false : true; 3685 } 3686 3687 // checkdnsrr() is available on Windows since PHP 5.3, 3688 // but until 5.3.3 it only works for MX records 3689 // See: http://bugs.php.net/bug.php?id=51844 3690 3691 // Call checkdnsrr() if 3692 // we're looking for an MX record or 3693 // we're not on Windows or 3694 // we're running a PHP version where #51844 has been fixed 3695 3696 // checkdnsrr() supports AAAA since 5.0.0 3697 // checkdnsrr() supports TXT since 5.2.4 3698 if ( 3699 ($type == 'MX' || DIRECTORY_SEPARATOR != '\\' || version_compare(PHP_VERSION, '5.3.3', '>=')) && 3700 ($type != 'AAAA' || version_compare(PHP_VERSION, '5.0.0', '>=')) && 3701 ($type != 'TXT' || version_compare(PHP_VERSION, '5.2.4', '>=')) && 3702 function_exists('checkdnsrr') 3703 ) 3704 { 3705 return checkdnsrr($host_fqdn, $type); 3706 } 3707 3708 // dns_get_record() is available since PHP 5; since PHP 5.3 also on Windows, 3709 // but on Windows it does not work reliable for AAAA records before PHP 5.3.1 3710 3711 // Call dns_get_record() if 3712 // we're not looking for an AAAA record or 3713 // we're not on Windows or 3714 // we're running a PHP version where AAAA lookups work reliable 3715 if ( 3716 ($type != 'AAAA' || DIRECTORY_SEPARATOR != '\\' || version_compare(PHP_VERSION, '5.3.1', '>=')) && 3717 function_exists('dns_get_record') 3718 ) 3719 { 3720 // dns_get_record() expects an integer as second parameter 3721 // We have to convert the string $type to the corresponding integer constant. 3722 $type_constant = 'DNS_' . $type; 3723 $type_param = (defined($type_constant)) ? constant($type_constant) : DNS_ANY; 3724 3725 // dns_get_record() might throw E_WARNING and return false for records that do not exist 3726 $resultset = @dns_get_record($host_fqdn, $type_param); 3727 3728 if (empty($resultset) || !is_array($resultset)) 3729 { 3730 return false; 3731 } 3732 else if ($type_param == DNS_ANY) 3733 { 3734 // $resultset is a non-empty array 3735 return true; 3736 } 3737 3738 foreach ($resultset as $result) 3739 { 3740 if ( 3741 isset($result['host']) && $result['host'] == $host && 3742 isset($result['type']) && $result['type'] == $type 3743 ) 3744 { 3745 return true; 3746 } 3747 } 3748 3749 return false; 3750 } 3751 3752 // If we're on Windows we can still try to call nslookup via exec() as a last resort 3753 if (DIRECTORY_SEPARATOR == '\\' && function_exists('exec')) 3754 { 3755 @exec('nslookup -type=' . escapeshellarg($type) . ' ' . escapeshellarg($host_fqdn), $output); 3756 3757 // If output is empty, the nslookup failed 3758 if (empty($output)) 3759 { 3760 return NULL; 3761 } 3762 3763 foreach ($output as $line) 3764 { 3765 $line = trim($line); 3766 3767 if (empty($line)) 3768 { 3769 continue; 3770 } 3771 3772 // Squash tabs and multiple whitespaces to a single whitespace. 3773 $line = preg_replace('/\s+/', ' ', $line); 3774 3775 switch ($type) 3776 { 3777 case 'MX': 3778 if (stripos($line, "$host MX") === 0) 3779 { 3780 return true; 3781 } 3782 break; 3783 3784 case 'NS': 3785 if (stripos($line, "$host nameserver") === 0) 3786 { 3787 return true; 3788 } 3789 break; 3790 3791 case 'TXT': 3792 if (stripos($line, "$host text") === 0) 3793 { 3794 return true; 3795 } 3796 break; 3797 3798 case 'CNAME': 3799 if (stripos($line, "$host canonical name") === 0) 3800 { 3801 return true; 3802 } 3803 break; 3804 3805 default: 3806 case 'AAAA': 3807 // AAAA records returned by nslookup on Windows XP/2003 have this format. 3808 // Later Windows versions use the A record format below for AAAA records. 3809 if (stripos($line, "$host AAAA IPv6 address") === 0) 3810 { 3811 return true; 3812 } 3813 // No break 3814 3815 case 'A': 3816 if (!empty($host_matches)) 3817 { 3818 // Second line 3819 if (stripos($line, "Address: ") === 0) 3820 { 3821 return true; 3822 } 3823 else 3824 { 3825 $host_matches = false; 3826 } 3827 } 3828 else if (stripos($line, "Name: $host") === 0) 3829 { 3830 // First line 3831 $host_matches = true; 3832 } 3833 break; 3834 } 3835 } 3836 3837 return false; 3838 } 3839 3840 return NULL; 3841 } 3842 3843 // Handler, header and footer 3844 3845 /** 3846 * Error and message handler, call with trigger_error if reqd 3847 */ 3848 function msg_handler($errno, $msg_text, $errfile, $errline) 3849 { 3850 global $cache, $db, $auth, $template, $config, $user; 3851 global $phpEx, $phpbb_root_path, $msg_title, $msg_long_text; 3852 3853 // Do not display notices if we suppress them via @ 3854 if (error_reporting() == 0 && $errno != E_USER_ERROR && $errno != E_USER_WARNING && $errno != E_USER_NOTICE) 3855 { 3856 return; 3857 } 3858 3859 // Message handler is stripping text. In case we need it, we are possible to define long text... 3860 if (isset($msg_long_text) && $msg_long_text && !$msg_text) 3861 { 3862 $msg_text = $msg_long_text; 3863 } 3864 3865 if (!defined('E_DEPRECATED')) 3866 { 3867 define('E_DEPRECATED', 8192); 3868 } 3869 3870 switch ($errno) 3871 { 3872 case E_NOTICE: 3873 case E_WARNING: 3874 3875 // Check the error reporting level and return if the error level does not match 3876 // If DEBUG is defined the default level is E_ALL 3877 if (($errno & ((defined('DEBUG')) ? E_ALL : error_reporting())) == 0) 3878 { 3879 return; 3880 } 3881 3882 if (strpos($errfile, 'cache') === false && strpos($errfile, 'template.') === false) 3883 { 3884 $errfile = phpbb_filter_root_path($errfile); 3885 $msg_text = phpbb_filter_root_path($msg_text); 3886 $error_name = ($errno === E_WARNING) ? 'PHP Warning' : 'PHP Notice'; 3887 echo '<b>[phpBB Debug] ' . $error_name . '</b>: in file <b>' . $errfile . '</b> on line <b>' . $errline . '</b>: <b>' . $msg_text . '</b><br />' . "\n"; 3888 3889 // we are writing an image - the user won't see the debug, so let's place it in the log 3890 if (defined('IMAGE_OUTPUT') || defined('IN_CRON')) 3891 { 3892 add_log('critical', 'LOG_IMAGE_GENERATION_ERROR', $errfile, $errline, $msg_text); 3893 } 3894 // echo '<br /><br />BACKTRACE<br />' . get_backtrace() . '<br />' . "\n"; 3895 } 3896 3897 return; 3898 3899 break; 3900 3901 case E_USER_ERROR: 3902 3903 if (!empty($user) && !empty($user->lang)) 3904 { 3905 $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text; 3906 $msg_title = (!isset($msg_title)) ? $user->lang['GENERAL_ERROR'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title); 3907 3908 $l_return_index = sprintf($user->lang['RETURN_INDEX'], '<a href="' . $phpbb_root_path . '">', '</a>'); 3909 $l_notify = ''; 3910 3911 if (!empty($config['board_contact'])) 3912 { 3913 $l_notify = '<p>' . sprintf($user->lang['NOTIFY_ADMIN_EMAIL'], $config['board_contact']) . '</p>'; 3914 } 3915 } 3916 else 3917 { 3918 $msg_title = 'General Error'; 3919 $l_return_index = '<a href="' . $phpbb_root_path . '">Return to index page</a>'; 3920 $l_notify = ''; 3921 3922 if (!empty($config['board_contact'])) 3923 { 3924 $l_notify = '<p>Please notify the board administrator or webmaster: <a href="mailto:' . $config['board_contact'] . '">' . $config['board_contact'] . '</a></p>'; 3925 } 3926 } 3927 3928 $log_text = $msg_text; 3929 $backtrace = get_backtrace(); 3930 if ($backtrace) 3931 { 3932 $log_text .= '<br /><br />BACKTRACE<br />' . $backtrace; 3933 } 3934 3935 if (defined('IN_INSTALL') || defined('DEBUG_EXTRA') || isset($auth) && $auth->acl_get('a_')) 3936 { 3937 $msg_text = $log_text; 3938 } 3939 3940 if ((defined('DEBUG') || defined('IN_CRON') || defined('IMAGE_OUTPUT')) && isset($db)) 3941 { 3942 // let's avoid loops 3943 $db->sql_return_on_error(true); 3944 add_log('critical', 'LOG_GENERAL_ERROR', $msg_title, $log_text); 3945 $db->sql_return_on_error(false); 3946 } 3947 3948 // Do not send 200 OK, but service unavailable on errors 3949 send_status_line(503, 'Service Unavailable'); 3950 3951 garbage_collection(); 3952 3953 // Try to not call the adm page data... 3954 3955 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'; 3956 echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">'; 3957 echo '<head>'; 3958 echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />'; 3959 echo '<title>' . $msg_title . '</title>'; 3960 echo '<style type="text/css">' . "\n" . '/* <![CDATA[ */' . "\n"; 3961 echo '* { margin: 0; padding: 0; } html { font-size: 100%; height: 100%; margin-bottom: 1px; background-color: #E4EDF0; } body { font-family: "Lucida Grande", Verdana, Helvetica, Arial, sans-serif; color: #536482; background: #E4EDF0; font-size: 62.5%; margin: 0; } '; 3962 echo 'a:link, a:active, a:visited { color: #006699; text-decoration: none; } a:hover { color: #DD6900; text-decoration: underline; } '; 3963 echo '#wrap { padding: 0 20px 15px 20px; min-width: 615px; } #page-header { text-align: right; height: 40px; } #page-footer { clear: both; font-size: 1em; text-align: center; } '; 3964 echo '.panel { margin: 4px 0; background-color: #FFFFFF; border: solid 1px #A9B8C2; } '; 3965 echo '#errorpage #page-header a { font-weight: bold; line-height: 6em; } #errorpage #content { padding: 10px; } #errorpage #content h1 { line-height: 1.2em; margin-bottom: 0; color: #DF075C; } '; 3966 echo '#errorpage #content div { margin-top: 20px; margin-bottom: 5px; border-bottom: 1px solid #CCCCCC; padding-bottom: 5px; color: #333333; font: bold 1.2em "Lucida Grande", Arial, Helvetica, sans-serif; text-decoration: none; line-height: 120%; text-align: left; } '; 3967 echo "\n" . '/* ]]> */' . "\n"; 3968 echo '</style>'; 3969 echo '</head>'; 3970 echo '<body id="errorpage">'; 3971 echo '<div id="wrap">'; 3972 echo ' <div id="page-header">'; 3973 echo ' ' . $l_return_index; 3974 echo ' </div>'; 3975 echo ' <div id="acp">'; 3976 echo ' <div class="panel">'; 3977 echo ' <div id="content">'; 3978 echo ' <h1>' . $msg_title . '</h1>'; 3979 3980 echo ' <div>' . $msg_text . '</div>'; 3981 3982 echo $l_notify; 3983 3984 echo ' </div>'; 3985 echo ' </div>'; 3986 echo ' </div>'; 3987 echo ' <div id="page-footer">'; 3988 echo ' Powered by <a href="https://www.phpbb.com/">phpBB</a>® Forum Software © phpBB Group'; 3989 echo ' </div>'; 3990 echo '</div>'; 3991 echo '</body>'; 3992 echo '</html>'; 3993 3994 exit_handler(); 3995 3996 // On a fatal error (and E_USER_ERROR *is* fatal) we never want other scripts to continue and force an exit here. 3997 exit; 3998 break; 3999 4000 case E_USER_WARNING: 4001 case E_USER_NOTICE: 4002 4003 define('IN_ERROR_HANDLER', true); 4004 4005 if (empty($user->data)) 4006 { 4007 $user->session_begin(); 4008 } 4009 4010 // We re-init the auth array to get correct results on login/logout 4011 $auth->acl($user->data); 4012 4013 if (empty($user->lang)) 4014 { 4015 $user->setup(); 4016 } 4017 4018 if ($msg_text == 'ERROR_NO_ATTACHMENT' || $msg_text == 'NO_FORUM' || $msg_text == 'NO_TOPIC' || $msg_text == 'NO_USER') 4019 { 4020 send_status_line(404, 'Not Found'); 4021 } 4022 4023 $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text; 4024 $msg_title = (!isset($msg_title)) ? $user->lang['INFORMATION'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title); 4025 4026 if (!defined('HEADER_INC')) 4027 { 4028 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin']) 4029 { 4030 adm_page_header($msg_title); 4031 } 4032 else 4033 { 4034 page_header($msg_title, false); 4035 } 4036 } 4037 4038 $template->set_filenames(array( 4039 'body' => 'message_body.html') 4040 ); 4041 4042 $template->assign_vars(array( 4043 'MESSAGE_TITLE' => $msg_title, 4044 'MESSAGE_TEXT' => $msg_text, 4045 'S_USER_WARNING' => ($errno == E_USER_WARNING) ? true : false, 4046 'S_USER_NOTICE' => ($errno == E_USER_NOTICE) ? true : false) 4047 ); 4048 4049 // We do not want the cron script to be called on error messages 4050 define('IN_CRON', true); 4051 4052 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin']) 4053 { 4054 adm_page_footer(); 4055 } 4056 else 4057 { 4058 page_footer(); 4059 } 4060 4061 exit_handler(); 4062 break; 4063 4064 // PHP4 compatibility 4065 case E_DEPRECATED: 4066 return true; 4067 break; 4068 } 4069 4070 // If we notice an error not handled here we pass this back to PHP by returning false 4071 // This may not work for all php versions 4072 return false; 4073 } 4074 4075 /** 4076 * Removes absolute path to phpBB root directory from error messages 4077 * and converts backslashes to forward slashes. 4078 * 4079 * @param string $errfile Absolute file path 4080 * (e.g. /var/www/phpbb3/phpBB/includes/functions.php) 4081 * Please note that if $errfile is outside of the phpBB root, 4082 * the root path will not be found and can not be filtered. 4083 * @return string Relative file path 4084 * (e.g. /includes/functions.php) 4085 */ 4086 function phpbb_filter_root_path($errfile) 4087 { 4088 static $root_path; 4089 4090 if (empty($root_path)) 4091 { 4092 $root_path = phpbb_realpath(dirname(__FILE__) . '/../'); 4093 } 4094 4095 return str_replace(array($root_path, '\\'), array('[ROOT]', '/'), $errfile); 4096 } 4097 4098 /** 4099 * Queries the session table to get information about online guests 4100 * @param int $item_id Limits the search to the item with this id 4101 * @param string $item The name of the item which is stored in the session table as session_{$item}_id 4102 * @return int The number of active distinct guest sessions 4103 */ 4104 function obtain_guest_count($item_id = 0, $item = 'forum') 4105 { 4106 global $db, $config; 4107 4108 if ($item_id) 4109 { 4110 $reading_sql = ' AND s.session_' . $item . '_id = ' . (int) $item_id; 4111 } 4112 else 4113 { 4114 $reading_sql = ''; 4115 } 4116 $time = (time() - (intval($config['load_online_time']) * 60)); 4117 4118 // Get number of online guests 4119 4120 if ($db->sql_layer === 'sqlite') 4121 { 4122 $sql = 'SELECT COUNT(session_ip) as num_guests 4123 FROM ( 4124 SELECT DISTINCT s.session_ip 4125 FROM ' . SESSIONS_TABLE . ' s 4126 WHERE s.session_user_id = ' . ANONYMOUS . ' 4127 AND s.session_time >= ' . ($time - ((int) ($time % 60))) . 4128 $reading_sql . 4129 ')'; 4130 } 4131 else 4132 { 4133 $sql = 'SELECT COUNT(DISTINCT s.session_ip) as num_guests 4134 FROM ' . SESSIONS_TABLE . ' s 4135 WHERE s.session_user_id = ' . ANONYMOUS . ' 4136 AND s.session_time >= ' . ($time - ((int) ($time % 60))) . 4137 $reading_sql; 4138 } 4139 $result = $db->sql_query($sql); 4140 $guests_online = (int) $db->sql_fetchfield('num_guests'); 4141 $db->sql_freeresult($result); 4142 4143 return $guests_online; 4144 } 4145 4146 /** 4147 * Queries the session table to get information about online users 4148 * @param int $item_id Limits the search to the item with this id 4149 * @param string $item The name of the item which is stored in the session table as session_{$item}_id 4150 * @return array An array containing the ids of online, hidden and visible users, as well as statistical info 4151 */ 4152 function obtain_users_online($item_id = 0, $item = 'forum') 4153 { 4154 global $db, $config, $user; 4155 4156 $reading_sql = ''; 4157 if ($item_id !== 0) 4158 { 4159 $reading_sql = ' AND s.session_' . $item . '_id = ' . (int) $item_id; 4160 } 4161 4162 $online_users = array( 4163 'online_users' => array(), 4164 'hidden_users' => array(), 4165 'total_online' => 0, 4166 'visible_online' => 0, 4167 'hidden_online' => 0, 4168 'guests_online' => 0, 4169 ); 4170 4171 if ($config['load_online_guests']) 4172 { 4173 $online_users['guests_online'] = obtain_guest_count($item_id, $item); 4174 } 4175 4176 // a little discrete magic to cache this for 30 seconds 4177 $time = (time() - (intval($config['load_online_time']) * 60)); 4178 4179 $sql = 'SELECT s.session_user_id, s.session_ip, s.session_viewonline 4180 FROM ' . SESSIONS_TABLE . ' s 4181 WHERE s.session_time >= ' . ($time - ((int) ($time % 30))) . 4182 $reading_sql . 4183 ' AND s.session_user_id <> ' . ANONYMOUS; 4184 $result = $db->sql_query($sql); 4185 4186 while ($row = $db->sql_fetchrow($result)) 4187 { 4188 // Skip multiple sessions for one user 4189 if (!isset($online_users['online_users'][$row['session_user_id']])) 4190 { 4191 $online_users['online_users'][$row['session_user_id']] = (int) $row['session_user_id']; 4192 if ($row['session_viewonline']) 4193 { 4194 $online_users['visible_online']++; 4195 } 4196 else 4197 { 4198 $online_users['hidden_users'][$row['session_user_id']] = (int) $row['session_user_id']; 4199 $online_users['hidden_online']++; 4200 } 4201 } 4202 } 4203 $online_users['total_online'] = $online_users['guests_online'] + $online_users['visible_online'] + $online_users['hidden_online']; 4204 $db->sql_freeresult($result); 4205 4206 return $online_users; 4207 } 4208 4209 /** 4210 * Uses the result of obtain_users_online to generate a localized, readable representation. 4211 * @param mixed $online_users result of obtain_users_online - array with user_id lists for total, hidden and visible users, and statistics 4212 * @param int $item_id Indicate that the data is limited to one item and not global 4213 * @param string $item The name of the item which is stored in the session table as session_{$item}_id 4214 * @return array An array containing the string for output to the template 4215 */ 4216 function obtain_users_online_string($online_users, $item_id = 0, $item = 'forum') 4217 { 4218 global $config, $db, $user, $auth; 4219 4220 $user_online_link = $online_userlist = ''; 4221 // Need caps version of $item for language-strings 4222 $item_caps = strtoupper($item); 4223 4224 if (sizeof($online_users['online_users'])) 4225 { 4226 $sql = 'SELECT username, username_clean, user_id, user_type, user_allow_viewonline, user_colour 4227 FROM ' . USERS_TABLE . ' 4228 WHERE ' . $db->sql_in_set('user_id', $online_users['online_users']) . ' 4229 ORDER BY username_clean ASC'; 4230 $result = $db->sql_query($sql); 4231 4232 while ($row = $db->sql_fetchrow($result)) 4233 { 4234 // User is logged in and therefore not a guest 4235 if ($row['user_id'] != ANONYMOUS) 4236 { 4237 if (isset($online_users['hidden_users'][$row['user_id']])) 4238 { 4239 $row['username'] = '<em>' . $row['username'] . '</em>'; 4240 } 4241 4242 if (!isset($online_users['hidden_users'][$row['user_id']]) || $auth->acl_get('u_viewonline')) 4243 { 4244 $user_online_link = get_username_string(($row['user_type'] <> USER_IGNORE) ? 'full' : 'no_profile', $row['user_id'], $row['username'], $row['user_colour']); 4245 $online_userlist .= ($online_userlist != '') ? ', ' . $user_online_link : $user_online_link; 4246 } 4247 } 4248 } 4249 $db->sql_freeresult($result); 4250 } 4251 4252 if (!$online_userlist) 4253 { 4254 $online_userlist = $user->lang['NO_ONLINE_USERS']; 4255 } 4256 4257 if ($item_id === 0) 4258 { 4259 $online_userlist = $user->lang['REGISTERED_USERS'] . ' ' . $online_userlist; 4260 } 4261 else if ($config['load_online_guests']) 4262 { 4263 $l_online = ($online_users['guests_online'] === 1) ? $user->lang['BROWSING_' . $item_caps . '_GUEST'] : $user->lang['BROWSING_' . $item_caps . '_GUESTS']; 4264 $online_userlist = sprintf($l_online, $online_userlist, $online_users['guests_online']); 4265 } 4266 else 4267 { 4268 $online_userlist = sprintf($user->lang['BROWSING_' . $item_caps], $online_userlist); 4269 } 4270 // Build online listing 4271 $vars_online = array( 4272 'ONLINE' => array('total_online', 'l_t_user_s', 0), 4273 'REG' => array('visible_online', 'l_r_user_s', !$config['load_online_guests']), 4274 'HIDDEN' => array('hidden_online', 'l_h_user_s', $config['load_online_guests']), 4275 'GUEST' => array('guests_online', 'l_g_user_s', 0) 4276 ); 4277 4278 foreach ($vars_online as $l_prefix => $var_ary) 4279 { 4280 if ($var_ary[2]) 4281 { 4282 $l_suffix = '_AND'; 4283 } 4284 else 4285 { 4286 $l_suffix = ''; 4287 } 4288 switch ($online_users[$var_ary[0]]) 4289 { 4290 case 0: 4291 ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_ZERO_TOTAL' . $l_suffix]; 4292 break; 4293 4294 case 1: 4295 ${$var_ary[1]} = $user->lang[$l_prefix . '_USER_TOTAL' . $l_suffix]; 4296 break; 4297 4298 default: 4299 ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_TOTAL' . $l_suffix]; 4300 break; 4301 } 4302 } 4303 unset($vars_online); 4304 4305 $l_online_users = sprintf($l_t_user_s, $online_users['total_online']); 4306 $l_online_users .= sprintf($l_r_user_s, $online_users['visible_online']); 4307 $l_online_users .= sprintf($l_h_user_s, $online_users['hidden_online']); 4308 4309 if ($config['load_online_guests']) 4310 { 4311 $l_online_users .= sprintf($l_g_user_s, $online_users['guests_online']); 4312 } 4313 4314 4315 4316 return array( 4317 'online_userlist' => $online_userlist, 4318 'l_online_users' => $l_online_users, 4319 ); 4320 } 4321 4322 /** 4323 * Get option bitfield from custom data 4324 * 4325 * @param int $bit The bit/value to get 4326 * @param int $data Current bitfield to check 4327 * @return bool Returns true if value of constant is set in bitfield, else false 4328 */ 4329 function phpbb_optionget($bit, $data) 4330 { 4331 return ($data & 1 << (int) $bit) ? true : false; 4332 } 4333 4334 /** 4335 * Set option bitfield 4336 * 4337 * @param int $bit The bit/value to set/unset 4338 * @param bool $set True if option should be set, false if option should be unset. 4339 * @param int $data Current bitfield to change 4340 * 4341 * @return int The new bitfield 4342 */ 4343 function phpbb_optionset($bit, $set, $data) 4344 { 4345 if ($set && !($data & 1 << $bit)) 4346 { 4347 $data += 1 << $bit; 4348 } 4349 else if (!$set && ($data & 1 << $bit)) 4350 { 4351 $data -= 1 << $bit; 4352 } 4353 4354 return $data; 4355 } 4356 4357 /** 4358 * Login using http authenticate. 4359 * 4360 * @param array $param Parameter array, see $param_defaults array. 4361 * 4362 * @return null 4363 */ 4364 function phpbb_http_login($param) 4365 { 4366 global $auth, $user; 4367 global $config; 4368 4369 $param_defaults = array( 4370 'auth_message' => '', 4371 4372 'autologin' => false, 4373 'viewonline' => true, 4374 'admin' => false, 4375 ); 4376 4377 // Overwrite default values with passed values 4378 $param = array_merge($param_defaults, $param); 4379 4380 // User is already logged in 4381 // We will not overwrite his session 4382 if (!empty($user->data['is_registered'])) 4383 { 4384 return; 4385 } 4386 4387 // $_SERVER keys to check 4388 $username_keys = array( 4389 'PHP_AUTH_USER', 4390 'Authorization', 4391 'REMOTE_USER', 'REDIRECT_REMOTE_USER', 4392 'HTTP_AUTHORIZATION', 'REDIRECT_HTTP_AUTHORIZATION', 4393 'REMOTE_AUTHORIZATION', 'REDIRECT_REMOTE_AUTHORIZATION', 4394 'AUTH_USER', 4395 ); 4396 4397 $password_keys = array( 4398 'PHP_AUTH_PW', 4399 'REMOTE_PASSWORD', 4400 'AUTH_PASSWORD', 4401 ); 4402 4403 $username = null; 4404 foreach ($username_keys as $k) 4405 { 4406 if (isset($_SERVER[$k])) 4407 { 4408 $username = $_SERVER[$k]; 4409 break; 4410 } 4411 } 4412 4413 $password = null; 4414 foreach ($password_keys as $k) 4415 { 4416 if (isset($_SERVER[$k])) 4417 { 4418 $password = $_SERVER[$k]; 4419 break; 4420 } 4421 } 4422 4423 // Decode encoded information (IIS, CGI, FastCGI etc.) 4424 if (!is_null($username) && is_null($password) && strpos($username, 'Basic ') === 0) 4425 { 4426 list($username, $password) = explode(':', base64_decode(substr($username, 6)), 2); 4427 } 4428 4429 if (!is_null($username) && !is_null($password)) 4430 { 4431 set_var($username, $username, 'string', true); 4432 set_var($password, $password, 'string', true); 4433 4434 $auth_result = $auth->login($username, $password, $param['autologin'], $param['viewonline'], $param['admin']); 4435 4436 if ($auth_result['status'] == LOGIN_SUCCESS) 4437 { 4438 return; 4439 } 4440 else if ($auth_result['status'] == LOGIN_ERROR_ATTEMPTS) 4441 { 4442 send_status_line(401, 'Unauthorized'); 4443 4444 trigger_error('NOT_AUTHORISED'); 4445 } 4446 } 4447 4448 // Prepend sitename to auth_message 4449 $param['auth_message'] = ($param['auth_message'] === '') ? $config['sitename'] : $config['sitename'] . ' - ' . $param['auth_message']; 4450 4451 // We should probably filter out non-ASCII characters - RFC2616 4452 $param['auth_message'] = preg_replace('/[\x80-\xFF]/', '?', $param['auth_message']); 4453 4454 header('WWW-Authenticate: Basic realm="' . $param['auth_message'] . '"'); 4455 send_status_line(401, 'Unauthorized'); 4456 4457 trigger_error('NOT_AUTHORISED'); 4458 } 4459 4460 /** 4461 * Generate page header 4462 */ 4463 function page_header($page_title = '', $display_online_list = true, $item_id = 0, $item = 'forum') 4464 { 4465 global $db, $config, $template, $SID, $_SID, $_EXTRA_URL, $user, $auth, $phpEx, $phpbb_root_path; 4466 4467 if (defined('HEADER_INC')) 4468 { 4469 return; 4470 } 4471 4472 define('HEADER_INC', true); 4473 4474 // gzip_compression 4475 if ($config['gzip_compress']) 4476 { 4477 // to avoid partially compressed output resulting in blank pages in 4478 // the browser or error messages, compression is disabled in a few cases: 4479 // 4480 // 1) if headers have already been sent, this indicates plaintext output 4481 // has been started so further content must not be compressed 4482 // 2) the length of the current output buffer is non-zero. This means 4483 // there is already some uncompressed content in this output buffer 4484 // so further output must not be compressed 4485 // 3) if more than one level of output buffering is used because we 4486 // cannot test all output buffer level content lengths. One level 4487 // could be caused by php.ini output_buffering. Anything 4488 // beyond that is manual, so the code wrapping phpBB in output buffering 4489 // can easily compress the output itself. 4490 // 4491 if (@extension_loaded('zlib') && !headers_sent() && ob_get_level() <= 1 && ob_get_length() == 0) 4492 { 4493 ob_start('ob_gzhandler'); 4494 } 4495 } 4496 4497 // Generate logged in/logged out status 4498 if ($user->data['user_id'] != ANONYMOUS) 4499 { 4500 $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=logout', true, $user->session_id); 4501 $l_login_logout = sprintf($user->lang['LOGOUT_USER'], $user->data['username']); 4502 } 4503 else 4504 { 4505 $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login'); 4506 $l_login_logout = $user->lang['LOGIN']; 4507 } 4508 4509 // Last visit date/time 4510 $s_last_visit = ($user->data['user_id'] != ANONYMOUS) ? $user->format_date($user->data['session_last_visit']) : ''; 4511 4512 // Get users online list ... if required 4513 $l_online_users = $online_userlist = $l_online_record = $l_online_time = ''; 4514 4515 if ($config['load_online'] && $config['load_online_time'] && $display_online_list) 4516 { 4517 /** 4518 * Load online data: 4519 * For obtaining another session column use $item and $item_id in the function-parameter, whereby the column is session_{$item}_id. 4520 */ 4521 $item_id = max($item_id, 0); 4522 4523 $online_users = obtain_users_online($item_id, $item); 4524 $user_online_strings = obtain_users_online_string($online_users, $item_id, $item); 4525 4526 $l_online_users = $user_online_strings['l_online_users']; 4527 $online_userlist = $user_online_strings['online_userlist']; 4528 $total_online_users = $online_users['total_online']; 4529 4530 if ($total_online_users > $config['record_online_users']) 4531 { 4532 set_config('record_online_users', $total_online_users, true); 4533 set_config('record_online_date', time(), true); 4534 } 4535 4536 $l_online_record = sprintf($user->lang['RECORD_ONLINE_USERS'], $config['record_online_users'], $user->format_date($config['record_online_date'], false, true)); 4537 4538 $l_online_time = ($config['load_online_time'] == 1) ? 'VIEW_ONLINE_TIME' : 'VIEW_ONLINE_TIMES'; 4539 $l_online_time = sprintf($user->lang[$l_online_time], $config['load_online_time']); 4540 } 4541 4542 $l_privmsgs_text = $l_privmsgs_text_unread = ''; 4543 $s_privmsg_new = false; 4544 4545 // Obtain number of new private messages if user is logged in 4546 if (!empty($user->data['is_registered'])) 4547 { 4548 if ($user->data['user_new_privmsg']) 4549 { 4550 $l_message_new = ($user->data['user_new_privmsg'] == 1) ? $user->lang['NEW_PM'] : $user->lang['NEW_PMS']; 4551 $l_privmsgs_text = sprintf($l_message_new, $user->data['user_new_privmsg']); 4552 4553 if (!$user->data['user_last_privmsg'] || $user->data['user_last_privmsg'] > $user->data['session_last_visit']) 4554 { 4555 $sql = 'UPDATE ' . USERS_TABLE . ' 4556 SET user_last_privmsg = ' . $user->data['session_last_visit'] . ' 4557 WHERE user_id = ' . $user->data['user_id']; 4558 $db->sql_query($sql); 4559 4560 $s_privmsg_new = true; 4561 } 4562 else 4563 { 4564 $s_privmsg_new = false; 4565 } 4566 } 4567 else 4568 { 4569 $l_privmsgs_text = $user->lang['NO_NEW_PM']; 4570 $s_privmsg_new = false; 4571 } 4572 4573 $l_privmsgs_text_unread = ''; 4574 4575 if ($user->data['user_unread_privmsg'] && $user->data['user_unread_privmsg'] != $user->data['user_new_privmsg']) 4576 { 4577 $l_message_unread = ($user->data['user_unread_privmsg'] == 1) ? $user->lang['UNREAD_PM'] : $user->lang['UNREAD_PMS']; 4578 $l_privmsgs_text_unread = sprintf($l_message_unread, $user->data['user_unread_privmsg']); 4579 } 4580 } 4581 4582 $forum_id = request_var('f', 0); 4583 $topic_id = request_var('t', 0); 4584 4585 $s_feed_news = false; 4586 4587 // Get option for news 4588 if ($config['feed_enable']) 4589 { 4590 $sql = 'SELECT forum_id 4591 FROM ' . FORUMS_TABLE . ' 4592 WHERE ' . $db->sql_bit_and('forum_options', FORUM_OPTION_FEED_NEWS, '<> 0'); 4593 $result = $db->sql_query_limit($sql, 1, 0, 600); 4594 $s_feed_news = (int) $db->sql_fetchfield('forum_id'); 4595 $db->sql_freeresult($result); 4596 } 4597 4598 // Determine board url - we may need it later 4599 $board_url = generate_board_url() . '/'; 4600 $web_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? $board_url : $phpbb_root_path; 4601 4602 // Which timezone? 4603 $tz = ($user->data['user_id'] != ANONYMOUS) ? strval(doubleval($user->data['user_timezone'])) : strval(doubleval($config['board_timezone'])); 4604 4605 // Send a proper content-language to the output 4606 $user_lang = $user->lang['USER_LANG']; 4607 if (strpos($user_lang, '-x-') !== false) 4608 { 4609 $user_lang = substr($user_lang, 0, strpos($user_lang, '-x-')); 4610 } 4611 4612 $s_search_hidden_fields = array(); 4613 if ($_SID) 4614 { 4615 $s_search_hidden_fields['sid'] = $_SID; 4616 } 4617 4618 if (!empty($_EXTRA_URL)) 4619 { 4620 foreach ($_EXTRA_URL as $url_param) 4621 { 4622 $url_param = explode('=', $url_param, 2); 4623 $s_search_hidden_fields[$url_param[0]] = $url_param[1]; 4624 } 4625 } 4626 4627 // The following assigns all _common_ variables that may be used at any point in a template. 4628 $template->assign_vars(array( 4629 'SITENAME' => $config['sitename'], 4630 'SITE_DESCRIPTION' => $config['site_desc'], 4631 'PAGE_TITLE' => $page_title, 4632 'SCRIPT_NAME' => str_replace('.' . $phpEx, '', $user->page['page_name']), 4633 'LAST_VISIT_DATE' => sprintf($user->lang['YOU_LAST_VISIT'], $s_last_visit), 4634 'LAST_VISIT_YOU' => $s_last_visit, 4635 'CURRENT_TIME' => sprintf($user->lang['CURRENT_TIME'], $user->format_date(time(), false, true)), 4636 'TOTAL_USERS_ONLINE' => $l_online_users, 4637 'LOGGED_IN_USER_LIST' => $online_userlist, 4638 'RECORD_USERS' => $l_online_record, 4639 'PRIVATE_MESSAGE_INFO' => $l_privmsgs_text, 4640 'PRIVATE_MESSAGE_INFO_UNREAD' => $l_privmsgs_text_unread, 4641 4642 'S_USER_NEW_PRIVMSG' => $user->data['user_new_privmsg'], 4643 'S_USER_UNREAD_PRIVMSG' => $user->data['user_unread_privmsg'], 4644 'S_USER_NEW' => $user->data['user_new'], 4645 4646 'SID' => $SID, 4647 '_SID' => $_SID, 4648 'SESSION_ID' => $user->session_id, 4649 'ROOT_PATH' => $phpbb_root_path, 4650 'BOARD_URL' => $board_url, 4651 4652 'L_LOGIN_LOGOUT' => $l_login_logout, 4653 'L_INDEX' => $user->lang['FORUM_INDEX'], 4654 'L_ONLINE_EXPLAIN' => $l_online_time, 4655 4656 'U_PRIVATEMSGS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox'), 4657 'U_RETURN_INBOX' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox'), 4658 'U_POPUP_PM' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=popup'), 4659 'UA_POPUP_PM' => addslashes(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=popup')), 4660 'U_MEMBERLIST' => append_sid("{$phpbb_root_path}memberlist.$phpEx"), 4661 'U_VIEWONLINE' => ($auth->acl_gets('u_viewprofile', 'a_user', 'a_useradd', 'a_userdel')) ? append_sid("{$phpbb_root_path}viewonline.$phpEx") : '', 4662 'U_LOGIN_LOGOUT' => $u_login_logout, 4663 'U_INDEX' => append_sid("{$phpbb_root_path}index.$phpEx"), 4664 'U_SEARCH' => append_sid("{$phpbb_root_path}search.$phpEx"), 4665 'U_REGISTER' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register'), 4666 'U_PROFILE' => append_sid("{$phpbb_root_path}ucp.$phpEx"), 4667 'U_MODCP' => append_sid("{$phpbb_root_path}mcp.$phpEx", false, true, $user->session_id), 4668 'U_FAQ' => append_sid("{$phpbb_root_path}faq.$phpEx"), 4669 'U_SEARCH_SELF' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=egosearch'), 4670 'U_SEARCH_NEW' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=newposts'), 4671 'U_SEARCH_UNANSWERED' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unanswered'), 4672 'U_SEARCH_UNREAD' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unreadposts'), 4673 'U_SEARCH_ACTIVE_TOPICS'=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=active_topics'), 4674 'U_DELETE_COOKIES' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=delete_cookies'), 4675 'U_TEAM' => ($user->data['user_id'] != ANONYMOUS && !$auth->acl_get('u_viewprofile')) ? '' : append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=leaders'), 4676 'U_TERMS_USE' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'), 4677 'U_PRIVACY' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'), 4678 'U_RESTORE_PERMISSIONS' => ($user->data['user_perm_from'] && $auth->acl_get('a_switchperm')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=restore_perm') : '', 4679 'U_FEED' => generate_board_url() . "/feed.$phpEx", 4680 4681 'S_USER_LOGGED_IN' => ($user->data['user_id'] != ANONYMOUS) ? true : false, 4682 'S_AUTOLOGIN_ENABLED' => ($config['allow_autologin']) ? true : false, 4683 'S_BOARD_DISABLED' => ($config['board_disable']) ? true : false, 4684 'S_REGISTERED_USER' => (!empty($user->data['is_registered'])) ? true : false, 4685 'S_IS_BOT' => (!empty($user->data['is_bot'])) ? true : false, 4686 'S_USER_PM_POPUP' => $user->optionget('popuppm'), 4687 'S_USER_LANG' => $user_lang, 4688 'S_USER_BROWSER' => (isset($user->data['session_browser'])) ? $user->data['session_browser'] : $user->lang['UNKNOWN_BROWSER'], 4689 'S_USERNAME' => $user->data['username'], 4690 'S_CONTENT_DIRECTION' => $user->lang['DIRECTION'], 4691 'S_CONTENT_FLOW_BEGIN' => ($user->lang['DIRECTION'] == 'ltr') ? 'left' : 'right', 4692 'S_CONTENT_FLOW_END' => ($user->lang['DIRECTION'] == 'ltr') ? 'right' : 'left', 4693 'S_CONTENT_ENCODING' => 'UTF-8', 4694 'S_TIMEZONE' => ($user->data['user_dst'] || ($user->data['user_id'] == ANONYMOUS && $config['board_dst'])) ? sprintf($user->lang['ALL_TIMES'], $user->lang['tz'][$tz], $user->lang['tz']['dst']) : sprintf($user->lang['ALL_TIMES'], $user->lang['tz'][$tz], ''), 4695 'S_DISPLAY_ONLINE_LIST' => ($l_online_time) ? 1 : 0, 4696 'S_DISPLAY_SEARCH' => (!$config['load_search']) ? 0 : (isset($auth) ? ($auth->acl_get('u_search') && $auth->acl_getf_global('f_search')) : 1), 4697 'S_DISPLAY_PM' => ($config['allow_privmsg'] && !empty($user->data['is_registered']) && ($auth->acl_get('u_readpm') || $auth->acl_get('u_sendpm'))) ? true : false, 4698 'S_DISPLAY_MEMBERLIST' => (isset($auth)) ? $auth->acl_get('u_viewprofile') : 0, 4699 'S_NEW_PM' => ($s_privmsg_new) ? 1 : 0, 4700 'S_REGISTER_ENABLED' => ($config['require_activation'] != USER_ACTIVATION_DISABLE) ? true : false, 4701 'S_FORUM_ID' => $forum_id, 4702 'S_TOPIC_ID' => $topic_id, 4703 4704 'S_LOGIN_ACTION' => ((!defined('ADMIN_START')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login') : append_sid("index.$phpEx", false, true, $user->session_id)), 4705 'S_LOGIN_REDIRECT' => build_hidden_fields(array('redirect' => build_url())), 4706 4707 'S_ENABLE_FEEDS' => ($config['feed_enable']) ? true : false, 4708 'S_ENABLE_FEEDS_OVERALL' => ($config['feed_overall']) ? true : false, 4709 'S_ENABLE_FEEDS_FORUMS' => ($config['feed_overall_forums']) ? true : false, 4710 'S_ENABLE_FEEDS_TOPICS' => ($config['feed_topics_new']) ? true : false, 4711 'S_ENABLE_FEEDS_TOPICS_ACTIVE' => ($config['feed_topics_active']) ? true : false, 4712 'S_ENABLE_FEEDS_NEWS' => ($s_feed_news) ? true : false, 4713 4714 'S_LOAD_UNREADS' => ($config['load_unreads_search'] && ($config['load_anon_lastread'] || $user->data['is_registered'])) ? true : false, 4715 4716 'S_SEARCH_HIDDEN_FIELDS' => build_hidden_fields($s_search_hidden_fields), 4717 4718 'T_THEME_PATH' => "{$web_path}styles/" . rawurlencode($user->theme['theme_path']) . '/theme', 4719 'T_TEMPLATE_PATH' => "{$web_path}styles/" . rawurlencode($user->theme['template_path']) . '/template', 4720 'T_SUPER_TEMPLATE_PATH' => (isset($user->theme['template_inherit_path']) && $user->theme['template_inherit_path']) ? "{$web_path}styles/" . rawurlencode($user->theme['template_inherit_path']) . '/template' : "{$web_path}styles/" . rawurlencode($user->theme['template_path']) . '/template', 4721 'T_IMAGESET_PATH' => "{$web_path}styles/" . rawurlencode($user->theme['imageset_path']) . '/imageset', 4722 'T_IMAGESET_LANG_PATH' => "{$web_path}styles/" . rawurlencode($user->theme['imageset_path']) . '/imageset/' . $user->lang_name, 4723 'T_IMAGES_PATH' => "{$web_path}images/", 4724 'T_SMILIES_PATH' => "{$web_path}{$config['smilies_path']}/", 4725 'T_AVATAR_PATH' => "{$web_path}{$config['avatar_path']}/", 4726 'T_AVATAR_GALLERY_PATH' => "{$web_path}{$config['avatar_gallery_path']}/", 4727 'T_ICONS_PATH' => "{$web_path}{$config['icons_path']}/", 4728 'T_RANKS_PATH' => "{$web_path}{$config['ranks_path']}/", 4729 'T_UPLOAD_PATH' => "{$web_path}{$config['upload_path']}/", 4730 'T_STYLESHEET_LINK' => (!$user->theme['theme_storedb']) ? "{$web_path}styles/" . rawurlencode($user->theme['theme_path']) . '/theme/stylesheet.css' : append_sid("{$phpbb_root_path}style.$phpEx", 'id=' . $user->theme['style_id'] . '&lang=' . $user->lang_name), 4731 'T_STYLESHEET_NAME' => $user->theme['theme_name'], 4732 4733 'T_THEME_NAME' => rawurlencode($user->theme['theme_path']), 4734 'T_TEMPLATE_NAME' => rawurlencode($user->theme['template_path']), 4735 'T_SUPER_TEMPLATE_NAME' => rawurlencode((isset($user->theme['template_inherit_path']) && $user->theme['template_inherit_path']) ? $user->theme['template_inherit_path'] : $user->theme['template_path']), 4736 'T_IMAGESET_NAME' => rawurlencode($user->theme['imageset_path']), 4737 'T_IMAGESET_LANG_NAME' => $user->data['user_lang'], 4738 'T_IMAGES' => 'images', 4739 'T_SMILIES' => $config['smilies_path'], 4740 'T_AVATAR' => $config['avatar_path'], 4741 'T_AVATAR_GALLERY' => $config['avatar_gallery_path'], 4742 'T_ICONS' => $config['icons_path'], 4743 'T_RANKS' => $config['ranks_path'], 4744 'T_UPLOAD' => $config['upload_path'], 4745 4746 'SITE_LOGO_IMG' => $user->img('site_logo'), 4747 4748 'A_COOKIE_SETTINGS' => addslashes('; path=' . $config['cookie_path'] . ((!$config['cookie_domain'] || $config['cookie_domain'] == 'localhost' || $config['cookie_domain'] == '127.0.0.1') ? '' : '; domain=' . $config['cookie_domain']) . ((!$config['cookie_secure']) ? '' : '; secure')), 4749 )); 4750 4751 // application/xhtml+xml not used because of IE 4752 header('Content-type: text/html; charset=UTF-8'); 4753 4754 header('Cache-Control: private, no-cache="set-cookie"'); 4755 header('Expires: 0'); 4756 header('Pragma: no-cache'); 4757 4758 if (!empty($user->data['is_bot'])) 4759 { 4760 // Let reverse proxies know we detected a bot. 4761 header('X-PHPBB-IS-BOT: yes'); 4762 } 4763 4764 return; 4765 } 4766 4767 /** 4768 * Generate page footer 4769 */ 4770 function page_footer($run_cron = true) 4771 { 4772 global $db, $config, $template, $user, $auth, $cache, $starttime, $phpbb_root_path, $phpEx; 4773 4774 // Output page creation time 4775 if (defined('DEBUG')) 4776 { 4777 $mtime = explode(' ', microtime()); 4778 $totaltime = $mtime[0] + $mtime[1] - $starttime; 4779 4780 if (!empty($_REQUEST['explain']) && $auth->acl_get('a_') && defined('DEBUG_EXTRA') && method_exists($db, 'sql_report')) 4781 { 4782 $db->sql_report('display'); 4783 } 4784 4785 $debug_output = sprintf('Time : %.3fs | ' . $db->sql_num_queries() . ' Queries | GZIP : ' . (($config['gzip_compress'] && @extension_loaded('zlib')) ? 'On' : 'Off') . (($user->load) ? ' | Load : ' . $user->load : ''), $totaltime); 4786 4787 if ($auth->acl_get('a_') && defined('DEBUG_EXTRA')) 4788 { 4789 if (function_exists('memory_get_usage')) 4790 { 4791 if ($memory_usage = memory_get_usage()) 4792 { 4793 global $base_memory_usage; 4794 $memory_usage -= $base_memory_usage; 4795 $memory_usage = get_formatted_filesize($memory_usage); 4796 4797 $debug_output .= ' | Memory Usage: ' . $memory_usage; 4798 } 4799 } 4800 4801 $debug_output .= ' | <a href="' . build_url() . '&explain=1">Explain</a>'; 4802 } 4803 } 4804 4805 $template->assign_vars(array( 4806 'DEBUG_OUTPUT' => (defined('DEBUG')) ? $debug_output : '', 4807 'TRANSLATION_INFO' => (!empty($user->lang['TRANSLATION_INFO'])) ? $user->lang['TRANSLATION_INFO'] : '', 4808 'CREDIT_LINE' => $user->lang('POWERED_BY', '<a href="https://www.phpbb.com/">phpBB</a>® Forum Software © phpBB Group'), 4809 4810 'U_ACP' => ($auth->acl_get('a_') && !empty($user->data['is_registered'])) ? append_sid("{$phpbb_root_path}adm/index.$phpEx", false, true, $user->session_id) : '') 4811 ); 4812 4813 // Call cron-type script 4814 $call_cron = false; 4815 if (!defined('IN_CRON') && $run_cron && !$config['board_disable'] && !$user->data['is_bot']) 4816 { 4817 $call_cron = true; 4818 $time_now = (!empty($user->time_now) && is_int($user->time_now)) ? $user->time_now : time(); 4819 4820 // Any old lock present? 4821 if (!empty($config['cron_lock'])) 4822 { 4823 $cron_time = explode(' ', $config['cron_lock']); 4824 4825 // If 1 hour lock is present we do not call cron.php 4826 if ($cron_time[0] + 3600 >= $time_now) 4827 { 4828 $call_cron = false; 4829 } 4830 } 4831 } 4832 4833 // Call cron job? 4834 if ($call_cron) 4835 { 4836 $cron_type = ''; 4837 4838 if ($time_now - $config['queue_interval'] > $config['last_queue_run'] && !defined('IN_ADMIN') && file_exists($phpbb_root_path . 'cache/queue.' . $phpEx)) 4839 { 4840 // Process email queue 4841 $cron_type = 'queue'; 4842 } 4843 else if (method_exists($cache, 'tidy') && $time_now - $config['cache_gc'] > $config['cache_last_gc']) 4844 { 4845 // Tidy the cache 4846 $cron_type = 'tidy_cache'; 4847 } 4848 else if ($config['warnings_expire_days'] && ($time_now - $config['warnings_gc'] > $config['warnings_last_gc'])) 4849 { 4850 $cron_type = 'tidy_warnings'; 4851 } 4852 else if ($time_now - $config['database_gc'] > $config['database_last_gc']) 4853 { 4854 // Tidy the database 4855 $cron_type = 'tidy_database'; 4856 } 4857 else if ($time_now - $config['search_gc'] > $config['search_last_gc']) 4858 { 4859 // Tidy the search 4860 $cron_type = 'tidy_search'; 4861 } 4862 else if ($time_now - $config['session_gc'] > $config['session_last_gc']) 4863 { 4864 $cron_type = 'tidy_sessions'; 4865 } 4866 4867 if ($cron_type) 4868 { 4869 $template->assign_var('RUN_CRON_TASK', '<img src="' . append_sid($phpbb_root_path . 'cron.' . $phpEx, 'cron_type=' . $cron_type) . '" width="1" height="1" alt="cron" />'); 4870 } 4871 } 4872 4873 $template->display('body'); 4874 4875 garbage_collection(); 4876 exit_handler(); 4877 } 4878 4879 /** 4880 * Closing the cache object and the database 4881 * Cool function name, eh? We might want to add operations to it later 4882 */ 4883 function garbage_collection() 4884 { 4885 global $cache, $db; 4886 4887 // Unload cache, must be done before the DB connection if closed 4888 if (!empty($cache)) 4889 { 4890 $cache->unload(); 4891 } 4892 4893 // Close our DB connection. 4894 if (!empty($db)) 4895 { 4896 $db->sql_close(); 4897 } 4898 } 4899 4900 /** 4901 * Handler for exit calls in phpBB. 4902 * This function supports hooks. 4903 * 4904 * Note: This function is called after the template has been outputted. 4905 */ 4906 function exit_handler() 4907 { 4908 global $phpbb_hook, $config; 4909 4910 if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__)) 4911 { 4912 if ($phpbb_hook->hook_return(__FUNCTION__)) 4913 { 4914 return $phpbb_hook->hook_return_result(__FUNCTION__); 4915 } 4916 } 4917 4918 // As a pre-caution... some setups display a blank page if the flush() is not there. 4919 (ob_get_level() > 0) ? @ob_flush() : @flush(); 4920 4921 exit; 4922 } 4923 4924 /** 4925 * Handler for init calls in phpBB. This function is called in user::setup(); 4926 * This function supports hooks. 4927 */ 4928 function phpbb_user_session_handler() 4929 { 4930 global $phpbb_hook; 4931 4932 if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__)) 4933 { 4934 if ($phpbb_hook->hook_return(__FUNCTION__)) 4935 { 4936 return $phpbb_hook->hook_return_result(__FUNCTION__); 4937 } 4938 } 4939 4940 return; 4941 } 4942 4943 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Oct 2 15:03:47 2013 | Cross-referenced by PHPXref 0.7.1 |