[ Index ] |
PHP Cross Reference of phpBB-3.1.12-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * This file is part of the phpBB Forum Software package. 5 * 6 * @copyright (c) phpBB Limited <https://www.phpbb.com> 7 * @license GNU General Public License, version 2 (GPL-2.0) 8 * 9 * For full copyright and license information, please see 10 * the docs/CREDITS.txt file. 11 * 12 */ 13 14 /** 15 * @ignore 16 */ 17 if (!defined('IN_PHPBB')) 18 { 19 exit; 20 } 21 22 // Common global functions 23 /** 24 * Load the autoloaders added by the extensions. 25 * 26 * @param string $phpbb_root_path Path to the phpbb root directory. 27 */ 28 function phpbb_load_extensions_autoloaders($phpbb_root_path) 29 { 30 $iterator = new \RecursiveIteratorIterator( 31 new \phpbb\recursive_dot_prefix_filter_iterator( 32 new \RecursiveDirectoryIterator( 33 $phpbb_root_path . 'ext/', 34 \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS 35 ) 36 ), 37 \RecursiveIteratorIterator::SELF_FIRST 38 ); 39 $iterator->setMaxDepth(2); 40 41 foreach ($iterator as $file_info) 42 { 43 if ($file_info->getFilename() === 'vendor' && $iterator->getDepth() === 2) 44 { 45 $filename = $file_info->getRealPath() . '/autoload.php'; 46 if (file_exists($filename)) 47 { 48 require $filename; 49 } 50 } 51 } 52 } 53 54 /** 55 * Casts a variable to the given type. 56 * 57 * @deprecated 58 */ 59 function set_var(&$result, $var, $type, $multibyte = false) 60 { 61 // no need for dependency injection here, if you have the object, call the method yourself! 62 $type_cast_helper = new \phpbb\request\type_cast_helper(); 63 $type_cast_helper->set_var($result, $var, $type, $multibyte); 64 } 65 66 /** 67 * Wrapper function of \phpbb\request\request::variable which exists for backwards compatability. 68 * See {@link \phpbb\request\request_interface::variable \phpbb\request\request_interface::variable} for 69 * documentation of this function's use. 70 * 71 * @deprecated 72 * @param mixed $var_name The form variable's name from which data shall be retrieved. 73 * If the value is an array this may be an array of indizes which will give 74 * direct access to a value at any depth. E.g. if the value of "var" is array(1 => "a") 75 * then specifying array("var", 1) as the name will return "a". 76 * If you pass an instance of {@link \phpbb\request\request_interface phpbb_request_interface} 77 * as this parameter it will overwrite the current request class instance. If you do 78 * not do so, it will create its own instance (but leave superglobals enabled). 79 * @param mixed $default A default value that is returned if the variable was not set. 80 * This function will always return a value of the same type as the default. 81 * @param bool $multibyte If $default is a string this paramater has to be true if the variable may contain any UTF-8 characters 82 * Default is false, causing all bytes outside the ASCII range (0-127) to be replaced with question marks 83 * @param bool $cookie This param is mapped to \phpbb\request\request_interface::COOKIE as the last param for 84 * \phpbb\request\request_interface::variable for backwards compatability reasons. 85 * @param \phpbb\request\request_interface|null|false If an instance of \phpbb\request\request_interface is given the instance is stored in 86 * a static variable and used for all further calls where this parameters is null. Until 87 * the function is called with an instance it automatically creates a new \phpbb\request\request 88 * instance on every call. By passing false this per-call instantiation can be restored 89 * after having passed in a \phpbb\request\request_interface instance. 90 * 91 * @return mixed The value of $_REQUEST[$var_name] run through {@link set_var set_var} to ensure that the type is the 92 * the same as that of $default. If the variable is not set $default is returned. 93 */ 94 function request_var($var_name, $default, $multibyte = false, $cookie = false, $request = null) 95 { 96 // This is all just an ugly hack to add "Dependency Injection" to a function 97 // the only real code is the function call which maps this function to a method. 98 static $static_request = null; 99 100 if ($request instanceof \phpbb\request\request_interface) 101 { 102 $static_request = $request; 103 104 if (empty($var_name)) 105 { 106 return; 107 } 108 } 109 else if ($request === false) 110 { 111 $static_request = null; 112 113 if (empty($var_name)) 114 { 115 return; 116 } 117 } 118 119 $tmp_request = $static_request; 120 121 // no request class set, create a temporary one ourselves to keep backwards compatability 122 if ($tmp_request === null) 123 { 124 // false param: enable super globals, so the created request class does not 125 // make super globals inaccessible everywhere outside this function. 126 $tmp_request = new \phpbb\request\request(new \phpbb\request\type_cast_helper(), false); 127 } 128 129 return $tmp_request->variable($var_name, $default, $multibyte, ($cookie) ? \phpbb\request\request_interface::COOKIE : \phpbb\request\request_interface::REQUEST); 130 } 131 132 /** 133 * Sets a configuration option's value. 134 * 135 * Please note that this function does not update the is_dynamic value for 136 * an already existing config option. 137 * 138 * @param string $config_name The configuration option's name 139 * @param string $config_value New configuration value 140 * @param bool $is_dynamic Whether this variable should be cached (false) or 141 * if it changes too frequently (true) to be 142 * efficiently cached. 143 * 144 * @return null 145 * 146 * @deprecated 147 */ 148 function set_config($config_name, $config_value, $is_dynamic = false, \phpbb\config\config $set_config = null) 149 { 150 static $config = null; 151 152 if ($set_config !== null) 153 { 154 $config = $set_config; 155 156 if (empty($config_name)) 157 { 158 return; 159 } 160 } 161 162 $config->set($config_name, $config_value, !$is_dynamic); 163 } 164 165 /** 166 * Increments an integer config value directly in the database. 167 * 168 * @param string $config_name The configuration option's name 169 * @param int $increment Amount to increment by 170 * @param bool $is_dynamic Whether this variable should be cached (false) or 171 * if it changes too frequently (true) to be 172 * efficiently cached. 173 * 174 * @return null 175 * 176 * @deprecated 177 */ 178 function set_config_count($config_name, $increment, $is_dynamic = false, \phpbb\config\config $set_config = null) 179 { 180 static $config = null; 181 182 if ($set_config !== null) 183 { 184 $config = $set_config; 185 186 if (empty($config_name)) 187 { 188 return; 189 } 190 } 191 192 $config->increment($config_name, $increment, !$is_dynamic); 193 } 194 195 /** 196 * Generates an alphanumeric random string of given length 197 * 198 * @return string 199 */ 200 function gen_rand_string($num_chars = 8) 201 { 202 // [a, z] + [0, 9] = 36 203 return substr(strtoupper(base_convert(unique_id(), 16, 36)), 0, $num_chars); 204 } 205 206 /** 207 * Generates a user-friendly alphanumeric random string of given length 208 * We remove 0 and O so users cannot confuse those in passwords etc. 209 * 210 * @return string 211 */ 212 function gen_rand_string_friendly($num_chars = 8) 213 { 214 $rand_str = unique_id(); 215 216 // Remove Z and Y from the base_convert(), replace 0 with Z and O with Y 217 // [a, z] + [0, 9] - {z, y} = [a, z] + [0, 9] - {0, o} = 34 218 $rand_str = str_replace(array('0', 'O'), array('Z', 'Y'), strtoupper(base_convert($rand_str, 16, 34))); 219 220 return substr($rand_str, 0, $num_chars); 221 } 222 223 /** 224 * Return unique id 225 * @param string $extra additional entropy 226 */ 227 function unique_id($extra = 'c') 228 { 229 static $dss_seeded = false; 230 global $config; 231 232 $val = $config['rand_seed'] . microtime(); 233 $val = md5($val); 234 $config['rand_seed'] = md5($config['rand_seed'] . $val . $extra); 235 236 if ($dss_seeded !== true && ($config['rand_seed_last_update'] < time() - rand(1,10))) 237 { 238 set_config('rand_seed_last_update', time(), true); 239 set_config('rand_seed', $config['rand_seed'], true); 240 $dss_seeded = true; 241 } 242 243 return substr($val, 4, 16); 244 } 245 246 /** 247 * Wrapper for mt_rand() which allows swapping $min and $max parameters. 248 * 249 * PHP does not allow us to swap the order of the arguments for mt_rand() anymore. 250 * (since PHP 5.3.4, see http://bugs.php.net/46587) 251 * 252 * @param int $min Lowest value to be returned 253 * @param int $max Highest value to be returned 254 * 255 * @return int Random integer between $min and $max (or $max and $min) 256 */ 257 function phpbb_mt_rand($min, $max) 258 { 259 return ($min > $max) ? mt_rand($max, $min) : mt_rand($min, $max); 260 } 261 262 /** 263 * Wrapper for getdate() which returns the equivalent array for UTC timestamps. 264 * 265 * @param int $time Unix timestamp (optional) 266 * 267 * @return array Returns an associative array of information related to the timestamp. 268 * See http://www.php.net/manual/en/function.getdate.php 269 */ 270 function phpbb_gmgetdate($time = false) 271 { 272 if ($time === false) 273 { 274 $time = time(); 275 } 276 277 // getdate() interprets timestamps in local time. 278 // What follows uses the fact that getdate() and 279 // date('Z') balance each other out. 280 return getdate($time - date('Z')); 281 } 282 283 /** 284 * Return formatted string for filesizes 285 * 286 * @param mixed $value filesize in bytes 287 * (non-negative number; int, float or string) 288 * @param bool $string_only true if language string should be returned 289 * @param array $allowed_units only allow these units (data array indexes) 290 * 291 * @return mixed data array if $string_only is false 292 */ 293 function get_formatted_filesize($value, $string_only = true, $allowed_units = false) 294 { 295 global $user; 296 297 $available_units = array( 298 'tb' => array( 299 'min' => 1099511627776, // pow(2, 40) 300 'index' => 4, 301 'si_unit' => 'TB', 302 'iec_unit' => 'TIB', 303 ), 304 'gb' => array( 305 'min' => 1073741824, // pow(2, 30) 306 'index' => 3, 307 'si_unit' => 'GB', 308 'iec_unit' => 'GIB', 309 ), 310 'mb' => array( 311 'min' => 1048576, // pow(2, 20) 312 'index' => 2, 313 'si_unit' => 'MB', 314 'iec_unit' => 'MIB', 315 ), 316 'kb' => array( 317 'min' => 1024, // pow(2, 10) 318 'index' => 1, 319 'si_unit' => 'KB', 320 'iec_unit' => 'KIB', 321 ), 322 'b' => array( 323 'min' => 0, 324 'index' => 0, 325 'si_unit' => 'BYTES', // Language index 326 'iec_unit' => 'BYTES', // Language index 327 ), 328 ); 329 330 foreach ($available_units as $si_identifier => $unit_info) 331 { 332 if (!empty($allowed_units) && $si_identifier != 'b' && !in_array($si_identifier, $allowed_units)) 333 { 334 continue; 335 } 336 337 if ($value >= $unit_info['min']) 338 { 339 $unit_info['si_identifier'] = $si_identifier; 340 341 break; 342 } 343 } 344 unset($available_units); 345 346 for ($i = 0; $i < $unit_info['index']; $i++) 347 { 348 $value /= 1024; 349 } 350 $value = round($value, 2); 351 352 // Lookup units in language dictionary 353 $unit_info['si_unit'] = (isset($user->lang[$unit_info['si_unit']])) ? $user->lang[$unit_info['si_unit']] : $unit_info['si_unit']; 354 $unit_info['iec_unit'] = (isset($user->lang[$unit_info['iec_unit']])) ? $user->lang[$unit_info['iec_unit']] : $unit_info['iec_unit']; 355 356 // Default to IEC 357 $unit_info['unit'] = $unit_info['iec_unit']; 358 359 if (!$string_only) 360 { 361 $unit_info['value'] = $value; 362 363 return $unit_info; 364 } 365 366 return $value . ' ' . $unit_info['unit']; 367 } 368 369 /** 370 * Determine whether we are approaching the maximum execution time. Should be called once 371 * at the beginning of the script in which it's used. 372 * @return bool Either true if the maximum execution time is nearly reached, or false 373 * if some time is still left. 374 */ 375 function still_on_time($extra_time = 15) 376 { 377 static $max_execution_time, $start_time; 378 379 $time = explode(' ', microtime()); 380 $current_time = $time[0] + $time[1]; 381 382 if (empty($max_execution_time)) 383 { 384 $max_execution_time = (function_exists('ini_get')) ? (int) @ini_get('max_execution_time') : (int) @get_cfg_var('max_execution_time'); 385 386 // If zero, then set to something higher to not let the user catch the ten seconds barrier. 387 if ($max_execution_time === 0) 388 { 389 $max_execution_time = 50 + $extra_time; 390 } 391 392 $max_execution_time = min(max(10, ($max_execution_time - $extra_time)), 50); 393 394 // For debugging purposes 395 // $max_execution_time = 10; 396 397 global $starttime; 398 $start_time = (empty($starttime)) ? $current_time : $starttime; 399 } 400 401 return (ceil($current_time - $start_time) < $max_execution_time) ? true : false; 402 } 403 404 /** 405 * Hashes an email address to a big integer 406 * 407 * @param string $email Email address 408 * 409 * @return string Unsigned Big Integer 410 */ 411 function phpbb_email_hash($email) 412 { 413 return sprintf('%u', crc32(strtolower($email))) . strlen($email); 414 } 415 416 /** 417 * Wrapper for version_compare() that allows using uppercase A and B 418 * for alpha and beta releases. 419 * 420 * See http://www.php.net/manual/en/function.version-compare.php 421 * 422 * @param string $version1 First version number 423 * @param string $version2 Second version number 424 * @param string $operator Comparison operator (optional) 425 * 426 * @return mixed Boolean (true, false) if comparison operator is specified. 427 * Integer (-1, 0, 1) otherwise. 428 */ 429 function phpbb_version_compare($version1, $version2, $operator = null) 430 { 431 $version1 = strtolower($version1); 432 $version2 = strtolower($version2); 433 434 if (is_null($operator)) 435 { 436 return version_compare($version1, $version2); 437 } 438 else 439 { 440 return version_compare($version1, $version2, $operator); 441 } 442 } 443 444 /** 445 * Global function for chmodding directories and files for internal use 446 * 447 * This function determines owner and group whom the file belongs to and user and group of PHP and then set safest possible file permissions. 448 * The function determines owner and group from common.php file and sets the same to the provided file. 449 * The function uses bit fields to build the permissions. 450 * The function sets the appropiate execute bit on directories. 451 * 452 * Supported constants representing bit fields are: 453 * 454 * CHMOD_ALL - all permissions (7) 455 * CHMOD_READ - read permission (4) 456 * CHMOD_WRITE - write permission (2) 457 * CHMOD_EXECUTE - execute permission (1) 458 * 459 * 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. 460 * 461 * @param string $filename The file/directory to be chmodded 462 * @param int $perms Permissions to set 463 * 464 * @return bool true on success, otherwise false 465 */ 466 function phpbb_chmod($filename, $perms = CHMOD_READ) 467 { 468 static $_chmod_info; 469 470 // Return if the file no longer exists. 471 if (!file_exists($filename)) 472 { 473 return false; 474 } 475 476 // Determine some common vars 477 if (empty($_chmod_info)) 478 { 479 if (!function_exists('fileowner') || !function_exists('filegroup')) 480 { 481 // No need to further determine owner/group - it is unknown 482 $_chmod_info['process'] = false; 483 } 484 else 485 { 486 global $phpbb_root_path, $phpEx; 487 488 // Determine owner/group of common.php file and the filename we want to change here 489 $common_php_owner = @fileowner($phpbb_root_path . 'common.' . $phpEx); 490 $common_php_group = @filegroup($phpbb_root_path . 'common.' . $phpEx); 491 492 // And the owner and the groups PHP is running under. 493 $php_uid = (function_exists('posix_getuid')) ? @posix_getuid() : false; 494 $php_gids = (function_exists('posix_getgroups')) ? @posix_getgroups() : false; 495 496 // If we are unable to get owner/group, then do not try to set them by guessing 497 if (!$php_uid || empty($php_gids) || !$common_php_owner || !$common_php_group) 498 { 499 $_chmod_info['process'] = false; 500 } 501 else 502 { 503 $_chmod_info = array( 504 'process' => true, 505 'common_owner' => $common_php_owner, 506 'common_group' => $common_php_group, 507 'php_uid' => $php_uid, 508 'php_gids' => $php_gids, 509 ); 510 } 511 } 512 } 513 514 if ($_chmod_info['process']) 515 { 516 $file_uid = @fileowner($filename); 517 $file_gid = @filegroup($filename); 518 519 // Change owner 520 if (@chown($filename, $_chmod_info['common_owner'])) 521 { 522 clearstatcache(); 523 $file_uid = @fileowner($filename); 524 } 525 526 // Change group 527 if (@chgrp($filename, $_chmod_info['common_group'])) 528 { 529 clearstatcache(); 530 $file_gid = @filegroup($filename); 531 } 532 533 // If the file_uid/gid now match the one from common.php we can process further, else we are not able to change something 534 if ($file_uid != $_chmod_info['common_owner'] || $file_gid != $_chmod_info['common_group']) 535 { 536 $_chmod_info['process'] = false; 537 } 538 } 539 540 // Still able to process? 541 if ($_chmod_info['process']) 542 { 543 if ($file_uid == $_chmod_info['php_uid']) 544 { 545 $php = 'owner'; 546 } 547 else if (in_array($file_gid, $_chmod_info['php_gids'])) 548 { 549 $php = 'group'; 550 } 551 else 552 { 553 // Since we are setting the everyone bit anyway, no need to do expensive operations 554 $_chmod_info['process'] = false; 555 } 556 } 557 558 // We are not able to determine or change something 559 if (!$_chmod_info['process']) 560 { 561 $php = 'other'; 562 } 563 564 // Owner always has read/write permission 565 $owner = CHMOD_READ | CHMOD_WRITE; 566 if (is_dir($filename)) 567 { 568 $owner |= CHMOD_EXECUTE; 569 570 // Only add execute bit to the permission if the dir needs to be readable 571 if ($perms & CHMOD_READ) 572 { 573 $perms |= CHMOD_EXECUTE; 574 } 575 } 576 577 switch ($php) 578 { 579 case 'owner': 580 $result = @chmod($filename, ($owner << 6) + (0 << 3) + (0 << 0)); 581 582 clearstatcache(); 583 584 if (is_readable($filename) && phpbb_is_writable($filename)) 585 { 586 break; 587 } 588 589 case 'group': 590 $result = @chmod($filename, ($owner << 6) + ($perms << 3) + (0 << 0)); 591 592 clearstatcache(); 593 594 if ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || phpbb_is_writable($filename))) 595 { 596 break; 597 } 598 599 case 'other': 600 $result = @chmod($filename, ($owner << 6) + ($perms << 3) + ($perms << 0)); 601 602 clearstatcache(); 603 604 if ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || phpbb_is_writable($filename))) 605 { 606 break; 607 } 608 609 default: 610 return false; 611 break; 612 } 613 614 return $result; 615 } 616 617 /** 618 * Test if a file/directory is writable 619 * 620 * This function calls the native is_writable() when not running under 621 * Windows and it is not disabled. 622 * 623 * @param string $file Path to perform write test on 624 * @return bool True when the path is writable, otherwise false. 625 */ 626 function phpbb_is_writable($file) 627 { 628 if (strtolower(substr(PHP_OS, 0, 3)) === 'win' || !function_exists('is_writable')) 629 { 630 if (file_exists($file)) 631 { 632 // Canonicalise path to absolute path 633 $file = phpbb_realpath($file); 634 635 if (is_dir($file)) 636 { 637 // Test directory by creating a file inside the directory 638 $result = @tempnam($file, 'i_w'); 639 640 if (is_string($result) && file_exists($result)) 641 { 642 unlink($result); 643 644 // Ensure the file is actually in the directory (returned realpathed) 645 return (strpos($result, $file) === 0) ? true : false; 646 } 647 } 648 else 649 { 650 $handle = @fopen($file, 'r+'); 651 652 if (is_resource($handle)) 653 { 654 fclose($handle); 655 return true; 656 } 657 } 658 } 659 else 660 { 661 // file does not exist test if we can write to the directory 662 $dir = dirname($file); 663 664 if (file_exists($dir) && is_dir($dir) && phpbb_is_writable($dir)) 665 { 666 return true; 667 } 668 } 669 670 return false; 671 } 672 else 673 { 674 return is_writable($file); 675 } 676 } 677 678 /** 679 * Checks if a path ($path) is absolute or relative 680 * 681 * @param string $path Path to check absoluteness of 682 * @return boolean 683 */ 684 function phpbb_is_absolute($path) 685 { 686 return (isset($path[0]) && $path[0] == '/' || preg_match('#^[a-z]:[/\\\]#i', $path)) ? true : false; 687 } 688 689 /** 690 * @author Chris Smith <chris@project-minerva.org> 691 * @copyright 2006 Project Minerva Team 692 * @param string $path The path which we should attempt to resolve. 693 * @return mixed 694 */ 695 function phpbb_own_realpath($path) 696 { 697 global $request; 698 699 // Now to perform funky shizzle 700 701 // Switch to use UNIX slashes 702 $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); 703 $path_prefix = ''; 704 705 // Determine what sort of path we have 706 if (phpbb_is_absolute($path)) 707 { 708 $absolute = true; 709 710 if ($path[0] == '/') 711 { 712 // Absolute path, *NIX style 713 $path_prefix = ''; 714 } 715 else 716 { 717 // Absolute path, Windows style 718 // Remove the drive letter and colon 719 $path_prefix = $path[0] . ':'; 720 $path = substr($path, 2); 721 } 722 } 723 else 724 { 725 // Relative Path 726 // Prepend the current working directory 727 if (function_exists('getcwd')) 728 { 729 // This is the best method, hopefully it is enabled! 730 $path = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . '/' . $path; 731 $absolute = true; 732 if (preg_match('#^[a-z]:#i', $path)) 733 { 734 $path_prefix = $path[0] . ':'; 735 $path = substr($path, 2); 736 } 737 else 738 { 739 $path_prefix = ''; 740 } 741 } 742 else if ($request->server('SCRIPT_FILENAME')) 743 { 744 // Warning: If chdir() has been used this will lie! 745 // Warning: This has some problems sometime (CLI can create them easily) 746 $filename = htmlspecialchars_decode($request->server('SCRIPT_FILENAME')); 747 $path = str_replace(DIRECTORY_SEPARATOR, '/', dirname($filename)) . '/' . $path; 748 $absolute = true; 749 $path_prefix = ''; 750 } 751 else 752 { 753 // We have no way of getting the absolute path, just run on using relative ones. 754 $absolute = false; 755 $path_prefix = '.'; 756 } 757 } 758 759 // Remove any repeated slashes 760 $path = preg_replace('#/{2,}#', '/', $path); 761 762 // Remove the slashes from the start and end of the path 763 $path = trim($path, '/'); 764 765 // Break the string into little bits for us to nibble on 766 $bits = explode('/', $path); 767 768 // Remove any . in the path, renumber array for the loop below 769 $bits = array_values(array_diff($bits, array('.'))); 770 771 // Lets get looping, run over and resolve any .. (up directory) 772 for ($i = 0, $max = sizeof($bits); $i < $max; $i++) 773 { 774 // @todo Optimise 775 if ($bits[$i] == '..' ) 776 { 777 if (isset($bits[$i - 1])) 778 { 779 if ($bits[$i - 1] != '..') 780 { 781 // We found a .. and we are able to traverse upwards, lets do it! 782 unset($bits[$i]); 783 unset($bits[$i - 1]); 784 $i -= 2; 785 $max -= 2; 786 $bits = array_values($bits); 787 } 788 } 789 else if ($absolute) // ie. !isset($bits[$i - 1]) && $absolute 790 { 791 // We have an absolute path trying to descend above the root of the filesystem 792 // ... Error! 793 return false; 794 } 795 } 796 } 797 798 // Prepend the path prefix 799 array_unshift($bits, $path_prefix); 800 801 $resolved = ''; 802 803 $max = sizeof($bits) - 1; 804 805 // Check if we are able to resolve symlinks, Windows cannot. 806 $symlink_resolve = (function_exists('readlink')) ? true : false; 807 808 foreach ($bits as $i => $bit) 809 { 810 if (@is_dir("$resolved/$bit") || ($i == $max && @is_file("$resolved/$bit"))) 811 { 812 // Path Exists 813 if ($symlink_resolve && is_link("$resolved/$bit") && ($link = readlink("$resolved/$bit"))) 814 { 815 // Resolved a symlink. 816 $resolved = $link . (($i == $max) ? '' : '/'); 817 continue; 818 } 819 } 820 else 821 { 822 // Something doesn't exist here! 823 // This is correct realpath() behaviour but sadly open_basedir and safe_mode make this problematic 824 // return false; 825 } 826 $resolved .= $bit . (($i == $max) ? '' : '/'); 827 } 828 829 // @todo If the file exists fine and open_basedir only has one path we should be able to prepend it 830 // because we must be inside that basedir, the question is where... 831 // @internal The slash in is_dir() gets around an open_basedir restriction 832 if (!@file_exists($resolved) || (!@is_dir($resolved . '/') && !is_file($resolved))) 833 { 834 return false; 835 } 836 837 // Put the slashes back to the native operating systems slashes 838 $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved); 839 840 // Check for DIRECTORY_SEPARATOR at the end (and remove it!) 841 if (substr($resolved, -1) == DIRECTORY_SEPARATOR) 842 { 843 return substr($resolved, 0, -1); 844 } 845 846 return $resolved; // We got here, in the end! 847 } 848 849 if (!function_exists('realpath')) 850 { 851 /** 852 * A wrapper for realpath 853 * @ignore 854 */ 855 function phpbb_realpath($path) 856 { 857 return phpbb_own_realpath($path); 858 } 859 } 860 else 861 { 862 /** 863 * A wrapper for realpath 864 */ 865 function phpbb_realpath($path) 866 { 867 $realpath = realpath($path); 868 869 // Strangely there are provider not disabling realpath but returning strange values. :o 870 // We at least try to cope with them. 871 if ($realpath === $path || $realpath === false) 872 { 873 return phpbb_own_realpath($path); 874 } 875 876 // Check for DIRECTORY_SEPARATOR at the end (and remove it!) 877 if (substr($realpath, -1) == DIRECTORY_SEPARATOR) 878 { 879 $realpath = substr($realpath, 0, -1); 880 } 881 882 return $realpath; 883 } 884 } 885 886 // functions used for building option fields 887 888 /** 889 * Pick a language, any language ... 890 */ 891 function language_select($default = '') 892 { 893 global $db; 894 895 $sql = 'SELECT lang_iso, lang_local_name 896 FROM ' . LANG_TABLE . ' 897 ORDER BY lang_english_name'; 898 $result = $db->sql_query($sql); 899 900 $lang_options = ''; 901 while ($row = $db->sql_fetchrow($result)) 902 { 903 $selected = ($row['lang_iso'] == $default) ? ' selected="selected"' : ''; 904 $lang_options .= '<option value="' . $row['lang_iso'] . '"' . $selected . '>' . $row['lang_local_name'] . '</option>'; 905 } 906 $db->sql_freeresult($result); 907 908 return $lang_options; 909 } 910 911 /** 912 * Pick a template/theme combo, 913 */ 914 function style_select($default = '', $all = false) 915 { 916 global $db; 917 918 $sql_where = (!$all) ? 'WHERE style_active = 1 ' : ''; 919 $sql = 'SELECT style_id, style_name 920 FROM ' . STYLES_TABLE . " 921 $sql_where 922 ORDER BY style_name"; 923 $result = $db->sql_query($sql); 924 925 $style_options = ''; 926 while ($row = $db->sql_fetchrow($result)) 927 { 928 $selected = ($row['style_id'] == $default) ? ' selected="selected"' : ''; 929 $style_options .= '<option value="' . $row['style_id'] . '"' . $selected . '>' . $row['style_name'] . '</option>'; 930 } 931 $db->sql_freeresult($result); 932 933 return $style_options; 934 } 935 936 /** 937 * Format the timezone offset with hours and minutes 938 * 939 * @param int $tz_offset Timezone offset in seconds 940 * @param bool $show_null Whether null offsets should be shown 941 * @return string Normalized offset string: -7200 => -02:00 942 * 16200 => +04:30 943 */ 944 function phpbb_format_timezone_offset($tz_offset, $show_null = false) 945 { 946 $sign = ($tz_offset < 0) ? '-' : '+'; 947 $time_offset = abs($tz_offset); 948 949 if ($time_offset == 0 && $show_null == false) 950 { 951 return ''; 952 } 953 954 $offset_seconds = $time_offset % 3600; 955 $offset_minutes = $offset_seconds / 60; 956 $offset_hours = ($time_offset - $offset_seconds) / 3600; 957 958 $offset_string = sprintf("%s%02d:%02d", $sign, $offset_hours, $offset_minutes); 959 return $offset_string; 960 } 961 962 /** 963 * Compares two time zone labels. 964 * Arranges them in increasing order by timezone offset. 965 * Places UTC before other timezones in the same offset. 966 */ 967 function phpbb_tz_select_compare($a, $b) 968 { 969 $a_sign = $a[3]; 970 $b_sign = $b[3]; 971 if ($a_sign != $b_sign) 972 { 973 return $a_sign == '-' ? -1 : 1; 974 } 975 976 $a_offset = substr($a, 4, 5); 977 $b_offset = substr($b, 4, 5); 978 if ($a_offset == $b_offset) 979 { 980 $a_name = substr($a, 12); 981 $b_name = substr($b, 12); 982 if ($a_name == $b_name) 983 { 984 return 0; 985 } 986 else if ($a_name == 'UTC') 987 { 988 return -1; 989 } 990 else if ($b_name == 'UTC') 991 { 992 return 1; 993 } 994 else 995 { 996 return $a_name < $b_name ? -1 : 1; 997 } 998 } 999 else 1000 { 1001 if ($a_sign == '-') 1002 { 1003 return $a_offset > $b_offset ? -1 : 1; 1004 } 1005 else 1006 { 1007 return $a_offset < $b_offset ? -1 : 1; 1008 } 1009 } 1010 } 1011 1012 /** 1013 * Return list of timezone identifiers 1014 * We also add the selected timezone if we can create an object with it. 1015 * DateTimeZone::listIdentifiers seems to not add all identifiers to the list, 1016 * because some are only kept for backward compatible reasons. If the user has 1017 * a deprecated value, we add it here, so it can still be kept. Once the user 1018 * changed his value, there is no way back to deprecated values. 1019 * 1020 * @param string $selected_timezone Additional timezone that shall 1021 * be added to the list of identiers 1022 * @return array DateTimeZone::listIdentifiers and additional 1023 * selected_timezone if it is a valid timezone. 1024 */ 1025 function phpbb_get_timezone_identifiers($selected_timezone) 1026 { 1027 $timezones = DateTimeZone::listIdentifiers(); 1028 1029 if (!in_array($selected_timezone, $timezones)) 1030 { 1031 try 1032 { 1033 // Add valid timezones that are currently selected but not returned 1034 // by DateTimeZone::listIdentifiers 1035 $validate_timezone = new DateTimeZone($selected_timezone); 1036 $timezones[] = $selected_timezone; 1037 } 1038 catch (\Exception $e) 1039 { 1040 } 1041 } 1042 1043 return $timezones; 1044 } 1045 1046 /** 1047 * Options to pick a timezone and date/time 1048 * 1049 * @param \phpbb\template\template $template phpBB template object 1050 * @param \phpbb\user $user Object of the current user 1051 * @param string $default A timezone to select 1052 * @param boolean $truncate Shall we truncate the options text 1053 * 1054 * @return array Returns an array containing the options for the time selector. 1055 */ 1056 function phpbb_timezone_select($template, $user, $default = '', $truncate = false) 1057 { 1058 static $timezones; 1059 1060 $default_offset = ''; 1061 if (!isset($timezones)) 1062 { 1063 $unsorted_timezones = phpbb_get_timezone_identifiers($default); 1064 1065 $timezones = array(); 1066 foreach ($unsorted_timezones as $timezone) 1067 { 1068 $tz = new DateTimeZone($timezone); 1069 $dt = $user->create_datetime('now', $tz); 1070 $offset = $dt->getOffset(); 1071 $current_time = $dt->format($user->lang['DATETIME_FORMAT'], true); 1072 $offset_string = phpbb_format_timezone_offset($offset, true); 1073 $timezones['UTC' . $offset_string . ' - ' . $timezone] = array( 1074 'tz' => $timezone, 1075 'offset' => $offset_string, 1076 'current' => $current_time, 1077 ); 1078 if ($timezone === $default) 1079 { 1080 $default_offset = 'UTC' . $offset_string; 1081 } 1082 } 1083 unset($unsorted_timezones); 1084 1085 uksort($timezones, 'phpbb_tz_select_compare'); 1086 } 1087 1088 $tz_select = $opt_group = ''; 1089 1090 foreach ($timezones as $key => $timezone) 1091 { 1092 if ($opt_group != $timezone['offset']) 1093 { 1094 // Generate tz_select for backwards compatibility 1095 $tz_select .= ($opt_group) ? '</optgroup>' : ''; 1096 $tz_select .= '<optgroup label="' . $user->lang(array('timezones', 'UTC_OFFSET_CURRENT'), $timezone['offset'], $timezone['current']) . '">'; 1097 $opt_group = $timezone['offset']; 1098 $template->assign_block_vars('timezone_select', array( 1099 'LABEL' => $user->lang(array('timezones', 'UTC_OFFSET_CURRENT'), $timezone['offset'], $timezone['current']), 1100 'VALUE' => $key . ' - ' . $timezone['current'], 1101 )); 1102 1103 $selected = (!empty($default_offset) && strpos($key, $default_offset) !== false) ? ' selected="selected"' : ''; 1104 $template->assign_block_vars('timezone_date', array( 1105 'VALUE' => $key . ' - ' . $timezone['current'], 1106 'SELECTED' => !empty($selected), 1107 'TITLE' => $user->lang(array('timezones', 'UTC_OFFSET_CURRENT'), $timezone['offset'], $timezone['current']), 1108 )); 1109 } 1110 1111 $label = $timezone['tz']; 1112 if (isset($user->lang['timezones'][$label])) 1113 { 1114 $label = $user->lang['timezones'][$label]; 1115 } 1116 $title = $user->lang(array('timezones', 'UTC_OFFSET_CURRENT'), $timezone['offset'], $label); 1117 1118 if ($truncate) 1119 { 1120 $label = truncate_string($label, 50, 255, false, '...'); 1121 } 1122 1123 // Also generate timezone_select for backwards compatibility 1124 $selected = ($timezone['tz'] === $default) ? ' selected="selected"' : ''; 1125 $tz_select .= '<option title="' . $title . '" value="' . $timezone['tz'] . '"' . $selected . '>' . $label . '</option>'; 1126 $template->assign_block_vars('timezone_select.timezone_options', array( 1127 'TITLE' => $title, 1128 'VALUE' => $timezone['tz'], 1129 'SELECTED' => !empty($selected), 1130 'LABEL' => $label, 1131 )); 1132 } 1133 $tz_select .= '</optgroup>'; 1134 1135 return $tz_select; 1136 } 1137 1138 // Functions handling topic/post tracking/marking 1139 1140 /** 1141 * Marks a topic/forum as read 1142 * Marks a topic as posted to 1143 * 1144 * @param string $mode (all, topics, topic, post) 1145 * @param int|bool $forum_id Used in all, topics, and topic mode 1146 * @param int|bool $topic_id Used in topic and post mode 1147 * @param int $post_time 0 means current time(), otherwise to set a specific mark time 1148 * @param int $user_id can only be used with $mode == 'post' 1149 */ 1150 function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $user_id = 0) 1151 { 1152 global $db, $user, $config; 1153 global $request, $phpbb_container, $phpbb_dispatcher; 1154 1155 $post_time = ($post_time === 0 || $post_time > time()) ? time() : (int) $post_time; 1156 1157 $should_markread = true; 1158 1159 /** 1160 * This event is used for performing actions directly before marking forums, 1161 * topics or posts as read. 1162 * 1163 * It is also possible to prevent the marking. For that, the $should_markread parameter 1164 * should be set to FALSE. 1165 * 1166 * @event core.markread_before 1167 * @var string mode Variable containing marking mode value 1168 * @var mixed forum_id Variable containing forum id, or false 1169 * @var mixed topic_id Variable containing topic id, or false 1170 * @var int post_time Variable containing post time 1171 * @var int user_id Variable containing the user id 1172 * @var bool should_markread Flag indicating if the markread should be done or not. 1173 * @since 3.1.4-RC1 1174 */ 1175 $vars = array( 1176 'mode', 1177 'forum_id', 1178 'topic_id', 1179 'post_time', 1180 'user_id', 1181 'should_markread', 1182 ); 1183 extract($phpbb_dispatcher->trigger_event('core.markread_before', compact($vars))); 1184 1185 if (!$should_markread) 1186 { 1187 return; 1188 } 1189 1190 if ($mode == 'all') 1191 { 1192 if ($forum_id === false || !sizeof($forum_id)) 1193 { 1194 // Mark all forums read (index page) 1195 1196 $phpbb_notifications = $phpbb_container->get('notification_manager'); 1197 1198 // Mark all topic notifications read for this user 1199 $phpbb_notifications->mark_notifications_read(array( 1200 'notification.type.topic', 1201 'notification.type.quote', 1202 'notification.type.bookmark', 1203 'notification.type.post', 1204 'notification.type.approve_topic', 1205 'notification.type.approve_post', 1206 ), false, $user->data['user_id'], $post_time); 1207 1208 if ($config['load_db_lastread'] && $user->data['is_registered']) 1209 { 1210 // Mark all forums read (index page) 1211 $tables = array(TOPICS_TRACK_TABLE, FORUMS_TRACK_TABLE); 1212 foreach ($tables as $table) 1213 { 1214 $sql = 'DELETE FROM ' . $table . " 1215 WHERE user_id = {$user->data['user_id']} 1216 AND mark_time < $post_time"; 1217 $db->sql_query($sql); 1218 } 1219 1220 $sql = 'UPDATE ' . USERS_TABLE . " 1221 SET user_lastmark = $post_time 1222 WHERE user_id = {$user->data['user_id']} 1223 AND user_lastmark < $post_time"; 1224 $db->sql_query($sql); 1225 } 1226 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 1227 { 1228 $tracking_topics = $request->variable($config['cookie_name'] . '_track', '', true, \phpbb\request\request_interface::COOKIE); 1229 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array(); 1230 1231 unset($tracking_topics['tf']); 1232 unset($tracking_topics['t']); 1233 unset($tracking_topics['f']); 1234 $tracking_topics['l'] = base_convert($post_time - $config['board_startdate'], 10, 36); 1235 1236 $user->set_cookie('track', tracking_serialize($tracking_topics), $post_time + 31536000); 1237 $request->overwrite($config['cookie_name'] . '_track', tracking_serialize($tracking_topics), \phpbb\request\request_interface::COOKIE); 1238 1239 unset($tracking_topics); 1240 1241 if ($user->data['is_registered']) 1242 { 1243 $sql = 'UPDATE ' . USERS_TABLE . " 1244 SET user_lastmark = $post_time 1245 WHERE user_id = {$user->data['user_id']} 1246 AND user_lastmark < $post_time"; 1247 $db->sql_query($sql); 1248 } 1249 } 1250 } 1251 1252 return; 1253 } 1254 else if ($mode == 'topics') 1255 { 1256 // Mark all topics in forums read 1257 if (!is_array($forum_id)) 1258 { 1259 $forum_id = array($forum_id); 1260 } 1261 else 1262 { 1263 $forum_id = array_unique($forum_id); 1264 } 1265 1266 $phpbb_notifications = $phpbb_container->get('notification_manager'); 1267 1268 $phpbb_notifications->mark_notifications_read_by_parent(array( 1269 'notification.type.topic', 1270 'notification.type.approve_topic', 1271 ), $forum_id, $user->data['user_id'], $post_time); 1272 1273 // Mark all post/quote notifications read for this user in this forum 1274 $topic_ids = array(); 1275 $sql = 'SELECT topic_id 1276 FROM ' . TOPICS_TABLE . ' 1277 WHERE ' . $db->sql_in_set('forum_id', $forum_id); 1278 $result = $db->sql_query($sql); 1279 while ($row = $db->sql_fetchrow($result)) 1280 { 1281 $topic_ids[] = $row['topic_id']; 1282 } 1283 $db->sql_freeresult($result); 1284 1285 $phpbb_notifications->mark_notifications_read_by_parent(array( 1286 'notification.type.quote', 1287 'notification.type.bookmark', 1288 'notification.type.post', 1289 'notification.type.approve_post', 1290 ), $topic_ids, $user->data['user_id'], $post_time); 1291 1292 // Add 0 to forums array to mark global announcements correctly 1293 // $forum_id[] = 0; 1294 1295 if ($config['load_db_lastread'] && $user->data['is_registered']) 1296 { 1297 $sql = 'DELETE FROM ' . TOPICS_TRACK_TABLE . " 1298 WHERE user_id = {$user->data['user_id']} 1299 AND mark_time < $post_time 1300 AND " . $db->sql_in_set('forum_id', $forum_id); 1301 $db->sql_query($sql); 1302 1303 $sql = 'SELECT forum_id 1304 FROM ' . FORUMS_TRACK_TABLE . " 1305 WHERE user_id = {$user->data['user_id']} 1306 AND " . $db->sql_in_set('forum_id', $forum_id); 1307 $result = $db->sql_query($sql); 1308 1309 $sql_update = array(); 1310 while ($row = $db->sql_fetchrow($result)) 1311 { 1312 $sql_update[] = (int) $row['forum_id']; 1313 } 1314 $db->sql_freeresult($result); 1315 1316 if (sizeof($sql_update)) 1317 { 1318 $sql = 'UPDATE ' . FORUMS_TRACK_TABLE . " 1319 SET mark_time = $post_time 1320 WHERE user_id = {$user->data['user_id']} 1321 AND mark_time < $post_time 1322 AND " . $db->sql_in_set('forum_id', $sql_update); 1323 $db->sql_query($sql); 1324 } 1325 1326 if ($sql_insert = array_diff($forum_id, $sql_update)) 1327 { 1328 $sql_ary = array(); 1329 foreach ($sql_insert as $f_id) 1330 { 1331 $sql_ary[] = array( 1332 'user_id' => (int) $user->data['user_id'], 1333 'forum_id' => (int) $f_id, 1334 'mark_time' => $post_time, 1335 ); 1336 } 1337 1338 $db->sql_multi_insert(FORUMS_TRACK_TABLE, $sql_ary); 1339 } 1340 } 1341 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 1342 { 1343 $tracking = $request->variable($config['cookie_name'] . '_track', '', true, \phpbb\request\request_interface::COOKIE); 1344 $tracking = ($tracking) ? tracking_unserialize($tracking) : array(); 1345 1346 foreach ($forum_id as $f_id) 1347 { 1348 $topic_ids36 = (isset($tracking['tf'][$f_id])) ? $tracking['tf'][$f_id] : array(); 1349 1350 if (isset($tracking['tf'][$f_id])) 1351 { 1352 unset($tracking['tf'][$f_id]); 1353 } 1354 1355 foreach ($topic_ids36 as $topic_id36) 1356 { 1357 unset($tracking['t'][$topic_id36]); 1358 } 1359 1360 if (isset($tracking['f'][$f_id])) 1361 { 1362 unset($tracking['f'][$f_id]); 1363 } 1364 1365 $tracking['f'][$f_id] = base_convert($post_time - $config['board_startdate'], 10, 36); 1366 } 1367 1368 if (isset($tracking['tf']) && empty($tracking['tf'])) 1369 { 1370 unset($tracking['tf']); 1371 } 1372 1373 $user->set_cookie('track', tracking_serialize($tracking), $post_time + 31536000); 1374 $request->overwrite($config['cookie_name'] . '_track', tracking_serialize($tracking), \phpbb\request\request_interface::COOKIE); 1375 1376 unset($tracking); 1377 } 1378 1379 return; 1380 } 1381 else if ($mode == 'topic') 1382 { 1383 if ($topic_id === false || $forum_id === false) 1384 { 1385 return; 1386 } 1387 1388 $phpbb_notifications = $phpbb_container->get('notification_manager'); 1389 1390 // Mark post notifications read for this user in this topic 1391 $phpbb_notifications->mark_notifications_read(array( 1392 'notification.type.topic', 1393 'notification.type.approve_topic', 1394 ), $topic_id, $user->data['user_id'], $post_time); 1395 1396 $phpbb_notifications->mark_notifications_read_by_parent(array( 1397 'notification.type.quote', 1398 'notification.type.bookmark', 1399 'notification.type.post', 1400 'notification.type.approve_post', 1401 ), $topic_id, $user->data['user_id'], $post_time); 1402 1403 if ($config['load_db_lastread'] && $user->data['is_registered']) 1404 { 1405 $sql = 'UPDATE ' . TOPICS_TRACK_TABLE . " 1406 SET mark_time = $post_time 1407 WHERE user_id = {$user->data['user_id']} 1408 AND mark_time < $post_time 1409 AND topic_id = $topic_id"; 1410 $db->sql_query($sql); 1411 1412 // insert row 1413 if (!$db->sql_affectedrows()) 1414 { 1415 $db->sql_return_on_error(true); 1416 1417 $sql_ary = array( 1418 'user_id' => (int) $user->data['user_id'], 1419 'topic_id' => (int) $topic_id, 1420 'forum_id' => (int) $forum_id, 1421 'mark_time' => $post_time, 1422 ); 1423 1424 $db->sql_query('INSERT INTO ' . TOPICS_TRACK_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); 1425 1426 $db->sql_return_on_error(false); 1427 } 1428 } 1429 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 1430 { 1431 $tracking = $request->variable($config['cookie_name'] . '_track', '', true, \phpbb\request\request_interface::COOKIE); 1432 $tracking = ($tracking) ? tracking_unserialize($tracking) : array(); 1433 1434 $topic_id36 = base_convert($topic_id, 10, 36); 1435 1436 if (!isset($tracking['t'][$topic_id36])) 1437 { 1438 $tracking['tf'][$forum_id][$topic_id36] = true; 1439 } 1440 1441 $tracking['t'][$topic_id36] = base_convert($post_time - $config['board_startdate'], 10, 36); 1442 1443 // If the cookie grows larger than 10000 characters we will remove the smallest value 1444 // This can result in old topics being unread - but most of the time it should be accurate... 1445 if (strlen($request->variable($config['cookie_name'] . '_track', '', true, \phpbb\request\request_interface::COOKIE)) > 10000) 1446 { 1447 //echo 'Cookie grown too large' . print_r($tracking, true); 1448 1449 // We get the ten most minimum stored time offsets and its associated topic ids 1450 $time_keys = array(); 1451 for ($i = 0; $i < 10 && sizeof($tracking['t']); $i++) 1452 { 1453 $min_value = min($tracking['t']); 1454 $m_tkey = array_search($min_value, $tracking['t']); 1455 unset($tracking['t'][$m_tkey]); 1456 1457 $time_keys[$m_tkey] = $min_value; 1458 } 1459 1460 // Now remove the topic ids from the array... 1461 foreach ($tracking['tf'] as $f_id => $topic_id_ary) 1462 { 1463 foreach ($time_keys as $m_tkey => $min_value) 1464 { 1465 if (isset($topic_id_ary[$m_tkey])) 1466 { 1467 $tracking['f'][$f_id] = $min_value; 1468 unset($tracking['tf'][$f_id][$m_tkey]); 1469 } 1470 } 1471 } 1472 1473 if ($user->data['is_registered']) 1474 { 1475 $user->data['user_lastmark'] = intval(base_convert(max($time_keys) + $config['board_startdate'], 36, 10)); 1476 1477 $sql = 'UPDATE ' . USERS_TABLE . " 1478 SET user_lastmark = $post_time 1479 WHERE user_id = {$user->data['user_id']} 1480 AND mark_time < $post_time"; 1481 $db->sql_query($sql); 1482 } 1483 else 1484 { 1485 $tracking['l'] = max($time_keys); 1486 } 1487 } 1488 1489 $user->set_cookie('track', tracking_serialize($tracking), $post_time + 31536000); 1490 $request->overwrite($config['cookie_name'] . '_track', tracking_serialize($tracking), \phpbb\request\request_interface::COOKIE); 1491 } 1492 1493 return; 1494 } 1495 else if ($mode == 'post') 1496 { 1497 if ($topic_id === false) 1498 { 1499 return; 1500 } 1501 1502 $use_user_id = (!$user_id) ? $user->data['user_id'] : $user_id; 1503 1504 if ($config['load_db_track'] && $use_user_id != ANONYMOUS) 1505 { 1506 $db->sql_return_on_error(true); 1507 1508 $sql_ary = array( 1509 'user_id' => (int) $use_user_id, 1510 'topic_id' => (int) $topic_id, 1511 'topic_posted' => 1, 1512 ); 1513 1514 $db->sql_query('INSERT INTO ' . TOPICS_POSTED_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); 1515 1516 $db->sql_return_on_error(false); 1517 } 1518 1519 return; 1520 } 1521 } 1522 1523 /** 1524 * Get topic tracking info by using already fetched info 1525 */ 1526 function get_topic_tracking($forum_id, $topic_ids, &$rowset, $forum_mark_time, $global_announce_list = false) 1527 { 1528 global $config, $user; 1529 1530 $last_read = array(); 1531 1532 if (!is_array($topic_ids)) 1533 { 1534 $topic_ids = array($topic_ids); 1535 } 1536 1537 foreach ($topic_ids as $topic_id) 1538 { 1539 if (!empty($rowset[$topic_id]['mark_time'])) 1540 { 1541 $last_read[$topic_id] = $rowset[$topic_id]['mark_time']; 1542 } 1543 } 1544 1545 $topic_ids = array_diff($topic_ids, array_keys($last_read)); 1546 1547 if (sizeof($topic_ids)) 1548 { 1549 $mark_time = array(); 1550 1551 if (!empty($forum_mark_time[$forum_id]) && $forum_mark_time[$forum_id] !== false) 1552 { 1553 $mark_time[$forum_id] = $forum_mark_time[$forum_id]; 1554 } 1555 1556 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark']; 1557 1558 foreach ($topic_ids as $topic_id) 1559 { 1560 $last_read[$topic_id] = $user_lastmark; 1561 } 1562 } 1563 1564 return $last_read; 1565 } 1566 1567 /** 1568 * Get topic tracking info from db (for cookie based tracking only this function is used) 1569 */ 1570 function get_complete_topic_tracking($forum_id, $topic_ids, $global_announce_list = false) 1571 { 1572 global $config, $user, $request; 1573 1574 $last_read = array(); 1575 1576 if (!is_array($topic_ids)) 1577 { 1578 $topic_ids = array($topic_ids); 1579 } 1580 1581 if ($config['load_db_lastread'] && $user->data['is_registered']) 1582 { 1583 global $db; 1584 1585 $sql = 'SELECT topic_id, mark_time 1586 FROM ' . TOPICS_TRACK_TABLE . " 1587 WHERE user_id = {$user->data['user_id']} 1588 AND " . $db->sql_in_set('topic_id', $topic_ids); 1589 $result = $db->sql_query($sql); 1590 1591 while ($row = $db->sql_fetchrow($result)) 1592 { 1593 $last_read[$row['topic_id']] = $row['mark_time']; 1594 } 1595 $db->sql_freeresult($result); 1596 1597 $topic_ids = array_diff($topic_ids, array_keys($last_read)); 1598 1599 if (sizeof($topic_ids)) 1600 { 1601 $sql = 'SELECT forum_id, mark_time 1602 FROM ' . FORUMS_TRACK_TABLE . " 1603 WHERE user_id = {$user->data['user_id']} 1604 AND forum_id = $forum_id"; 1605 $result = $db->sql_query($sql); 1606 1607 $mark_time = array(); 1608 while ($row = $db->sql_fetchrow($result)) 1609 { 1610 $mark_time[$row['forum_id']] = $row['mark_time']; 1611 } 1612 $db->sql_freeresult($result); 1613 1614 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark']; 1615 1616 foreach ($topic_ids as $topic_id) 1617 { 1618 $last_read[$topic_id] = $user_lastmark; 1619 } 1620 } 1621 } 1622 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 1623 { 1624 global $tracking_topics; 1625 1626 if (!isset($tracking_topics) || !sizeof($tracking_topics)) 1627 { 1628 $tracking_topics = $request->variable($config['cookie_name'] . '_track', '', true, \phpbb\request\request_interface::COOKIE); 1629 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array(); 1630 } 1631 1632 if (!$user->data['is_registered']) 1633 { 1634 $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0; 1635 } 1636 else 1637 { 1638 $user_lastmark = $user->data['user_lastmark']; 1639 } 1640 1641 foreach ($topic_ids as $topic_id) 1642 { 1643 $topic_id36 = base_convert($topic_id, 10, 36); 1644 1645 if (isset($tracking_topics['t'][$topic_id36])) 1646 { 1647 $last_read[$topic_id] = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate']; 1648 } 1649 } 1650 1651 $topic_ids = array_diff($topic_ids, array_keys($last_read)); 1652 1653 if (sizeof($topic_ids)) 1654 { 1655 $mark_time = array(); 1656 1657 if (isset($tracking_topics['f'][$forum_id])) 1658 { 1659 $mark_time[$forum_id] = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate']; 1660 } 1661 1662 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user_lastmark; 1663 1664 foreach ($topic_ids as $topic_id) 1665 { 1666 $last_read[$topic_id] = $user_lastmark; 1667 } 1668 } 1669 } 1670 1671 return $last_read; 1672 } 1673 1674 /** 1675 * Get list of unread topics 1676 * 1677 * @param int $user_id User ID (or false for current user) 1678 * @param string $sql_extra Extra WHERE SQL statement 1679 * @param string $sql_sort ORDER BY SQL sorting statement 1680 * @param string $sql_limit Limits the size of unread topics list, 0 for unlimited query 1681 * @param string $sql_limit_offset Sets the offset of the first row to search, 0 to search from the start 1682 * 1683 * @return array[int][int] Topic ids as keys, mark_time of topic as value 1684 */ 1685 function get_unread_topics($user_id = false, $sql_extra = '', $sql_sort = '', $sql_limit = 1001, $sql_limit_offset = 0) 1686 { 1687 global $config, $db, $user; 1688 global $phpbb_dispatcher; 1689 1690 $user_id = ($user_id === false) ? (int) $user->data['user_id'] : (int) $user_id; 1691 1692 // Data array we're going to return 1693 $unread_topics = array(); 1694 1695 if (empty($sql_sort)) 1696 { 1697 $sql_sort = 'ORDER BY t.topic_last_post_time DESC, t.topic_last_post_id DESC'; 1698 } 1699 1700 if ($config['load_db_lastread'] && $user->data['is_registered']) 1701 { 1702 // Get list of the unread topics 1703 $last_mark = (int) $user->data['user_lastmark']; 1704 1705 $sql_array = array( 1706 'SELECT' => 't.topic_id, t.topic_last_post_time, tt.mark_time as topic_mark_time, ft.mark_time as forum_mark_time', 1707 1708 'FROM' => array(TOPICS_TABLE => 't'), 1709 1710 'LEFT_JOIN' => array( 1711 array( 1712 'FROM' => array(TOPICS_TRACK_TABLE => 'tt'), 1713 'ON' => "tt.user_id = $user_id AND t.topic_id = tt.topic_id", 1714 ), 1715 array( 1716 'FROM' => array(FORUMS_TRACK_TABLE => 'ft'), 1717 'ON' => "ft.user_id = $user_id AND t.forum_id = ft.forum_id", 1718 ), 1719 ), 1720 1721 'WHERE' => " 1722 t.topic_last_post_time > $last_mark AND 1723 ( 1724 (tt.mark_time IS NOT NULL AND t.topic_last_post_time > tt.mark_time) OR 1725 (tt.mark_time IS NULL AND ft.mark_time IS NOT NULL AND t.topic_last_post_time > ft.mark_time) OR 1726 (tt.mark_time IS NULL AND ft.mark_time IS NULL) 1727 ) 1728 $sql_extra 1729 $sql_sort", 1730 ); 1731 1732 /** 1733 * Change SQL query for fetching unread topics data 1734 * 1735 * @event core.get_unread_topics_modify_sql 1736 * @var array sql_array Fully assembled SQL query with keys SELECT, FROM, LEFT_JOIN, WHERE 1737 * @var int last_mark User's last_mark time 1738 * @var string sql_extra Extra WHERE SQL statement 1739 * @var string sql_sort ORDER BY SQL sorting statement 1740 * @since 3.1.4-RC1 1741 */ 1742 $vars = array( 1743 'sql_array', 1744 'last_mark', 1745 'sql_extra', 1746 'sql_sort', 1747 ); 1748 extract($phpbb_dispatcher->trigger_event('core.get_unread_topics_modify_sql', compact($vars))); 1749 1750 $sql = $db->sql_build_query('SELECT', $sql_array); 1751 $result = $db->sql_query_limit($sql, $sql_limit, $sql_limit_offset); 1752 1753 while ($row = $db->sql_fetchrow($result)) 1754 { 1755 $topic_id = (int) $row['topic_id']; 1756 $unread_topics[$topic_id] = ($row['topic_mark_time']) ? (int) $row['topic_mark_time'] : (($row['forum_mark_time']) ? (int) $row['forum_mark_time'] : $last_mark); 1757 } 1758 $db->sql_freeresult($result); 1759 } 1760 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 1761 { 1762 global $tracking_topics; 1763 1764 if (empty($tracking_topics)) 1765 { 1766 $tracking_topics = request_var($config['cookie_name'] . '_track', '', false, true); 1767 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array(); 1768 } 1769 1770 if (!$user->data['is_registered']) 1771 { 1772 $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0; 1773 } 1774 else 1775 { 1776 $user_lastmark = (int) $user->data['user_lastmark']; 1777 } 1778 1779 $sql = 'SELECT t.topic_id, t.forum_id, t.topic_last_post_time 1780 FROM ' . TOPICS_TABLE . ' t 1781 WHERE t.topic_last_post_time > ' . $user_lastmark . " 1782 $sql_extra 1783 $sql_sort"; 1784 $result = $db->sql_query_limit($sql, $sql_limit, $sql_limit_offset); 1785 1786 while ($row = $db->sql_fetchrow($result)) 1787 { 1788 $forum_id = (int) $row['forum_id']; 1789 $topic_id = (int) $row['topic_id']; 1790 $topic_id36 = base_convert($topic_id, 10, 36); 1791 1792 if (isset($tracking_topics['t'][$topic_id36])) 1793 { 1794 $last_read = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate']; 1795 1796 if ($row['topic_last_post_time'] > $last_read) 1797 { 1798 $unread_topics[$topic_id] = $last_read; 1799 } 1800 } 1801 else if (isset($tracking_topics['f'][$forum_id])) 1802 { 1803 $mark_time = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate']; 1804 1805 if ($row['topic_last_post_time'] > $mark_time) 1806 { 1807 $unread_topics[$topic_id] = $mark_time; 1808 } 1809 } 1810 else 1811 { 1812 $unread_topics[$topic_id] = $user_lastmark; 1813 } 1814 } 1815 $db->sql_freeresult($result); 1816 } 1817 1818 return $unread_topics; 1819 } 1820 1821 /** 1822 * Check for read forums and update topic tracking info accordingly 1823 * 1824 * @param int $forum_id the forum id to check 1825 * @param int $forum_last_post_time the forums last post time 1826 * @param int $f_mark_time the forums last mark time if user is registered and load_db_lastread enabled 1827 * @param int $mark_time_forum false if the mark time needs to be obtained, else the last users forum mark time 1828 * 1829 * @return true if complete forum got marked read, else false. 1830 */ 1831 function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_time = false, $mark_time_forum = false) 1832 { 1833 global $db, $tracking_topics, $user, $config, $auth, $request, $phpbb_container; 1834 1835 // Determine the users last forum mark time if not given. 1836 if ($mark_time_forum === false) 1837 { 1838 if ($config['load_db_lastread'] && $user->data['is_registered']) 1839 { 1840 $mark_time_forum = (!empty($f_mark_time)) ? $f_mark_time : $user->data['user_lastmark']; 1841 } 1842 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 1843 { 1844 $tracking_topics = $request->variable($config['cookie_name'] . '_track', '', true, \phpbb\request\request_interface::COOKIE); 1845 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array(); 1846 1847 if (!$user->data['is_registered']) 1848 { 1849 $user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0; 1850 } 1851 1852 $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']; 1853 } 1854 } 1855 1856 // Handle update of unapproved topics info. 1857 // Only update for moderators having m_approve permission for the forum. 1858 $phpbb_content_visibility = $phpbb_container->get('content.visibility'); 1859 1860 // Check the forum for any left unread topics. 1861 // If there are none, we mark the forum as read. 1862 if ($config['load_db_lastread'] && $user->data['is_registered']) 1863 { 1864 if ($mark_time_forum >= $forum_last_post_time) 1865 { 1866 // We do not need to mark read, this happened before. Therefore setting this to true 1867 $row = true; 1868 } 1869 else 1870 { 1871 $sql = 'SELECT t.forum_id 1872 FROM ' . TOPICS_TABLE . ' t 1873 LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt 1874 ON (tt.topic_id = t.topic_id 1875 AND tt.user_id = ' . $user->data['user_id'] . ') 1876 WHERE t.forum_id = ' . $forum_id . ' 1877 AND t.topic_last_post_time > ' . $mark_time_forum . ' 1878 AND t.topic_moved_id = 0 1879 AND ' . $phpbb_content_visibility->get_visibility_sql('topic', $forum_id, 't.') . ' 1880 AND (tt.topic_id IS NULL 1881 OR tt.mark_time < t.topic_last_post_time)'; 1882 $result = $db->sql_query_limit($sql, 1); 1883 $row = $db->sql_fetchrow($result); 1884 $db->sql_freeresult($result); 1885 } 1886 } 1887 else if ($config['load_anon_lastread'] || $user->data['is_registered']) 1888 { 1889 // Get information from cookie 1890 $row = false; 1891 1892 if (!isset($tracking_topics['tf'][$forum_id])) 1893 { 1894 // We do not need to mark read, this happened before. Therefore setting this to true 1895 $row = true; 1896 } 1897 else 1898 { 1899 $sql = 'SELECT t.topic_id 1900 FROM ' . TOPICS_TABLE . ' t 1901 WHERE t.forum_id = ' . $forum_id . ' 1902 AND t.topic_last_post_time > ' . $mark_time_forum . ' 1903 AND t.topic_moved_id = 0 1904 AND ' . $phpbb_content_visibility->get_visibility_sql('topic', $forum_id, 't.'); 1905 $result = $db->sql_query($sql); 1906 1907 $check_forum = $tracking_topics['tf'][$forum_id]; 1908 $unread = false; 1909 1910 while ($row = $db->sql_fetchrow($result)) 1911 { 1912 if (!isset($check_forum[base_convert($row['topic_id'], 10, 36)])) 1913 { 1914 $unread = true; 1915 break; 1916 } 1917 } 1918 $db->sql_freeresult($result); 1919 1920 $row = $unread; 1921 } 1922 } 1923 else 1924 { 1925 $row = true; 1926 } 1927 1928 if (!$row) 1929 { 1930 markread('topics', $forum_id); 1931 return true; 1932 } 1933 1934 return false; 1935 } 1936 1937 /** 1938 * Transform an array into a serialized format 1939 */ 1940 function tracking_serialize($input) 1941 { 1942 $out = ''; 1943 foreach ($input as $key => $value) 1944 { 1945 if (is_array($value)) 1946 { 1947 $out .= $key . ':(' . tracking_serialize($value) . ');'; 1948 } 1949 else 1950 { 1951 $out .= $key . ':' . $value . ';'; 1952 } 1953 } 1954 return $out; 1955 } 1956 1957 /** 1958 * Transform a serialized array into an actual array 1959 */ 1960 function tracking_unserialize($string, $max_depth = 3) 1961 { 1962 $n = strlen($string); 1963 if ($n > 10010) 1964 { 1965 die('Invalid data supplied'); 1966 } 1967 $data = $stack = array(); 1968 $key = ''; 1969 $mode = 0; 1970 $level = &$data; 1971 for ($i = 0; $i < $n; ++$i) 1972 { 1973 switch ($mode) 1974 { 1975 case 0: 1976 switch ($string[$i]) 1977 { 1978 case ':': 1979 $level[$key] = 0; 1980 $mode = 1; 1981 break; 1982 case ')': 1983 unset($level); 1984 $level = array_pop($stack); 1985 $mode = 3; 1986 break; 1987 default: 1988 $key .= $string[$i]; 1989 } 1990 break; 1991 1992 case 1: 1993 switch ($string[$i]) 1994 { 1995 case '(': 1996 if (sizeof($stack) >= $max_depth) 1997 { 1998 die('Invalid data supplied'); 1999 } 2000 $stack[] = &$level; 2001 $level[$key] = array(); 2002 $level = &$level[$key]; 2003 $key = ''; 2004 $mode = 0; 2005 break; 2006 default: 2007 $level[$key] = $string[$i]; 2008 $mode = 2; 2009 break; 2010 } 2011 break; 2012 2013 case 2: 2014 switch ($string[$i]) 2015 { 2016 case ')': 2017 unset($level); 2018 $level = array_pop($stack); 2019 $mode = 3; 2020 break; 2021 case ';': 2022 $key = ''; 2023 $mode = 0; 2024 break; 2025 default: 2026 $level[$key] .= $string[$i]; 2027 break; 2028 } 2029 break; 2030 2031 case 3: 2032 switch ($string[$i]) 2033 { 2034 case ')': 2035 unset($level); 2036 $level = array_pop($stack); 2037 break; 2038 case ';': 2039 $key = ''; 2040 $mode = 0; 2041 break; 2042 default: 2043 die('Invalid data supplied'); 2044 break; 2045 } 2046 break; 2047 } 2048 } 2049 2050 if (sizeof($stack) != 0 || ($mode != 0 && $mode != 3)) 2051 { 2052 die('Invalid data supplied'); 2053 } 2054 2055 return $level; 2056 } 2057 2058 // Server functions (building urls, redirecting...) 2059 2060 /** 2061 * Append session id to url. 2062 * This function supports hooks. 2063 * 2064 * @param string $url The url the session id needs to be appended to (can have params) 2065 * @param mixed $params String or array of additional url parameters 2066 * @param bool $is_amp Is url using & (true) or & (false) 2067 * @param string $session_id Possibility to use a custom session id instead of the global one 2068 * @param bool $is_route Is url generated by a route. 2069 * 2070 * @return string The corrected url. 2071 * 2072 * Examples: 2073 * <code> 2074 * append_sid("{$phpbb_root_path}viewtopic.$phpEx?t=1&f=2"); 2075 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2'); 2076 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2', false); 2077 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", array('t' => 1, 'f' => 2)); 2078 * </code> 2079 * 2080 */ 2081 function append_sid($url, $params = false, $is_amp = true, $session_id = false, $is_route = false) 2082 { 2083 global $_SID, $_EXTRA_URL, $phpbb_hook, $phpbb_path_helper; 2084 global $phpbb_dispatcher; 2085 2086 if ($params === '' || (is_array($params) && empty($params))) 2087 { 2088 // Do not append the ? if the param-list is empty anyway. 2089 $params = false; 2090 } 2091 2092 // Update the root path with the correct relative web path 2093 if (!$is_route && $phpbb_path_helper instanceof \phpbb\path_helper) 2094 { 2095 $url = $phpbb_path_helper->update_web_root_path($url); 2096 } 2097 2098 $append_sid_overwrite = false; 2099 2100 /** 2101 * This event can either supplement or override the append_sid() function 2102 * 2103 * To override this function, the event must set $append_sid_overwrite to 2104 * the new URL value, which will be returned following the event 2105 * 2106 * @event core.append_sid 2107 * @var string url The url the session id needs 2108 * to be appended to (can have 2109 * params) 2110 * @var mixed params String or array of additional 2111 * url parameters 2112 * @var bool is_amp Is url using & (true) or 2113 * & (false) 2114 * @var bool|string session_id Possibility to use a custom 2115 * session id (string) instead of 2116 * the global one (false) 2117 * @var bool|string append_sid_overwrite Overwrite function (string 2118 * URL) or not (false) 2119 * @var bool is_route Is url generated by a route. 2120 * @since 3.1.0-a1 2121 */ 2122 $vars = array('url', 'params', 'is_amp', 'session_id', 'append_sid_overwrite', 'is_route'); 2123 extract($phpbb_dispatcher->trigger_event('core.append_sid', compact($vars))); 2124 2125 if ($append_sid_overwrite) 2126 { 2127 return $append_sid_overwrite; 2128 } 2129 2130 // The following hook remains for backwards compatibility, though use of 2131 // the event above is preferred. 2132 // Developers using the hook function need to globalise the $_SID and $_EXTRA_URL on their own and also handle it appropriately. 2133 // They could mimic most of what is within this function 2134 if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__, $url, $params, $is_amp, $session_id)) 2135 { 2136 if ($phpbb_hook->hook_return(__FUNCTION__)) 2137 { 2138 return $phpbb_hook->hook_return_result(__FUNCTION__); 2139 } 2140 } 2141 2142 $params_is_array = is_array($params); 2143 2144 // Get anchor 2145 $anchor = ''; 2146 if (strpos($url, '#') !== false) 2147 { 2148 list($url, $anchor) = explode('#', $url, 2); 2149 $anchor = '#' . $anchor; 2150 } 2151 else if (!$params_is_array && strpos($params, '#') !== false) 2152 { 2153 list($params, $anchor) = explode('#', $params, 2); 2154 $anchor = '#' . $anchor; 2155 } 2156 2157 // Handle really simple cases quickly 2158 if ($_SID == '' && $session_id === false && empty($_EXTRA_URL) && !$params_is_array && !$anchor) 2159 { 2160 if ($params === false) 2161 { 2162 return $url; 2163 } 2164 2165 $url_delim = (strpos($url, '?') === false) ? '?' : (($is_amp) ? '&' : '&'); 2166 return $url . ($params !== false ? $url_delim. $params : ''); 2167 } 2168 2169 // Assign sid if session id is not specified 2170 if ($session_id === false) 2171 { 2172 $session_id = $_SID; 2173 } 2174 2175 $amp_delim = ($is_amp) ? '&' : '&'; 2176 $url_delim = (strpos($url, '?') === false) ? '?' : $amp_delim; 2177 2178 // Appending custom url parameter? 2179 $append_url = (!empty($_EXTRA_URL)) ? implode($amp_delim, $_EXTRA_URL) : ''; 2180 2181 // Use the short variant if possible ;) 2182 if ($params === false) 2183 { 2184 // Append session id 2185 if (!$session_id) 2186 { 2187 return $url . (($append_url) ? $url_delim . $append_url : '') . $anchor; 2188 } 2189 else 2190 { 2191 return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . 'sid=' . $session_id . $anchor; 2192 } 2193 } 2194 2195 // Build string if parameters are specified as array 2196 if (is_array($params)) 2197 { 2198 $output = array(); 2199 2200 foreach ($params as $key => $item) 2201 { 2202 if ($item === NULL) 2203 { 2204 continue; 2205 } 2206 2207 if ($key == '#') 2208 { 2209 $anchor = '#' . $item; 2210 continue; 2211 } 2212 2213 $output[] = $key . '=' . $item; 2214 } 2215 2216 $params = implode($amp_delim, $output); 2217 } 2218 2219 // Append session id and parameters (even if they are empty) 2220 // If parameters are empty, the developer can still append his/her parameters without caring about the delimiter 2221 return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . $params . ((!$session_id) ? '' : $amp_delim . 'sid=' . $session_id) . $anchor; 2222 } 2223 2224 /** 2225 * Generate board url (example: http://www.example.com/phpBB) 2226 * 2227 * @param bool $without_script_path if set to true the script path gets not appended (example: http://www.example.com) 2228 * 2229 * @return string the generated board url 2230 */ 2231 function generate_board_url($without_script_path = false) 2232 { 2233 global $config, $user, $request; 2234 2235 $server_name = $user->host; 2236 2237 // Forcing server vars is the only way to specify/override the protocol 2238 if ($config['force_server_vars'] || !$server_name) 2239 { 2240 $server_protocol = ($config['server_protocol']) ? $config['server_protocol'] : (($config['cookie_secure']) ? 'https://' : 'http://'); 2241 $server_name = $config['server_name']; 2242 $server_port = (int) $config['server_port']; 2243 $script_path = $config['script_path']; 2244 2245 $url = $server_protocol . $server_name; 2246 $cookie_secure = $config['cookie_secure']; 2247 } 2248 else 2249 { 2250 $server_port = $request->server('SERVER_PORT', 0); 2251 $forwarded_proto = $request->server('HTTP_X_FORWARDED_PROTO'); 2252 2253 if (!empty($forwarded_proto) && $forwarded_proto === 'https') 2254 { 2255 $server_port = 443; 2256 } 2257 // Do not rely on cookie_secure, users seem to think that it means a secured cookie instead of an encrypted connection 2258 $cookie_secure = $request->is_secure() ? 1 : 0; 2259 $url = (($cookie_secure) ? 'https://' : 'http://') . $server_name; 2260 2261 $script_path = $user->page['root_script_path']; 2262 } 2263 2264 if ($server_port && (($cookie_secure && $server_port <> 443) || (!$cookie_secure && $server_port <> 80))) 2265 { 2266 // HTTP HOST can carry a port number (we fetch $user->host, but for old versions this may be true) 2267 if (strpos($server_name, ':') === false) 2268 { 2269 $url .= ':' . $server_port; 2270 } 2271 } 2272 2273 if (!$without_script_path) 2274 { 2275 $url .= $script_path; 2276 } 2277 2278 // Strip / from the end 2279 if (substr($url, -1, 1) == '/') 2280 { 2281 $url = substr($url, 0, -1); 2282 } 2283 2284 return $url; 2285 } 2286 2287 /** 2288 * Redirects the user to another page then exits the script nicely 2289 * This function is intended for urls within the board. It's not meant to redirect to cross-domains. 2290 * 2291 * @param string $url The url to redirect to 2292 * @param bool $return If true, do not redirect but return the sanitized URL. Default is no return. 2293 * @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. 2294 */ 2295 function redirect($url, $return = false, $disable_cd_check = false) 2296 { 2297 global $db, $cache, $config, $user, $phpbb_root_path, $phpbb_filesystem, $phpbb_path_helper, $phpEx, $phpbb_dispatcher; 2298 2299 $failover_flag = false; 2300 2301 if (empty($user->lang)) 2302 { 2303 $user->add_lang('common'); 2304 } 2305 2306 // Make sure no &'s are in, this will break the redirect 2307 $url = str_replace('&', '&', $url); 2308 2309 // Determine which type of redirect we need to handle... 2310 $url_parts = @parse_url($url); 2311 2312 if ($url_parts === false) 2313 { 2314 // Malformed url, redirect to current page... 2315 $url = generate_board_url() . '/' . $user->page['page']; 2316 } 2317 else if (!empty($url_parts['scheme']) && !empty($url_parts['host'])) 2318 { 2319 // Attention: only able to redirect within the same domain if $disable_cd_check is false (yourdomain.com -> www.yourdomain.com will not work) 2320 if (!$disable_cd_check && $url_parts['host'] !== $user->host) 2321 { 2322 trigger_error('INSECURE_REDIRECT', E_USER_ERROR); 2323 } 2324 } 2325 else if ($url[0] == '/') 2326 { 2327 // Absolute uri, prepend direct url... 2328 $url = generate_board_url(true) . $url; 2329 } 2330 else 2331 { 2332 // Relative uri 2333 $pathinfo = pathinfo($url); 2334 2335 // Is the uri pointing to the current directory? 2336 if ($pathinfo['dirname'] == '.') 2337 { 2338 $url = str_replace('./', '', $url); 2339 2340 // Strip / from the beginning 2341 if ($url && substr($url, 0, 1) == '/') 2342 { 2343 $url = substr($url, 1); 2344 } 2345 } 2346 2347 $url = $phpbb_path_helper->remove_web_root_path($url); 2348 2349 if ($user->page['page_dir']) 2350 { 2351 $url = $user->page['page_dir'] . '/' . $url; 2352 } 2353 2354 $url = generate_board_url() . '/' . $url; 2355 } 2356 2357 // Clean URL and check if we go outside the forum directory 2358 $url = $phpbb_path_helper->clean_url($url); 2359 2360 if (!$disable_cd_check && strpos($url, generate_board_url(true) . '/') !== 0) 2361 { 2362 trigger_error('INSECURE_REDIRECT', E_USER_ERROR); 2363 } 2364 2365 // Make sure no linebreaks are there... to prevent http response splitting for PHP < 4.4.2 2366 if (strpos(urldecode($url), "\n") !== false || strpos(urldecode($url), "\r") !== false || strpos($url, ';') !== false) 2367 { 2368 trigger_error('INSECURE_REDIRECT', E_USER_ERROR); 2369 } 2370 2371 // Now, also check the protocol and for a valid url the last time... 2372 $allowed_protocols = array('http', 'https', 'ftp', 'ftps'); 2373 $url_parts = parse_url($url); 2374 2375 if ($url_parts === false || empty($url_parts['scheme']) || !in_array($url_parts['scheme'], $allowed_protocols)) 2376 { 2377 trigger_error('INSECURE_REDIRECT', E_USER_ERROR); 2378 } 2379 2380 /** 2381 * Execute code and/or overwrite redirect() 2382 * 2383 * @event core.functions.redirect 2384 * @var string url The url 2385 * @var bool return If true, do not redirect but return the sanitized URL. 2386 * @var 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. 2387 * @since 3.1.0-RC3 2388 */ 2389 $vars = array('url', 'return', 'disable_cd_check'); 2390 extract($phpbb_dispatcher->trigger_event('core.functions.redirect', compact($vars))); 2391 2392 if ($return) 2393 { 2394 return $url; 2395 } 2396 else 2397 { 2398 garbage_collection(); 2399 } 2400 2401 // Redirect via an HTML form for PITA webservers 2402 if (@preg_match('#WebSTAR|Xitami#', getenv('SERVER_SOFTWARE'))) 2403 { 2404 header('Refresh: 0; URL=' . $url); 2405 2406 echo '<!DOCTYPE html>'; 2407 echo '<html dir="' . $user->lang['DIRECTION'] . '" lang="' . $user->lang['USER_LANG'] . '">'; 2408 echo '<head>'; 2409 echo '<meta charset="utf-8">'; 2410 echo '<meta http-equiv="X-UA-Compatible" content="IE=edge">'; 2411 echo '<meta http-equiv="refresh" content="0; url=' . str_replace('&', '&', $url) . '" />'; 2412 echo '<title>' . $user->lang['REDIRECT'] . '</title>'; 2413 echo '</head>'; 2414 echo '<body>'; 2415 echo '<div style="text-align: center;">' . sprintf($user->lang['URL_REDIRECT'], '<a href="' . str_replace('&', '&', $url) . '">', '</a>') . '</div>'; 2416 echo '</body>'; 2417 echo '</html>'; 2418 2419 exit; 2420 } 2421 2422 // Behave as per HTTP/1.1 spec for others 2423 header('Location: ' . $url); 2424 exit; 2425 } 2426 2427 /** 2428 * Re-Apply session id after page reloads 2429 */ 2430 function reapply_sid($url) 2431 { 2432 global $phpEx, $phpbb_root_path; 2433 2434 if ($url === "index.$phpEx") 2435 { 2436 return append_sid("index.$phpEx"); 2437 } 2438 else if ($url === "{$phpbb_root_path}index.$phpEx") 2439 { 2440 return append_sid("{$phpbb_root_path}index.$phpEx"); 2441 } 2442 2443 // Remove previously added sid 2444 if (strpos($url, 'sid=') !== false) 2445 { 2446 // All kind of links 2447 $url = preg_replace('/(\?)?(&|&)?sid=[a-z0-9]+/', '', $url); 2448 // if the sid was the first param, make the old second as first ones 2449 $url = preg_replace("/$phpEx(&|&)+?/", "$phpEx?", $url); 2450 } 2451 2452 return append_sid($url); 2453 } 2454 2455 /** 2456 * Returns url from the session/current page with an re-appended SID with optionally stripping vars from the url 2457 */ 2458 function build_url($strip_vars = false) 2459 { 2460 global $config, $user, $phpbb_path_helper; 2461 2462 $page = $phpbb_path_helper->get_valid_page($user->page['page'], $config['enable_mod_rewrite']); 2463 2464 // Append SID 2465 $redirect = append_sid($page, false, false); 2466 2467 if ($strip_vars !== false) 2468 { 2469 $redirect = $phpbb_path_helper->strip_url_params($redirect, $strip_vars, false); 2470 } 2471 else 2472 { 2473 $redirect = str_replace('&', '&', $redirect); 2474 } 2475 2476 return $redirect . ((strpos($redirect, '?') === false) ? '?' : ''); 2477 } 2478 2479 /** 2480 * Meta refresh assignment 2481 * Adds META template variable with meta http tag. 2482 * 2483 * @param int $time Time in seconds for meta refresh tag 2484 * @param string $url URL to redirect to. The url will go through redirect() first before the template variable is assigned 2485 * @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. 2486 */ 2487 function meta_refresh($time, $url, $disable_cd_check = false) 2488 { 2489 global $template, $refresh_data, $request; 2490 2491 $url = redirect($url, true, $disable_cd_check); 2492 if ($request->is_ajax()) 2493 { 2494 $refresh_data = array( 2495 'time' => $time, 2496 'url' => $url, 2497 ); 2498 } 2499 else 2500 { 2501 // For XHTML compatibility we change back & to & 2502 $url = str_replace('&', '&', $url); 2503 2504 $template->assign_vars(array( 2505 'META' => '<meta http-equiv="refresh" content="' . $time . '; url=' . $url . '" />') 2506 ); 2507 } 2508 2509 return $url; 2510 } 2511 2512 /** 2513 * Outputs correct status line header. 2514 * 2515 * Depending on php sapi one of the two following forms is used: 2516 * 2517 * Status: 404 Not Found 2518 * 2519 * HTTP/1.x 404 Not Found 2520 * 2521 * HTTP version is taken from HTTP_VERSION environment variable, 2522 * and defaults to 1.0. 2523 * 2524 * Sample usage: 2525 * 2526 * send_status_line(404, 'Not Found'); 2527 * 2528 * @param int $code HTTP status code 2529 * @param string $message Message for the status code 2530 * @return null 2531 */ 2532 function send_status_line($code, $message) 2533 { 2534 if (substr(strtolower(@php_sapi_name()), 0, 3) === 'cgi') 2535 { 2536 // in theory, we shouldn't need that due to php doing it. Reality offers a differing opinion, though 2537 header("Status: $code $message", true, $code); 2538 } 2539 else 2540 { 2541 $version = phpbb_request_http_version(); 2542 header("$version $code $message", true, $code); 2543 } 2544 } 2545 2546 /** 2547 * Returns the HTTP version used in the current request. 2548 * 2549 * Handles the case of being called before $request is present, 2550 * in which case it falls back to the $_SERVER superglobal. 2551 * 2552 * @return string HTTP version 2553 */ 2554 function phpbb_request_http_version() 2555 { 2556 global $request; 2557 2558 $version = ''; 2559 if ($request && $request->server('SERVER_PROTOCOL')) 2560 { 2561 $version = $request->server('SERVER_PROTOCOL'); 2562 } 2563 else if (isset($_SERVER['SERVER_PROTOCOL'])) 2564 { 2565 $version = $_SERVER['SERVER_PROTOCOL']; 2566 } 2567 2568 if (!empty($version) && is_string($version) && preg_match('#^HTTP/[0-9]\.[0-9]$#', $version)) 2569 { 2570 return $version; 2571 } 2572 2573 return 'HTTP/1.0'; 2574 } 2575 2576 //Form validation 2577 2578 2579 /** 2580 * Add a secret hash for use in links/GET requests 2581 * @param string $link_name The name of the link; has to match the name used in check_link_hash, otherwise no restrictions apply 2582 * @return string the hash 2583 2584 */ 2585 function generate_link_hash($link_name) 2586 { 2587 global $user; 2588 2589 if (!isset($user->data["hash_$link_name"])) 2590 { 2591 $user->data["hash_$link_name"] = substr(sha1($user->data['user_form_salt'] . $link_name), 0, 8); 2592 } 2593 2594 return $user->data["hash_$link_name"]; 2595 } 2596 2597 2598 /** 2599 * checks a link hash - for GET requests 2600 * @param string $token the submitted token 2601 * @param string $link_name The name of the link 2602 * @return boolean true if all is fine 2603 */ 2604 function check_link_hash($token, $link_name) 2605 { 2606 return $token === generate_link_hash($link_name); 2607 } 2608 2609 /** 2610 * Add a secret token to the form (requires the S_FORM_TOKEN template variable) 2611 * @param string $form_name The name of the form; has to match the name used in check_form_key, otherwise no restrictions apply 2612 * @param string $template_variable_suffix A string that is appended to the name of the template variable to which the form elements are assigned 2613 */ 2614 function add_form_key($form_name, $template_variable_suffix = '') 2615 { 2616 global $config, $template, $user, $phpbb_dispatcher; 2617 2618 $now = time(); 2619 $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : ''; 2620 $token = sha1($now . $user->data['user_form_salt'] . $form_name . $token_sid); 2621 2622 $s_fields = build_hidden_fields(array( 2623 'creation_time' => $now, 2624 'form_token' => $token, 2625 )); 2626 2627 /** 2628 * Perform additional actions on creation of the form token 2629 * 2630 * @event core.add_form_key 2631 * @var string form_name The form name 2632 * @var int now Current time timestamp 2633 * @var string s_fields Generated hidden fields 2634 * @var string token Form token 2635 * @var string token_sid User session ID 2636 * @var string template_variable_suffix The string that is appended to template variable name 2637 * 2638 * @since 3.1.0-RC3 2639 * @changed 3.1.11-RC1 Added template_variable_suffix 2640 */ 2641 $vars = array( 2642 'form_name', 2643 'now', 2644 's_fields', 2645 'token', 2646 'token_sid', 2647 'template_variable_suffix', 2648 ); 2649 extract($phpbb_dispatcher->trigger_event('core.add_form_key', compact($vars))); 2650 2651 $template->assign_var('S_FORM_TOKEN' . $template_variable_suffix, $s_fields); 2652 } 2653 2654 /** 2655 * Check the form key. Required for all altering actions not secured by confirm_box 2656 * 2657 * @param string $form_name The name of the form; has to match the name used 2658 * in add_form_key, otherwise no restrictions apply 2659 * @param int $timespan The maximum acceptable age for a submitted form 2660 * in seconds. Defaults to the config setting. 2661 * @return bool True, if the form key was valid, false otherwise 2662 */ 2663 function check_form_key($form_name, $timespan = false) 2664 { 2665 global $config, $request, $user; 2666 2667 if ($timespan === false) 2668 { 2669 // we enforce a minimum value of half a minute here. 2670 $timespan = ($config['form_token_lifetime'] == -1) ? -1 : max(30, $config['form_token_lifetime']); 2671 } 2672 2673 if ($request->is_set_post('creation_time') && $request->is_set_post('form_token')) 2674 { 2675 $creation_time = abs($request->variable('creation_time', 0)); 2676 $token = $request->variable('form_token', ''); 2677 2678 $diff = time() - $creation_time; 2679 2680 // If creation_time and the time() now is zero we can assume it was not a human doing this (the check for if ($diff)... 2681 if (defined('DEBUG_TEST') || $diff && ($diff <= $timespan || $timespan === -1)) 2682 { 2683 $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : ''; 2684 $key = sha1($creation_time . $user->data['user_form_salt'] . $form_name . $token_sid); 2685 2686 if ($key === $token) 2687 { 2688 return true; 2689 } 2690 } 2691 } 2692 2693 return false; 2694 } 2695 2696 // Message/Login boxes 2697 2698 /** 2699 * Build Confirm box 2700 * @param boolean $check True for checking if confirmed (without any additional parameters) and false for displaying the confirm box 2701 * @param string $title Title/Message used for confirm box. 2702 * message text is _CONFIRM appended to title. 2703 * If title cannot be found in user->lang a default one is displayed 2704 * If title_CONFIRM cannot be found in user->lang the text given is used. 2705 * @param string $hidden Hidden variables 2706 * @param string $html_body Template used for confirm box 2707 * @param string $u_action Custom form action 2708 */ 2709 function confirm_box($check, $title = '', $hidden = '', $html_body = 'confirm_body.html', $u_action = '') 2710 { 2711 global $user, $template, $db, $request; 2712 global $config, $phpbb_path_helper; 2713 2714 if (isset($_POST['cancel'])) 2715 { 2716 return false; 2717 } 2718 2719 $confirm = ($user->lang['YES'] === $request->variable('confirm', '', true, \phpbb\request\request_interface::POST)); 2720 2721 if ($check && $confirm) 2722 { 2723 $user_id = request_var('confirm_uid', 0); 2724 $session_id = request_var('sess', ''); 2725 $confirm_key = request_var('confirm_key', ''); 2726 2727 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']) 2728 { 2729 return false; 2730 } 2731 2732 // Reset user_last_confirm_key 2733 $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = '' 2734 WHERE user_id = " . $user->data['user_id']; 2735 $db->sql_query($sql); 2736 2737 return true; 2738 } 2739 else if ($check) 2740 { 2741 return false; 2742 } 2743 2744 $s_hidden_fields = build_hidden_fields(array( 2745 'confirm_uid' => $user->data['user_id'], 2746 'sess' => $user->session_id, 2747 'sid' => $user->session_id, 2748 )); 2749 2750 // generate activation key 2751 $confirm_key = gen_rand_string(10); 2752 2753 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin']) 2754 { 2755 adm_page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]); 2756 } 2757 else 2758 { 2759 page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]); 2760 } 2761 2762 $template->set_filenames(array( 2763 'body' => $html_body) 2764 ); 2765 2766 // If activation key already exist, we better do not re-use the key (something very strange is going on...) 2767 if (request_var('confirm_key', '')) 2768 { 2769 // This should not occur, therefore we cancel the operation to safe the user 2770 return false; 2771 } 2772 2773 // re-add sid / transform & to & for user->page (user->page is always using &) 2774 $use_page = ($u_action) ? $u_action : str_replace('&', '&', $user->page['page']); 2775 $u_action = reapply_sid($phpbb_path_helper->get_valid_page($use_page, $config['enable_mod_rewrite'])); 2776 $u_action .= ((strpos($u_action, '?') === false) ? '?' : '&') . 'confirm_key=' . $confirm_key; 2777 2778 $template->assign_vars(array( 2779 'MESSAGE_TITLE' => (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang($title, 1), 2780 'MESSAGE_TEXT' => (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'], 2781 2782 'YES_VALUE' => $user->lang['YES'], 2783 'S_CONFIRM_ACTION' => $u_action, 2784 'S_HIDDEN_FIELDS' => $hidden . $s_hidden_fields, 2785 'S_AJAX_REQUEST' => $request->is_ajax(), 2786 )); 2787 2788 $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = '" . $db->sql_escape($confirm_key) . "' 2789 WHERE user_id = " . $user->data['user_id']; 2790 $db->sql_query($sql); 2791 2792 if ($request->is_ajax()) 2793 { 2794 $u_action .= '&confirm_uid=' . $user->data['user_id'] . '&sess=' . $user->session_id . '&sid=' . $user->session_id; 2795 $json_response = new \phpbb\json_response; 2796 $json_response->send(array( 2797 'MESSAGE_BODY' => $template->assign_display('body'), 2798 'MESSAGE_TITLE' => (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title], 2799 'MESSAGE_TEXT' => (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'], 2800 2801 'YES_VALUE' => $user->lang['YES'], 2802 'S_CONFIRM_ACTION' => str_replace('&', '&', $u_action), //inefficient, rewrite whole function 2803 'S_HIDDEN_FIELDS' => $hidden . $s_hidden_fields 2804 )); 2805 } 2806 2807 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin']) 2808 { 2809 adm_page_footer(); 2810 } 2811 else 2812 { 2813 page_footer(); 2814 } 2815 } 2816 2817 /** 2818 * Generate login box or verify password 2819 */ 2820 function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = false, $s_display = true) 2821 { 2822 global $db, $user, $template, $auth, $phpEx, $phpbb_root_path, $config; 2823 global $request, $phpbb_container, $phpbb_dispatcher; 2824 2825 $err = ''; 2826 2827 // Make sure user->setup() has been called 2828 if (empty($user->lang)) 2829 { 2830 $user->setup(); 2831 } 2832 2833 /** 2834 * This event allows an extension to modify the login process 2835 * 2836 * @event core.login_box_before 2837 * @var string redirect Redirect string 2838 * @var string l_explain Explain language string 2839 * @var string l_success Success language string 2840 * @var bool admin Is admin? 2841 * @var bool s_display Display full login form? 2842 * @var string err Error string 2843 * @since 3.1.9-RC1 2844 */ 2845 $vars = array('redirect', 'l_explain', 'l_success', 'admin', 's_display', 'err'); 2846 extract($phpbb_dispatcher->trigger_event('core.login_box_before', compact($vars))); 2847 2848 // Print out error if user tries to authenticate as an administrator without having the privileges... 2849 if ($admin && !$auth->acl_get('a_')) 2850 { 2851 // Not authd 2852 // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions 2853 if ($user->data['is_registered']) 2854 { 2855 add_log('admin', 'LOG_ADMIN_AUTH_FAIL'); 2856 } 2857 trigger_error('NO_AUTH_ADMIN'); 2858 } 2859 2860 if (empty($err) && ($request->is_set_post('login') || ($request->is_set('login') && $request->variable('login', '') == 'external'))) 2861 { 2862 // Get credential 2863 if ($admin) 2864 { 2865 $credential = request_var('credential', ''); 2866 2867 if (strspn($credential, 'abcdef0123456789') !== strlen($credential) || strlen($credential) != 32) 2868 { 2869 if ($user->data['is_registered']) 2870 { 2871 add_log('admin', 'LOG_ADMIN_AUTH_FAIL'); 2872 } 2873 trigger_error('NO_AUTH_ADMIN'); 2874 } 2875 2876 $password = $request->untrimmed_variable('password_' . $credential, '', true); 2877 } 2878 else 2879 { 2880 $password = $request->untrimmed_variable('password', '', true); 2881 } 2882 2883 $username = request_var('username', '', true); 2884 $autologin = $request->is_set_post('autologin'); 2885 $viewonline = (int) !$request->is_set_post('viewonline'); 2886 $admin = ($admin) ? 1 : 0; 2887 $viewonline = ($admin) ? $user->data['session_viewonline'] : $viewonline; 2888 2889 // Check if the supplied username is equal to the one stored within the database if re-authenticating 2890 if ($admin && utf8_clean_string($username) != utf8_clean_string($user->data['username'])) 2891 { 2892 // We log the attempt to use a different username... 2893 add_log('admin', 'LOG_ADMIN_AUTH_FAIL'); 2894 trigger_error('NO_AUTH_ADMIN_USER_DIFFER'); 2895 } 2896 2897 // If authentication is successful we redirect user to previous page 2898 $result = $auth->login($username, $password, $autologin, $viewonline, $admin); 2899 2900 // If admin authentication and login, we will log if it was a success or not... 2901 // We also break the operation on the first non-success login - it could be argued that the user already knows 2902 if ($admin) 2903 { 2904 if ($result['status'] == LOGIN_SUCCESS) 2905 { 2906 add_log('admin', 'LOG_ADMIN_AUTH_SUCCESS'); 2907 } 2908 else 2909 { 2910 // Only log the failed attempt if a real user tried to. 2911 // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions 2912 if ($user->data['is_registered']) 2913 { 2914 add_log('admin', 'LOG_ADMIN_AUTH_FAIL'); 2915 } 2916 } 2917 } 2918 2919 // The result parameter is always an array, holding the relevant information... 2920 if ($result['status'] == LOGIN_SUCCESS) 2921 { 2922 $redirect = request_var('redirect', "{$phpbb_root_path}index.$phpEx"); 2923 2924 /** 2925 * This event allows an extension to modify the redirection when a user successfully logs in 2926 * 2927 * @event core.login_box_redirect 2928 * @var string redirect Redirect string 2929 * @var bool admin Is admin? 2930 * @since 3.1.0-RC5 2931 * @changed 3.1.9-RC1 Removed undefined return variable 2932 */ 2933 $vars = array('redirect', 'admin'); 2934 extract($phpbb_dispatcher->trigger_event('core.login_box_redirect', compact($vars))); 2935 2936 // append/replace SID (may change during the session for AOL users) 2937 $redirect = reapply_sid($redirect); 2938 2939 // Special case... the user is effectively banned, but we allow founders to login 2940 if (defined('IN_CHECK_BAN') && $result['user_row']['user_type'] != USER_FOUNDER) 2941 { 2942 return; 2943 } 2944 2945 redirect($redirect); 2946 } 2947 2948 // Something failed, determine what... 2949 if ($result['status'] == LOGIN_BREAK) 2950 { 2951 trigger_error($result['error_msg']); 2952 } 2953 2954 // Special cases... determine 2955 switch ($result['status']) 2956 { 2957 case LOGIN_ERROR_PASSWORD_CONVERT: 2958 $err = sprintf( 2959 $user->lang[$result['error_msg']], 2960 ($config['email_enable']) ? '<a href="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') . '">' : '', 2961 ($config['email_enable']) ? '</a>' : '', 2962 '<a href="' . phpbb_get_board_contact_link($config, $phpbb_root_path, $phpEx) . '">', 2963 '</a>' 2964 ); 2965 break; 2966 2967 case LOGIN_ERROR_ATTEMPTS: 2968 2969 $captcha = $phpbb_container->get('captcha.factory')->get_instance($config['captcha_plugin']); 2970 $captcha->init(CONFIRM_LOGIN); 2971 // $captcha->reset(); 2972 2973 $template->assign_vars(array( 2974 'CAPTCHA_TEMPLATE' => $captcha->get_template(), 2975 )); 2976 // no break; 2977 2978 // Username, password, etc... 2979 default: 2980 $err = $user->lang[$result['error_msg']]; 2981 2982 // Assign admin contact to some error messages 2983 if ($result['error_msg'] == 'LOGIN_ERROR_USERNAME' || $result['error_msg'] == 'LOGIN_ERROR_PASSWORD') 2984 { 2985 $err = sprintf($user->lang[$result['error_msg']], '<a href="' . append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=contactadmin') . '">', '</a>'); 2986 } 2987 2988 break; 2989 } 2990 2991 /** 2992 * This event allows an extension to process when a user fails a login attempt 2993 * 2994 * @event core.login_box_failed 2995 * @var array result Login result data 2996 * @var string username User name used to login 2997 * @var string password Password used to login 2998 * @var string err Error message 2999 * @since 3.1.3-RC1 3000 */ 3001 $vars = array('result', 'username', 'password', 'err'); 3002 extract($phpbb_dispatcher->trigger_event('core.login_box_failed', compact($vars))); 3003 } 3004 3005 // Assign credential for username/password pair 3006 $credential = ($admin) ? md5(unique_id()) : false; 3007 3008 $s_hidden_fields = array( 3009 'sid' => $user->session_id, 3010 ); 3011 3012 if ($redirect) 3013 { 3014 $s_hidden_fields['redirect'] = $redirect; 3015 } 3016 3017 if ($admin) 3018 { 3019 $s_hidden_fields['credential'] = $credential; 3020 } 3021 3022 $provider_collection = $phpbb_container->get('auth.provider_collection'); 3023 $auth_provider = $provider_collection->get_provider(); 3024 3025 $auth_provider_data = $auth_provider->get_login_data(); 3026 if ($auth_provider_data) 3027 { 3028 if (isset($auth_provider_data['VARS'])) 3029 { 3030 $template->assign_vars($auth_provider_data['VARS']); 3031 } 3032 3033 if (isset($auth_provider_data['BLOCK_VAR_NAME'])) 3034 { 3035 foreach ($auth_provider_data['BLOCK_VARS'] as $block_vars) 3036 { 3037 $template->assign_block_vars($auth_provider_data['BLOCK_VAR_NAME'], $block_vars); 3038 } 3039 } 3040 3041 $template->assign_vars(array( 3042 'PROVIDER_TEMPLATE_FILE' => $auth_provider_data['TEMPLATE_FILE'], 3043 )); 3044 } 3045 3046 $s_hidden_fields = build_hidden_fields($s_hidden_fields); 3047 3048 $template->assign_vars(array( 3049 'LOGIN_ERROR' => $err, 3050 'LOGIN_EXPLAIN' => $l_explain, 3051 3052 'U_SEND_PASSWORD' => ($config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') : '', 3053 'U_RESEND_ACTIVATION' => ($config['require_activation'] == USER_ACTIVATION_SELF && $config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=resend_act') : '', 3054 'U_TERMS_USE' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'), 3055 'U_PRIVACY' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'), 3056 3057 'S_DISPLAY_FULL_LOGIN' => ($s_display) ? true : false, 3058 'S_HIDDEN_FIELDS' => $s_hidden_fields, 3059 3060 'S_ADMIN_AUTH' => $admin, 3061 'USERNAME' => ($admin) ? $user->data['username'] : '', 3062 3063 'USERNAME_CREDENTIAL' => 'username', 3064 'PASSWORD_CREDENTIAL' => ($admin) ? 'password_' . $credential : 'password', 3065 )); 3066 3067 page_header($user->lang['LOGIN']); 3068 3069 $template->set_filenames(array( 3070 'body' => 'login_body.html') 3071 ); 3072 make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx")); 3073 3074 page_footer(); 3075 } 3076 3077 /** 3078 * Generate forum login box 3079 */ 3080 function login_forum_box($forum_data) 3081 { 3082 global $db, $phpbb_container, $request, $template, $user, $phpbb_dispatcher; 3083 3084 $password = $request->variable('password', '', true); 3085 3086 $sql = 'SELECT forum_id 3087 FROM ' . FORUMS_ACCESS_TABLE . ' 3088 WHERE forum_id = ' . $forum_data['forum_id'] . ' 3089 AND user_id = ' . $user->data['user_id'] . " 3090 AND session_id = '" . $db->sql_escape($user->session_id) . "'"; 3091 $result = $db->sql_query($sql); 3092 $row = $db->sql_fetchrow($result); 3093 $db->sql_freeresult($result); 3094 3095 if ($row) 3096 { 3097 return true; 3098 } 3099 3100 if ($password) 3101 { 3102 // Remove expired authorised sessions 3103 $sql = 'SELECT f.session_id 3104 FROM ' . FORUMS_ACCESS_TABLE . ' f 3105 LEFT JOIN ' . SESSIONS_TABLE . ' s ON (f.session_id = s.session_id) 3106 WHERE s.session_id IS NULL'; 3107 $result = $db->sql_query($sql); 3108 3109 if ($row = $db->sql_fetchrow($result)) 3110 { 3111 $sql_in = array(); 3112 do 3113 { 3114 $sql_in[] = (string) $row['session_id']; 3115 } 3116 while ($row = $db->sql_fetchrow($result)); 3117 3118 // Remove expired sessions 3119 $sql = 'DELETE FROM ' . FORUMS_ACCESS_TABLE . ' 3120 WHERE ' . $db->sql_in_set('session_id', $sql_in); 3121 $db->sql_query($sql); 3122 } 3123 $db->sql_freeresult($result); 3124 3125 $passwords_manager = $phpbb_container->get('passwords.manager'); 3126 3127 if ($passwords_manager->check($password, $forum_data['forum_password'])) 3128 { 3129 $sql_ary = array( 3130 'forum_id' => (int) $forum_data['forum_id'], 3131 'user_id' => (int) $user->data['user_id'], 3132 'session_id' => (string) $user->session_id, 3133 ); 3134 3135 $db->sql_query('INSERT INTO ' . FORUMS_ACCESS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary)); 3136 3137 return true; 3138 } 3139 3140 $template->assign_var('LOGIN_ERROR', $user->lang['WRONG_PASSWORD']); 3141 } 3142 3143 /** 3144 * Performing additional actions, load additional data on forum login 3145 * 3146 * @event core.login_forum_box 3147 * @var array forum_data Array with forum data 3148 * @var string password Password entered 3149 * @since 3.1.0-RC3 3150 */ 3151 $vars = array('forum_data', 'password'); 3152 extract($phpbb_dispatcher->trigger_event('core.login_forum_box', compact($vars))); 3153 3154 page_header($user->lang['LOGIN']); 3155 3156 $template->assign_vars(array( 3157 'FORUM_NAME' => isset($forum_data['forum_name']) ? $forum_data['forum_name'] : '', 3158 'S_LOGIN_ACTION' => build_url(array('f')), 3159 'S_HIDDEN_FIELDS' => build_hidden_fields(array('f' => $forum_data['forum_id']))) 3160 ); 3161 3162 $template->set_filenames(array( 3163 'body' => 'login_forum.html') 3164 ); 3165 3166 page_footer(); 3167 } 3168 3169 // Little helpers 3170 3171 /** 3172 * Little helper for the build_hidden_fields function 3173 */ 3174 function _build_hidden_fields($key, $value, $specialchar, $stripslashes) 3175 { 3176 $hidden_fields = ''; 3177 3178 if (!is_array($value)) 3179 { 3180 $value = ($stripslashes) ? stripslashes($value) : $value; 3181 $value = ($specialchar) ? htmlspecialchars($value, ENT_COMPAT, 'UTF-8') : $value; 3182 3183 $hidden_fields .= '<input type="hidden" name="' . $key . '" value="' . $value . '" />' . "\n"; 3184 } 3185 else 3186 { 3187 foreach ($value as $_key => $_value) 3188 { 3189 $_key = ($stripslashes) ? stripslashes($_key) : $_key; 3190 $_key = ($specialchar) ? htmlspecialchars($_key, ENT_COMPAT, 'UTF-8') : $_key; 3191 3192 $hidden_fields .= _build_hidden_fields($key . '[' . $_key . ']', $_value, $specialchar, $stripslashes); 3193 } 3194 } 3195 3196 return $hidden_fields; 3197 } 3198 3199 /** 3200 * Build simple hidden fields from array 3201 * 3202 * @param array $field_ary an array of values to build the hidden field from 3203 * @param bool $specialchar if true, keys and values get specialchared 3204 * @param bool $stripslashes if true, keys and values get stripslashed 3205 * 3206 * @return string the hidden fields 3207 */ 3208 function build_hidden_fields($field_ary, $specialchar = false, $stripslashes = false) 3209 { 3210 $s_hidden_fields = ''; 3211 3212 foreach ($field_ary as $name => $vars) 3213 { 3214 $name = ($stripslashes) ? stripslashes($name) : $name; 3215 $name = ($specialchar) ? htmlspecialchars($name, ENT_COMPAT, 'UTF-8') : $name; 3216 3217 $s_hidden_fields .= _build_hidden_fields($name, $vars, $specialchar, $stripslashes); 3218 } 3219 3220 return $s_hidden_fields; 3221 } 3222 3223 /** 3224 * Parse cfg file 3225 */ 3226 function parse_cfg_file($filename, $lines = false) 3227 { 3228 $parsed_items = array(); 3229 3230 if ($lines === false) 3231 { 3232 $lines = file($filename); 3233 } 3234 3235 foreach ($lines as $line) 3236 { 3237 $line = trim($line); 3238 3239 if (!$line || $line[0] == '#' || ($delim_pos = strpos($line, '=')) === false) 3240 { 3241 continue; 3242 } 3243 3244 // Determine first occurrence, since in values the equal sign is allowed 3245 $key = htmlspecialchars(strtolower(trim(substr($line, 0, $delim_pos)))); 3246 $value = trim(substr($line, $delim_pos + 1)); 3247 3248 if (in_array($value, array('off', 'false', '0'))) 3249 { 3250 $value = false; 3251 } 3252 else if (in_array($value, array('on', 'true', '1'))) 3253 { 3254 $value = true; 3255 } 3256 else if (!trim($value)) 3257 { 3258 $value = ''; 3259 } 3260 else if (($value[0] == "'" && $value[sizeof($value) - 1] == "'") || ($value[0] == '"' && $value[sizeof($value) - 1] == '"')) 3261 { 3262 $value = htmlspecialchars(substr($value, 1, sizeof($value)-2)); 3263 } 3264 else 3265 { 3266 $value = htmlspecialchars($value); 3267 } 3268 3269 $parsed_items[$key] = $value; 3270 } 3271 3272 if (isset($parsed_items['parent']) && isset($parsed_items['name']) && $parsed_items['parent'] == $parsed_items['name']) 3273 { 3274 unset($parsed_items['parent']); 3275 } 3276 3277 return $parsed_items; 3278 } 3279 3280 /** 3281 * Add log entry 3282 * 3283 * @param string $mode The mode defines which log_type is used and from which log the entry is retrieved 3284 * @param int $forum_id Mode 'mod' ONLY: forum id of the related item, NOT INCLUDED otherwise 3285 * @param int $topic_id Mode 'mod' ONLY: topic id of the related item, NOT INCLUDED otherwise 3286 * @param int $reportee_id Mode 'user' ONLY: user id of the reportee, NOT INCLUDED otherwise 3287 * @param string $log_operation Name of the operation 3288 * @param array $additional_data More arguments can be added, depending on the log_type 3289 * 3290 * @return int|bool Returns the log_id, if the entry was added to the database, false otherwise. 3291 * 3292 * @deprecated Use $phpbb_log->add() instead 3293 */ 3294 function add_log() 3295 { 3296 global $phpbb_log, $user; 3297 3298 $args = func_get_args(); 3299 $mode = array_shift($args); 3300 3301 // This looks kind of dirty, but add_log has some additional data before the log_operation 3302 $additional_data = array(); 3303 switch ($mode) 3304 { 3305 case 'admin': 3306 case 'critical': 3307 break; 3308 case 'mod': 3309 $additional_data['forum_id'] = array_shift($args); 3310 $additional_data['topic_id'] = array_shift($args); 3311 break; 3312 case 'user': 3313 $additional_data['reportee_id'] = array_shift($args); 3314 break; 3315 } 3316 3317 $log_operation = array_shift($args); 3318 $additional_data = array_merge($additional_data, $args); 3319 3320 $user_id = (empty($user->data)) ? ANONYMOUS : $user->data['user_id']; 3321 $user_ip = (empty($user->ip)) ? '' : $user->ip; 3322 3323 return $phpbb_log->add($mode, $user_id, $user_ip, $log_operation, time(), $additional_data); 3324 } 3325 3326 /** 3327 * Return a nicely formatted backtrace. 3328 * 3329 * Turns the array returned by debug_backtrace() into HTML markup. 3330 * Also filters out absolute paths to phpBB root. 3331 * 3332 * @return string HTML markup 3333 */ 3334 function get_backtrace() 3335 { 3336 $output = '<div style="font-family: monospace;">'; 3337 $backtrace = debug_backtrace(); 3338 3339 // We skip the first one, because it only shows this file/function 3340 unset($backtrace[0]); 3341 3342 foreach ($backtrace as $trace) 3343 { 3344 // Strip the current directory from path 3345 $trace['file'] = (empty($trace['file'])) ? '(not given by php)' : htmlspecialchars(phpbb_filter_root_path($trace['file'])); 3346 $trace['line'] = (empty($trace['line'])) ? '(not given by php)' : $trace['line']; 3347 3348 // Only show function arguments for include etc. 3349 // Other parameters may contain sensible information 3350 $argument = ''; 3351 if (!empty($trace['args'][0]) && in_array($trace['function'], array('include', 'require', 'include_once', 'require_once'))) 3352 { 3353 $argument = htmlspecialchars(phpbb_filter_root_path($trace['args'][0])); 3354 } 3355 3356 $trace['class'] = (!isset($trace['class'])) ? '' : $trace['class']; 3357 $trace['type'] = (!isset($trace['type'])) ? '' : $trace['type']; 3358 3359 $output .= '<br />'; 3360 $output .= '<b>FILE:</b> ' . $trace['file'] . '<br />'; 3361 $output .= '<b>LINE:</b> ' . ((!empty($trace['line'])) ? $trace['line'] : '') . '<br />'; 3362 3363 $output .= '<b>CALL:</b> ' . htmlspecialchars($trace['class'] . $trace['type'] . $trace['function']); 3364 $output .= '(' . (($argument !== '') ? "'$argument'" : '') . ')<br />'; 3365 } 3366 $output .= '</div>'; 3367 return $output; 3368 } 3369 3370 /** 3371 * This function returns a regular expression pattern for commonly used expressions 3372 * Use with / as delimiter for email mode and # for url modes 3373 * mode can be: email|bbcode_htm|url|url_inline|www_url|www_url_inline|relative_url|relative_url_inline|ipv4|ipv6 3374 */ 3375 function get_preg_expression($mode) 3376 { 3377 switch ($mode) 3378 { 3379 case 'email': 3380 // Regex written by James Watts and Francisco Jose Martin Moreno 3381 // http://fightingforalostcause.net/misc/2006/compare-email-regex.php 3382 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})?)'; 3383 break; 3384 3385 case 'bbcode_htm': 3386 return array( 3387 '#<!\-\- e \-\-><a href="mailto:(.*?)">.*?</a><!\-\- e \-\->#', 3388 '#<!\-\- l \-\-><a (?:class="[\w-]+" )?href="(.*?)(?:(&|\?)sid=[0-9a-f]{32})?">.*?</a><!\-\- l \-\->#', 3389 '#<!\-\- ([mw]) \-\-><a (?:class="[\w-]+" )?href="(.*?)">.*?</a><!\-\- \1 \-\->#', 3390 '#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#', 3391 '#<!\-\- .*? \-\->#s', 3392 '#<.*?>#s', 3393 ); 3394 break; 3395 3396 // Whoa these look impressive! 3397 // The code to generate the following two regular expressions which match valid IPv4/IPv6 addresses 3398 // can be found in the develop directory 3399 case 'ipv4': 3400 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])$#'; 3401 break; 3402 3403 case 'ipv6': 3404 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'; 3405 break; 3406 3407 case 'url': 3408 // generated with regex_idn.php file in the develop folder 3409 return "[a-z][a-z\d+\-.]*(?<!javascript):/{2}(?:(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@|]+|%[\dA-F]{2})+|[0-9.]+|\[[a-z0-9.]+:[a-z0-9.]+:[a-z0-9.:]+\])(?::\d*)?(?:/(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@/?|]+|%[\dA-F]{2})*)?"; 3410 break; 3411 3412 case 'url_http': 3413 // generated with regex_idn.php file in the develop folder 3414 return "http[s]?:/{2}(?:(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@|]+|%[\dA-F]{2})+|[0-9.]+|\[[a-z0-9.]+:[a-z0-9.]+:[a-z0-9.:]+\])(?::\d*)?(?:/(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@/?|]+|%[\dA-F]{2})*)?"; 3415 break; 3416 3417 case 'url_inline': 3418 // generated with regex_idn.php file in the develop folder 3419 return "[a-z][a-z\d+]*(?<!javascript):/{2}(?:(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})+|[0-9.]+|\[[a-z0-9.]+:[a-z0-9.]+:[a-z0-9.:]+\])(?::\d*)?(?:/(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?"; 3420 break; 3421 3422 case 'www_url': 3423 // generated with regex_idn.php file in the develop folder 3424 return "www\.(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@|]+|%[\dA-F]{2})+(?::\d*)?(?:/(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@/?|]+|%[\dA-F]{2})*)?"; 3425 break; 3426 3427 case 'www_url_inline': 3428 // generated with regex_idn.php file in the develop folder 3429 return "www\.(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})+(?::\d*)?(?:/(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?"; 3430 break; 3431 3432 case 'relative_url': 3433 // generated with regex_idn.php file in the develop folder 3434 return "(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@|]+|%[\dA-F]{2})*(?:/(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'()*+,;=:@/?|]+|%[\dA-F]{2})*)?"; 3435 break; 3436 3437 case 'relative_url_inline': 3438 // generated with regex_idn.php file in the develop folder 3439 return "(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})*(?:/(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[^\p{C}\p{Z}\p{S}\p{P}\p{Nl}\p{No}\p{Me}\x{1100}-\x{115F}\x{A960}-\x{A97C}\x{1160}-\x{11A7}\x{D7B0}-\x{D7C6}\x{20D0}-\x{20FF}\x{1D100}-\x{1D1FF}\x{1D200}-\x{1D24F}\x{0640}\x{07FA}\x{302E}\x{302F}\x{3031}-\x{3035}\x{303B}]*[\x{00B7}\x{0375}\x{05F3}\x{05F4}\x{30FB}\x{002D}\x{06FD}\x{06FE}\x{0F0B}\x{3007}\x{00DF}\x{03C2}\x{200C}\x{200D}\pL0-9\-._~!$&'(*+,;=:@/?|]+|%[\dA-F]{2})*)?"; 3440 break; 3441 3442 case 'table_prefix': 3443 return '#^[a-zA-Z][a-zA-Z0-9_]*$#'; 3444 break; 3445 3446 // Matches the predecing dot 3447 case 'path_remove_dot_trailing_slash': 3448 return '#^(?:(\.)?)+(?:(.+)?)+(?:([\\/\\\])$)#'; 3449 break; 3450 3451 case 'semantic_version': 3452 // Regular expression to match semantic versions by http://rgxdb.com/ 3453 return '/(?<=^[Vv]|^)(?:(?<major>(?:0|[1-9](?:(?:0|[1-9])+)*))[.](?<minor>(?:0|[1-9](?:(?:0|[1-9])+)*))[.](?<patch>(?:0|[1-9](?:(?:0|[1-9])+)*))(?:-(?<prerelease>(?:(?:(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?|(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?)|(?:0|[1-9](?:(?:0|[1-9])+)*))(?:[.](?:(?:(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?|(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?)|(?:0|[1-9](?:(?:0|[1-9])+)*)))*))?(?:[+](?<build>(?:(?:(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?|(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?)|(?:(?:0|[1-9])+))(?:[.](?:(?:(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?|(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?)|(?:(?:0|[1-9])+)))*))?)$/'; 3454 break; 3455 } 3456 3457 return ''; 3458 } 3459 3460 /** 3461 * Generate regexp for naughty words censoring 3462 * Depends on whether installed PHP version supports unicode properties 3463 * 3464 * @param string $word word template to be replaced 3465 * @param bool $use_unicode whether or not to take advantage of PCRE supporting unicode 3466 * 3467 * @return string $preg_expr regex to use with word censor 3468 */ 3469 function get_censor_preg_expression($word, $use_unicode = true) 3470 { 3471 // Unescape the asterisk to simplify further conversions 3472 $word = str_replace('\*', '*', preg_quote($word, '#')); 3473 3474 if ($use_unicode && phpbb_pcre_utf8_support()) 3475 { 3476 // Replace asterisk(s) inside the pattern, at the start and at the end of it with regexes 3477 $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); 3478 3479 // Generate the final substitution 3480 $preg_expr = '#(?<![\p{Nd}\p{L}_-])(' . $word . ')(?![\p{Nd}\p{L}_-])#iu'; 3481 } 3482 else 3483 { 3484 // Replace the asterisk inside the pattern, at the start and at the end of it with regexes 3485 $word = preg_replace(array('#(?<=\S)\*+(?=\S)#iu', '#^\*+#', '#\*+$#'), array('(\x20*?\S*?)', '\S*?', '\S*?'), $word); 3486 3487 // Generate the final substitution 3488 $preg_expr = '#(?<!\S)(' . $word . ')(?!\S)#iu'; 3489 } 3490 3491 return $preg_expr; 3492 } 3493 3494 /** 3495 * Returns the first block of the specified IPv6 address and as many additional 3496 * ones as specified in the length paramater. 3497 * If length is zero, then an empty string is returned. 3498 * If length is greater than 3 the complete IP will be returned 3499 */ 3500 function short_ipv6($ip, $length) 3501 { 3502 if ($length < 1) 3503 { 3504 return ''; 3505 } 3506 3507 // extend IPv6 addresses 3508 $blocks = substr_count($ip, ':') + 1; 3509 if ($blocks < 9) 3510 { 3511 $ip = str_replace('::', ':' . str_repeat('0000:', 9 - $blocks), $ip); 3512 } 3513 if ($ip[0] == ':') 3514 { 3515 $ip = '0000' . $ip; 3516 } 3517 if ($length < 4) 3518 { 3519 $ip = implode(':', array_slice(explode(':', $ip), 0, 1 + $length)); 3520 } 3521 3522 return $ip; 3523 } 3524 3525 /** 3526 * Normalises an internet protocol address, 3527 * also checks whether the specified address is valid. 3528 * 3529 * IPv4 addresses are returned 'as is'. 3530 * 3531 * IPv6 addresses are normalised according to 3532 * A Recommendation for IPv6 Address Text Representation 3533 * http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-07 3534 * 3535 * @param string $address IP address 3536 * 3537 * @return mixed false if specified address is not valid, 3538 * string otherwise 3539 */ 3540 function phpbb_ip_normalise($address) 3541 { 3542 $address = trim($address); 3543 3544 if (empty($address) || !is_string($address)) 3545 { 3546 return false; 3547 } 3548 3549 if (preg_match(get_preg_expression('ipv4'), $address)) 3550 { 3551 return $address; 3552 } 3553 3554 return phpbb_inet_ntop(phpbb_inet_pton($address)); 3555 } 3556 3557 /** 3558 * Wrapper for inet_ntop() 3559 * 3560 * Converts a packed internet address to a human readable representation 3561 * inet_ntop() is supported by PHP since 5.1.0, since 5.3.0 also on Windows. 3562 * 3563 * @param string $in_addr A 32bit IPv4, or 128bit IPv6 address. 3564 * 3565 * @return mixed false on failure, 3566 * string otherwise 3567 */ 3568 function phpbb_inet_ntop($in_addr) 3569 { 3570 $in_addr = bin2hex($in_addr); 3571 3572 switch (strlen($in_addr)) 3573 { 3574 case 8: 3575 return implode('.', array_map('hexdec', str_split($in_addr, 2))); 3576 3577 case 32: 3578 if (substr($in_addr, 0, 24) === '00000000000000000000ffff') 3579 { 3580 return phpbb_inet_ntop(pack('H*', substr($in_addr, 24))); 3581 } 3582 3583 $parts = str_split($in_addr, 4); 3584 $parts = preg_replace('/^0+(?!$)/', '', $parts); 3585 $ret = implode(':', $parts); 3586 3587 $matches = array(); 3588 preg_match_all('/(?<=:|^)(?::?0){2,}/', $ret, $matches, PREG_OFFSET_CAPTURE); 3589 $matches = $matches[0]; 3590 3591 if (empty($matches)) 3592 { 3593 return $ret; 3594 } 3595 3596 $longest_match = ''; 3597 $longest_match_offset = 0; 3598 foreach ($matches as $match) 3599 { 3600 if (strlen($match[0]) > strlen($longest_match)) 3601 { 3602 $longest_match = $match[0]; 3603 $longest_match_offset = $match[1]; 3604 } 3605 } 3606 3607 $ret = substr_replace($ret, '', $longest_match_offset, strlen($longest_match)); 3608 3609 if ($longest_match_offset == strlen($ret)) 3610 { 3611 $ret .= ':'; 3612 } 3613 3614 if ($longest_match_offset == 0) 3615 { 3616 $ret = ':' . $ret; 3617 } 3618 3619 return $ret; 3620 3621 default: 3622 return false; 3623 } 3624 } 3625 3626 /** 3627 * Wrapper for inet_pton() 3628 * 3629 * Converts a human readable IP address to its packed in_addr representation 3630 * inet_pton() is supported by PHP since 5.1.0, since 5.3.0 also on Windows. 3631 * 3632 * @param string $address A human readable IPv4 or IPv6 address. 3633 * 3634 * @return mixed false if address is invalid, 3635 * in_addr representation of the given address otherwise (string) 3636 */ 3637 function phpbb_inet_pton($address) 3638 { 3639 $ret = ''; 3640 if (preg_match(get_preg_expression('ipv4'), $address)) 3641 { 3642 foreach (explode('.', $address) as $part) 3643 { 3644 $ret .= ($part <= 0xF ? '0' : '') . dechex($part); 3645 } 3646 3647 return pack('H*', $ret); 3648 } 3649 3650 if (preg_match(get_preg_expression('ipv6'), $address)) 3651 { 3652 $parts = explode(':', $address); 3653 $missing_parts = 8 - sizeof($parts) + 1; 3654 3655 if (substr($address, 0, 2) === '::') 3656 { 3657 ++$missing_parts; 3658 } 3659 3660 if (substr($address, -2) === '::') 3661 { 3662 ++$missing_parts; 3663 } 3664 3665 $embedded_ipv4 = false; 3666 $last_part = end($parts); 3667 3668 if (preg_match(get_preg_expression('ipv4'), $last_part)) 3669 { 3670 $parts[sizeof($parts) - 1] = ''; 3671 $last_part = phpbb_inet_pton($last_part); 3672 $embedded_ipv4 = true; 3673 --$missing_parts; 3674 } 3675 3676 foreach ($parts as $i => $part) 3677 { 3678 if (strlen($part)) 3679 { 3680 $ret .= str_pad($part, 4, '0', STR_PAD_LEFT); 3681 } 3682 else if ($i && $i < sizeof($parts) - 1) 3683 { 3684 $ret .= str_repeat('0000', $missing_parts); 3685 } 3686 } 3687 3688 $ret = pack('H*', $ret); 3689 3690 if ($embedded_ipv4) 3691 { 3692 $ret .= $last_part; 3693 } 3694 3695 return $ret; 3696 } 3697 3698 return false; 3699 } 3700 3701 /** 3702 * Wrapper for php's checkdnsrr function. 3703 * 3704 * @param string $host Fully-Qualified Domain Name 3705 * @param string $type Resource record type to lookup 3706 * Supported types are: MX (default), A, AAAA, NS, TXT, CNAME 3707 * Other types may work or may not work 3708 * 3709 * @return mixed true if entry found, 3710 * false if entry not found, 3711 * null if this function is not supported by this environment 3712 * 3713 * Since null can also be returned, you probably want to compare the result 3714 * with === true or === false, 3715 */ 3716 function phpbb_checkdnsrr($host, $type = 'MX') 3717 { 3718 // The dot indicates to search the DNS root (helps those having DNS prefixes on the same domain) 3719 if (substr($host, -1) == '.') 3720 { 3721 $host_fqdn = $host; 3722 $host = substr($host, 0, -1); 3723 } 3724 else 3725 { 3726 $host_fqdn = $host . '.'; 3727 } 3728 // $host has format some.host.example.com 3729 // $host_fqdn has format some.host.example.com. 3730 3731 // If we're looking for an A record we can use gethostbyname() 3732 if ($type == 'A' && function_exists('gethostbyname')) 3733 { 3734 return (@gethostbyname($host_fqdn) == $host_fqdn) ? false : true; 3735 } 3736 3737 // checkdnsrr() is available on Windows since PHP 5.3, 3738 // but until 5.3.3 it only works for MX records 3739 // See: http://bugs.php.net/bug.php?id=51844 3740 3741 // Call checkdnsrr() if 3742 // we're looking for an MX record or 3743 // we're not on Windows or 3744 // we're running a PHP version where #51844 has been fixed 3745 3746 // checkdnsrr() supports AAAA since 5.0.0 3747 // checkdnsrr() supports TXT since 5.2.4 3748 if ( 3749 ($type == 'MX' || DIRECTORY_SEPARATOR != '\\' || version_compare(PHP_VERSION, '5.3.3', '>=')) && 3750 ($type != 'AAAA' || version_compare(PHP_VERSION, '5.0.0', '>=')) && 3751 ($type != 'TXT' || version_compare(PHP_VERSION, '5.2.4', '>=')) && 3752 function_exists('checkdnsrr') 3753 ) 3754 { 3755 return checkdnsrr($host_fqdn, $type); 3756 } 3757 3758 // dns_get_record() is available since PHP 5; since PHP 5.3 also on Windows, 3759 // but on Windows it does not work reliable for AAAA records before PHP 5.3.1 3760 3761 // Call dns_get_record() if 3762 // we're not looking for an AAAA record or 3763 // we're not on Windows or 3764 // we're running a PHP version where AAAA lookups work reliable 3765 if ( 3766 ($type != 'AAAA' || DIRECTORY_SEPARATOR != '\\' || version_compare(PHP_VERSION, '5.3.1', '>=')) && 3767 function_exists('dns_get_record') 3768 ) 3769 { 3770 // dns_get_record() expects an integer as second parameter 3771 // We have to convert the string $type to the corresponding integer constant. 3772 $type_constant = 'DNS_' . $type; 3773 $type_param = (defined($type_constant)) ? constant($type_constant) : DNS_ANY; 3774 3775 // dns_get_record() might throw E_WARNING and return false for records that do not exist 3776 $resultset = @dns_get_record($host_fqdn, $type_param); 3777 3778 if (empty($resultset) || !is_array($resultset)) 3779 { 3780 return false; 3781 } 3782 else if ($type_param == DNS_ANY) 3783 { 3784 // $resultset is a non-empty array 3785 return true; 3786 } 3787 3788 foreach ($resultset as $result) 3789 { 3790 if ( 3791 isset($result['host']) && $result['host'] == $host && 3792 isset($result['type']) && $result['type'] == $type 3793 ) 3794 { 3795 return true; 3796 } 3797 } 3798 3799 return false; 3800 } 3801 3802 // If we're on Windows we can still try to call nslookup via exec() as a last resort 3803 if (DIRECTORY_SEPARATOR == '\\' && function_exists('exec')) 3804 { 3805 @exec('nslookup -type=' . escapeshellarg($type) . ' ' . escapeshellarg($host_fqdn), $output); 3806 3807 // If output is empty, the nslookup failed 3808 if (empty($output)) 3809 { 3810 return NULL; 3811 } 3812 3813 foreach ($output as $line) 3814 { 3815 $line = trim($line); 3816 3817 if (empty($line)) 3818 { 3819 continue; 3820 } 3821 3822 // Squash tabs and multiple whitespaces to a single whitespace. 3823 $line = preg_replace('/\s+/', ' ', $line); 3824 3825 switch ($type) 3826 { 3827 case 'MX': 3828 if (stripos($line, "$host MX") === 0) 3829 { 3830 return true; 3831 } 3832 break; 3833 3834 case 'NS': 3835 if (stripos($line, "$host nameserver") === 0) 3836 { 3837 return true; 3838 } 3839 break; 3840 3841 case 'TXT': 3842 if (stripos($line, "$host text") === 0) 3843 { 3844 return true; 3845 } 3846 break; 3847 3848 case 'CNAME': 3849 if (stripos($line, "$host canonical name") === 0) 3850 { 3851 return true; 3852 } 3853 break; 3854 3855 default: 3856 case 'AAAA': 3857 // AAAA records returned by nslookup on Windows XP/2003 have this format. 3858 // Later Windows versions use the A record format below for AAAA records. 3859 if (stripos($line, "$host AAAA IPv6 address") === 0) 3860 { 3861 return true; 3862 } 3863 // No break 3864 3865 case 'A': 3866 if (!empty($host_matches)) 3867 { 3868 // Second line 3869 if (stripos($line, "Address: ") === 0) 3870 { 3871 return true; 3872 } 3873 else 3874 { 3875 $host_matches = false; 3876 } 3877 } 3878 else if (stripos($line, "Name: $host") === 0) 3879 { 3880 // First line 3881 $host_matches = true; 3882 } 3883 break; 3884 } 3885 } 3886 3887 return false; 3888 } 3889 3890 return NULL; 3891 } 3892 3893 // Handler, header and footer 3894 3895 /** 3896 * Error and message handler, call with trigger_error if read 3897 */ 3898 function msg_handler($errno, $msg_text, $errfile, $errline) 3899 { 3900 global $cache, $db, $auth, $template, $config, $user, $request; 3901 global $phpEx, $phpbb_root_path, $msg_title, $msg_long_text; 3902 3903 // Do not display notices if we suppress them via @ 3904 if (error_reporting() == 0 && $errno != E_USER_ERROR && $errno != E_USER_WARNING && $errno != E_USER_NOTICE) 3905 { 3906 return; 3907 } 3908 3909 // Message handler is stripping text. In case we need it, we are possible to define long text... 3910 if (isset($msg_long_text) && $msg_long_text && !$msg_text) 3911 { 3912 $msg_text = $msg_long_text; 3913 } 3914 3915 if (!defined('E_DEPRECATED')) 3916 { 3917 define('E_DEPRECATED', 8192); 3918 } 3919 3920 switch ($errno) 3921 { 3922 case E_NOTICE: 3923 case E_WARNING: 3924 3925 // Check the error reporting level and return if the error level does not match 3926 // If DEBUG is defined the default level is E_ALL 3927 if (($errno & ((defined('DEBUG')) ? E_ALL : error_reporting())) == 0) 3928 { 3929 return; 3930 } 3931 3932 if (strpos($errfile, 'cache') === false && strpos($errfile, 'template.') === false) 3933 { 3934 $errfile = phpbb_filter_root_path($errfile); 3935 $msg_text = phpbb_filter_root_path($msg_text); 3936 $error_name = ($errno === E_WARNING) ? 'PHP Warning' : 'PHP Notice'; 3937 echo '<b>[phpBB Debug] ' . $error_name . '</b>: in file <b>' . $errfile . '</b> on line <b>' . $errline . '</b>: <b>' . $msg_text . '</b><br />' . "\n"; 3938 3939 // we are writing an image - the user won't see the debug, so let's place it in the log 3940 if (defined('IMAGE_OUTPUT') || defined('IN_CRON')) 3941 { 3942 add_log('critical', 'LOG_IMAGE_GENERATION_ERROR', $errfile, $errline, $msg_text); 3943 } 3944 // echo '<br /><br />BACKTRACE<br />' . get_backtrace() . '<br />' . "\n"; 3945 } 3946 3947 return; 3948 3949 break; 3950 3951 case E_USER_ERROR: 3952 3953 if (!empty($user) && !empty($user->lang)) 3954 { 3955 $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text; 3956 $msg_title = (!isset($msg_title)) ? $user->lang['GENERAL_ERROR'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title); 3957 3958 $l_return_index = sprintf($user->lang['RETURN_INDEX'], '<a href="' . $phpbb_root_path . '">', '</a>'); 3959 $l_notify = ''; 3960 3961 if (!empty($config['board_contact'])) 3962 { 3963 $l_notify = '<p>' . sprintf($user->lang['NOTIFY_ADMIN_EMAIL'], $config['board_contact']) . '</p>'; 3964 } 3965 } 3966 else 3967 { 3968 $msg_title = 'General Error'; 3969 $l_return_index = '<a href="' . $phpbb_root_path . '">Return to index page</a>'; 3970 $l_notify = ''; 3971 3972 if (!empty($config['board_contact'])) 3973 { 3974 $l_notify = '<p>Please notify the board administrator or webmaster: <a href="mailto:' . $config['board_contact'] . '">' . $config['board_contact'] . '</a></p>'; 3975 } 3976 } 3977 3978 $log_text = $msg_text; 3979 $backtrace = get_backtrace(); 3980 if ($backtrace) 3981 { 3982 $log_text .= '<br /><br />BACKTRACE<br />' . $backtrace; 3983 } 3984 3985 if (defined('IN_INSTALL') || defined('DEBUG') || isset($auth) && $auth->acl_get('a_')) 3986 { 3987 $msg_text = $log_text; 3988 3989 // If this is defined there already was some output 3990 // So let's not break it 3991 if (defined('IN_DB_UPDATE')) 3992 { 3993 echo '<div class="errorbox">' . $msg_text . '</div>'; 3994 3995 $db->sql_return_on_error(true); 3996 phpbb_end_update($cache, $config); 3997 } 3998 } 3999 4000 if ((defined('IN_CRON') || defined('IMAGE_OUTPUT')) && isset($db)) 4001 { 4002 // let's avoid loops 4003 $db->sql_return_on_error(true); 4004 add_log('critical', 'LOG_GENERAL_ERROR', $msg_title, $log_text); 4005 $db->sql_return_on_error(false); 4006 } 4007 4008 // Do not send 200 OK, but service unavailable on errors 4009 send_status_line(503, 'Service Unavailable'); 4010 4011 garbage_collection(); 4012 4013 // Try to not call the adm page data... 4014 4015 echo '<!DOCTYPE html>'; 4016 echo '<html dir="ltr">'; 4017 echo '<head>'; 4018 echo '<meta charset="utf-8">'; 4019 echo '<meta http-equiv="X-UA-Compatible" content="IE=edge">'; 4020 echo '<title>' . $msg_title . '</title>'; 4021 echo '<style type="text/css">' . "\n" . '/* <![CDATA[ */' . "\n"; 4022 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; } '; 4023 echo 'a:link, a:active, a:visited { color: #006699; text-decoration: none; } a:hover { color: #DD6900; text-decoration: underline; } '; 4024 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; } '; 4025 echo '.panel { margin: 4px 0; background-color: #FFFFFF; border: solid 1px #A9B8C2; } '; 4026 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; } '; 4027 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; } '; 4028 echo "\n" . '/* ]]> */' . "\n"; 4029 echo '</style>'; 4030 echo '</head>'; 4031 echo '<body id="errorpage">'; 4032 echo '<div id="wrap">'; 4033 echo ' <div id="page-header">'; 4034 echo ' ' . $l_return_index; 4035 echo ' </div>'; 4036 echo ' <div id="acp">'; 4037 echo ' <div class="panel">'; 4038 echo ' <div id="content">'; 4039 echo ' <h1>' . $msg_title . '</h1>'; 4040 4041 echo ' <div>' . $msg_text . '</div>'; 4042 4043 echo $l_notify; 4044 4045 echo ' </div>'; 4046 echo ' </div>'; 4047 echo ' </div>'; 4048 echo ' <div id="page-footer">'; 4049 echo ' Powered by <a href="https://www.phpbb.com/">phpBB</a>® Forum Software © phpBB Limited'; 4050 echo ' </div>'; 4051 echo '</div>'; 4052 echo '</body>'; 4053 echo '</html>'; 4054 4055 exit_handler(); 4056 4057 // On a fatal error (and E_USER_ERROR *is* fatal) we never want other scripts to continue and force an exit here. 4058 exit; 4059 break; 4060 4061 case E_USER_WARNING: 4062 case E_USER_NOTICE: 4063 4064 define('IN_ERROR_HANDLER', true); 4065 4066 if (empty($user->data)) 4067 { 4068 $user->session_begin(); 4069 } 4070 4071 // We re-init the auth array to get correct results on login/logout 4072 $auth->acl($user->data); 4073 4074 if (empty($user->lang)) 4075 { 4076 $user->setup(); 4077 } 4078 4079 if ($msg_text == 'ERROR_NO_ATTACHMENT' || $msg_text == 'NO_FORUM' || $msg_text == 'NO_TOPIC' || $msg_text == 'NO_USER') 4080 { 4081 send_status_line(404, 'Not Found'); 4082 } 4083 4084 $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text; 4085 $msg_title = (!isset($msg_title)) ? $user->lang['INFORMATION'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title); 4086 4087 if (!defined('HEADER_INC')) 4088 { 4089 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin']) 4090 { 4091 adm_page_header($msg_title); 4092 } 4093 else 4094 { 4095 page_header($msg_title); 4096 } 4097 } 4098 4099 $template->set_filenames(array( 4100 'body' => 'message_body.html') 4101 ); 4102 4103 $template->assign_vars(array( 4104 'MESSAGE_TITLE' => $msg_title, 4105 'MESSAGE_TEXT' => $msg_text, 4106 'S_USER_WARNING' => ($errno == E_USER_WARNING) ? true : false, 4107 'S_USER_NOTICE' => ($errno == E_USER_NOTICE) ? true : false) 4108 ); 4109 4110 if ($request->is_ajax()) 4111 { 4112 global $refresh_data; 4113 4114 $json_response = new \phpbb\json_response; 4115 $json_response->send(array( 4116 'MESSAGE_TITLE' => $msg_title, 4117 'MESSAGE_TEXT' => $msg_text, 4118 'S_USER_WARNING' => ($errno == E_USER_WARNING) ? true : false, 4119 'S_USER_NOTICE' => ($errno == E_USER_NOTICE) ? true : false, 4120 'REFRESH_DATA' => (!empty($refresh_data)) ? $refresh_data : null 4121 )); 4122 } 4123 4124 // We do not want the cron script to be called on error messages 4125 define('IN_CRON', true); 4126 4127 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin']) 4128 { 4129 adm_page_footer(); 4130 } 4131 else 4132 { 4133 page_footer(); 4134 } 4135 4136 exit_handler(); 4137 break; 4138 4139 // PHP4 compatibility 4140 case E_DEPRECATED: 4141 return true; 4142 break; 4143 } 4144 4145 // If we notice an error not handled here we pass this back to PHP by returning false 4146 // This may not work for all php versions 4147 return false; 4148 } 4149 4150 /** 4151 * Removes absolute path to phpBB root directory from error messages 4152 * and converts backslashes to forward slashes. 4153 * 4154 * @param string $errfile Absolute file path 4155 * (e.g. /var/www/phpbb3/phpBB/includes/functions.php) 4156 * Please note that if $errfile is outside of the phpBB root, 4157 * the root path will not be found and can not be filtered. 4158 * @return string Relative file path 4159 * (e.g. /includes/functions.php) 4160 */ 4161 function phpbb_filter_root_path($errfile) 4162 { 4163 static $root_path; 4164 4165 if (empty($root_path)) 4166 { 4167 $root_path = phpbb_realpath(dirname(__FILE__) . '/../'); 4168 } 4169 4170 return str_replace(array($root_path, '\\'), array('[ROOT]', '/'), $errfile); 4171 } 4172 4173 /** 4174 * Queries the session table to get information about online guests 4175 * @param int $item_id Limits the search to the item with this id 4176 * @param string $item The name of the item which is stored in the session table as session_{$item}_id 4177 * @return int The number of active distinct guest sessions 4178 */ 4179 function obtain_guest_count($item_id = 0, $item = 'forum') 4180 { 4181 global $db, $config; 4182 4183 if ($item_id) 4184 { 4185 $reading_sql = ' AND s.session_' . $item . '_id = ' . (int) $item_id; 4186 } 4187 else 4188 { 4189 $reading_sql = ''; 4190 } 4191 $time = (time() - (intval($config['load_online_time']) * 60)); 4192 4193 // Get number of online guests 4194 4195 if ($db->get_sql_layer() === 'sqlite' || $db->get_sql_layer() === 'sqlite3') 4196 { 4197 $sql = 'SELECT COUNT(session_ip) as num_guests 4198 FROM ( 4199 SELECT DISTINCT s.session_ip 4200 FROM ' . SESSIONS_TABLE . ' s 4201 WHERE s.session_user_id = ' . ANONYMOUS . ' 4202 AND s.session_time >= ' . ($time - ((int) ($time % 60))) . 4203 $reading_sql . 4204 ')'; 4205 } 4206 else 4207 { 4208 $sql = 'SELECT COUNT(DISTINCT s.session_ip) as num_guests 4209 FROM ' . SESSIONS_TABLE . ' s 4210 WHERE s.session_user_id = ' . ANONYMOUS . ' 4211 AND s.session_time >= ' . ($time - ((int) ($time % 60))) . 4212 $reading_sql; 4213 } 4214 $result = $db->sql_query($sql); 4215 $guests_online = (int) $db->sql_fetchfield('num_guests'); 4216 $db->sql_freeresult($result); 4217 4218 return $guests_online; 4219 } 4220 4221 /** 4222 * Queries the session table to get information about online users 4223 * @param int $item_id Limits the search to the item with this id 4224 * @param string $item The name of the item which is stored in the session table as session_{$item}_id 4225 * @return array An array containing the ids of online, hidden and visible users, as well as statistical info 4226 */ 4227 function obtain_users_online($item_id = 0, $item = 'forum') 4228 { 4229 global $db, $config, $user; 4230 4231 $reading_sql = ''; 4232 if ($item_id !== 0) 4233 { 4234 $reading_sql = ' AND s.session_' . $item . '_id = ' . (int) $item_id; 4235 } 4236 4237 $online_users = array( 4238 'online_users' => array(), 4239 'hidden_users' => array(), 4240 'total_online' => 0, 4241 'visible_online' => 0, 4242 'hidden_online' => 0, 4243 'guests_online' => 0, 4244 ); 4245 4246 if ($config['load_online_guests']) 4247 { 4248 $online_users['guests_online'] = obtain_guest_count($item_id, $item); 4249 } 4250 4251 // a little discrete magic to cache this for 30 seconds 4252 $time = (time() - (intval($config['load_online_time']) * 60)); 4253 4254 $sql = 'SELECT s.session_user_id, s.session_ip, s.session_viewonline 4255 FROM ' . SESSIONS_TABLE . ' s 4256 WHERE s.session_time >= ' . ($time - ((int) ($time % 30))) . 4257 $reading_sql . 4258 ' AND s.session_user_id <> ' . ANONYMOUS; 4259 $result = $db->sql_query($sql); 4260 4261 while ($row = $db->sql_fetchrow($result)) 4262 { 4263 // Skip multiple sessions for one user 4264 if (!isset($online_users['online_users'][$row['session_user_id']])) 4265 { 4266 $online_users['online_users'][$row['session_user_id']] = (int) $row['session_user_id']; 4267 if ($row['session_viewonline']) 4268 { 4269 $online_users['visible_online']++; 4270 } 4271 else 4272 { 4273 $online_users['hidden_users'][$row['session_user_id']] = (int) $row['session_user_id']; 4274 $online_users['hidden_online']++; 4275 } 4276 } 4277 } 4278 $online_users['total_online'] = $online_users['guests_online'] + $online_users['visible_online'] + $online_users['hidden_online']; 4279 $db->sql_freeresult($result); 4280 4281 return $online_users; 4282 } 4283 4284 /** 4285 * Uses the result of obtain_users_online to generate a localized, readable representation. 4286 * @param mixed $online_users result of obtain_users_online - array with user_id lists for total, hidden and visible users, and statistics 4287 * @param int $item_id Indicate that the data is limited to one item and not global 4288 * @param string $item The name of the item which is stored in the session table as session_{$item}_id 4289 * @return array An array containing the string for output to the template 4290 */ 4291 function obtain_users_online_string($online_users, $item_id = 0, $item = 'forum') 4292 { 4293 global $config, $db, $user, $auth, $phpbb_dispatcher; 4294 4295 $guests_online = $hidden_online = $l_online_users = $online_userlist = $visible_online = ''; 4296 $user_online_link = $rowset = array(); 4297 // Need caps version of $item for language-strings 4298 $item_caps = strtoupper($item); 4299 4300 if (sizeof($online_users['online_users'])) 4301 { 4302 $sql_ary = array( 4303 'SELECT' => 'u.username, u.username_clean, u.user_id, u.user_type, u.user_allow_viewonline, u.user_colour', 4304 'FROM' => array( 4305 USERS_TABLE => 'u', 4306 ), 4307 'WHERE' => $db->sql_in_set('u.user_id', $online_users['online_users']), 4308 'ORDER_BY' => 'u.username_clean ASC', 4309 ); 4310 4311 /** 4312 * Modify SQL query to obtain online users data 4313 * 4314 * @event core.obtain_users_online_string_sql 4315 * @var array online_users Array with online users data 4316 * from obtain_users_online() 4317 * @var int item_id Restrict online users to item id 4318 * @var string item Restrict online users to a certain 4319 * session item, e.g. forum for 4320 * session_forum_id 4321 * @var array sql_ary SQL query array to obtain users online data 4322 * @since 3.1.4-RC1 4323 * @changed 3.1.7-RC1 Change sql query into array and adjust var accordingly. Allows extension authors the ability to adjust the sql_ary. 4324 */ 4325 $vars = array('online_users', 'item_id', 'item', 'sql_ary'); 4326 extract($phpbb_dispatcher->trigger_event('core.obtain_users_online_string_sql', compact($vars))); 4327 4328 $result = $db->sql_query($db->sql_build_query('SELECT', $sql_ary)); 4329 $rowset = $db->sql_fetchrowset($result); 4330 $db->sql_freeresult($result); 4331 4332 foreach ($rowset as $row) 4333 { 4334 // User is logged in and therefore not a guest 4335 if ($row['user_id'] != ANONYMOUS) 4336 { 4337 if (isset($online_users['hidden_users'][$row['user_id']])) 4338 { 4339 $row['username'] = '<em>' . $row['username'] . '</em>'; 4340 } 4341 4342 if (!isset($online_users['hidden_users'][$row['user_id']]) || $auth->acl_get('u_viewonline') || $row['user_id'] === $user->data['user_id']) 4343 { 4344 $user_online_link[$row['user_id']] = get_username_string(($row['user_type'] <> USER_IGNORE) ? 'full' : 'no_profile', $row['user_id'], $row['username'], $row['user_colour']); 4345 } 4346 } 4347 } 4348 } 4349 4350 /** 4351 * Modify online userlist data 4352 * 4353 * @event core.obtain_users_online_string_before_modify 4354 * @var array online_users Array with online users data 4355 * from obtain_users_online() 4356 * @var int item_id Restrict online users to item id 4357 * @var string item Restrict online users to a certain 4358 * session item, e.g. forum for 4359 * session_forum_id 4360 * @var array rowset Array with online users data 4361 * @var array user_online_link Array with online users items (usernames) 4362 * @since 3.1.10-RC1 4363 */ 4364 $vars = array( 4365 'online_users', 4366 'item_id', 4367 'item', 4368 'rowset', 4369 'user_online_link', 4370 ); 4371 extract($phpbb_dispatcher->trigger_event('core.obtain_users_online_string_before_modify', compact($vars))); 4372 4373 $online_userlist = implode(', ', $user_online_link); 4374 4375 if (!$online_userlist) 4376 { 4377 $online_userlist = $user->lang['NO_ONLINE_USERS']; 4378 } 4379 4380 if ($item_id === 0) 4381 { 4382 $online_userlist = $user->lang['REGISTERED_USERS'] . ' ' . $online_userlist; 4383 } 4384 else if ($config['load_online_guests']) 4385 { 4386 $online_userlist = $user->lang('BROWSING_' . $item_caps . '_GUESTS', $online_users['guests_online'], $online_userlist); 4387 } 4388 else 4389 { 4390 $online_userlist = sprintf($user->lang['BROWSING_' . $item_caps], $online_userlist); 4391 } 4392 // Build online listing 4393 $visible_online = $user->lang('REG_USERS_TOTAL', (int) $online_users['visible_online']); 4394 $hidden_online = $user->lang('HIDDEN_USERS_TOTAL', (int) $online_users['hidden_online']); 4395 4396 if ($config['load_online_guests']) 4397 { 4398 $guests_online = $user->lang('GUEST_USERS_TOTAL', (int) $online_users['guests_online']); 4399 $l_online_users = $user->lang('ONLINE_USERS_TOTAL_GUESTS', (int) $online_users['total_online'], $visible_online, $hidden_online, $guests_online); 4400 } 4401 else 4402 { 4403 $l_online_users = $user->lang('ONLINE_USERS_TOTAL', (int) $online_users['total_online'], $visible_online, $hidden_online); 4404 } 4405 4406 /** 4407 * Modify online userlist data 4408 * 4409 * @event core.obtain_users_online_string_modify 4410 * @var array online_users Array with online users data 4411 * from obtain_users_online() 4412 * @var int item_id Restrict online users to item id 4413 * @var string item Restrict online users to a certain 4414 * session item, e.g. forum for 4415 * session_forum_id 4416 * @var array rowset Array with online users data 4417 * @var array user_online_link Array with online users items (usernames) 4418 * @var string online_userlist String containing users online list 4419 * @var string l_online_users String with total online users count info 4420 * @since 3.1.4-RC1 4421 */ 4422 $vars = array( 4423 'online_users', 4424 'item_id', 4425 'item', 4426 'rowset', 4427 'user_online_link', 4428 'online_userlist', 4429 'l_online_users', 4430 ); 4431 extract($phpbb_dispatcher->trigger_event('core.obtain_users_online_string_modify', compact($vars))); 4432 4433 return array( 4434 'online_userlist' => $online_userlist, 4435 'l_online_users' => $l_online_users, 4436 ); 4437 } 4438 4439 /** 4440 * Get option bitfield from custom data 4441 * 4442 * @param int $bit The bit/value to get 4443 * @param int $data Current bitfield to check 4444 * @return bool Returns true if value of constant is set in bitfield, else false 4445 */ 4446 function phpbb_optionget($bit, $data) 4447 { 4448 return ($data & 1 << (int) $bit) ? true : false; 4449 } 4450 4451 /** 4452 * Set option bitfield 4453 * 4454 * @param int $bit The bit/value to set/unset 4455 * @param bool $set True if option should be set, false if option should be unset. 4456 * @param int $data Current bitfield to change 4457 * 4458 * @return int The new bitfield 4459 */ 4460 function phpbb_optionset($bit, $set, $data) 4461 { 4462 if ($set && !($data & 1 << $bit)) 4463 { 4464 $data += 1 << $bit; 4465 } 4466 else if (!$set && ($data & 1 << $bit)) 4467 { 4468 $data -= 1 << $bit; 4469 } 4470 4471 return $data; 4472 } 4473 4474 /** 4475 * Determine which plural form we should use. 4476 * For some languages this is not as simple as for English. 4477 * 4478 * @param $rule int ID of the plural rule we want to use, see http://wiki.phpbb.com/Plural_Rules#Plural_Rules 4479 * @param $number int|float The number we want to get the plural case for. Float numbers are floored. 4480 * @return int The plural-case we need to use for the number plural-rule combination 4481 */ 4482 function phpbb_get_plural_form($rule, $number) 4483 { 4484 $number = (int) $number; 4485 4486 if ($rule > 15 || $rule < 0) 4487 { 4488 trigger_error('INVALID_PLURAL_RULE'); 4489 } 4490 4491 /** 4492 * The following plural rules are based on a list published by the Mozilla Developer Network 4493 * https://developer.mozilla.org/en/Localization_and_Plurals 4494 */ 4495 switch ($rule) 4496 { 4497 case 0: 4498 /** 4499 * Families: Asian (Chinese, Japanese, Korean, Vietnamese), Persian, Turkic/Altaic (Turkish), Thai, Lao 4500 * 1 - everything: 0, 1, 2, ... 4501 */ 4502 return 1; 4503 4504 case 1: 4505 /** 4506 * Families: Germanic (Danish, Dutch, English, Faroese, Frisian, German, Norwegian, Swedish), Finno-Ugric (Estonian, Finnish, Hungarian), Language isolate (Basque), Latin/Greek (Greek), Semitic (Hebrew), Romanic (Italian, Portuguese, Spanish, Catalan) 4507 * 1 - 1 4508 * 2 - everything else: 0, 2, 3, ... 4509 */ 4510 return ($number == 1) ? 1 : 2; 4511 4512 case 2: 4513 /** 4514 * Families: Romanic (French, Brazilian Portuguese) 4515 * 1 - 0, 1 4516 * 2 - everything else: 2, 3, ... 4517 */ 4518 return (($number == 0) || ($number == 1)) ? 1 : 2; 4519 4520 case 3: 4521 /** 4522 * Families: Baltic (Latvian) 4523 * 1 - 0 4524 * 2 - ends in 1, not 11: 1, 21, ... 101, 121, ... 4525 * 3 - everything else: 2, 3, ... 10, 11, 12, ... 20, 22, ... 4526 */ 4527 return ($number == 0) ? 1 : ((($number % 10 == 1) && ($number % 100 != 11)) ? 2 : 3); 4528 4529 case 4: 4530 /** 4531 * Families: Celtic (Scottish Gaelic) 4532 * 1 - is 1 or 11: 1, 11 4533 * 2 - is 2 or 12: 2, 12 4534 * 3 - others between 3 and 19: 3, 4, ... 10, 13, ... 18, 19 4535 * 4 - everything else: 0, 20, 21, ... 4536 */ 4537 return ($number == 1 || $number == 11) ? 1 : (($number == 2 || $number == 12) ? 2 : (($number >= 3 && $number <= 19) ? 3 : 4)); 4538 4539 case 5: 4540 /** 4541 * Families: Romanic (Romanian) 4542 * 1 - 1 4543 * 2 - is 0 or ends in 01-19: 0, 2, 3, ... 19, 101, 102, ... 119, 201, ... 4544 * 3 - everything else: 20, 21, ... 4545 */ 4546 return ($number == 1) ? 1 : ((($number == 0) || (($number % 100 > 0) && ($number % 100 < 20))) ? 2 : 3); 4547 4548 case 6: 4549 /** 4550 * Families: Baltic (Lithuanian) 4551 * 1 - ends in 1, not 11: 1, 21, 31, ... 101, 121, ... 4552 * 2 - ends in 0 or ends in 10-20: 0, 10, 11, 12, ... 19, 20, 30, 40, ... 4553 * 3 - everything else: 2, 3, ... 8, 9, 22, 23, ... 29, 32, 33, ... 4554 */ 4555 return (($number % 10 == 1) && ($number % 100 != 11)) ? 1 : ((($number % 10 < 2) || (($number % 100 >= 10) && ($number % 100 < 20))) ? 2 : 3); 4556 4557 case 7: 4558 /** 4559 * Families: Slavic (Croatian, Serbian, Russian, Ukrainian) 4560 * 1 - ends in 1, not 11: 1, 21, 31, ... 101, 121, ... 4561 * 2 - ends in 2-4, not 12-14: 2, 3, 4, 22, 23, 24, 32, ... 4562 * 3 - everything else: 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 26, ... 4563 */ 4564 return (($number % 10 == 1) && ($number % 100 != 11)) ? 1 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 2 : 3); 4565 4566 case 8: 4567 /** 4568 * Families: Slavic (Slovak, Czech) 4569 * 1 - 1 4570 * 2 - 2, 3, 4 4571 * 3 - everything else: 0, 5, 6, 7, ... 4572 */ 4573 return ($number == 1) ? 1 : ((($number >= 2) && ($number <= 4)) ? 2 : 3); 4574 4575 case 9: 4576 /** 4577 * Families: Slavic (Polish) 4578 * 1 - 1 4579 * 2 - ends in 2-4, not 12-14: 2, 3, 4, 22, 23, 24, 32, ... 104, 122, ... 4580 * 3 - everything else: 0, 5, 6, ... 11, 12, 13, 14, 15, ... 20, 21, 25, ... 4581 */ 4582 return ($number == 1) ? 1 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 2 : 3); 4583 4584 case 10: 4585 /** 4586 * Families: Slavic (Slovenian, Sorbian) 4587 * 1 - ends in 01: 1, 101, 201, ... 4588 * 2 - ends in 02: 2, 102, 202, ... 4589 * 3 - ends in 03-04: 3, 4, 103, 104, 203, 204, ... 4590 * 4 - everything else: 0, 5, 6, 7, 8, 9, 10, 11, ... 4591 */ 4592 return ($number % 100 == 1) ? 1 : (($number % 100 == 2) ? 2 : ((($number % 100 == 3) || ($number % 100 == 4)) ? 3 : 4)); 4593 4594 case 11: 4595 /** 4596 * Families: Celtic (Irish Gaeilge) 4597 * 1 - 1 4598 * 2 - 2 4599 * 3 - is 3-6: 3, 4, 5, 6 4600 * 4 - is 7-10: 7, 8, 9, 10 4601 * 5 - everything else: 0, 11, 12, ... 4602 */ 4603 return ($number == 1) ? 1 : (($number == 2) ? 2 : (($number >= 3 && $number <= 6) ? 3 : (($number >= 7 && $number <= 10) ? 4 : 5))); 4604 4605 case 12: 4606 /** 4607 * Families: Semitic (Arabic) 4608 * 1 - 1 4609 * 2 - 2 4610 * 3 - ends in 03-10: 3, 4, ... 10, 103, 104, ... 110, 203, 204, ... 4611 * 4 - ends in 11-99: 11, ... 99, 111, 112, ... 4612 * 5 - everything else: 100, 101, 102, 200, 201, 202, ... 4613 * 6 - 0 4614 */ 4615 return ($number == 1) ? 1 : (($number == 2) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : (($number != 0) ? 5 : 6)))); 4616 4617 case 13: 4618 /** 4619 * Families: Semitic (Maltese) 4620 * 1 - 1 4621 * 2 - is 0 or ends in 01-10: 0, 2, 3, ... 9, 10, 101, 102, ... 4622 * 3 - ends in 11-19: 11, 12, ... 18, 19, 111, 112, ... 4623 * 4 - everything else: 20, 21, ... 4624 */ 4625 return ($number == 1) ? 1 : ((($number == 0) || (($number % 100 > 1) && ($number % 100 < 11))) ? 2 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 3 : 4)); 4626 4627 case 14: 4628 /** 4629 * Families: Slavic (Macedonian) 4630 * 1 - ends in 1: 1, 11, 21, ... 4631 * 2 - ends in 2: 2, 12, 22, ... 4632 * 3 - everything else: 0, 3, 4, ... 10, 13, 14, ... 20, 23, ... 4633 */ 4634 return ($number % 10 == 1) ? 1 : (($number % 10 == 2) ? 2 : 3); 4635 4636 case 15: 4637 /** 4638 * Families: Icelandic 4639 * 1 - ends in 1, not 11: 1, 21, 31, ... 101, 121, 131, ... 4640 * 2 - everything else: 0, 2, 3, ... 10, 11, 12, ... 20, 22, ... 4641 */ 4642 return (($number % 10 == 1) && ($number % 100 != 11)) ? 1 : 2; 4643 } 4644 } 4645 4646 /** 4647 * Login using http authenticate. 4648 * 4649 * @param array $param Parameter array, see $param_defaults array. 4650 * 4651 * @return null 4652 */ 4653 function phpbb_http_login($param) 4654 { 4655 global $auth, $user, $request; 4656 global $config; 4657 4658 $param_defaults = array( 4659 'auth_message' => '', 4660 4661 'autologin' => false, 4662 'viewonline' => true, 4663 'admin' => false, 4664 ); 4665 4666 // Overwrite default values with passed values 4667 $param = array_merge($param_defaults, $param); 4668 4669 // User is already logged in 4670 // We will not overwrite his session 4671 if (!empty($user->data['is_registered'])) 4672 { 4673 return; 4674 } 4675 4676 // $_SERVER keys to check 4677 $username_keys = array( 4678 'PHP_AUTH_USER', 4679 'Authorization', 4680 'REMOTE_USER', 'REDIRECT_REMOTE_USER', 4681 'HTTP_AUTHORIZATION', 'REDIRECT_HTTP_AUTHORIZATION', 4682 'REMOTE_AUTHORIZATION', 'REDIRECT_REMOTE_AUTHORIZATION', 4683 'AUTH_USER', 4684 ); 4685 4686 $password_keys = array( 4687 'PHP_AUTH_PW', 4688 'REMOTE_PASSWORD', 4689 'AUTH_PASSWORD', 4690 ); 4691 4692 $username = null; 4693 foreach ($username_keys as $k) 4694 { 4695 if ($request->is_set($k, \phpbb\request\request_interface::SERVER)) 4696 { 4697 $username = htmlspecialchars_decode($request->server($k)); 4698 break; 4699 } 4700 } 4701 4702 $password = null; 4703 foreach ($password_keys as $k) 4704 { 4705 if ($request->is_set($k, \phpbb\request\request_interface::SERVER)) 4706 { 4707 $password = htmlspecialchars_decode($request->server($k)); 4708 break; 4709 } 4710 } 4711 4712 // Decode encoded information (IIS, CGI, FastCGI etc.) 4713 if (!is_null($username) && is_null($password) && strpos($username, 'Basic ') === 0) 4714 { 4715 list($username, $password) = explode(':', base64_decode(substr($username, 6)), 2); 4716 } 4717 4718 if (!is_null($username) && !is_null($password)) 4719 { 4720 set_var($username, $username, 'string', true); 4721 set_var($password, $password, 'string', true); 4722 4723 $auth_result = $auth->login($username, $password, $param['autologin'], $param['viewonline'], $param['admin']); 4724 4725 if ($auth_result['status'] == LOGIN_SUCCESS) 4726 { 4727 return; 4728 } 4729 else if ($auth_result['status'] == LOGIN_ERROR_ATTEMPTS) 4730 { 4731 send_status_line(401, 'Unauthorized'); 4732 4733 trigger_error('NOT_AUTHORISED'); 4734 } 4735 } 4736 4737 // Prepend sitename to auth_message 4738 $param['auth_message'] = ($param['auth_message'] === '') ? $config['sitename'] : $config['sitename'] . ' - ' . $param['auth_message']; 4739 4740 // We should probably filter out non-ASCII characters - RFC2616 4741 $param['auth_message'] = preg_replace('/[\x80-\xFF]/', '?', $param['auth_message']); 4742 4743 header('WWW-Authenticate: Basic realm="' . $param['auth_message'] . '"'); 4744 send_status_line(401, 'Unauthorized'); 4745 4746 trigger_error('NOT_AUTHORISED'); 4747 } 4748 4749 /** 4750 * Escapes and quotes a string for use as an HTML/XML attribute value. 4751 * 4752 * This is a port of Python xml.sax.saxutils quoteattr. 4753 * 4754 * The function will attempt to choose a quote character in such a way as to 4755 * avoid escaping quotes in the string. If this is not possible the string will 4756 * be wrapped in double quotes and double quotes will be escaped. 4757 * 4758 * @param string $data The string to be escaped 4759 * @param array $entities Associative array of additional entities to be escaped 4760 * @return string Escaped and quoted string 4761 */ 4762 function phpbb_quoteattr($data, $entities = null) 4763 { 4764 $data = str_replace('&', '&', $data); 4765 $data = str_replace('>', '>', $data); 4766 $data = str_replace('<', '<', $data); 4767 4768 $data = str_replace("\n", ' ', $data); 4769 $data = str_replace("\r", ' ', $data); 4770 $data = str_replace("\t", '	', $data); 4771 4772 if (!empty($entities)) 4773 { 4774 $data = str_replace(array_keys($entities), array_values($entities), $data); 4775 } 4776 4777 if (strpos($data, '"') !== false) 4778 { 4779 if (strpos($data, "'") !== false) 4780 { 4781 $data = '"' . str_replace('"', '"', $data) . '"'; 4782 } 4783 else 4784 { 4785 $data = "'" . $data . "'"; 4786 } 4787 } 4788 else 4789 { 4790 $data = '"' . $data . '"'; 4791 } 4792 4793 return $data; 4794 } 4795 4796 /** 4797 * Converts query string (GET) parameters in request into hidden fields. 4798 * 4799 * Useful for forwarding GET parameters when submitting forms with GET method. 4800 * 4801 * It is possible to omit some of the GET parameters, which is useful if 4802 * they are specified in the form being submitted. 4803 * 4804 * sid is always omitted. 4805 * 4806 * @param \phpbb\request\request $request Request object 4807 * @param array $exclude A list of variable names that should not be forwarded 4808 * @return string HTML with hidden fields 4809 */ 4810 function phpbb_build_hidden_fields_for_query_params($request, $exclude = null) 4811 { 4812 $names = $request->variable_names(\phpbb\request\request_interface::GET); 4813 $hidden = ''; 4814 foreach ($names as $name) 4815 { 4816 // Sessions are dealt with elsewhere, omit sid always 4817 if ($name == 'sid') 4818 { 4819 continue; 4820 } 4821 4822 // Omit any additional parameters requested 4823 if (!empty($exclude) && in_array($name, $exclude)) 4824 { 4825 continue; 4826 } 4827 4828 $escaped_name = phpbb_quoteattr($name); 4829 4830 // Note: we might retrieve the variable from POST or cookies 4831 // here. To avoid exposing cookies, skip variables that are 4832 // overwritten somewhere other than GET entirely. 4833 $value = $request->variable($name, '', true); 4834 $get_value = $request->variable($name, '', true, \phpbb\request\request_interface::GET); 4835 if ($value === $get_value) 4836 { 4837 $escaped_value = phpbb_quoteattr($value); 4838 $hidden .= "<input type='hidden' name=$escaped_name value=$escaped_value />"; 4839 } 4840 } 4841 return $hidden; 4842 } 4843 4844 /** 4845 * Get user avatar 4846 * 4847 * @param array $user_row Row from the users table 4848 * @param string $alt Optional language string for alt tag within image, can be a language key or text 4849 * @param bool $ignore_config Ignores the config-setting, to be still able to view the avatar in the UCP 4850 * @param bool $lazy If true, will be lazy loaded (requires JS) 4851 * 4852 * @return string Avatar html 4853 */ 4854 function phpbb_get_user_avatar($user_row, $alt = 'USER_AVATAR', $ignore_config = false, $lazy = false) 4855 { 4856 $row = \phpbb\avatar\manager::clean_row($user_row, 'user'); 4857 return phpbb_get_avatar($row, $alt, $ignore_config, $lazy); 4858 } 4859 4860 /** 4861 * Get group avatar 4862 * 4863 * @param array $group_row Row from the groups table 4864 * @param string $alt Optional language string for alt tag within image, can be a language key or text 4865 * @param bool $ignore_config Ignores the config-setting, to be still able to view the avatar in the UCP 4866 * @param bool $lazy If true, will be lazy loaded (requires JS) 4867 * 4868 * @return string Avatar html 4869 */ 4870 function phpbb_get_group_avatar($user_row, $alt = 'GROUP_AVATAR', $ignore_config = false, $lazy = false) 4871 { 4872 $row = \phpbb\avatar\manager::clean_row($user_row, 'group'); 4873 return phpbb_get_avatar($row, $alt, $ignore_config, $lazy); 4874 } 4875 4876 /** 4877 * Get avatar 4878 * 4879 * @param array $row Row cleaned by \phpbb\avatar\manager::clean_row 4880 * @param string $alt Optional language string for alt tag within image, can be a language key or text 4881 * @param bool $ignore_config Ignores the config-setting, to be still able to view the avatar in the UCP 4882 * @param bool $lazy If true, will be lazy loaded (requires JS) 4883 * 4884 * @return string Avatar html 4885 */ 4886 function phpbb_get_avatar($row, $alt, $ignore_config = false, $lazy = false) 4887 { 4888 global $user, $config, $cache, $phpbb_root_path, $phpEx; 4889 global $request; 4890 global $phpbb_container, $phpbb_dispatcher; 4891 4892 if (!$config['allow_avatar'] && !$ignore_config) 4893 { 4894 return ''; 4895 } 4896 4897 $avatar_data = array( 4898 'src' => $row['avatar'], 4899 'width' => $row['avatar_width'], 4900 'height' => $row['avatar_height'], 4901 ); 4902 4903 $phpbb_avatar_manager = $phpbb_container->get('avatar.manager'); 4904 $driver = $phpbb_avatar_manager->get_driver($row['avatar_type'], !$ignore_config); 4905 $html = ''; 4906 4907 if ($driver) 4908 { 4909 $html = $driver->get_custom_html($user, $row, $alt); 4910 if (!empty($html)) 4911 { 4912 return $html; 4913 } 4914 4915 $avatar_data = $driver->get_data($row); 4916 } 4917 else 4918 { 4919 $avatar_data['src'] = ''; 4920 } 4921 4922 if (!empty($avatar_data['src'])) 4923 { 4924 if ($lazy) 4925 { 4926 // Determine board url - we may need it later 4927 $board_url = generate_board_url() . '/'; 4928 // This path is sent with the base template paths in the assign_vars() 4929 // call below. We need to correct it in case we are accessing from a 4930 // controller because the web paths will be incorrect otherwise. 4931 $phpbb_path_helper = $phpbb_container->get('path_helper'); 4932 $corrected_path = $phpbb_path_helper->get_web_root_path(); 4933 4934 $web_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? $board_url : $corrected_path; 4935 4936 $theme = "{$web_path}styles/" . rawurlencode($user->style['style_path']) . '/theme'; 4937 4938 $src = 'src="' . $theme . '/images/no_avatar.gif" data-src="' . $avatar_data['src'] . '"'; 4939 } 4940 else 4941 { 4942 $src = 'src="' . $avatar_data['src'] . '"'; 4943 } 4944 4945 $html = '<img class="avatar" ' . $src . ' ' . 4946 ($avatar_data['width'] ? ('width="' . $avatar_data['width'] . '" ') : '') . 4947 ($avatar_data['height'] ? ('height="' . $avatar_data['height'] . '" ') : '') . 4948 'alt="' . ((!empty($user->lang[$alt])) ? $user->lang[$alt] : $alt) . '" />'; 4949 } 4950 4951 /** 4952 * Event to modify HTML <img> tag of avatar 4953 * 4954 * @event core.get_avatar_after 4955 * @var array row Row cleaned by \phpbb\avatar\manager::clean_row 4956 * @var string alt Optional language string for alt tag within image, can be a language key or text 4957 * @var bool ignore_config Ignores the config-setting, to be still able to view the avatar in the UCP 4958 * @var array avatar_data The HTML attributes for avatar <img> tag 4959 * @var string html The HTML <img> tag of generated avatar 4960 * @since 3.1.6-RC1 4961 */ 4962 $vars = array('row', 'alt', 'ignore_config', 'avatar_data', 'html'); 4963 extract($phpbb_dispatcher->trigger_event('core.get_avatar_after', compact($vars))); 4964 4965 return $html; 4966 } 4967 4968 /** 4969 * Generate page header 4970 */ 4971 function page_header($page_title = '', $display_online_list = false, $item_id = 0, $item = 'forum', $send_headers = true) 4972 { 4973 global $db, $config, $template, $SID, $_SID, $_EXTRA_URL, $user, $auth, $phpEx, $phpbb_root_path; 4974 global $phpbb_dispatcher, $request, $phpbb_container, $phpbb_admin_path; 4975 4976 if (defined('HEADER_INC')) 4977 { 4978 return; 4979 } 4980 4981 define('HEADER_INC', true); 4982 4983 // A listener can set this variable to `true` when it overrides this function 4984 $page_header_override = false; 4985 4986 /** 4987 * Execute code and/or overwrite page_header() 4988 * 4989 * @event core.page_header 4990 * @var string page_title Page title 4991 * @var bool display_online_list Do we display online users list 4992 * @var string item Restrict online users to a certain 4993 * session item, e.g. forum for 4994 * session_forum_id 4995 * @var int item_id Restrict online users to item id 4996 * @var bool page_header_override Shall we return instead of running 4997 * the rest of page_header() 4998 * @since 3.1.0-a1 4999 */ 5000 $vars = array('page_title', 'display_online_list', 'item_id', 'item', 'page_header_override'); 5001 extract($phpbb_dispatcher->trigger_event('core.page_header', compact($vars))); 5002 5003 if ($page_header_override) 5004 { 5005 return; 5006 } 5007 5008 // gzip_compression 5009 if ($config['gzip_compress']) 5010 { 5011 // to avoid partially compressed output resulting in blank pages in 5012 // the browser or error messages, compression is disabled in a few cases: 5013 // 5014 // 1) if headers have already been sent, this indicates plaintext output 5015 // has been started so further content must not be compressed 5016 // 2) the length of the current output buffer is non-zero. This means 5017 // there is already some uncompressed content in this output buffer 5018 // so further output must not be compressed 5019 // 3) if more than one level of output buffering is used because we 5020 // cannot test all output buffer level content lengths. One level 5021 // could be caused by php.ini output_buffering. Anything 5022 // beyond that is manual, so the code wrapping phpBB in output buffering 5023 // can easily compress the output itself. 5024 // 5025 if (@extension_loaded('zlib') && !headers_sent() && ob_get_level() <= 1 && ob_get_length() == 0) 5026 { 5027 ob_start('ob_gzhandler'); 5028 } 5029 } 5030 5031 $user->update_session_infos(); 5032 5033 // Generate logged in/logged out status 5034 if ($user->data['user_id'] != ANONYMOUS) 5035 { 5036 $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=logout', true, $user->session_id); 5037 $l_login_logout = $user->lang['LOGOUT']; 5038 } 5039 else 5040 { 5041 $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login'); 5042 $l_login_logout = $user->lang['LOGIN']; 5043 } 5044 5045 // Last visit date/time 5046 $s_last_visit = ($user->data['user_id'] != ANONYMOUS) ? $user->format_date($user->data['session_last_visit']) : ''; 5047 5048 // Get users online list ... if required 5049 $l_online_users = $online_userlist = $l_online_record = $l_online_time = ''; 5050 5051 if ($config['load_online'] && $config['load_online_time'] && $display_online_list) 5052 { 5053 /** 5054 * Load online data: 5055 * For obtaining another session column use $item and $item_id in the function-parameter, whereby the column is session_{$item}_id. 5056 */ 5057 $item_id = max($item_id, 0); 5058 5059 $online_users = obtain_users_online($item_id, $item); 5060 $user_online_strings = obtain_users_online_string($online_users, $item_id, $item); 5061 5062 $l_online_users = $user_online_strings['l_online_users']; 5063 $online_userlist = $user_online_strings['online_userlist']; 5064 $total_online_users = $online_users['total_online']; 5065 5066 if ($total_online_users > $config['record_online_users']) 5067 { 5068 set_config('record_online_users', $total_online_users, true); 5069 set_config('record_online_date', time(), true); 5070 } 5071 5072 $l_online_record = $user->lang('RECORD_ONLINE_USERS', (int) $config['record_online_users'], $user->format_date($config['record_online_date'], false, true)); 5073 5074 $l_online_time = $user->lang('VIEW_ONLINE_TIMES', (int) $config['load_online_time']); 5075 } 5076 5077 $s_privmsg_new = false; 5078 5079 // Check for new private messages if user is logged in 5080 if (!empty($user->data['is_registered'])) 5081 { 5082 if ($user->data['user_new_privmsg']) 5083 { 5084 if (!$user->data['user_last_privmsg'] || $user->data['user_last_privmsg'] > $user->data['session_last_visit']) 5085 { 5086 $sql = 'UPDATE ' . USERS_TABLE . ' 5087 SET user_last_privmsg = ' . $user->data['session_last_visit'] . ' 5088 WHERE user_id = ' . $user->data['user_id']; 5089 $db->sql_query($sql); 5090 5091 $s_privmsg_new = true; 5092 } 5093 else 5094 { 5095 $s_privmsg_new = false; 5096 } 5097 } 5098 else 5099 { 5100 $s_privmsg_new = false; 5101 } 5102 } 5103 5104 $forum_id = request_var('f', 0); 5105 $topic_id = request_var('t', 0); 5106 5107 $s_feed_news = false; 5108 5109 // Get option for news 5110 if ($config['feed_enable']) 5111 { 5112 $sql = 'SELECT forum_id 5113 FROM ' . FORUMS_TABLE . ' 5114 WHERE ' . $db->sql_bit_and('forum_options', FORUM_OPTION_FEED_NEWS, '<> 0'); 5115 $result = $db->sql_query_limit($sql, 1, 0, 600); 5116 $s_feed_news = (int) $db->sql_fetchfield('forum_id'); 5117 $db->sql_freeresult($result); 5118 } 5119 5120 // Determine board url - we may need it later 5121 $board_url = generate_board_url() . '/'; 5122 // This path is sent with the base template paths in the assign_vars() 5123 // call below. We need to correct it in case we are accessing from a 5124 // controller because the web paths will be incorrect otherwise. 5125 $phpbb_path_helper = $phpbb_container->get('path_helper'); 5126 $corrected_path = $phpbb_path_helper->get_web_root_path(); 5127 $web_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? $board_url : $corrected_path; 5128 5129 // Send a proper content-language to the output 5130 $user_lang = $user->lang['USER_LANG']; 5131 if (strpos($user_lang, '-x-') !== false) 5132 { 5133 $user_lang = substr($user_lang, 0, strpos($user_lang, '-x-')); 5134 } 5135 5136 $s_search_hidden_fields = array(); 5137 if ($_SID) 5138 { 5139 $s_search_hidden_fields['sid'] = $_SID; 5140 } 5141 5142 if (!empty($_EXTRA_URL)) 5143 { 5144 foreach ($_EXTRA_URL as $url_param) 5145 { 5146 $url_param = explode('=', $url_param, 2); 5147 $s_search_hidden_fields[$url_param[0]] = $url_param[1]; 5148 } 5149 } 5150 5151 $dt = $user->create_datetime(); 5152 $timezone_offset = $user->lang(array('timezones', 'UTC_OFFSET'), phpbb_format_timezone_offset($dt->getOffset())); 5153 $timezone_name = $user->timezone->getName(); 5154 if (isset($user->lang['timezones'][$timezone_name])) 5155 { 5156 $timezone_name = $user->lang['timezones'][$timezone_name]; 5157 } 5158 5159 // Output the notifications 5160 $notifications = false; 5161 if ($config['load_notifications'] && $user->data['user_id'] != ANONYMOUS && $user->data['user_type'] != USER_IGNORE) 5162 { 5163 $phpbb_notifications = $phpbb_container->get('notification_manager'); 5164 5165 $notifications = $phpbb_notifications->load_notifications(array( 5166 'all_unread' => true, 5167 'limit' => 5, 5168 )); 5169 5170 foreach ($notifications['notifications'] as $notification) 5171 { 5172 $template->assign_block_vars('notifications', $notification->prepare_for_display()); 5173 } 5174 } 5175 5176 $notification_mark_hash = generate_link_hash('mark_all_notifications_read'); 5177 5178 // The following assigns all _common_ variables that may be used at any point in a template. 5179 $template->assign_vars(array( 5180 'SITENAME' => $config['sitename'], 5181 'SITE_DESCRIPTION' => $config['site_desc'], 5182 'PAGE_TITLE' => $page_title, 5183 'SCRIPT_NAME' => str_replace('.' . $phpEx, '', $user->page['page_name']), 5184 'LAST_VISIT_DATE' => sprintf($user->lang['YOU_LAST_VISIT'], $s_last_visit), 5185 'LAST_VISIT_YOU' => $s_last_visit, 5186 'CURRENT_TIME' => sprintf($user->lang['CURRENT_TIME'], $user->format_date(time(), false, true)), 5187 'TOTAL_USERS_ONLINE' => $l_online_users, 5188 'LOGGED_IN_USER_LIST' => $online_userlist, 5189 'RECORD_USERS' => $l_online_record, 5190 5191 'PRIVATE_MESSAGE_COUNT' => (!empty($user->data['user_unread_privmsg'])) ? $user->data['user_unread_privmsg'] : 0, 5192 'CURRENT_USER_AVATAR' => phpbb_get_user_avatar($user->data), 5193 'CURRENT_USERNAME_SIMPLE' => get_username_string('no_profile', $user->data['user_id'], $user->data['username'], $user->data['user_colour']), 5194 'CURRENT_USERNAME_FULL' => get_username_string('full', $user->data['user_id'], $user->data['username'], $user->data['user_colour']), 5195 'UNREAD_NOTIFICATIONS_COUNT' => ($notifications !== false) ? $notifications['unread_count'] : '', 5196 'NOTIFICATIONS_COUNT' => ($notifications !== false) ? $notifications['unread_count'] : '', 5197 'U_VIEW_ALL_NOTIFICATIONS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=ucp_notifications'), 5198 'U_MARK_ALL_NOTIFICATIONS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=ucp_notifications&mode=notification_list&mark=all&token=' . $notification_mark_hash), 5199 'U_NOTIFICATION_SETTINGS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=ucp_notifications&mode=notification_options'), 5200 'S_NOTIFICATIONS_DISPLAY' => $config['load_notifications'], 5201 5202 'S_USER_NEW_PRIVMSG' => $user->data['user_new_privmsg'], 5203 'S_USER_UNREAD_PRIVMSG' => $user->data['user_unread_privmsg'], 5204 'S_USER_NEW' => $user->data['user_new'], 5205 5206 'SID' => $SID, 5207 '_SID' => $_SID, 5208 'SESSION_ID' => $user->session_id, 5209 'ROOT_PATH' => $web_path, 5210 'BOARD_URL' => $board_url, 5211 5212 'L_LOGIN_LOGOUT' => $l_login_logout, 5213 'L_INDEX' => ($config['board_index_text'] !== '') ? $config['board_index_text'] : $user->lang['FORUM_INDEX'], 5214 'L_SITE_HOME' => ($config['site_home_text'] !== '') ? $config['site_home_text'] : $user->lang['HOME'], 5215 'L_ONLINE_EXPLAIN' => $l_online_time, 5216 5217 'U_PRIVATEMSGS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox'), 5218 'U_RETURN_INBOX' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox'), 5219 'U_MEMBERLIST' => append_sid("{$phpbb_root_path}memberlist.$phpEx"), 5220 'U_VIEWONLINE' => ($auth->acl_gets('u_viewprofile', 'a_user', 'a_useradd', 'a_userdel')) ? append_sid("{$phpbb_root_path}viewonline.$phpEx") : '', 5221 'U_LOGIN_LOGOUT' => $u_login_logout, 5222 'U_INDEX' => append_sid("{$phpbb_root_path}index.$phpEx"), 5223 'U_SEARCH' => append_sid("{$phpbb_root_path}search.$phpEx"), 5224 'U_SITE_HOME' => $config['site_home_url'], 5225 'U_REGISTER' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register'), 5226 'U_PROFILE' => append_sid("{$phpbb_root_path}ucp.$phpEx"), 5227 'U_USER_PROFILE' => get_username_string('profile', $user->data['user_id'], $user->data['username'], $user->data['user_colour']), 5228 'U_MODCP' => append_sid("{$phpbb_root_path}mcp.$phpEx", false, true, $user->session_id), 5229 'U_FAQ' => append_sid("{$phpbb_root_path}faq.$phpEx"), 5230 'U_SEARCH_SELF' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=egosearch'), 5231 'U_SEARCH_NEW' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=newposts'), 5232 'U_SEARCH_UNANSWERED' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unanswered'), 5233 'U_SEARCH_UNREAD' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unreadposts'), 5234 'U_SEARCH_ACTIVE_TOPICS'=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=active_topics'), 5235 'U_DELETE_COOKIES' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=delete_cookies'), 5236 'U_CONTACT_US' => ($config['contact_admin_form_enable'] && $config['email_enable']) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=contactadmin') : '', 5237 'U_TEAM' => ($user->data['user_id'] != ANONYMOUS && !$auth->acl_get('u_viewprofile')) ? '' : append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=team'), 5238 'U_TERMS_USE' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'), 5239 'U_PRIVACY' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'), 5240 'U_RESTORE_PERMISSIONS' => ($user->data['user_perm_from'] && $auth->acl_get('a_switchperm')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=restore_perm') : '', 5241 'U_FEED' => generate_board_url() . "/feed.$phpEx", 5242 5243 'S_USER_LOGGED_IN' => ($user->data['user_id'] != ANONYMOUS) ? true : false, 5244 'S_AUTOLOGIN_ENABLED' => ($config['allow_autologin']) ? true : false, 5245 'S_BOARD_DISABLED' => ($config['board_disable']) ? true : false, 5246 'S_REGISTERED_USER' => (!empty($user->data['is_registered'])) ? true : false, 5247 'S_IS_BOT' => (!empty($user->data['is_bot'])) ? true : false, 5248 'S_USER_LANG' => $user_lang, 5249 'S_USER_BROWSER' => (isset($user->data['session_browser'])) ? $user->data['session_browser'] : $user->lang['UNKNOWN_BROWSER'], 5250 'S_USERNAME' => $user->data['username'], 5251 'S_CONTENT_DIRECTION' => $user->lang['DIRECTION'], 5252 'S_CONTENT_FLOW_BEGIN' => ($user->lang['DIRECTION'] == 'ltr') ? 'left' : 'right', 5253 'S_CONTENT_FLOW_END' => ($user->lang['DIRECTION'] == 'ltr') ? 'right' : 'left', 5254 'S_CONTENT_ENCODING' => 'UTF-8', 5255 'S_TIMEZONE' => sprintf($user->lang['ALL_TIMES'], $timezone_offset, $timezone_name), 5256 'S_DISPLAY_ONLINE_LIST' => ($l_online_time) ? 1 : 0, 5257 'S_DISPLAY_SEARCH' => (!$config['load_search']) ? 0 : (isset($auth) ? ($auth->acl_get('u_search') && $auth->acl_getf_global('f_search')) : 1), 5258 'S_DISPLAY_PM' => ($config['allow_privmsg'] && !empty($user->data['is_registered']) && ($auth->acl_get('u_readpm') || $auth->acl_get('u_sendpm'))) ? true : false, 5259 'S_DISPLAY_MEMBERLIST' => (isset($auth)) ? $auth->acl_get('u_viewprofile') : 0, 5260 'S_NEW_PM' => ($s_privmsg_new) ? 1 : 0, 5261 'S_REGISTER_ENABLED' => ($config['require_activation'] != USER_ACTIVATION_DISABLE) ? true : false, 5262 'S_FORUM_ID' => $forum_id, 5263 'S_TOPIC_ID' => $topic_id, 5264 5265 'S_LOGIN_ACTION' => ((!defined('ADMIN_START')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login') : append_sid("{$phpbb_admin_path}index.$phpEx", false, true, $user->session_id)), 5266 'S_LOGIN_REDIRECT' => build_hidden_fields(array('redirect' => $phpbb_path_helper->remove_web_root_path(build_url()))), 5267 5268 'S_ENABLE_FEEDS' => ($config['feed_enable']) ? true : false, 5269 'S_ENABLE_FEEDS_OVERALL' => ($config['feed_overall']) ? true : false, 5270 'S_ENABLE_FEEDS_FORUMS' => ($config['feed_overall_forums']) ? true : false, 5271 'S_ENABLE_FEEDS_TOPICS' => ($config['feed_topics_new']) ? true : false, 5272 'S_ENABLE_FEEDS_TOPICS_ACTIVE' => ($config['feed_topics_active']) ? true : false, 5273 'S_ENABLE_FEEDS_NEWS' => ($s_feed_news) ? true : false, 5274 5275 'S_LOAD_UNREADS' => ($config['load_unreads_search'] && ($config['load_anon_lastread'] || $user->data['is_registered'])) ? true : false, 5276 5277 'S_SEARCH_HIDDEN_FIELDS' => build_hidden_fields($s_search_hidden_fields), 5278 5279 'T_ASSETS_VERSION' => $config['assets_version'], 5280 'T_ASSETS_PATH' => "{$web_path}assets", 5281 'T_THEME_PATH' => "{$web_path}styles/" . rawurlencode($user->style['style_path']) . '/theme', 5282 'T_TEMPLATE_PATH' => "{$web_path}styles/" . rawurlencode($user->style['style_path']) . '/template', 5283 'T_SUPER_TEMPLATE_PATH' => "{$web_path}styles/" . rawurlencode($user->style['style_path']) . '/template', 5284 'T_IMAGES_PATH' => "{$web_path}images/", 5285 'T_SMILIES_PATH' => "{$web_path}{$config['smilies_path']}/", 5286 'T_AVATAR_PATH' => "{$web_path}{$config['avatar_path']}/", 5287 'T_AVATAR_GALLERY_PATH' => "{$web_path}{$config['avatar_gallery_path']}/", 5288 'T_ICONS_PATH' => "{$web_path}{$config['icons_path']}/", 5289 'T_RANKS_PATH' => "{$web_path}{$config['ranks_path']}/", 5290 'T_UPLOAD_PATH' => "{$web_path}{$config['upload_path']}/", 5291 'T_STYLESHEET_LINK' => "{$web_path}styles/" . rawurlencode($user->style['style_path']) . '/theme/stylesheet.css?assets_version=' . $config['assets_version'], 5292 'T_STYLESHEET_LANG_LINK' => "{$web_path}styles/" . rawurlencode($user->style['style_path']) . '/theme/' . $user->lang_name . '/stylesheet.css?assets_version=' . $config['assets_version'], 5293 'T_JQUERY_LINK' => !empty($config['allow_cdn']) && !empty($config['load_jquery_url']) ? $config['load_jquery_url'] : "{$web_path}assets/javascript/jquery.min.js?assets_version=" . $config['assets_version'], 5294 'S_ALLOW_CDN' => !empty($config['allow_cdn']), 5295 5296 'T_THEME_NAME' => rawurlencode($user->style['style_path']), 5297 'T_THEME_LANG_NAME' => $user->data['user_lang'], 5298 'T_TEMPLATE_NAME' => $user->style['style_path'], 5299 'T_SUPER_TEMPLATE_NAME' => rawurlencode((isset($user->style['style_parent_tree']) && $user->style['style_parent_tree']) ? $user->style['style_parent_tree'] : $user->style['style_path']), 5300 'T_IMAGES' => 'images', 5301 'T_SMILIES' => $config['smilies_path'], 5302 'T_AVATAR' => $config['avatar_path'], 5303 'T_AVATAR_GALLERY' => $config['avatar_gallery_path'], 5304 'T_ICONS' => $config['icons_path'], 5305 'T_RANKS' => $config['ranks_path'], 5306 'T_UPLOAD' => $config['upload_path'], 5307 5308 'SITE_LOGO_IMG' => $user->img('site_logo'), 5309 )); 5310 5311 $http_headers = array(); 5312 5313 if ($send_headers) 5314 { 5315 // An array of http headers that phpbb will set. The following event may override these. 5316 $http_headers += array( 5317 // application/xhtml+xml not used because of IE 5318 'Content-type' => 'text/html; charset=UTF-8', 5319 'Cache-Control' => 'private, no-cache="set-cookie"', 5320 'Expires' => gmdate('D, d M Y H:i:s', time()) . ' GMT', 5321 ); 5322 if (!empty($user->data['is_bot'])) 5323 { 5324 // Let reverse proxies know we detected a bot. 5325 $http_headers['X-PHPBB-IS-BOT'] = 'yes'; 5326 } 5327 } 5328 5329 /** 5330 * Execute code and/or overwrite _common_ template variables after they have been assigned. 5331 * 5332 * @event core.page_header_after 5333 * @var string page_title Page title 5334 * @var bool display_online_list Do we display online users list 5335 * @var string item Restrict online users to a certain 5336 * session item, e.g. forum for 5337 * session_forum_id 5338 * @var int item_id Restrict online users to item id 5339 * @var array http_headers HTTP headers that should be set by phpbb 5340 * 5341 * @since 3.1.0-b3 5342 */ 5343 $vars = array('page_title', 'display_online_list', 'item_id', 'item', 'http_headers'); 5344 extract($phpbb_dispatcher->trigger_event('core.page_header_after', compact($vars))); 5345 5346 foreach ($http_headers as $hname => $hval) 5347 { 5348 header((string) $hname . ': ' . (string) $hval); 5349 } 5350 5351 return; 5352 } 5353 5354 /** 5355 * Check and display the SQL report if requested. 5356 * 5357 * @param \phpbb\request\request_interface $request Request object 5358 * @param \phpbb\auth\auth $auth Auth object 5359 * @param \phpbb\db\driver\driver_interface $db Database connection 5360 */ 5361 function phpbb_check_and_display_sql_report(\phpbb\request\request_interface $request, \phpbb\auth\auth $auth, \phpbb\db\driver\driver_interface $db) 5362 { 5363 if ($request->variable('explain', false) && $auth->acl_get('a_') && defined('DEBUG')) 5364 { 5365 $db->sql_report('display'); 5366 } 5367 } 5368 5369 /** 5370 * Generate the debug output string 5371 * 5372 * @param \phpbb\db\driver\driver_interface $db Database connection 5373 * @param \phpbb\config\config $config Config object 5374 * @param \phpbb\auth\auth $auth Auth object 5375 * @param \phpbb\user $user User object 5376 * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher 5377 * @return string 5378 */ 5379 function phpbb_generate_debug_output(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\auth\auth $auth, \phpbb\user $user, \phpbb\event\dispatcher_interface $phpbb_dispatcher) 5380 { 5381 $debug_info = array(); 5382 5383 // Output page creation time 5384 if (defined('PHPBB_DISPLAY_LOAD_TIME')) 5385 { 5386 if (isset($GLOBALS['starttime'])) 5387 { 5388 $totaltime = microtime(true) - $GLOBALS['starttime']; 5389 $debug_info[] = sprintf('<abbr title="SQL time: %.3fs / PHP time: %.3fs">Time: %.3fs</abbr>', $db->get_sql_time(), ($totaltime - $db->get_sql_time()), $totaltime); 5390 } 5391 5392 $debug_info[] = sprintf('<abbr title="Cached: %d">Queries: %d</abbr>', $db->sql_num_queries(true), $db->sql_num_queries()); 5393 5394 $memory_usage = memory_get_peak_usage(); 5395 if ($memory_usage) 5396 { 5397 $memory_usage = get_formatted_filesize($memory_usage); 5398 5399 $debug_info[] = 'Peak Memory Usage: ' . $memory_usage; 5400 } 5401 } 5402 5403 if (defined('DEBUG')) 5404 { 5405 $debug_info[] = 'GZIP: ' . (($config['gzip_compress'] && @extension_loaded('zlib')) ? 'On' : 'Off'); 5406 5407 if ($user->load) 5408 { 5409 $debug_info[] = 'Load: ' . $user->load; 5410 } 5411 5412 if ($auth->acl_get('a_')) 5413 { 5414 $debug_info[] = '<a href="' . build_url() . '&explain=1">SQL Explain</a>'; 5415 } 5416 } 5417 5418 /** 5419 * Modify debug output information 5420 * 5421 * @event core.phpbb_generate_debug_output 5422 * @var array debug_info Array of strings with debug information 5423 * 5424 * @since 3.1.0-RC3 5425 */ 5426 $vars = array('debug_info'); 5427 extract($phpbb_dispatcher->trigger_event('core.phpbb_generate_debug_output', compact($vars))); 5428 5429 return implode(' | ', $debug_info); 5430 } 5431 5432 /** 5433 * Generate page footer 5434 * 5435 * @param bool $run_cron Whether or not to run the cron 5436 * @param bool $display_template Whether or not to display the template 5437 * @param bool $exit_handler Whether or not to run the exit_handler() 5438 */ 5439 function page_footer($run_cron = true, $display_template = true, $exit_handler = true) 5440 { 5441 global $db, $config, $template, $user, $auth, $cache, $starttime, $phpbb_root_path, $phpEx; 5442 global $request, $phpbb_dispatcher, $phpbb_admin_path; 5443 5444 // A listener can set this variable to `true` when it overrides this function 5445 $page_footer_override = false; 5446 5447 /** 5448 * Execute code and/or overwrite page_footer() 5449 * 5450 * @event core.page_footer 5451 * @var bool run_cron Shall we run cron tasks 5452 * @var bool page_footer_override Shall we return instead of running 5453 * the rest of page_footer() 5454 * @since 3.1.0-a1 5455 */ 5456 $vars = array('run_cron', 'page_footer_override'); 5457 extract($phpbb_dispatcher->trigger_event('core.page_footer', compact($vars))); 5458 5459 if ($page_footer_override) 5460 { 5461 return; 5462 } 5463 5464 phpbb_check_and_display_sql_report($request, $auth, $db); 5465 5466 $template->assign_vars(array( 5467 'DEBUG_OUTPUT' => phpbb_generate_debug_output($db, $config, $auth, $user, $phpbb_dispatcher), 5468 'TRANSLATION_INFO' => (!empty($user->lang['TRANSLATION_INFO'])) ? $user->lang['TRANSLATION_INFO'] : '', 5469 'CREDIT_LINE' => $user->lang('POWERED_BY', '<a href="https://www.phpbb.com/">phpBB</a>® Forum Software © phpBB Limited'), 5470 5471 'U_ACP' => ($auth->acl_get('a_') && !empty($user->data['is_registered'])) ? append_sid("{$phpbb_admin_path}index.$phpEx", false, true, $user->session_id) : '') 5472 ); 5473 5474 // Call cron-type script 5475 $call_cron = false; 5476 if (!defined('IN_CRON') && !$config['use_system_cron'] && $run_cron && !$config['board_disable'] && !$user->data['is_bot'] && !$cache->get('_cron.lock_check')) 5477 { 5478 $call_cron = true; 5479 $time_now = (!empty($user->time_now) && is_int($user->time_now)) ? $user->time_now : time(); 5480 5481 // Any old lock present? 5482 if (!empty($config['cron_lock'])) 5483 { 5484 $cron_time = explode(' ', $config['cron_lock']); 5485 5486 // If 1 hour lock is present we do not call cron.php 5487 if ($cron_time[0] + 3600 >= $time_now) 5488 { 5489 $call_cron = false; 5490 } 5491 } 5492 } 5493 5494 // Call cron job? 5495 if ($call_cron) 5496 { 5497 global $phpbb_container; 5498 $cron = $phpbb_container->get('cron.manager'); 5499 $task = $cron->find_one_ready_task(); 5500 5501 if ($task) 5502 { 5503 $url = $task->get_url(); 5504 $template->assign_var('RUN_CRON_TASK', '<img src="' . $url . '" width="1" height="1" alt="cron" />'); 5505 } 5506 else 5507 { 5508 $cache->put('_cron.lock_check', true, 60); 5509 } 5510 } 5511 5512 /** 5513 * Execute code and/or modify output before displaying the template. 5514 * 5515 * @event core.page_footer_after 5516 * @var bool display_template Whether or not to display the template 5517 * @var bool exit_handler Whether or not to run the exit_handler() 5518 * 5519 * @since 3.1.0-RC5 5520 */ 5521 $vars = array('display_template', 'exit_handler'); 5522 extract($phpbb_dispatcher->trigger_event('core.page_footer_after', compact($vars))); 5523 5524 if ($display_template) 5525 { 5526 $template->display('body'); 5527 } 5528 5529 garbage_collection(); 5530 5531 if ($exit_handler) 5532 { 5533 exit_handler(); 5534 } 5535 } 5536 5537 /** 5538 * Closing the cache object and the database 5539 * Cool function name, eh? We might want to add operations to it later 5540 */ 5541 function garbage_collection() 5542 { 5543 global $cache, $db; 5544 global $phpbb_dispatcher; 5545 5546 if (!empty($phpbb_dispatcher)) 5547 { 5548 /** 5549 * Unload some objects, to free some memory, before we finish our task 5550 * 5551 * @event core.garbage_collection 5552 * @since 3.1.0-a1 5553 */ 5554 $phpbb_dispatcher->dispatch('core.garbage_collection'); 5555 } 5556 5557 // Unload cache, must be done before the DB connection if closed 5558 if (!empty($cache)) 5559 { 5560 $cache->unload(); 5561 } 5562 5563 // Close our DB connection. 5564 if (!empty($db)) 5565 { 5566 $db->sql_close(); 5567 } 5568 } 5569 5570 /** 5571 * Handler for exit calls in phpBB. 5572 * This function supports hooks. 5573 * 5574 * Note: This function is called after the template has been outputted. 5575 */ 5576 function exit_handler() 5577 { 5578 global $phpbb_hook, $config; 5579 5580 if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__)) 5581 { 5582 if ($phpbb_hook->hook_return(__FUNCTION__)) 5583 { 5584 return $phpbb_hook->hook_return_result(__FUNCTION__); 5585 } 5586 } 5587 5588 // As a pre-caution... some setups display a blank page if the flush() is not there. 5589 (ob_get_level() > 0) ? @ob_flush() : @flush(); 5590 5591 exit; 5592 } 5593 5594 /** 5595 * Handler for init calls in phpBB. This function is called in \phpbb\user::setup(); 5596 * This function supports hooks. 5597 */ 5598 function phpbb_user_session_handler() 5599 { 5600 global $phpbb_hook; 5601 5602 if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__)) 5603 { 5604 if ($phpbb_hook->hook_return(__FUNCTION__)) 5605 { 5606 return $phpbb_hook->hook_return_result(__FUNCTION__); 5607 } 5608 } 5609 5610 return; 5611 } 5612 5613 /** 5614 * Check if PCRE has UTF-8 support 5615 * PHP may not be linked with the bundled PCRE lib and instead with an older version 5616 * 5617 * @return bool Returns true if PCRE (the regular expressions library) supports UTF-8 encoding 5618 */ 5619 function phpbb_pcre_utf8_support() 5620 { 5621 static $utf8_pcre_properties = null; 5622 if (is_null($utf8_pcre_properties)) 5623 { 5624 $utf8_pcre_properties = (@preg_match('/\p{L}/u', 'a') !== false); 5625 } 5626 return $utf8_pcre_properties; 5627 } 5628 5629 /** 5630 * Casts a numeric string $input to an appropriate numeric type (i.e. integer or float) 5631 * 5632 * @param string $input A numeric string. 5633 * 5634 * @return int|float Integer $input if $input fits integer, 5635 * float $input otherwise. 5636 */ 5637 function phpbb_to_numeric($input) 5638 { 5639 return ($input > PHP_INT_MAX) ? (float) $input : (int) $input; 5640 } 5641 5642 /** 5643 * Get the board contact details (e.g. for emails) 5644 * 5645 * @param \phpbb\config\config $config 5646 * @param string $phpEx 5647 * @return string 5648 */ 5649 function phpbb_get_board_contact(\phpbb\config\config $config, $phpEx) 5650 { 5651 if ($config['contact_admin_form_enable']) 5652 { 5653 return generate_board_url() . '/memberlist.' . $phpEx . '?mode=contactadmin'; 5654 } 5655 else 5656 { 5657 return $config['board_contact']; 5658 } 5659 } 5660 5661 /** 5662 * Get a clickable board contact details link 5663 * 5664 * @param \phpbb\config\config $config 5665 * @param string $phpbb_root_path 5666 * @param string $phpEx 5667 * @return string 5668 */ 5669 function phpbb_get_board_contact_link(\phpbb\config\config $config, $phpbb_root_path, $phpEx) 5670 { 5671 if ($config['contact_admin_form_enable'] && $config['email_enable']) 5672 { 5673 return append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=contactadmin'); 5674 } 5675 else 5676 { 5677 return 'mailto:' . htmlspecialchars($config['board_contact']); 5678 } 5679 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jan 11 00:25:41 2018 | Cross-referenced by PHPXref 0.7.1 |