Verzeichnisstruktur phpBB-3.0.0
- Veröffentlicht
- 12.12.2007
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
functions.php
0001 <?php
0002 /**
0003 *
0004 * @package phpBB3
0005 * @version $Id$
0006 * @copyright (c) 2005 phpBB Group
0007 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
0008 *
0009 */
0010
0011 /**
0012 * @ignore
0013 */
0014 if (!defined('IN_PHPBB'))
0015 {
0016 exit;
0017 }
0018
0019 // Common global functions
0020
0021 /**
0022 * set_var
0023 *
0024 * Set variable, used by {@link request_var the request_var function}
0025 *
0026 * @access private
0027 */
0028 function set_var(&$result, $var, $type, $multibyte = false)
0029 {
0030 settype($var, $type);
0031 $result = $var;
0032
0033 if ($type == 'string')
0034 {
0035 $result = trim(htmlspecialchars(str_replace(array("\r\n", "\r"), array("\n", "\n"), $result), ENT_COMPAT, 'UTF-8'));
0036
0037 if (!empty($result))
0038 {
0039 // Make sure multibyte characters are wellformed
0040 if ($multibyte)
0041 {
0042 if (!preg_match('/^./u', $result))
0043 {
0044 $result = '';
0045 }
0046 }
0047 else
0048 {
0049 // no multibyte, allow only ASCII (0-127)
0050 $result = preg_replace('/[\x80-\xFF]/', '?', $result);
0051 }
0052 }
0053
0054 $result = (STRIP) ? stripslashes($result) : $result;
0055 }
0056 }
0057
0058 /**
0059 * request_var
0060 *
0061 * Used to get passed variable
0062 */
0063 function request_var($var_name, $default, $multibyte = false, $cookie = false)
0064 {
0065 if (!$cookie && isset($_COOKIE[$var_name]))
0066 {
0067 if (!isset($_GET[$var_name]) && !isset($_POST[$var_name]))
0068 {
0069 return (is_array($default)) ? array() : $default;
0070 }
0071 $_REQUEST[$var_name] = isset($_POST[$var_name]) ? $_POST[$var_name] : $_GET[$var_name];
0072 }
0073
0074 if (!isset($_REQUEST[$var_name]) || (is_array($_REQUEST[$var_name]) && !is_array($default)) || (is_array($default) && !is_array($_REQUEST[$var_name])))
0075 {
0076 return (is_array($default)) ? array() : $default;
0077 }
0078
0079 $var = $_REQUEST[$var_name];
0080 if (!is_array($default))
0081 {
0082 $type = gettype($default);
0083 }
0084 else
0085 {
0086 list($key_type, $type) = each($default);
0087 $type = gettype($type);
0088 $key_type = gettype($key_type);
0089 if ($type == 'array')
0090 {
0091 reset($default);
0092 $default = current($default);
0093 list($sub_key_type, $sub_type) = each($default);
0094 $sub_type = gettype($sub_type);
0095 $sub_type = ($sub_type == 'array') ? 'NULL' : $sub_type;
0096 $sub_key_type = gettype($sub_key_type);
0097 }
0098 }
0099
0100 if (is_array($var))
0101 {
0102 $_var = $var;
0103 $var = array();
0104
0105 foreach ($_var as $k => $v)
0106 {
0107 set_var($k, $k, $key_type);
0108 if ($type == 'array' && is_array($v))
0109 {
0110 foreach ($v as $_k => $_v)
0111 {
0112 if (is_array($_v))
0113 {
0114 $_v = null;
0115 }
0116 set_var($_k, $_k, $sub_key_type);
0117 set_var($var[$k][$_k], $_v, $sub_type, $multibyte);
0118 }
0119 }
0120 else
0121 {
0122 if ($type == 'array' || is_array($v))
0123 {
0124 $v = null;
0125 }
0126 set_var($var[$k], $v, $type, $multibyte);
0127 }
0128 }
0129 }
0130 else
0131 {
0132 set_var($var, $var, $type, $multibyte);
0133 }
0134
0135 return $var;
0136 }
0137
0138 /**
0139 * Set config value. Creates missing config entry.
0140 */
0141 function set_config($config_name, $config_value, $is_dynamic = false)
0142 {
0143 global $db, $cache, $config;
0144
0145 $sql = 'UPDATE ' . CONFIG_TABLE . "
0146 SET config_value = '" . $db->sql_escape($config_value) . "'
0147 WHERE config_name = '" . $db->sql_escape($config_name) . "'";
0148 $db->sql_query($sql);
0149
0150 if (!$db->sql_affectedrows() && !isset($config[$config_name]))
0151 {
0152 $sql = 'INSERT INTO ' . CONFIG_TABLE . ' ' . $db->sql_build_array('INSERT', array(
0153 'config_name' => $config_name,
0154 'config_value' => $config_value,
0155 'is_dynamic' => ($is_dynamic) ? 1 : 0));
0156 $db->sql_query($sql);
0157 }
0158
0159 $config[$config_name] = $config_value;
0160
0161 if (!$is_dynamic)
0162 {
0163 $cache->destroy('config');
0164 }
0165 }
0166
0167 /**
0168 * Generates an alphanumeric random string of given length
0169 */
0170 function gen_rand_string($num_chars = 8)
0171 {
0172 $rand_str = unique_id();
0173 $rand_str = str_replace('0', 'Z', strtoupper(base_convert($rand_str, 16, 35)));
0174
0175 return substr($rand_str, 0, $num_chars);
0176 }
0177
0178 /**
0179 * Return unique id
0180 * @param string $extra additional entropy
0181 */
0182 function unique_id($extra = 'c')
0183 {
0184 static $dss_seeded = false;
0185 global $config;
0186
0187 $val = $config['rand_seed'] . microtime();
0188 $val = md5($val);
0189 $config['rand_seed'] = md5($config['rand_seed'] . $val . $extra);
0190
0191 if ($dss_seeded !== true && ($config['rand_seed_last_update'] < time() - rand(1,10)))
0192 {
0193 set_config('rand_seed', $config['rand_seed'], true);
0194 set_config('rand_seed_last_update', time(), true);
0195 $dss_seeded = true;
0196 }
0197
0198 return substr($val, 4, 16);
0199 }
0200
0201 /**
0202 * Determine whether we are approaching the maximum execution time. Should be called once
0203 * at the beginning of the script in which it's used.
0204 * @return bool Either true if the maximum execution time is nearly reached, or false
0205 * if some time is still left.
0206 */
0207 function still_on_time($extra_time = 15)
0208 {
0209 static $max_execution_time, $start_time;
0210
0211 $time = explode(' ', microtime());
0212 $current_time = $time[0] + $time[1];
0213
0214 if (empty($max_execution_time))
0215 {
0216 $max_execution_time = (function_exists('ini_get')) ? (int) @ini_get('max_execution_time') : (int) @get_cfg_var('max_execution_time');
0217
0218 // If zero, then set to something higher to not let the user catch the ten seconds barrier.
0219 if ($max_execution_time === 0)
0220 {
0221 $max_execution_time = 50 + $extra_time;
0222 }
0223
0224 $max_execution_time = min(max(10, ($max_execution_time - $extra_time)), 50);
0225
0226 // For debugging purposes
0227 // $max_execution_time = 10;
0228
0229 global $starttime;
0230 $start_time = (empty($starttime)) ? $current_time : $starttime;
0231 }
0232
0233 return (ceil($current_time - $start_time) < $max_execution_time) ? true : false;
0234 }
0235
0236 /**
0237 *
0238 * @version Version 0.1 / $Id$
0239 *
0240 * Portable PHP password hashing framework.
0241 *
0242 * Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
0243 * the public domain.
0244 *
0245 * There's absolutely no warranty.
0246 *
0247 * The homepage URL for this framework is:
0248 *
0249 * http://www.openwall.com/phpass/
0250 *
0251 * Please be sure to update the Version line if you edit this file in any way.
0252 * It is suggested that you leave the main version number intact, but indicate
0253 * your project name (after the slash) and add your own revision information.
0254 *
0255 * Please do not change the "private" password hashing method implemented in
0256 * here, thereby making your hashes incompatible. However, if you must, please
0257 * change the hash type identifier (the "$P$") to something different.
0258 *
0259 * Obviously, since this code is in the public domain, the above are not
0260 * requirements (there can be none), but merely suggestions.
0261 *
0262 *
0263 * Hash the password
0264 */
0265 function phpbb_hash($password)
0266 {
0267 $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
0268
0269 $random_state = unique_id();
0270 $random = '';
0271 $count = 6;
0272
0273 if (($fh = @fopen('/dev/urandom', 'rb')))
0274 {
0275 $random = fread($fh, $count);
0276 fclose($fh);
0277 }
0278
0279 if (strlen($random) < $count)
0280 {
0281 $random = '';
0282
0283 for ($i = 0; $i < $count; $i += 16)
0284 {
0285 $random_state = md5(unique_id() . $random_state);
0286 $random .= pack('H*', md5($random_state));
0287 }
0288 $random = substr($random, 0, $count);
0289 }
0290
0291 $hash = _hash_crypt_private($password, _hash_gensalt_private($random, $itoa64), $itoa64);
0292
0293 if (strlen($hash) == 34)
0294 {
0295 return $hash;
0296 }
0297
0298 return md5($password);
0299 }
0300
0301 /**
0302 * Check for correct password
0303 */
0304 function phpbb_check_hash($password, $hash)
0305 {
0306 $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
0307 if (strlen($hash) == 34)
0308 {
0309 return (_hash_crypt_private($password, $hash, $itoa64) === $hash) ? true : false;
0310 }
0311
0312 return (md5($password) === $hash) ? true : false;
0313 }
0314
0315 /**
0316 * Generate salt for hash generation
0317 */
0318 function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6)
0319 {
0320 if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
0321 {
0322 $iteration_count_log2 = 8;
0323 }
0324
0325 $output = '$H$';
0326 $output .= $itoa64[min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)];
0327 $output .= _hash_encode64($input, 6, $itoa64);
0328
0329 return $output;
0330 }
0331
0332 /**
0333 * Encode hash
0334 */
0335 function _hash_encode64($input, $count, &$itoa64)
0336 {
0337 $output = '';
0338 $i = 0;
0339
0340 do
0341 {
0342 $value = ord($input[$i++]);
0343 $output .= $itoa64[$value & 0x3f];
0344
0345 if ($i < $count)
0346 {
0347 $value |= ord($input[$i]) << 8;
0348 }
0349
0350 $output .= $itoa64[($value >> 6) & 0x3f];
0351
0352 if ($i++ >= $count)
0353 {
0354 break;
0355 }
0356
0357 if ($i < $count)
0358 {
0359 $value |= ord($input[$i]) << 16;
0360 }
0361
0362 $output .= $itoa64[($value >> 12) & 0x3f];
0363
0364 if ($i++ >= $count)
0365 {
0366 break;
0367 }
0368
0369 $output .= $itoa64[($value >> 18) & 0x3f];
0370 }
0371 while ($i < $count);
0372
0373 return $output;
0374 }
0375
0376 /**
0377 * The crypt function/replacement
0378 */
0379 function _hash_crypt_private($password, $setting, &$itoa64)
0380 {
0381 $output = '*';
0382
0383 // Check for correct hash
0384 if (substr($setting, 0, 3) != '$H$')
0385 {
0386 return $output;
0387 }
0388
0389 $count_log2 = strpos($itoa64, $setting[3]);
0390
0391 if ($count_log2 < 7 || $count_log2 > 30)
0392 {
0393 return $output;
0394 }
0395
0396 $count = 1 << $count_log2;
0397 $salt = substr($setting, 4, 8);
0398
0399 if (strlen($salt) != 8)
0400 {
0401 return $output;
0402 }
0403
0404 /**
0405 * We're kind of forced to use MD5 here since it's the only
0406 * cryptographic primitive available in all versions of PHP
0407 * currently in use. To implement our own low-level crypto
0408 * in PHP would result in much worse performance and
0409 * consequently in lower iteration counts and hashes that are
0410 * quicker to crack (by non-PHP code).
0411 */
0412 if (PHP_VERSION >= 5)
0413 {
0414 $hash = md5($salt . $password, true);
0415 do
0416 {
0417 $hash = md5($hash . $password, true);
0418 }
0419 while (--$count);
0420 }
0421 else
0422 {
0423 $hash = pack('H*', md5($salt . $password));
0424 do
0425 {
0426 $hash = pack('H*', md5($hash . $password));
0427 }
0428 while (--$count);
0429 }
0430
0431 $output = substr($setting, 0, 12);
0432 $output .= _hash_encode64($hash, 16, $itoa64);
0433
0434 return $output;
0435 }
0436
0437 // Compatibility functions
0438
0439 if (!function_exists('array_combine'))
0440 {
0441 /**
0442 * A wrapper for the PHP5 function array_combine()
0443 * @param array $keys contains keys for the resulting array
0444 * @param array $values contains values for the resulting array
0445 *
0446 * @return Returns an array by using the values from the keys array as keys and the
0447 * values from the values array as the corresponding values. Returns false if the
0448 * number of elements for each array isn't equal or if the arrays are empty.
0449 */
0450 function array_combine($keys, $values)
0451 {
0452 $keys = array_values($keys);
0453 $values = array_values($values);
0454
0455 $n = sizeof($keys);
0456 $m = sizeof($values);
0457 if (!$n || !$m || ($n != $m))
0458 {
0459 return false;
0460 }
0461
0462 $combined = array();
0463 for ($i = 0; $i < $n; $i++)
0464 {
0465 $combined[$keys[$i]] = $values[$i];
0466 }
0467 return $combined;
0468 }
0469 }
0470
0471 if (!function_exists('str_split'))
0472 {
0473 /**
0474 * A wrapper for the PHP5 function str_split()
0475 * @param array $string contains the string to be converted
0476 * @param array $split_length contains the length of each chunk
0477 *
0478 * @return Converts a string to an array. If the optional split_length parameter is specified,
0479 * the returned array will be broken down into chunks with each being split_length in length,
0480 * otherwise each chunk will be one character in length. FALSE is returned if split_length is
0481 * less than 1. If the split_length length exceeds the length of string, the entire string is
0482 * returned as the first (and only) array element.
0483 */
0484 function str_split($string, $split_length = 1)
0485 {
0486 if ($split_length < 1)
0487 {
0488 return false;
0489 }
0490 else if ($split_length >= strlen($string))
0491 {
0492 return array($string);
0493 }
0494 else
0495 {
0496 preg_match_all('#.{1,' . $split_length . '}#s', $string, $matches);
0497 return $matches[0];
0498 }
0499 }
0500 }
0501
0502 if (!function_exists('stripos'))
0503 {
0504 /**
0505 * A wrapper for the PHP5 function stripos
0506 * Find position of first occurrence of a case-insensitive string
0507 *
0508 * @param string $haystack is the string to search in
0509 * @param string $needle is the string to search for
0510 *
0511 * @return mixed Returns the numeric position of the first occurrence of needle in the haystack string. Unlike strpos(), stripos() is case-insensitive.
0512 * Note that the needle may be a string of one or more characters.
0513 * If needle is not found, stripos() will return boolean FALSE.
0514 */
0515 function stripos($haystack, $needle)
0516 {
0517 if (preg_match('#' . preg_quote($needle, '#') . '#i', $haystack, $m))
0518 {
0519 return strpos($haystack, $m[0]);
0520 }
0521
0522 return false;
0523 }
0524 }
0525
0526 if (!function_exists('realpath'))
0527 {
0528 /**
0529 * Checks if a path ($path) is absolute or relative
0530 *
0531 * @param string $path Path to check absoluteness of
0532 * @return boolean
0533 */
0534 function is_absolute($path)
0535 {
0536 return ($path[0] == '/' || (DIRECTORY_SEPARATOR == '\\' && preg_match('#^[a-z]:/#i', $path))) ? true : false;
0537 }
0538
0539 /**
0540 * @author Chris Smith <chris@project-minerva.org>
0541 * @copyright 2006 Project Minerva Team
0542 * @param string $path The path which we should attempt to resolve.
0543 * @return mixed
0544 */
0545 function phpbb_realpath($path)
0546 {
0547 // Now to perform funky shizzle
0548
0549 // Switch to use UNIX slashes
0550 $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
0551 $path_prefix = '';
0552
0553 // Determine what sort of path we have
0554 if (is_absolute($path))
0555 {
0556 $absolute = true;
0557
0558 if ($path[0] == '/')
0559 {
0560 // Absolute path, *NIX style
0561 $path_prefix = '';
0562 }
0563 else
0564 {
0565 // Absolute path, Windows style
0566 // Remove the drive letter and colon
0567 $path_prefix = $path[0] . ':';
0568 $path = substr($path, 2);
0569 }
0570 }
0571 else
0572 {
0573 // Relative Path
0574 // Prepend the current working directory
0575 if (function_exists('getcwd'))
0576 {
0577 // This is the best method, hopefully it is enabled!
0578 $path = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . '/' . $path;
0579 $absolute = true;
0580 if (preg_match('#^[a-z]:#i', $path))
0581 {
0582 $path_prefix = $path[0] . ':';
0583 $path = substr($path, 2);
0584 }
0585 else
0586 {
0587 $path_prefix = '';
0588 }
0589 }
0590 else if (isset($_SERVER['SCRIPT_FILENAME']) && !empty($_SERVER['SCRIPT_FILENAME']))
0591 {
0592 // Warning: If chdir() has been used this will lie!
0593 // Warning: This has some problems sometime (CLI can create them easily)
0594 $path = str_replace(DIRECTORY_SEPARATOR, '/', dirname($_SERVER['SCRIPT_FILENAME'])) . '/' . $path;
0595 $absolute = true;
0596 $path_prefix = '';
0597 }
0598 else
0599 {
0600 // We have no way of getting the absolute path, just run on using relative ones.
0601 $absolute = false;
0602 $path_prefix = '.';
0603 }
0604 }
0605
0606 // Remove any repeated slashes
0607 $path = preg_replace('#/{2,}#', '/', $path);
0608
0609 // Remove the slashes from the start and end of the path
0610 $path = trim($path, '/');
0611
0612 // Break the string into little bits for us to nibble on
0613 $bits = explode('/', $path);
0614
0615 // Remove any . in the path, renumber array for the loop below
0616 $bits = array_values(array_diff($bits, array('.')));
0617
0618 // Lets get looping, run over and resolve any .. (up directory)
0619 for ($i = 0, $max = sizeof($bits); $i < $max; $i++)
0620 {
0621 // @todo Optimise
0622 if ($bits[$i] == '..' )
0623 {
0624 if (isset($bits[$i - 1]))
0625 {
0626 if ($bits[$i - 1] != '..')
0627 {
0628 // We found a .. and we are able to traverse upwards, lets do it!
0629 unset($bits[$i]);
0630 unset($bits[$i - 1]);
0631 $i -= 2;
0632 $max -= 2;
0633 $bits = array_values($bits);
0634 }
0635 }
0636 else if ($absolute) // ie. !isset($bits[$i - 1]) && $absolute
0637 {
0638 // We have an absolute path trying to descend above the root of the filesystem
0639 // ... Error!
0640 return false;
0641 }
0642 }
0643 }
0644
0645 // Prepend the path prefix
0646 array_unshift($bits, $path_prefix);
0647
0648 $resolved = '';
0649
0650 $max = sizeof($bits) - 1;
0651
0652 // Check if we are able to resolve symlinks, Windows cannot.
0653 $symlink_resolve = (function_exists('readlink')) ? true : false;
0654
0655 foreach ($bits as $i => $bit)
0656 {
0657 if (@is_dir("$resolved/$bit") || ($i == $max && @is_file("$resolved/$bit")))
0658 {
0659 // Path Exists
0660 if ($symlink_resolve && is_link("$resolved/$bit") && ($link = readlink("$resolved/$bit")))
0661 {
0662 // Resolved a symlink.
0663 $resolved = $link . (($i == $max) ? '' : '/');
0664 continue;
0665 }
0666 }
0667 else
0668 {
0669 // Something doesn't exist here!
0670 // This is correct realpath() behaviour but sadly open_basedir and safe_mode make this problematic
0671 // return false;
0672 }
0673 $resolved .= $bit . (($i == $max) ? '' : '/');
0674 }
0675
0676 // @todo If the file exists fine and open_basedir only has one path we should be able to prepend it
0677 // because we must be inside that basedir, the question is where...
0678 // @internal The slash in is_dir() gets around an open_basedir restriction
0679 if (!@file_exists($resolved) || (!is_dir($resolved . '/') && !is_file($resolved)))
0680 {
0681 return false;
0682 }
0683
0684 // Put the slashes back to the native operating systems slashes
0685 $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved);
0686
0687 // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
0688 if (substr($resolved, -1) == DIRECTORY_SEPARATOR)
0689 {
0690 return substr($resolved, 0, -1);
0691 }
0692
0693 return $resolved; // We got here, in the end!
0694 }
0695 }
0696 else
0697 {
0698 /**
0699 * A wrapper for realpath
0700 * @ignore
0701 */
0702 function phpbb_realpath($path)
0703 {
0704 $path = realpath($path);
0705
0706 // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
0707 if (substr($path, -1) == DIRECTORY_SEPARATOR)
0708 {
0709 return substr($path, 0, -1);
0710 }
0711
0712 return $path;
0713 }
0714 }
0715
0716 if (!function_exists('htmlspecialchars_decode'))
0717 {
0718 /**
0719 * A wrapper for htmlspecialchars_decode
0720 * @ignore
0721 */
0722 function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT)
0723 {
0724 return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
0725 }
0726 }
0727
0728 // functions used for building option fields
0729
0730 /**
0731 * Pick a language, any language ...
0732 */
0733 function language_select($default = '')
0734 {
0735 global $db;
0736
0737 $sql = 'SELECT lang_iso, lang_local_name
0738 FROM ' . LANG_TABLE . '
0739 ORDER BY lang_english_name';
0740 $result = $db->sql_query($sql);
0741
0742 $lang_options = '';
0743 while ($row = $db->sql_fetchrow($result))
0744 {
0745 $selected = ($row['lang_iso'] == $default) ? ' selected="selected"' : '';
0746 $lang_options .= '<option value="' . $row['lang_iso'] . '"' . $selected . '>' . $row['lang_local_name'] . '</option>';
0747 }
0748 $db->sql_freeresult($result);
0749
0750 return $lang_options;
0751 }
0752
0753 /**
0754 * Pick a template/theme combo,
0755 */
0756 function style_select($default = '', $all = false)
0757 {
0758 global $db;
0759
0760 $sql_where = (!$all) ? 'WHERE style_active = 1 ' : '';
0761 $sql = 'SELECT style_id, style_name
0762 FROM ' . STYLES_TABLE . "
0763 $sql_where
0764 ORDER BY style_name";
0765 $result = $db->sql_query($sql);
0766
0767 $style_options = '';
0768 while ($row = $db->sql_fetchrow($result))
0769 {
0770 $selected = ($row['style_id'] == $default) ? ' selected="selected"' : '';
0771 $style_options .= '<option value="' . $row['style_id'] . '"' . $selected . '>' . $row['style_name'] . '</option>';
0772 }
0773 $db->sql_freeresult($result);
0774
0775 return $style_options;
0776 }
0777
0778 /**
0779 * Pick a timezone
0780 */
0781 function tz_select($default = '', $truncate = false)
0782 {
0783 global $user;
0784
0785 $tz_select = '';
0786 foreach ($user->lang['tz_zones'] as $offset => $zone)
0787 {
0788 if ($truncate)
0789 {
0790 $zone_trunc = truncate_string($zone, 50, false, '...');
0791 }
0792 else
0793 {
0794 $zone_trunc = $zone;
0795 }
0796
0797 if (is_numeric($offset))
0798 {
0799 $selected = ($offset == $default) ? ' selected="selected"' : '';
0800 $tz_select .= '<option title="'.$zone.'" value="' . $offset . '"' . $selected . '>' . $zone_trunc . '</option>';
0801 }
0802 }
0803
0804 return $tz_select;
0805 }
0806
0807 // Functions handling topic/post tracking/marking
0808
0809 /**
0810 * Marks a topic/forum as read
0811 * Marks a topic as posted to
0812 *
0813 * @param int $user_id can only be used with $mode == 'post'
0814 */
0815 function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $user_id = 0)
0816 {
0817 global $db, $user, $config;
0818
0819 if ($mode == 'all')
0820 {
0821 if ($forum_id === false || !sizeof($forum_id))
0822 {
0823 if ($config['load_db_lastread'] && $user->data['is_registered'])
0824 {
0825 // Mark all forums read (index page)
0826 $db->sql_query('DELETE FROM ' . TOPICS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
0827 $db->sql_query('DELETE FROM ' . FORUMS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
0828 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
0829 }
0830 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
0831 {
0832 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
0833 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
0834
0835 unset($tracking_topics['tf']);
0836 unset($tracking_topics['t']);
0837 unset($tracking_topics['f']);
0838 $tracking_topics['l'] = base_convert(time() - $config['board_startdate'], 10, 36);
0839
0840 $user->set_cookie('track', tracking_serialize($tracking_topics), time() + 31536000);
0841 $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking_topics)) : tracking_serialize($tracking_topics);
0842
0843 unset($tracking_topics);
0844
0845 if ($user->data['is_registered'])
0846 {
0847 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
0848 }
0849 }
0850 }
0851
0852 return;
0853 }
0854 else if ($mode == 'topics')
0855 {
0856 // Mark all topics in forums read
0857 if (!is_array($forum_id))
0858 {
0859 $forum_id = array($forum_id);
0860 }
0861
0862 // Add 0 to forums array to mark global announcements correctly
0863 $forum_id[] = 0;
0864
0865 if ($config['load_db_lastread'] && $user->data['is_registered'])
0866 {
0867 $sql = 'DELETE FROM ' . TOPICS_TRACK_TABLE . "
0868 WHERE user_id = {$user->data['user_id']}
0869 AND " . $db->sql_in_set('forum_id', $forum_id);
0870 $db->sql_query($sql);
0871
0872 $sql = 'SELECT forum_id
0873 FROM ' . FORUMS_TRACK_TABLE . "
0874 WHERE user_id = {$user->data['user_id']}
0875 AND " . $db->sql_in_set('forum_id', $forum_id);
0876 $result = $db->sql_query($sql);
0877
0878 $sql_update = array();
0879 while ($row = $db->sql_fetchrow($result))
0880 {
0881 $sql_update[] = $row['forum_id'];
0882 }
0883 $db->sql_freeresult($result);
0884
0885 if (sizeof($sql_update))
0886 {
0887 $sql = 'UPDATE ' . FORUMS_TRACK_TABLE . '
0888 SET mark_time = ' . time() . "
0889 WHERE user_id = {$user->data['user_id']}
0890 AND " . $db->sql_in_set('forum_id', $sql_update);
0891 $db->sql_query($sql);
0892 }
0893
0894 if ($sql_insert = array_diff($forum_id, $sql_update))
0895 {
0896 $sql_ary = array();
0897 foreach ($sql_insert as $f_id)
0898 {
0899 $sql_ary[] = array(
0900 'user_id' => (int) $user->data['user_id'],
0901 'forum_id' => (int) $f_id,
0902 'mark_time' => time()
0903 );
0904 }
0905
0906 $db->sql_multi_insert(FORUMS_TRACK_TABLE, $sql_ary);
0907 }
0908 }
0909 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
0910 {
0911 $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
0912 $tracking = ($tracking) ? tracking_unserialize($tracking) : array();
0913
0914 foreach ($forum_id as $f_id)
0915 {
0916 $topic_ids36 = (isset($tracking['tf'][$f_id])) ? $tracking['tf'][$f_id] : array();
0917
0918 if (isset($tracking['tf'][$f_id]))
0919 {
0920 unset($tracking['tf'][$f_id]);
0921 }
0922
0923 foreach ($topic_ids36 as $topic_id36)
0924 {
0925 unset($tracking['t'][$topic_id36]);
0926 }
0927
0928 if (isset($tracking['f'][$f_id]))
0929 {
0930 unset($tracking['f'][$f_id]);
0931 }
0932
0933 $tracking['f'][$f_id] = base_convert(time() - $config['board_startdate'], 10, 36);
0934 }
0935
0936 if (isset($tracking['tf']) && empty($tracking['tf']))
0937 {
0938 unset($tracking['tf']);
0939 }
0940
0941 $user->set_cookie('track', tracking_serialize($tracking), time() + 31536000);
0942 $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking);
0943
0944 unset($tracking);
0945 }
0946
0947 return;
0948 }
0949 else if ($mode == 'topic')
0950 {
0951 if ($topic_id === false || $forum_id === false)
0952 {
0953 return;
0954 }
0955
0956 if ($config['load_db_lastread'] && $user->data['is_registered'])
0957 {
0958 $sql = 'UPDATE ' . TOPICS_TRACK_TABLE . '
0959 SET mark_time = ' . (($post_time) ? $post_time : time()) . "
0960 WHERE user_id = {$user->data['user_id']}
0961 AND topic_id = $topic_id";
0962 $db->sql_query($sql);
0963
0964 // insert row
0965 if (!$db->sql_affectedrows())
0966 {
0967 $db->sql_return_on_error(true);
0968
0969 $sql_ary = array(
0970 'user_id' => (int) $user->data['user_id'],
0971 'topic_id' => (int) $topic_id,
0972 'forum_id' => (int) $forum_id,
0973 'mark_time' => ($post_time) ? (int) $post_time : time(),
0974 );
0975
0976 $db->sql_query('INSERT INTO ' . TOPICS_TRACK_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
0977
0978 $db->sql_return_on_error(false);
0979 }
0980 }
0981 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
0982 {
0983 $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
0984 $tracking = ($tracking) ? tracking_unserialize($tracking) : array();
0985
0986 $topic_id36 = base_convert($topic_id, 10, 36);
0987
0988 if (!isset($tracking['t'][$topic_id36]))
0989 {
0990 $tracking['tf'][$forum_id][$topic_id36] = true;
0991 }
0992
0993 $post_time = ($post_time) ? $post_time : time();
0994 $tracking['t'][$topic_id36] = base_convert($post_time - $config['board_startdate'], 10, 36);
0995
0996 // If the cookie grows larger than 10000 characters we will remove the smallest value
0997 // This can result in old topics being unread - but most of the time it should be accurate...
0998 if (isset($_COOKIE[$config['cookie_name'] . '_track']) && strlen($_COOKIE[$config['cookie_name'] . '_track']) > 10000)
0999 {
1000 //echo 'Cookie grown too large' . print_r($tracking, true);
1001
1002 // We get the ten most minimum stored time offsets and its associated topic ids
1003 $time_keys = array();
1004 for ($i = 0; $i < 10 && sizeof($tracking['t']); $i++)
1005 {
1006 $min_value = min($tracking['t']);
1007 $m_tkey = array_search($min_value, $tracking['t']);
1008 unset($tracking['t'][$m_tkey]);
1009
1010 $time_keys[$m_tkey] = $min_value;
1011 }
1012
1013 // Now remove the topic ids from the array...
1014 foreach ($tracking['tf'] as $f_id => $topic_id_ary)
1015 {
1016 foreach ($time_keys as $m_tkey => $min_value)
1017 {
1018 if (isset($topic_id_ary[$m_tkey]))
1019 {
1020 $tracking['f'][$f_id] = $min_value;
1021 unset($tracking['tf'][$f_id][$m_tkey]);
1022 }
1023 }
1024 }
1025
1026 if ($user->data['is_registered'])
1027 {
1028 $user->data['user_lastmark'] = intval(base_convert(max($time_keys) + $config['board_startdate'], 36, 10));
1029 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . $user->data['user_lastmark'] . " WHERE user_id = {$user->data['user_id']}");
1030 }
1031 else
1032 {
1033 $tracking['l'] = max($time_keys);
1034 }
1035 }
1036
1037 $user->set_cookie('track', tracking_serialize($tracking), time() + 31536000);
1038 $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking);
1039 }
1040
1041 return;
1042 }
1043 else if ($mode == 'post')
1044 {
1045 if ($topic_id === false)
1046 {
1047 return;
1048 }
1049
1050 $use_user_id = (!$user_id) ? $user->data['user_id'] : $user_id;
1051
1052 if ($config['load_db_track'] && $use_user_id != ANONYMOUS)
1053 {
1054 $db->sql_return_on_error(true);
1055
1056 $sql_ary = array(
1057 'user_id' => (int) $use_user_id,
1058 'topic_id' => (int) $topic_id,
1059 'topic_posted' => 1
1060 );
1061
1062 $db->sql_query('INSERT INTO ' . TOPICS_POSTED_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1063
1064 $db->sql_return_on_error(false);
1065 }
1066
1067 return;
1068 }
1069 }
1070
1071 /**
1072 * Get topic tracking info by using already fetched info
1073 */
1074 function get_topic_tracking($forum_id, $topic_ids, &$rowset, $forum_mark_time, $global_announce_list = false)
1075 {
1076 global $config, $user;
1077
1078 $last_read = array();
1079
1080 if (!is_array($topic_ids))
1081 {
1082 $topic_ids = array($topic_ids);
1083 }
1084
1085 foreach ($topic_ids as $topic_id)
1086 {
1087 if (!empty($rowset[$topic_id]['mark_time']))
1088 {
1089 $last_read[$topic_id] = $rowset[$topic_id]['mark_time'];
1090 }
1091 }
1092
1093 $topic_ids = array_diff($topic_ids, array_keys($last_read));
1094
1095 if (sizeof($topic_ids))
1096 {
1097 $mark_time = array();
1098
1099 // Get global announcement info
1100 if ($global_announce_list && sizeof($global_announce_list))
1101 {
1102 if (!isset($forum_mark_time[0]))
1103 {
1104 global $db;
1105
1106 $sql = 'SELECT mark_time
1107 FROM ' . FORUMS_TRACK_TABLE . "
1108 WHERE user_id = {$user->data['user_id']}
1109 AND forum_id = 0";
1110 $result = $db->sql_query($sql);
1111 $row = $db->sql_fetchrow($result);
1112 $db->sql_freeresult($result);
1113
1114 if ($row)
1115 {
1116 $mark_time[0] = $row['mark_time'];
1117 }
1118 }
1119 else
1120 {
1121 if ($forum_mark_time[0] !== false)
1122 {
1123 $mark_time[0] = $forum_mark_time[0];
1124 }
1125 }
1126 }
1127
1128 if (!empty($forum_mark_time[$forum_id]) && $forum_mark_time[$forum_id] !== false)
1129 {
1130 $mark_time[$forum_id] = $forum_mark_time[$forum_id];
1131 }
1132
1133 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
1134
1135 foreach ($topic_ids as $topic_id)
1136 {
1137 if ($global_announce_list && isset($global_announce_list[$topic_id]))
1138 {
1139 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1140 }
1141 else
1142 {
1143 $last_read[$topic_id] = $user_lastmark;
1144 }
1145 }
1146 }
1147
1148 return $last_read;
1149 }
1150
1151 /**
1152 * Get topic tracking info from db (for cookie based tracking only this function is used)
1153 */
1154 function get_complete_topic_tracking($forum_id, $topic_ids, $global_announce_list = false)
1155 {
1156 global $config, $user;
1157
1158 $last_read = array();
1159
1160 if (!is_array($topic_ids))
1161 {
1162 $topic_ids = array($topic_ids);
1163 }
1164
1165 if ($config['load_db_lastread'] && $user->data['is_registered'])
1166 {
1167 global $db;
1168
1169 $sql = 'SELECT topic_id, mark_time
1170 FROM ' . TOPICS_TRACK_TABLE . "
1171 WHERE user_id = {$user->data['user_id']}
1172 AND " . $db->sql_in_set('topic_id', $topic_ids);
1173 $result = $db->sql_query($sql);
1174
1175 while ($row = $db->sql_fetchrow($result))
1176 {
1177 $last_read[$row['topic_id']] = $row['mark_time'];
1178 }
1179 $db->sql_freeresult($result);
1180
1181 $topic_ids = array_diff($topic_ids, array_keys($last_read));
1182
1183 if (sizeof($topic_ids))
1184 {
1185 $sql = 'SELECT forum_id, mark_time
1186 FROM ' . FORUMS_TRACK_TABLE . "
1187 WHERE user_id = {$user->data['user_id']}
1188 AND forum_id " .
1189 (($global_announce_list && sizeof($global_announce_list)) ? "IN (0, $forum_id)" : "= $forum_id");
1190 $result = $db->sql_query($sql);
1191
1192 $mark_time = array();
1193 while ($row = $db->sql_fetchrow($result))
1194 {
1195 $mark_time[$row['forum_id']] = $row['mark_time'];
1196 }
1197 $db->sql_freeresult($result);
1198
1199 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
1200
1201 foreach ($topic_ids as $topic_id)
1202 {
1203 if ($global_announce_list && isset($global_announce_list[$topic_id]))
1204 {
1205 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1206 }
1207 else
1208 {
1209 $last_read[$topic_id] = $user_lastmark;
1210 }
1211 }
1212 }
1213 }
1214 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1215 {
1216 global $tracking_topics;
1217
1218 if (!isset($tracking_topics) || !sizeof($tracking_topics))
1219 {
1220 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1221 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
1222 }
1223
1224 if (!$user->data['is_registered'])
1225 {
1226 $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0;
1227 }
1228 else
1229 {
1230 $user_lastmark = $user->data['user_lastmark'];
1231 }
1232
1233 foreach ($topic_ids as $topic_id)
1234 {
1235 $topic_id36 = base_convert($topic_id, 10, 36);
1236
1237 if (isset($tracking_topics['t'][$topic_id36]))
1238 {
1239 $last_read[$topic_id] = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate'];
1240 }
1241 }
1242
1243 $topic_ids = array_diff($topic_ids, array_keys($last_read));
1244
1245 if (sizeof($topic_ids))
1246 {
1247 $mark_time = array();
1248 if ($global_announce_list && sizeof($global_announce_list))
1249 {
1250 if (isset($tracking_topics['f'][0]))
1251 {
1252 $mark_time[0] = base_convert($tracking_topics['f'][0], 36, 10) + $config['board_startdate'];
1253 }
1254 }
1255
1256 if (isset($tracking_topics['f'][$forum_id]))
1257 {
1258 $mark_time[$forum_id] = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate'];
1259 }
1260
1261 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user_lastmark;
1262
1263 foreach ($topic_ids as $topic_id)
1264 {
1265 if ($global_announce_list && isset($global_announce_list[$topic_id]))
1266 {
1267 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1268 }
1269 else
1270 {
1271 $last_read[$topic_id] = $user_lastmark;
1272 }
1273 }
1274 }
1275 }
1276
1277 return $last_read;
1278 }
1279
1280 /**
1281 * Check for read forums and update topic tracking info accordingly
1282 *
1283 * @param int $forum_id the forum id to check
1284 * @param int $forum_last_post_time the forums last post time
1285 * @param int $f_mark_time the forums last mark time if user is registered and load_db_lastread enabled
1286 * @param int $mark_time_forum false if the mark time needs to be obtained, else the last users forum mark time
1287 *
1288 * @return true if complete forum got marked read, else false.
1289 */
1290 function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_time = false, $mark_time_forum = false)
1291 {
1292 global $db, $tracking_topics, $user, $config;
1293
1294 // Determine the users last forum mark time if not given.
1295 if ($mark_time_forum === false)
1296 {
1297 if ($config['load_db_lastread'] && $user->data['is_registered'])
1298 {
1299 $mark_time_forum = (!empty($f_mark_time)) ? $f_mark_time : $user->data['user_lastmark'];
1300 }
1301 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1302 {
1303 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1304 $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
1305
1306 if (!$user->data['is_registered'])
1307 {
1308 $user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0;
1309 }
1310
1311 $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'];
1312 }
1313 }
1314
1315 // Check the forum for any left unread topics.
1316 // If there are none, we mark the forum as read.
1317 if ($config['load_db_lastread'] && $user->data['is_registered'])
1318 {
1319 if ($mark_time_forum >= $forum_last_post_time)
1320 {
1321 // We do not need to mark read, this happened before. Therefore setting this to true
1322 $row = true;
1323 }
1324 else
1325 {
1326 $sql = 'SELECT t.forum_id FROM ' . TOPICS_TABLE . ' t
1327 LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt ON (tt.topic_id = t.topic_id AND tt.user_id = ' . $user->data['user_id'] . ')
1328 WHERE t.forum_id = ' . $forum_id . '
1329 AND t.topic_last_post_time > ' . $mark_time_forum . '
1330 AND t.topic_moved_id = 0
1331 AND (tt.topic_id IS NULL OR tt.mark_time < t.topic_last_post_time)
1332 GROUP BY t.forum_id';
1333 $result = $db->sql_query_limit($sql, 1);
1334 $row = $db->sql_fetchrow($result);
1335 $db->sql_freeresult($result);
1336 }
1337 }
1338 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1339 {
1340 // Get information from cookie
1341 $row = false;
1342
1343 if (!isset($tracking_topics['tf'][$forum_id]))
1344 {
1345 // We do not need to mark read, this happened before. Therefore setting this to true
1346 $row = true;
1347 }
1348 else
1349 {
1350 $sql = 'SELECT topic_id
1351 FROM ' . TOPICS_TABLE . '
1352 WHERE forum_id = ' . $forum_id . '
1353 AND topic_last_post_time > ' . $mark_time_forum . '
1354 AND topic_moved_id = 0';
1355 $result = $db->sql_query($sql);
1356
1357 $check_forum = $tracking_topics['tf'][$forum_id];
1358 $unread = false;
1359
1360 while ($row = $db->sql_fetchrow($result))
1361 {
1362 if (!in_array(base_convert($row['topic_id'], 10, 36), array_keys($check_forum)))
1363 {
1364 $unread = true;
1365 break;
1366 }
1367 }
1368 $db->sql_freeresult($result);
1369
1370 $row = $unread;
1371 }
1372 }
1373 else
1374 {
1375 $row = true;
1376 }
1377
1378 if (!$row)
1379 {
1380 markread('topics', $forum_id);
1381 return true;
1382 }
1383
1384 return false;
1385 }
1386
1387 /**
1388 * Transform an array into a serialized format
1389 */
1390 function tracking_serialize($input)
1391 {
1392 $out = '';
1393 foreach ($input as $key => $value)
1394 {
1395 if (is_array($value))
1396 {
1397 $out .= $key . ':(' . tracking_serialize($value) . ');';
1398 }
1399 else
1400 {
1401 $out .= $key . ':' . $value . ';';
1402 }
1403 }
1404 return $out;
1405 }
1406
1407 /**
1408 * Transform a serialized array into an actual array
1409 */
1410 function tracking_unserialize($string, $max_depth = 3)
1411 {
1412 $n = strlen($string);
1413 if ($n > 10010)
1414 {
1415 die('Invalid data supplied');
1416 }
1417 $data = $stack = array();
1418 $key = '';
1419 $mode = 0;
1420 $level = &$data;
1421 for ($i = 0; $i < $n; ++$i)
1422 {
1423 switch ($mode)
1424 {
1425 case 0:
1426 switch ($string[$i])
1427 {
1428 case ':':
1429 $level[$key] = 0;
1430 $mode = 1;
1431 break;
1432 case ')':
1433 unset($level);
1434 $level = array_pop($stack);
1435 $mode = 3;
1436 break;
1437 default:
1438 $key .= $string[$i];
1439 }
1440 break;
1441
1442 case 1:
1443 switch ($string[$i])
1444 {
1445 case '(':
1446 if (sizeof($stack) >= $max_depth)
1447 {
1448 die('Invalid data supplied');
1449 }
1450 $stack[] = &$level;
1451 $level[$key] = array();
1452 $level = &$level[$key];
1453 $key = '';
1454 $mode = 0;
1455 break;
1456 default:
1457 $level[$key] = $string[$i];
1458 $mode = 2;
1459 break;
1460 }
1461 break;
1462
1463 case 2:
1464 switch ($string[$i])
1465 {
1466 case ')':
1467 unset($level);
1468 $level = array_pop($stack);
1469 $mode = 3;
1470 break;
1471 case ';':
1472 $key = '';
1473 $mode = 0;
1474 break;
1475 default:
1476 $level[$key] .= $string[$i];
1477 break;
1478 }
1479 break;
1480
1481 case 3:
1482 switch ($string[$i])
1483 {
1484 case ')':
1485 unset($level);
1486 $level = array_pop($stack);
1487 break;
1488 case ';':
1489 $key = '';
1490 $mode = 0;
1491 break;
1492 default:
1493 die('Invalid data supplied');
1494 break;
1495 }
1496 break;
1497 }
1498 }
1499
1500 if (sizeof($stack) != 0 || ($mode != 0 && $mode != 3))
1501 {
1502 die('Invalid data supplied');
1503 }
1504
1505 return $level;
1506 }
1507
1508 // Pagination functions
1509
1510 /**
1511 * Pagination routine, generates page number sequence
1512 * tpl_prefix is for using different pagination blocks at one page
1513 */
1514 function generate_pagination($base_url, $num_items, $per_page, $start_item, $add_prevnext_text = false, $tpl_prefix = '')
1515 {
1516 global $template, $user;
1517
1518 // Make sure $per_page is a valid value
1519 $per_page = ($per_page <= 0) ? 1 : $per_page;
1520
1521 $seperator = '<span class="page-sep">' . $user->lang['COMMA_SEPARATOR'] . '</span>';
1522 $total_pages = ceil($num_items / $per_page);
1523
1524 if ($total_pages == 1 || !$num_items)
1525 {
1526 return false;
1527 }
1528
1529 $on_page = floor($start_item / $per_page) + 1;
1530 $url_delim = (strpos($base_url, '?') === false) ? '?' : '&';
1531
1532 $page_string = ($on_page == 1) ? '<strong>1</strong>' : '<a href="' . $base_url . '">1</a>';
1533
1534 if ($total_pages > 5)
1535 {
1536 $start_cnt = min(max(1, $on_page - 4), $total_pages - 5);
1537 $end_cnt = max(min($total_pages, $on_page + 4), 6);
1538
1539 $page_string .= ($start_cnt > 1) ? ' ... ' : $seperator;
1540
1541 for ($i = $start_cnt + 1; $i < $end_cnt; $i++)
1542 {
1543 $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
1544 if ($i < $end_cnt - 1)
1545 {
1546 $page_string .= $seperator;
1547 }
1548 }
1549
1550 $page_string .= ($end_cnt < $total_pages) ? ' ... ' : $seperator;
1551 }
1552 else
1553 {
1554 $page_string .= $seperator;
1555
1556 for ($i = 2; $i < $total_pages; $i++)
1557 {
1558 $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
1559 if ($i < $total_pages)
1560 {
1561 $page_string .= $seperator;
1562 }
1563 }
1564 }
1565
1566 $page_string .= ($on_page == $total_pages) ? '<strong>' . $total_pages . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($total_pages - 1) * $per_page) . '">' . $total_pages . '</a>';
1567
1568 if ($add_prevnext_text)
1569 {
1570 if ($on_page != 1)
1571 {
1572 $page_string = '<a href="' . $base_url . "{$url_delim}start=" . (($on_page - 2) * $per_page) . '">' . $user->lang['PREVIOUS'] . '</a> ' . $page_string;
1573 }
1574
1575 if ($on_page != $total_pages)
1576 {
1577 $page_string .= ' <a href="' . $base_url . "{$url_delim}start=" . ($on_page * $per_page) . '">' . $user->lang['NEXT'] . '</a>';
1578 }
1579 }
1580
1581 $template->assign_vars(array(
1582 $tpl_prefix . 'BASE_URL' => $base_url,
1583 'A_' . $tpl_prefix . 'BASE_URL' => addslashes($base_url),
1584 $tpl_prefix . 'PER_PAGE' => $per_page,
1585
1586 $tpl_prefix . 'PREVIOUS_PAGE' => ($on_page == 1) ? '' : $base_url . "{$url_delim}start=" . (($on_page - 2) * $per_page),
1587 $tpl_prefix . 'NEXT_PAGE' => ($on_page == $total_pages) ? '' : $base_url . "{$url_delim}start=" . ($on_page * $per_page),
1588 $tpl_prefix . 'TOTAL_PAGES' => $total_pages,
1589 ));
1590
1591 return $page_string;
1592 }
1593
1594 /**
1595 * Return current page (pagination)
1596 */
1597 function on_page($num_items, $per_page, $start)
1598 {
1599 global $template, $user;
1600
1601 // Make sure $per_page is a valid value
1602 $per_page = ($per_page <= 0) ? 1 : $per_page;
1603
1604 $on_page = floor($start / $per_page) + 1;
1605
1606 $template->assign_vars(array(
1607 'ON_PAGE' => $on_page)
1608 );
1609
1610 return sprintf($user->lang['PAGE_OF'], $on_page, max(ceil($num_items / $per_page), 1));
1611 }
1612
1613 // Server functions (building urls, redirecting...)
1614
1615 /**
1616 * Append session id to url.
1617 * This function supports hooks.
1618 *
1619 * @param string $url The url the session id needs to be appended to (can have params)
1620 * @param mixed $params String or array of additional url parameters
1621 * @param bool $is_amp Is url using & (true) or & (false)
1622 * @param string $session_id Possibility to use a custom session id instead of the global one
1623 *
1624 * Examples:
1625 * <code>
1626 * append_sid("{$phpbb_root_path}viewtopic.$phpEx?t=1&f=2");
1627 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2');
1628 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2', false);
1629 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", array('t' => 1, 'f' => 2));
1630 * </code>
1631 *
1632 */
1633 function append_sid($url, $params = false, $is_amp = true, $session_id = false)
1634 {
1635 global $_SID, $_EXTRA_URL, $phpbb_hook;
1636
1637 // Developers using the hook function need to globalise the $_SID and $_EXTRA_URL on their own and also handle it appropiatly.
1638 // They could mimick most of what is within this function
1639 if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__, $url, $params, $is_amp, $session_id))
1640 {
1641 if ($phpbb_hook->hook_return(__FUNCTION__))
1642 {
1643 return $phpbb_hook->hook_return_result(__FUNCTION__);
1644 }
1645 }
1646
1647 // Assign sid if session id is not specified
1648 if ($session_id === false)
1649 {
1650 $session_id = $_SID;
1651 }
1652
1653 $amp_delim = ($is_amp) ? '&' : '&';
1654 $url_delim = (strpos($url, '?') === false) ? '?' : $amp_delim;
1655
1656 // Appending custom url parameter?
1657 $append_url = (!empty($_EXTRA_URL)) ? implode($amp_delim, $_EXTRA_URL) : '';
1658
1659 $anchor = '';
1660 if (strpos($url, '#') !== false)
1661 {
1662 list($url, $anchor) = explode('#', $url, 2);
1663 $anchor = '#' . $anchor;
1664 }
1665 else if (!is_array($params) && strpos($params, '#') !== false)
1666 {
1667 list($params, $anchor) = explode('#', $params, 2);
1668 $anchor = '#' . $anchor;
1669 }
1670
1671 // Use the short variant if possible ;)
1672 if ($params === false)
1673 {
1674 // Append session id
1675 if (!$session_id)
1676 {
1677 return $url . (($append_url) ? $url_delim . $append_url : '') . $anchor;
1678 }
1679 else
1680 {
1681 return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . 'sid=' . $session_id . $anchor;
1682 }
1683 }
1684
1685 // Build string if parameters are specified as array
1686 if (is_array($params))
1687 {
1688 $output = array();
1689
1690 foreach ($params as $key => $item)
1691 {
1692 if ($item === NULL)
1693 {
1694 continue;
1695 }
1696
1697 if ($key == '#')
1698 {
1699 $anchor = '#' . $item;
1700 continue;
1701 }
1702
1703 $output[] = $key . '=' . $item;
1704 }
1705
1706 $params = implode($amp_delim, $output);
1707 }
1708
1709 // Append session id and parameters (even if they are empty)
1710 // If parameters are empty, the developer can still append his/her parameters without caring about the delimiter
1711 return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . $params . ((!$session_id) ? '' : $amp_delim . 'sid=' . $session_id) . $anchor;
1712 }
1713
1714 /**
1715 * Generate board url (example: http://www.example.com/phpBB)
1716 * @param bool $without_script_path if set to true the script path gets not appended (example: http://www.example.com)
1717 */
1718 function generate_board_url($without_script_path = false)
1719 {
1720 global $config, $user;
1721
1722 $server_name = (!empty($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : getenv('SERVER_NAME');
1723 $server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT');
1724
1725 // Forcing server vars is the only way to specify/override the protocol
1726 if ($config['force_server_vars'] || !$server_name)
1727 {
1728 $server_protocol = ($config['server_protocol']) ? $config['server_protocol'] : (($config['cookie_secure']) ? 'https://' : 'http://');
1729 $server_name = $config['server_name'];
1730 $server_port = (int) $config['server_port'];
1731 $script_path = $config['script_path'];
1732
1733 $url = $server_protocol . $server_name;
1734 }
1735 else
1736 {
1737 // Do not rely on cookie_secure, users seem to think that it means a secured cookie instead of an encrypted connection
1738 $cookie_secure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 1 : 0;
1739 $url = (($cookie_secure) ? 'https://' : 'http://') . $server_name;
1740
1741 $script_path = $user->page['root_script_path'];
1742 }
1743
1744 if ($server_port && (($config['cookie_secure'] && $server_port <> 443) || (!$config['cookie_secure'] && $server_port <> 80)))
1745 {
1746 $url .= ':' . $server_port;
1747 }
1748
1749 if (!$without_script_path)
1750 {
1751 $url .= $script_path;
1752 }
1753
1754 // Strip / from the end
1755 if (substr($url, -1, 1) == '/')
1756 {
1757 $url = substr($url, 0, -1);
1758 }
1759
1760 return $url;
1761 }
1762
1763 /**
1764 * Redirects the user to another page then exits the script nicely
1765 */
1766 function redirect($url, $return = false)
1767 {
1768 global $db, $cache, $config, $user, $phpbb_root_path;
1769
1770 if (empty($user->lang))
1771 {
1772 $user->add_lang('common');
1773 }
1774
1775 if (!$return)
1776 {
1777 garbage_collection();
1778 }
1779
1780 // Make sure no &'s are in, this will break the redirect
1781 $url = str_replace('&', '&', $url);
1782
1783 // Determine which type of redirect we need to handle...
1784 $url_parts = parse_url($url);
1785
1786 if ($url_parts === false)
1787 {
1788 // Malformed url, redirect to current page...
1789 $url = generate_board_url() . '/' . $user->page['page'];
1790 }
1791 else if (!empty($url_parts['scheme']) && !empty($url_parts['host']))
1792 {
1793 // Full URL
1794 }
1795 else if ($url[0] == '/')
1796 {
1797 // Absolute uri, prepend direct url...
1798 $url = generate_board_url(true) . $url;
1799 }
1800 else
1801 {
1802 // Relative uri
1803 $pathinfo = pathinfo($url);
1804
1805 // Is the uri pointing to the current directory?
1806 if ($pathinfo['dirname'] == '.')
1807 {
1808 $url = str_replace('./', '', $url);
1809
1810 // Strip / from the beginning
1811 if ($url && substr($url, 0, 1) == '/')
1812 {
1813 $url = substr($url, 1);
1814 }
1815
1816 if ($user->page['page_dir'])
1817 {
1818 $url = generate_board_url() . '/' . $user->page['page_dir'] . '/' . $url;
1819 }
1820 else
1821 {
1822 $url = generate_board_url() . '/' . $url;
1823 }
1824 }
1825 else
1826 {
1827 // Used ./ before, but $phpbb_root_path is working better with urls within another root path
1828 $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($phpbb_root_path)));
1829 $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($pathinfo['dirname'])));
1830 $intersection = array_intersect_assoc($root_dirs, $page_dirs);
1831
1832 $root_dirs = array_diff_assoc($root_dirs, $intersection);
1833 $page_dirs = array_diff_assoc($page_dirs, $intersection);
1834
1835 $dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs);
1836
1837 // Strip / from the end
1838 if ($dir && substr($dir, -1, 1) == '/')
1839 {
1840 $dir = substr($dir, 0, -1);
1841 }
1842
1843 // Strip / from the beginning
1844 if ($dir && substr($dir, 0, 1) == '/')
1845 {
1846 $dir = substr($dir, 1);
1847 }
1848
1849 $url = str_replace($pathinfo['dirname'] . '/', '', $url);
1850
1851 // Strip / from the beginning
1852 if (substr($url, 0, 1) == '/')
1853 {
1854 $url = substr($url, 1);
1855 }
1856
1857 $url = $dir . '/' . $url;
1858 $url = generate_board_url() . '/' . $url;
1859 }
1860 }
1861
1862 // Make sure no linebreaks are there... to prevent http response splitting for PHP < 4.4.2
1863 if (strpos(urldecode($url), "\n") !== false || strpos(urldecode($url), "\r") !== false || strpos($url, ';') !== false)
1864 {
1865 trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
1866 }
1867
1868 // Now, also check the protocol and for a valid url the last time...
1869 $allowed_protocols = array('http', 'https', 'ftp', 'ftps');
1870 $url_parts = parse_url($url);
1871
1872 if ($url_parts === false || empty($url_parts['scheme']) || !in_array($url_parts['scheme'], $allowed_protocols))
1873 {
1874 trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
1875 }
1876
1877 if ($return)
1878 {
1879 return $url;
1880 }
1881
1882 // Redirect via an HTML form for PITA webservers
1883 if (@preg_match('#Microsoft|WebSTAR|Xitami#', getenv('SERVER_SOFTWARE')))
1884 {
1885 header('Refresh: 0; URL=' . $url);
1886
1887 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1888 echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="' . $user->lang['DIRECTION'] . '" lang="' . $user->lang['USER_LANG'] . '" xml:lang="' . $user->lang['USER_LANG'] . '">';
1889 echo '<head>';
1890 echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
1891 echo '<meta http-equiv="refresh" content="0; url=' . str_replace('&', '&', $url) . '" />';
1892 echo '<title>' . $user->lang['REDIRECT'] . '</title>';
1893 echo '</head>';
1894 echo '<body>';
1895 echo '<div style="text-align: center;">' . sprintf($user->lang['URL_REDIRECT'], '<a href="' . str_replace('&', '&', $url) . '">', '</a>') . '</div>';
1896 echo '</body>';
1897 echo '</html>';
1898
1899 exit;
1900 }
1901
1902 // Behave as per HTTP/1.1 spec for others
1903 header('Location: ' . $url);
1904 exit;
1905 }
1906
1907 /**
1908 * Re-Apply session id after page reloads
1909 */
1910 function reapply_sid($url)
1911 {
1912 global $phpEx, $phpbb_root_path;
1913
1914 if ($url === "index.$phpEx")
1915 {
1916 return append_sid("index.$phpEx");
1917 }
1918 else if ($url === "{$phpbb_root_path}index.$phpEx")
1919 {
1920 return append_sid("{$phpbb_root_path}index.$phpEx");
1921 }
1922
1923 // Remove previously added sid
1924 if (strpos($url, '?sid=') !== false)
1925 {
1926 $url = preg_replace('/(\?)sid=[a-z0-9]+(&|&)?/', '\1', $url);
1927 }
1928 else if (strpos($url, '&sid=') !== false)
1929 {
1930 $url = preg_replace('/&sid=[a-z0-9]+(&)?/', '\1', $url);
1931 }
1932 else if (strpos($url, '&sid=') !== false)
1933 {
1934 $url = preg_replace('/&sid=[a-z0-9]+(&)?/', '\1', $url);
1935 }
1936
1937 return append_sid($url);
1938 }
1939
1940 /**
1941 * Returns url from the session/current page with an re-appended SID with optionally stripping vars from the url
1942 */
1943 function build_url($strip_vars = false)
1944 {
1945 global $user, $phpbb_root_path;
1946
1947 // Append SID
1948 $redirect = append_sid($user->page['page'], false, false);
1949
1950 // Add delimiter if not there...
1951 if (strpos($redirect, '?') === false)
1952 {
1953 $redirect .= '?';
1954 }
1955
1956 // Strip vars...
1957 if ($strip_vars !== false && strpos($redirect, '?') !== false)
1958 {
1959 if (!is_array($strip_vars))
1960 {
1961 $strip_vars = array($strip_vars);
1962 }
1963
1964 $query = $_query = array();
1965
1966 $args = substr($redirect, strpos($redirect, '?') + 1);
1967 $args = ($args) ? explode('&', $args) : array();
1968 $redirect = substr($redirect, 0, strpos($redirect, '?'));
1969
1970 foreach ($args as $argument)
1971 {
1972 $arguments = explode('=', $argument);
1973 $key = $arguments[0];
1974 unset($arguments[0]);
1975
1976 $query[$key] = implode('=', $arguments);
1977 }
1978
1979 // Strip the vars off
1980 foreach ($strip_vars as $strip)
1981 {
1982 if (isset($query[$strip]))
1983 {
1984 unset($query[$strip]);
1985 }
1986 }
1987
1988 // Glue the remaining parts together... already urlencoded
1989 foreach ($query as $key => $value)
1990 {
1991 $_query[] = $key . '=' . $value;
1992 }
1993 $query = implode('&', $_query);
1994
1995 $redirect .= ($query) ? '?' . $query : '';
1996 }
1997
1998 return $phpbb_root_path . str_replace('&', '&', $redirect);
1999 }
2000
2001 /**
2002 * Meta refresh assignment
2003 */
2004 function meta_refresh($time, $url)
2005 {
2006 global $template;
2007
2008 $url = redirect($url, true);
2009
2010 // For XHTML compatibility we change back & to &
2011 $template->assign_vars(array(
2012 'META' => '<meta http-equiv="refresh" content="' . $time . ';url=' . str_replace('&', '&', $url) . '" />')
2013 );
2014 }
2015
2016 //Form validation
2017
2018 /**
2019 * Add a secret token to the form (requires the S_FORM_TOKEN template variable)
2020 * @param string $form_name The name of the form; has to match the name used in check_form_key, otherwise no restrictions apply
2021 */
2022 function add_form_key($form_name)
2023 {
2024 global $config, $template, $user;
2025 $now = time();
2026 $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : '';
2027 $token = sha1($now . $user->data['user_form_salt'] . $form_name . $token_sid);
2028
2029 $s_fields = build_hidden_fields(array(
2030 'creation_time' => $now,
2031 'form_token' => $token,
2032 ));
2033 $template->assign_vars(array(
2034 'S_FORM_TOKEN' => $s_fields,
2035 ));
2036 }
2037
2038 /**
2039 * Check the form key. Required for all altering actions not secured by confirm_box
2040 * @param string $form_name The name of the form; has to match the name used in add_form_key, otherwise no restrictions apply
2041 * @param int $timespan The maximum acceptable age for a submitted form in seconds. Defaults to the config setting.
2042 * @param string $return_page The address for the return link
2043 * @param bool $trigger If true, the function will triger an error when encountering an invalid form
2044 * @param int $minimum_time The minimum acceptable age for a submitted form in seconds
2045 */
2046 function check_form_key($form_name, $timespan = false, $return_page = '', $trigger = false, $minimum_time = false)
2047 {
2048 global $config, $user;
2049
2050 if ($timespan === false)
2051 {
2052 // we enforce a minimum value of half a minute here.
2053 $timespan = ($config['form_token_lifetime'] == -1) ? -1 : max(30, $config['form_token_lifetime']);
2054 }
2055 if ($minimum_time === false)
2056 {
2057 $minimum_time = (int) $config['form_token_mintime'];
2058 }
2059
2060 if (isset($_POST['creation_time']) && isset($_POST['form_token']))
2061 {
2062 $creation_time = abs(request_var('creation_time', 0));
2063 $token = request_var('form_token', '');
2064
2065 $diff = (time() - $creation_time);
2066
2067 if (($diff >= $minimum_time) && (($diff <= $timespan) || $timespan == -1))
2068 {
2069 $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : '';
2070
2071 $key = sha1($creation_time . $user->data['user_form_salt'] . $form_name . $token_sid);
2072 if ($key === $token)
2073 {
2074 return true;
2075 }
2076 }
2077 }
2078 if ($trigger)
2079 {
2080 trigger_error($user->lang['FORM_INVALID'] . $return_page);
2081 }
2082 return false;
2083 }
2084
2085 // Message/Login boxes
2086
2087 /**
2088 * Build Confirm box
2089 * @param boolean $check True for checking if confirmed (without any additional parameters) and false for displaying the confirm box
2090 * @param string $title Title/Message used for confirm box.
2091 * message text is _CONFIRM appended to title.
2092 * If title cannot be found in user->lang a default one is displayed
2093 * If title_CONFIRM cannot be found in user->lang the text given is used.
2094 * @param string $hidden Hidden variables
2095 * @param string $html_body Template used for confirm box
2096 * @param string $u_action Custom form action
2097 */
2098 function confirm_box($check, $title = '', $hidden = '', $html_body = 'confirm_body.html', $u_action = '')
2099 {
2100 global $user, $template, $db;
2101 global $phpEx, $phpbb_root_path;
2102
2103 if (isset($_POST['cancel']))
2104 {
2105 return false;
2106 }
2107
2108 $confirm = false;
2109 if (isset($_POST['confirm']))
2110 {
2111 // language frontier
2112 if ($_POST['confirm'] === $user->lang['YES'])
2113 {
2114 $confirm = true;
2115 }
2116 }
2117
2118 if ($check && $confirm)
2119 {
2120 $user_id = request_var('user_id', 0);
2121 $session_id = request_var('sess', '');
2122 $confirm_key = request_var('confirm_key', '');
2123
2124 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'])
2125 {
2126 return false;
2127 }
2128
2129 // Reset user_last_confirm_key
2130 $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = ''
2131 WHERE user_id = " . $user->data['user_id'];
2132 $db->sql_query($sql);
2133
2134 return true;
2135 }
2136 else if ($check)
2137 {
2138 return false;
2139 }
2140
2141 $s_hidden_fields = build_hidden_fields(array(
2142 'user_id' => $user->data['user_id'],
2143 'sess' => $user->session_id,
2144 'sid' => $user->session_id)
2145 );
2146
2147 // generate activation key
2148 $confirm_key = gen_rand_string(10);
2149
2150 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
2151 {
2152 adm_page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
2153 }
2154 else
2155 {
2156 page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
2157 }
2158
2159 $template->set_filenames(array(
2160 'body' => $html_body)
2161 );
2162
2163 // If activation key already exist, we better do not re-use the key (something very strange is going on...)
2164 if (request_var('confirm_key', ''))
2165 {
2166 // This should not occur, therefore we cancel the operation to safe the user
2167 return false;
2168 }
2169
2170 // re-add sid / transform & to & for user->page (user->page is always using &)
2171 $use_page = ($u_action) ? $phpbb_root_path . $u_action : $phpbb_root_path . str_replace('&', '&', $user->page['page']);
2172 $u_action = reapply_sid($use_page);
2173 $u_action .= ((strpos($u_action, '?') === false) ? '?' : '&') . 'confirm_key=' . $confirm_key;
2174
2175 $template->assign_vars(array(
2176 'MESSAGE_TITLE' => (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title],
2177 'MESSAGE_TEXT' => (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'],
2178
2179 'YES_VALUE' => $user->lang['YES'],
2180 'S_CONFIRM_ACTION' => $u_action,
2181 'S_HIDDEN_FIELDS' => $hidden . $s_hidden_fields)
2182 );
2183
2184 $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = '" . $db->sql_escape($confirm_key) . "'
2185 WHERE user_id = " . $user->data['user_id'];
2186 $db->sql_query($sql);
2187
2188 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
2189 {
2190 adm_page_footer();
2191 }
2192 else
2193 {
2194 page_footer();
2195 }
2196 }
2197
2198 /**
2199 * Generate login box or verify password
2200 */
2201 function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = false, $s_display = true)
2202 {
2203 global $db, $user, $template, $auth, $phpEx, $phpbb_root_path, $config;
2204
2205 $err = '';
2206
2207 // Make sure user->setup() has been called
2208 if (empty($user->lang))
2209 {
2210 $user->setup();
2211 }
2212
2213 // Print out error if user tries to authenticate as an administrator without having the privileges...
2214 if ($admin && !$auth->acl_get('a_'))
2215 {
2216 // Not authd
2217 // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
2218 if ($user->data['is_registered'])
2219 {
2220 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
2221 }
2222 trigger_error('NO_AUTH_ADMIN');
2223 }
2224
2225 if (isset($_POST['login']))
2226 {
2227 // Get credential
2228 if ($admin)
2229 {
2230 $credential = request_var('credential', '');
2231
2232 if (strspn($credential, 'abcdef0123456789') !== strlen($credential) || strlen($credential) != 32)
2233 {
2234 if ($user->data['is_registered'])
2235 {
2236 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
2237 }
2238 trigger_error('NO_AUTH_ADMIN');
2239 }
2240
2241 $password = request_var('password_' . $credential, '', true);
2242 }
2243 else
2244 {
2245 $password = request_var('password', '', true);
2246 }
2247
2248 $username = request_var('username', '', true);
2249 $autologin = (!empty($_POST['autologin'])) ? true : false;
2250 $viewonline = (!empty($_POST['viewonline'])) ? 0 : 1;
2251 $admin = ($admin) ? 1 : 0;
2252 $viewonline = ($admin) ? $user->data['session_viewonline'] : $viewonline;
2253
2254 // Check if the supplied username is equal to the one stored within the database if re-authenticating
2255 if ($admin && utf8_clean_string($username) != utf8_clean_string($user->data['username']))
2256 {
2257 // We log the attempt to use a different username...
2258 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
2259 trigger_error('NO_AUTH_ADMIN_USER_DIFFER');
2260 }
2261
2262 // If authentication is successful we redirect user to previous page
2263 $result = $auth->login($username, $password, $autologin, $viewonline, $admin);
2264
2265 // If admin authentication and login, we will log if it was a success or not...
2266 // We also break the operation on the first non-success login - it could be argued that the user already knows
2267 if ($admin)
2268 {
2269 if ($result['status'] == LOGIN_SUCCESS)
2270 {
2271 add_log('admin', 'LOG_ADMIN_AUTH_SUCCESS');
2272 }
2273 else
2274 {
2275 // Only log the failed attempt if a real user tried to.
2276 // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
2277 if ($user->data['is_registered'])
2278 {
2279 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
2280 }
2281 }
2282 }
2283
2284 // The result parameter is always an array, holding the relevant information...
2285 if ($result['status'] == LOGIN_SUCCESS)
2286 {
2287 $redirect = request_var('redirect', "{$phpbb_root_path}index.$phpEx");
2288 $message = ($l_success) ? $l_success : $user->lang['LOGIN_REDIRECT'];
2289 $l_redirect = ($admin) ? $user->lang['PROCEED_TO_ACP'] : (($redirect === "{$phpbb_root_path}index.$phpEx" || $redirect === "index.$phpEx") ? $user->lang['RETURN_INDEX'] : $user->lang['RETURN_PAGE']);
2290
2291 // append/replace SID (may change during the session for AOL users)
2292 $redirect = reapply_sid($redirect);
2293
2294 // Special case... the user is effectively banned, but we allow founders to login
2295 if (defined('IN_CHECK_BAN') && $result['user_row']['user_type'] != USER_FOUNDER)
2296 {
2297 return;
2298 }
2299
2300 meta_refresh(3, $redirect);
2301 trigger_error($message . '<br /><br />' . sprintf($l_redirect, '<a href="' . $redirect . '">', '</a>'));
2302 }
2303
2304 // Something failed, determine what...
2305 if ($result['status'] == LOGIN_BREAK)
2306 {
2307 trigger_error($result['error_msg'], E_USER_ERROR);
2308 }
2309
2310 // Special cases... determine
2311 switch ($result['status'])
2312 {
2313 case LOGIN_ERROR_ATTEMPTS:
2314
2315 // Show confirm image
2316 $sql = 'DELETE FROM ' . CONFIRM_TABLE . "
2317 WHERE session_id = '" . $db->sql_escape($user->session_id) . "'
2318 AND confirm_type = " . CONFIRM_LOGIN;
2319 $db->sql_query($sql);
2320
2321 // Generate code
2322 $code = gen_rand_string(mt_rand(5, 8));
2323 $confirm_id = md5(unique_id($user->ip));
2324 $seed = hexdec(substr(unique_id(), 4, 10));
2325
2326 // compute $seed % 0x7fffffff
2327 $seed -= 0x7fffffff * floor($seed / 0x7fffffff);
2328
2329 $sql = 'INSERT INTO ' . CONFIRM_TABLE . ' ' . $db->sql_build_array('INSERT', array(
2330 'confirm_id' => (string) $confirm_id,
2331 'session_id' => (string) $user->session_id,
2332 'confirm_type' => (int) CONFIRM_LOGIN,
2333 'code' => (string) $code,
2334 'seed' => (int) $seed)
2335 );
2336 $db->sql_query($sql);
2337
2338 $template->assign_vars(array(
2339 'S_CONFIRM_CODE' => true,
2340 'CONFIRM_ID' => $confirm_id,
2341 'CONFIRM_IMAGE' => '<img src="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=confirm&id=' . $confirm_id . '&type=' . CONFIRM_LOGIN) . '" alt="" title="" />',
2342 'L_LOGIN_CONFIRM_EXPLAIN' => sprintf($user->lang['LOGIN_CONFIRM_EXPLAIN'], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>'),
2343 ));
2344
2345 $err = $user->lang[$result['error_msg']];
2346
2347 break;
2348
2349 case LOGIN_ERROR_PASSWORD_CONVERT:
2350 $err = sprintf(
2351 $user->lang[$result['error_msg']],
2352 ($config['email_enable']) ? '<a href="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') . '">' : '',
2353 ($config['email_enable']) ? '</a>' : '',
2354 ($config['board_contact']) ? '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">' : '',
2355 ($config['board_contact']) ? '</a>' : ''
2356 );
2357 break;
2358
2359 // Username, password, etc...
2360 default:
2361 $err = $user->lang[$result['error_msg']];
2362
2363 // Assign admin contact to some error messages
2364 if ($result['error_msg'] == 'LOGIN_ERROR_USERNAME' || $result['error_msg'] == 'LOGIN_ERROR_PASSWORD')
2365 {
2366 $err = (!$config['board_contact']) ? sprintf($user->lang[$result['error_msg']], '', '') : sprintf($user->lang[$result['error_msg']], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>');
2367 }
2368
2369 break;
2370 }
2371 }
2372
2373 if (!$redirect)
2374 {
2375 // We just use what the session code determined...
2376 // If we are not within the admin directory we use the page dir...
2377 $redirect = '';
2378
2379 if (!$admin)
2380 {
2381 $redirect .= ($user->page['page_dir']) ? $user->page['page_dir'] . '/' : '';
2382 }
2383
2384 $redirect .= $user->page['page_name'] . (($user->page['query_string']) ? '?' . htmlspecialchars($user->page['query_string']) : '');
2385 }
2386
2387 // Assign credential for username/password pair
2388 $credential = ($admin) ? md5(unique_id()) : false;
2389
2390 $s_hidden_fields = array(
2391 'redirect' => $redirect,
2392 'sid' => $user->session_id,
2393 );
2394
2395 if ($admin)
2396 {
2397 $s_hidden_fields['credential'] = $credential;
2398 }
2399
2400 $s_hidden_fields = build_hidden_fields($s_hidden_fields);
2401
2402 $template->assign_vars(array(
2403 'LOGIN_ERROR' => $err,
2404 'LOGIN_EXPLAIN' => $l_explain,
2405
2406 'U_SEND_PASSWORD' => ($config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') : '',
2407 'U_RESEND_ACTIVATION' => ($config['require_activation'] != USER_ACTIVATION_NONE && $config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=resend_act') : '',
2408 'U_TERMS_USE' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'),
2409 'U_PRIVACY' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'),
2410
2411 'S_DISPLAY_FULL_LOGIN' => ($s_display) ? true : false,
2412 'S_LOGIN_ACTION' => (!$admin) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login') : append_sid("index.$phpEx", false, true, $user->session_id), // Needs to stay index.$phpEx because we are within the admin directory
2413 'S_HIDDEN_FIELDS' => $s_hidden_fields,
2414
2415 'S_ADMIN_AUTH' => $admin,
2416 'USERNAME' => ($admin) ? $user->data['username'] : '',
2417
2418 'USERNAME_CREDENTIAL' => 'username',
2419 'PASSWORD_CREDENTIAL' => ($admin) ? 'password_' . $credential : 'password',
2420 ));
2421
2422 page_header($user->lang['LOGIN']);
2423
2424 $template->set_filenames(array(
2425 'body' => 'login_body.html')
2426 );
2427 make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx"));
2428
2429 page_footer();
2430 }
2431
2432 /**
2433 * Generate forum login box
2434 */
2435 function login_forum_box($forum_data)
2436 {
2437 global $db, $config, $user, $template, $phpEx;
2438
2439 $password = request_var('password', '', true);
2440
2441 $sql = 'SELECT forum_id
2442 FROM ' . FORUMS_ACCESS_TABLE . '
2443 WHERE forum_id = ' . $forum_data['forum_id'] . '
2444 AND user_id = ' . $user->data['user_id'] . "
2445 AND session_id = '" . $db->sql_escape($user->session_id) . "'";
2446 $result = $db->sql_query($sql);
2447 $row = $db->sql_fetchrow($result);
2448 $db->sql_freeresult($result);
2449
2450 if ($row)
2451 {
2452 return true;
2453 }
2454
2455 if ($password)
2456 {
2457 // Remove expired authorised sessions
2458 $sql = 'SELECT f.session_id
2459 FROM ' . FORUMS_ACCESS_TABLE . ' f
2460 LEFT JOIN ' . SESSIONS_TABLE . ' s ON (f.session_id = s.session_id)
2461 WHERE s.session_id IS NULL';
2462 $result = $db->sql_query($sql);
2463
2464 if ($row = $db->sql_fetchrow($result))
2465 {
2466 $sql_in = array();
2467 do
2468 {
2469 $sql_in[] = (string) $row['session_id'];
2470 }
2471 while ($row = $db->sql_fetchrow($result));
2472
2473 // Remove expired sessions
2474 $sql = 'DELETE FROM ' . FORUMS_ACCESS_TABLE . '
2475 WHERE ' . $db->sql_in_set('session_id', $sql_in);
2476 $db->sql_query($sql);
2477 }
2478 $db->sql_freeresult($result);
2479
2480 if (phpbb_check_hash($password, $forum_data['forum_password']))
2481 {
2482 $sql_ary = array(
2483 'forum_id' => (int) $forum_data['forum_id'],
2484 'user_id' => (int) $user->data['user_id'],
2485 'session_id' => (string) $user->session_id,
2486 );
2487
2488 $db->sql_query('INSERT INTO ' . FORUMS_ACCESS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
2489
2490 return true;
2491 }
2492
2493 $template->assign_var('LOGIN_ERROR', $user->lang['WRONG_PASSWORD']);
2494 }
2495
2496 page_header($user->lang['LOGIN']);
2497
2498 $template->assign_vars(array(
2499 'S_HIDDEN_FIELDS' => build_hidden_fields(array('f' => $forum_data['forum_id'])))
2500 );
2501
2502 $template->set_filenames(array(
2503 'body' => 'login_forum.html')
2504 );
2505
2506 page_footer();
2507 }
2508
2509 // Little helpers
2510
2511 /**
2512 * Little helper for the build_hidden_fields function
2513 */
2514 function _build_hidden_fields($key, $value, $specialchar, $stripslashes)
2515 {
2516 $hidden_fields = '';
2517
2518 if (!is_array($value))
2519 {
2520 $value = ($stripslashes) ? stripslashes($value) : $value;
2521 $value = ($specialchar) ? htmlspecialchars($value, ENT_COMPAT, 'UTF-8') : $value;
2522
2523 $hidden_fields .= '<input type="hidden" name="' . $key . '" value="' . $value . '" />' . "\n";
2524 }
2525 else
2526 {
2527 foreach ($value as $_key => $_value)
2528 {
2529 $_key = ($stripslashes) ? stripslashes($_key) : $_key;
2530 $_key = ($specialchar) ? htmlspecialchars($_key, ENT_COMPAT, 'UTF-8') : $_key;
2531
2532 $hidden_fields .= _build_hidden_fields($key . '[' . $_key . ']', $_value, $specialchar, $stripslashes);
2533 }
2534 }
2535
2536 return $hidden_fields;
2537 }
2538
2539 /**
2540 * Build simple hidden fields from array
2541 *
2542 * @param array $field_ary an array of values to build the hidden field from
2543 * @param bool $specialchar if true, keys and values get specialchared
2544 * @param bool $stripslashes if true, keys and values get stripslashed
2545 *
2546 * @return string the hidden fields
2547 */
2548 function build_hidden_fields($field_ary, $specialchar = false, $stripslashes = false)
2549 {
2550 $s_hidden_fields = '';
2551
2552 foreach ($field_ary as $name => $vars)
2553 {
2554 $name = ($stripslashes) ? stripslashes($name) : $name;
2555 $name = ($specialchar) ? htmlspecialchars($name, ENT_COMPAT, 'UTF-8') : $name;
2556
2557 $s_hidden_fields .= _build_hidden_fields($name, $vars, $specialchar, $stripslashes);
2558 }
2559
2560 return $s_hidden_fields;
2561 }
2562
2563 /**
2564 * Parse cfg file
2565 */
2566 function parse_cfg_file($filename, $lines = false)
2567 {
2568 $parsed_items = array();
2569
2570 if ($lines === false)
2571 {
2572 $lines = file($filename);
2573 }
2574
2575 foreach ($lines as $line)
2576 {
2577 $line = trim($line);
2578
2579 if (!$line || $line[0] == '#' || ($delim_pos = strpos($line, '=')) === false)
2580 {
2581 continue;
2582 }
2583
2584 // Determine first occurrence, since in values the equal sign is allowed
2585 $key = strtolower(trim(substr($line, 0, $delim_pos)));
2586 $value = trim(substr($line, $delim_pos + 1));
2587
2588 if (in_array($value, array('off', 'false', '0')))
2589 {
2590 $value = false;
2591 }
2592 else if (in_array($value, array('on', 'true', '1')))
2593 {
2594 $value = true;
2595 }
2596 else if (!trim($value))
2597 {
2598 $value = '';
2599 }
2600 else if (($value[0] == "'" && $value[sizeof($value) - 1] == "'") || ($value[0] == '"' && $value[sizeof($value) - 1] == '"'))
2601 {
2602 $value = substr($value, 1, sizeof($value)-2);
2603 }
2604
2605 $parsed_items[$key] = $value;
2606 }
2607
2608 return $parsed_items;
2609 }
2610
2611 /**
2612 * Add log event
2613 */
2614 function add_log()
2615 {
2616 global $db, $user;
2617
2618 $args = func_get_args();
2619
2620 $mode = array_shift($args);
2621 $reportee_id = ($mode == 'user') ? intval(array_shift($args)) : '';
2622 $forum_id = ($mode == 'mod') ? intval(array_shift($args)) : '';
2623 $topic_id = ($mode == 'mod') ? intval(array_shift($args)) : '';
2624 $action = array_shift($args);
2625 $data = (!sizeof($args)) ? '' : serialize($args);
2626
2627 $sql_ary = array(
2628 'user_id' => (empty($user->data)) ? ANONYMOUS : $user->data['user_id'],
2629 'log_ip' => $user->ip,
2630 'log_time' => time(),
2631 'log_operation' => $action,
2632 'log_data' => $data,
2633 );
2634
2635 switch ($mode)
2636 {
2637 case 'admin':
2638 $sql_ary['log_type'] = LOG_ADMIN;
2639 break;
2640
2641 case 'mod':
2642 $sql_ary += array(
2643 'log_type' => LOG_MOD,
2644 'forum_id' => $forum_id,
2645 'topic_id' => $topic_id
2646 );
2647 break;
2648
2649 case 'user':
2650 $sql_ary += array(
2651 'log_type' => LOG_USERS,
2652 'reportee_id' => $reportee_id
2653 );
2654 break;
2655
2656 case 'critical':
2657 $sql_ary['log_type'] = LOG_CRITICAL;
2658 break;
2659
2660 default:
2661 return false;
2662 }
2663
2664 $db->sql_query('INSERT INTO ' . LOG_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
2665
2666 return $db->sql_nextid();
2667 }
2668
2669 /**
2670 * Return a nicely formatted backtrace (parts from the php manual by diz at ysagoon dot com)
2671 */
2672 function get_backtrace()
2673 {
2674 global $phpbb_root_path;
2675
2676 $output = '<div style="font-family: monospace;">';
2677 $backtrace = debug_backtrace();
2678 $path = phpbb_realpath($phpbb_root_path);
2679
2680 foreach ($backtrace as $number => $trace)
2681 {
2682 // We skip the first one, because it only shows this file/function
2683 if ($number == 0)
2684 {
2685 continue;
2686 }
2687
2688 // Strip the current directory from path
2689 if (empty($trace['file']))
2690 {
2691 $trace['file'] = '';
2692 }
2693 else
2694 {
2695 $trace['file'] = str_replace(array($path, '\\'), array('', '/'), $trace['file']);
2696 $trace['file'] = substr($trace['file'], 1);
2697 }
2698 $args = array();
2699
2700 // If include/require/include_once is not called, do not show arguments - they may contain sensible information
2701 if (!in_array($trace['function'], array('include', 'require', 'include_once')))
2702 {
2703 unset($trace['args']);
2704 }
2705 else
2706 {
2707 // Path...
2708 if (!empty($trace['args'][0]))
2709 {
2710 $argument = htmlspecialchars($trace['args'][0]);
2711 $argument = str_replace(array($path, '\\'), array('', '/'), $argument);
2712 $argument = substr($argument, 1);
2713 $args[] = "'{$argument}'";
2714 }
2715 }
2716
2717 $trace['class'] = (!isset($trace['class'])) ? '' : $trace['class'];
2718 $trace['type'] = (!isset($trace['type'])) ? '' : $trace['type'];
2719
2720 $output .= '<br />';
2721 $output .= '<b>FILE:</b> ' . htmlspecialchars($trace['file']) . '<br />';
2722 $output .= '<b>LINE:</b> ' . ((!empty($trace['line'])) ? $trace['line'] : '') . '<br />';
2723
2724 $output .= '<b>CALL:</b> ' . htmlspecialchars($trace['class'] . $trace['type'] . $trace['function']) . '(' . ((sizeof($args)) ? implode(', ', $args) : '') . ')<br />';
2725 }
2726 $output .= '</div>';
2727 return $output;
2728 }
2729
2730 /**
2731 * This function returns a regular expression pattern for commonly used expressions
2732 * Use with / as delimiter for email mode and # for url modes
2733 * mode can be: email|bbcode_htm|url|url_inline|www_url|www_url_inline|relative_url|relative_url_inline|ipv4|ipv6
2734 */
2735 function get_preg_expression($mode)
2736 {
2737 switch ($mode)
2738 {
2739 case 'email':
2740 return '[a-z0-9&\'\.\-_\+]+@[a-z0-9\-]+\.([a-z0-9\-]+\.)*[a-z]+';
2741 break;
2742
2743 case 'bbcode_htm':
2744 return array(
2745 '#<!\-\- e \-\-><a href="mailto:(.*?)">.*?</a><!\-\- e \-\->#',
2746 '#<!\-\- l \-\-><a (?:class="[\w-]+" )?href="(.*?)(?:(&|\?)sid=[0-9a-f]{32})?">.*?</a><!\-\- l \-\->#',
2747 '#<!\-\- ([mw]) \-\-><a (?:class="[\w-]+" )?href="(.*?)">.*?</a><!\-\- \1 \-\->#',
2748 '#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#',
2749 '#<!\-\- .*? \-\->#s',
2750 '#<.*?>#s',
2751 );
2752 break;
2753
2754 // Whoa these look impressive!
2755 // The code to generate the following two regular expressions which match valid IPv4/IPv6 addresses
2756 // can be found in the develop directory
2757 case 'ipv4':
2758 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])$#';
2759 break;
2760
2761 case 'ipv6':
2762 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}:){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';
2763 break;
2764
2765 case 'url':
2766 case 'url_inline':
2767 $inline = ($mode == 'url') ? ')' : '';
2768 $scheme = ($mode == 'url') ? '[a-z\d+\-.]' : '[a-z\d+]'; // avoid automatic parsing of "word" in "last word.http://..."
2769 // generated with regex generation file in the develop folder
2770 return "[a-z]$scheme*:/{2}(?:(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})+|[0-9.]+|\[[a-z0-9.]+:[a-z0-9.]+:[a-z0-9.:]+\])(?::\d*)?(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?";
2771 break;
2772
2773 case 'www_url':
2774 case 'www_url_inline':
2775 $inline = ($mode == 'www_url') ? ')' : '';
2776 return "www\.(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})+(?::\d*)?(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?";
2777 break;
2778
2779 case 'relative_url':
2780 case 'relative_url_inline':
2781 $inline = ($mode == 'relative_url') ? ')' : '';
2782 return "(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?";
2783 break;
2784 }
2785
2786 return '';
2787 }
2788
2789 /**
2790 * Returns the first block of the specified IPv6 address and as many additional
2791 * ones as specified in the length paramater.
2792 * If length is zero, then an empty string is returned.
2793 * If length is greater than 3 the complete IP will be returned
2794 */
2795 function short_ipv6($ip, $length)
2796 {
2797 if ($length < 1)
2798 {
2799 return '';
2800 }
2801
2802 // extend IPv6 addresses
2803 $blocks = substr_count($ip, ':') + 1;
2804 if ($blocks < 9)
2805 {
2806 $ip = str_replace('::', ':' . str_repeat('0000:', 9 - $blocks), $ip);
2807 }
2808 if ($ip[0] == ':')
2809 {
2810 $ip = '0000' . $ip;
2811 }
2812 if ($length < 4)
2813 {
2814 $ip = implode(':', array_slice(explode(':', $ip), 0, 1 + $length));
2815 }
2816
2817 return $ip;
2818 }
2819
2820 /**
2821 * Wrapper for php's checkdnsrr function.
2822 *
2823 * The windows failover is from the php manual
2824 * Please make sure to check the return value for === true and === false, since NULL could
2825 * be returned too.
2826 *
2827 * @return true if entry found, false if not, NULL if this function is not supported by this environment
2828 */
2829 function phpbb_checkdnsrr($host, $type = '')
2830 {
2831 $type = (!$type) ? 'MX' : $type;
2832
2833 if (DIRECTORY_SEPARATOR == '\\')
2834 {
2835 if (!function_exists('exec'))
2836 {
2837 return NULL;
2838 }
2839
2840 // @exec('nslookup -retry=1 -timout=1 -type=' . escapeshellarg($type) . ' ' . escapeshellarg($host), $output);
2841 @exec('nslookup -type=' . escapeshellarg($type) . ' ' . escapeshellarg($host), $output);
2842
2843 // If output is empty, the nslookup failed
2844 if (empty($output))
2845 {
2846 return NULL;
2847 }
2848
2849 foreach ($output as $line)
2850 {
2851 if (!trim($line))
2852 {
2853 continue;
2854 }
2855
2856 // Valid records begin with host name:
2857 if (strpos($line, $host) === 0)
2858 {
2859 return true;
2860 }
2861 }
2862
2863 return false;
2864 }
2865 else if (function_exists('checkdnsrr'))
2866 {
2867 return (checkdnsrr($host, $type)) ? true : false;
2868 }
2869
2870 return NULL;
2871 }
2872
2873 // Handler, header and footer
2874
2875 /**
2876 * Error and message handler, call with trigger_error if reqd
2877 */
2878 function msg_handler($errno, $msg_text, $errfile, $errline)
2879 {
2880 global $cache, $db, $auth, $template, $config, $user;
2881 global $phpEx, $phpbb_root_path, $msg_title, $msg_long_text;
2882
2883 // Do not display notices if we suppress them via @
2884 if (error_reporting() == 0)
2885 {
2886 return;
2887 }
2888
2889 // Message handler is stripping text. In case we need it, we are possible to define long text...
2890 if (isset($msg_long_text) && $msg_long_text && !$msg_text)
2891 {
2892 $msg_text = $msg_long_text;
2893 }
2894
2895 switch ($errno)
2896 {
2897 case E_NOTICE:
2898 case E_WARNING:
2899
2900 // Check the error reporting level and return if the error level does not match
2901 // If DEBUG is defined the default level is E_ALL
2902 if (($errno & ((defined('DEBUG')) ? E_ALL : error_reporting())) == 0)
2903 {
2904 return;
2905 }
2906
2907 if (strpos($errfile, 'cache') === false && strpos($errfile, 'template.') === false)
2908 {
2909 // flush the content, else we get a white page if output buffering is on
2910 if ($config['gzip_compress'])
2911 {
2912 if (@extension_loaded('zlib') && !headers_sent())
2913 {
2914 ob_flush();
2915 }
2916 }
2917
2918 // remove complete path to installation, with the risk of changing backslashes meant to be there
2919 $errfile = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $errfile);
2920 $msg_text = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $msg_text);
2921
2922 echo '<b>[phpBB Debug] PHP Notice</b>: in file <b>' . $errfile . '</b> on line <b>' . $errline . '</b>: <b>' . $msg_text . '</b><br />' . "\n";
2923 }
2924
2925 return;
2926
2927 break;
2928
2929 case E_USER_ERROR:
2930
2931 if (!empty($user) && !empty($user->lang))
2932 {
2933 $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text;
2934 $msg_title = (!isset($msg_title)) ? $user->lang['GENERAL_ERROR'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title);
2935
2936 $l_return_index = sprintf($user->lang['RETURN_INDEX'], '<a href="' . $phpbb_root_path . '">', '</a>');
2937 $l_notify = '';
2938
2939 if (!empty($config['board_contact']))
2940 {
2941 $l_notify = '<p>' . sprintf($user->lang['NOTIFY_ADMIN_EMAIL'], $config['board_contact']) . '</p>';
2942 }
2943 }
2944 else
2945 {
2946 $msg_title = 'General Error';
2947 $l_return_index = '<a href="' . $phpbb_root_path . '">Return to index page</a>';
2948 $l_notify = '';
2949
2950 if (!empty($config['board_contact']))
2951 {
2952 $l_notify = '<p>Please notify the board administrator or webmaster: <a href="mailto:' . $config['board_contact'] . '">' . $config['board_contact'] . '</a></p>';
2953 }
2954 }
2955
2956 garbage_collection();
2957
2958 // Try to not call the adm page data...
2959
2960 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
2961 echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">';
2962 echo '<head>';
2963 echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
2964 echo '<title>' . $msg_title . '</title>';
2965 echo '<style type="text/css">' . "\n" . '<!--' . "\n";
2966 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; } ';
2967 echo 'a:link, a:active, a:visited { color: #006699; text-decoration: none; } a:hover { color: #DD6900; text-decoration: underline; } ';
2968 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; } ';
2969 echo '.panel { margin: 4px 0; background-color: #FFFFFF; border: solid 1px #A9B8C2; } ';
2970 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; } ';
2971 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; } ';
2972 echo "\n" . '//-->' . "\n";
2973 echo '</style>';
2974 echo '</head>';
2975 echo '<body id="errorpage">';
2976 echo '<div id="wrap">';
2977 echo ' <div id="page-header">';
2978 echo ' ' . $l_return_index;
2979 echo ' </div>';
2980 echo ' <div id="acp">';
2981 echo ' <div class="panel">';
2982 echo ' <div id="content">';
2983 echo ' <h1>' . $msg_title . '</h1>';
2984
2985 echo ' <div>' . $msg_text . '</div>';
2986
2987 echo $l_notify;
2988
2989 echo ' </div>';
2990 echo ' </div>';
2991 echo ' </div>';
2992 echo ' <div id="page-footer">';
2993 echo ' Powered by phpBB © 2000, 2002, 2005, 2007 <a href="http://www.phpbb.com/">phpBB Group</a>';
2994 echo ' </div>';
2995 echo '</div>';
2996 echo '</body>';
2997 echo '</html>';
2998
2999 exit_handler();
3000 break;
3001
3002 case E_USER_WARNING:
3003 case E_USER_NOTICE:
3004
3005 define('IN_ERROR_HANDLER', true);
3006
3007 if (empty($user->data))
3008 {
3009 $user->session_begin();
3010 }
3011
3012 // We re-init the auth array to get correct results on login/logout
3013 $auth->acl($user->data);
3014
3015 if (empty($user->lang))
3016 {
3017 $user->setup();
3018 }
3019
3020 $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text;
3021 $msg_title = (!isset($msg_title)) ? $user->lang['INFORMATION'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title);
3022
3023 if (!defined('HEADER_INC'))
3024 {
3025 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
3026 {
3027 adm_page_header($msg_title);
3028 }
3029 else
3030 {
3031 page_header($msg_title);
3032 }
3033 }
3034
3035 $template->set_filenames(array(
3036 'body' => 'message_body.html')
3037 );
3038
3039 $template->assign_vars(array(
3040 'MESSAGE_TITLE' => $msg_title,
3041 'MESSAGE_TEXT' => $msg_text,
3042 'S_USER_WARNING' => ($errno == E_USER_WARNING) ? true : false,
3043 'S_USER_NOTICE' => ($errno == E_USER_NOTICE) ? true : false)
3044 );
3045
3046 // We do not want the cron script to be called on error messages
3047 define('IN_CRON', true);
3048
3049 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
3050 {
3051 adm_page_footer();
3052 }
3053 else
3054 {
3055 page_footer();
3056 }
3057
3058 exit_handler();
3059 break;
3060 }
3061
3062 // If we notice an error not handled here we pass this back to PHP by returning false
3063 // This may not work for all php versions
3064 return false;
3065 }
3066
3067 /**
3068 * Generate page header
3069 */
3070 function page_header($page_title = '', $display_online_list = true)
3071 {
3072 global $db, $config, $template, $SID, $_SID, $user, $auth, $phpEx, $phpbb_root_path;
3073
3074 if (defined('HEADER_INC'))
3075 {
3076 return;
3077 }
3078
3079 define('HEADER_INC', true);
3080
3081 // gzip_compression
3082 if ($config['gzip_compress'])
3083 {
3084 if (@extension_loaded('zlib') && !headers_sent())
3085 {
3086 ob_start('ob_gzhandler');
3087 }
3088 }
3089
3090 // Generate logged in/logged out status
3091 if ($user->data['user_id'] != ANONYMOUS)
3092 {
3093 $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=logout', true, $user->session_id);
3094 $l_login_logout = sprintf($user->lang['LOGOUT_USER'], $user->data['username']);
3095 }
3096 else
3097 {
3098 $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login');
3099 $l_login_logout = $user->lang['LOGIN'];
3100 }
3101
3102 // Last visit date/time
3103 $s_last_visit = ($user->data['user_id'] != ANONYMOUS) ? $user->format_date($user->data['session_last_visit']) : '';
3104
3105 // Get users online list ... if required
3106 $l_online_users = $online_userlist = $l_online_record = '';
3107
3108 if ($config['load_online'] && $config['load_online_time'] && $display_online_list)
3109 {
3110 $logged_visible_online = $logged_hidden_online = $guests_online = $prev_user_id = 0;
3111 $prev_session_ip = $reading_sql = '';
3112
3113 if (!empty($_REQUEST['f']))
3114 {
3115 $f = request_var('f', 0);
3116
3117 $reading_sql = ' AND s.session_page ' . $db->sql_like_expression("{$db->any_char}_f_={$f}x{$db->any_char}");
3118 }
3119
3120 // Get number of online guests
3121 if (!$config['load_online_guests'])
3122 {
3123 if ($db->sql_layer === 'sqlite')
3124 {
3125 $sql = 'SELECT COUNT(session_ip) as num_guests
3126 FROM (
3127 SELECT DISTINCT s.session_ip
3128 FROM ' . SESSIONS_TABLE . ' s
3129 WHERE s.session_user_id = ' . ANONYMOUS . '
3130 AND s.session_time >= ' . (time() - ($config['load_online_time'] * 60)) .
3131 $reading_sql .
3132 ')';
3133 }
3134 else
3135 {
3136 $sql = 'SELECT COUNT(DISTINCT s.session_ip) as num_guests
3137 FROM ' . SESSIONS_TABLE . ' s
3138 WHERE s.session_user_id = ' . ANONYMOUS . '
3139 AND s.session_time >= ' . (time() - ($config['load_online_time'] * 60)) .
3140 $reading_sql;
3141 }
3142 $result = $db->sql_query($sql);
3143 $guests_online = (int) $db->sql_fetchfield('num_guests');
3144 $db->sql_freeresult($result);
3145 }
3146
3147 $sql = 'SELECT u.username, u.username_clean, u.user_id, u.user_type, u.user_allow_viewonline, u.user_colour, s.session_ip, s.session_viewonline
3148 FROM ' . USERS_TABLE . ' u, ' . SESSIONS_TABLE . ' s
3149 WHERE s.session_time >= ' . (time() - (intval($config['load_online_time']) * 60)) .
3150 $reading_sql .
3151 ((!$config['load_online_guests']) ? ' AND s.session_user_id <> ' . ANONYMOUS : '') . '
3152 AND u.user_id = s.session_user_id
3153 ORDER BY u.username_clean ASC, s.session_ip ASC';
3154 $result = $db->sql_query($sql);
3155
3156 while ($row = $db->sql_fetchrow($result))
3157 {
3158 // User is logged in and therefore not a guest
3159 if ($row['user_id'] != ANONYMOUS)
3160 {
3161 // Skip multiple sessions for one user
3162 if ($row['user_id'] != $prev_user_id)
3163 {
3164 if ($row['session_viewonline'])
3165 {
3166 $logged_visible_online++;
3167 }
3168 else
3169 {
3170 $row['username'] = '<em>' . $row['username'] . '</em>';
3171 $logged_hidden_online++;
3172 }
3173
3174 if (($row['session_viewonline']) || $auth->acl_get('u_viewonline'))
3175 {
3176 $user_online_link = get_username_string(($row['user_type'] <> USER_IGNORE) ? 'full' : 'no_profile', $row['user_id'], $row['username'], $row['user_colour']);
3177 $online_userlist .= ($online_userlist != '') ? ', ' . $user_online_link : $user_online_link;
3178 }
3179 }
3180
3181 $prev_user_id = $row['user_id'];
3182 }
3183 else
3184 {
3185 // Skip multiple sessions for one user
3186 if ($row['session_ip'] != $prev_session_ip)
3187 {
3188 $guests_online++;
3189 }
3190 }
3191
3192 $prev_session_ip = $row['session_ip'];
3193 }
3194 $db->sql_freeresult($result);
3195
3196 if (!$online_userlist)
3197 {
3198 $online_userlist = $user->lang['NO_ONLINE_USERS'];
3199 }
3200
3201 if (empty($_REQUEST['f']))
3202 {
3203 $online_userlist = $user->lang['REGISTERED_USERS'] . ' ' . $online_userlist;
3204 }
3205 else
3206 {
3207 $l_online = ($guests_online == 1) ? $user->lang['BROWSING_FORUM_GUEST'] : $user->lang['BROWSING_FORUM_GUESTS'];
3208 $online_userlist = sprintf($l_online, $online_userlist, $guests_online);
3209 }
3210
3211 $total_online_users = $logged_visible_online + $logged_hidden_online + $guests_online;
3212
3213 if ($total_online_users > $config['record_online_users'])
3214 {
3215 set_config('record_online_users', $total_online_users, true);
3216 set_config('record_online_date', time(), true);
3217 }
3218
3219 // Build online listing
3220 $vars_online = array(
3221 'ONLINE' => array('total_online_users', 'l_t_user_s'),
3222 'REG' => array('logged_visible_online', 'l_r_user_s'),
3223 'HIDDEN' => array('logged_hidden_online', 'l_h_user_s'),
3224 'GUEST' => array('guests_online', 'l_g_user_s')
3225 );
3226
3227 foreach ($vars_online as $l_prefix => $var_ary)
3228 {
3229 switch (${$var_ary[0]})
3230 {
3231 case 0:
3232 ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_ZERO_TOTAL'];
3233 break;
3234
3235 case 1:
3236 ${$var_ary[1]} = $user->lang[$l_prefix . '_USER_TOTAL'];
3237 break;
3238
3239 default:
3240 ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_TOTAL'];
3241 break;
3242 }
3243 }
3244 unset($vars_online);
3245
3246 $l_online_users = sprintf($l_t_user_s, $total_online_users);
3247 $l_online_users .= sprintf($l_r_user_s, $logged_visible_online);
3248 $l_online_users .= sprintf($l_h_user_s, $logged_hidden_online);
3249 $l_online_users .= sprintf($l_g_user_s, $guests_online);
3250
3251 $l_online_record = sprintf($user->lang['RECORD_ONLINE_USERS'], $config['record_online_users'], $user->format_date($config['record_online_date']));
3252
3253 $l_online_time = ($config['load_online_time'] == 1) ? 'VIEW_ONLINE_TIME' : 'VIEW_ONLINE_TIMES';
3254 $l_online_time = sprintf($user->lang[$l_online_time], $config['load_online_time']);
3255 }
3256 else
3257 {
3258 $l_online_time = '';
3259 }
3260
3261 $l_privmsgs_text = $l_privmsgs_text_unread = '';
3262 $s_privmsg_new = false;
3263
3264 // Obtain number of new private messages if user is logged in
3265 if (isset($user->data['is_registered']) && $user->data['is_registered'])
3266 {
3267 if ($user->data['user_new_privmsg'])
3268 {
3269 $l_message_new = ($user->data['user_new_privmsg'] == 1) ? $user->lang['NEW_PM'] : $user->lang['NEW_PMS'];
3270 $l_privmsgs_text = sprintf($l_message_new, $user->data['user_new_privmsg']);
3271
3272 if (!$user->data['user_last_privmsg'] || $user->data['user_last_privmsg'] > $user->data['session_last_visit'])
3273 {
3274 $sql = 'UPDATE ' . USERS_TABLE . '
3275 SET user_last_privmsg = ' . $user->data['session_last_visit'] . '
3276 WHERE user_id = ' . $user->data['user_id'];
3277 $db->sql_query($sql);
3278
3279 $s_privmsg_new = true;
3280 }
3281 else
3282 {
3283 $s_privmsg_new = false;
3284 }
3285 }
3286 else
3287 {
3288 $l_privmsgs_text = $user->lang['NO_NEW_PM'];
3289 $s_privmsg_new = false;
3290 }
3291
3292 $l_privmsgs_text_unread = '';
3293
3294 if ($user->data['user_unread_privmsg'] && $user->data['user_unread_privmsg'] != $user->data['user_new_privmsg'])
3295 {
3296 $l_message_unread = ($user->data['user_unread_privmsg'] == 1) ? $user->lang['UNREAD_PM'] : $user->lang['UNREAD_PMS'];
3297 $l_privmsgs_text_unread = sprintf($l_message_unread, $user->data['user_unread_privmsg']);
3298 }
3299 }
3300
3301 // Which timezone?
3302 $tz = ($user->data['user_id'] != ANONYMOUS) ? strval(doubleval($user->data['user_timezone'])) : strval(doubleval($config['board_timezone']));
3303
3304 // The following assigns all _common_ variables that may be used at any point in a template.
3305 $template->assign_vars(array(
3306 'SITENAME' => $config['sitename'],
3307 'SITE_DESCRIPTION' => $config['site_desc'],
3308 'PAGE_TITLE' => $page_title,
3309 'SCRIPT_NAME' => str_replace('.' . $phpEx, '', $user->page['page_name']),
3310 'LAST_VISIT_DATE' => sprintf($user->lang['YOU_LAST_VISIT'], $s_last_visit),
3311 'LAST_VISIT_YOU' => $s_last_visit,
3312 'CURRENT_TIME' => sprintf($user->lang['CURRENT_TIME'], $user->format_date(time(), false, true)),
3313 'TOTAL_USERS_ONLINE' => $l_online_users,
3314 'LOGGED_IN_USER_LIST' => $online_userlist,
3315 'RECORD_USERS' => $l_online_record,
3316 'PRIVATE_MESSAGE_INFO' => $l_privmsgs_text,
3317 'PRIVATE_MESSAGE_INFO_UNREAD' => $l_privmsgs_text_unread,
3318
3319 'S_USER_NEW_PRIVMSG' => $user->data['user_new_privmsg'],
3320 'S_USER_UNREAD_PRIVMSG' => $user->data['user_unread_privmsg'],
3321
3322 'SID' => $SID,
3323 '_SID' => $_SID,
3324 'SESSION_ID' => $user->session_id,
3325 'ROOT_PATH' => $phpbb_root_path,
3326
3327 'L_LOGIN_LOGOUT' => $l_login_logout,
3328 'L_INDEX' => $user->lang['FORUM_INDEX'],
3329 'L_ONLINE_EXPLAIN' => $l_online_time,
3330
3331 'U_PRIVATEMSGS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox'),
3332 'U_RETURN_INBOX' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox'),
3333 'U_POPUP_PM' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=popup'),
3334 'UA_POPUP_PM' => addslashes(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=popup')),
3335 'U_MEMBERLIST' => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
3336 'U_MEMBERSLIST' => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
3337 'U_VIEWONLINE' => ($auth->acl_gets('u_viewprofile', 'a_user', 'a_useradd', 'a_userdel')) ? append_sid("{$phpbb_root_path}viewonline.$phpEx") : '',
3338 'U_LOGIN_LOGOUT' => $u_login_logout,
3339 'U_INDEX' => append_sid("{$phpbb_root_path}index.$phpEx"),
3340 'U_SEARCH' => append_sid("{$phpbb_root_path}search.$phpEx"),
3341 'U_REGISTER' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register'),
3342 'U_PROFILE' => append_sid("{$phpbb_root_path}ucp.$phpEx"),
3343 'U_MODCP' => append_sid("{$phpbb_root_path}mcp.$phpEx", false, true, $user->session_id),
3344 'U_FAQ' => append_sid("{$phpbb_root_path}faq.$phpEx"),
3345 'U_SEARCH_SELF' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=egosearch'),
3346 'U_SEARCH_NEW' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=newposts'),
3347 'U_SEARCH_UNANSWERED' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unanswered'),
3348 'U_SEARCH_ACTIVE_TOPICS'=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=active_topics'),
3349 'U_DELETE_COOKIES' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=delete_cookies'),
3350 'U_TEAM' => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=leaders'),
3351 'U_RESTORE_PERMISSIONS' => ($user->data['user_perm_from'] && $auth->acl_get('a_switchperm')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=restore_perm') : '',
3352
3353 'S_USER_LOGGED_IN' => ($user->data['user_id'] != ANONYMOUS) ? true : false,
3354 'S_AUTOLOGIN_ENABLED' => ($config['allow_autologin']) ? true : false,
3355 'S_BOARD_DISABLED' => ($config['board_disable']) ? true : false,
3356 'S_REGISTERED_USER' => $user->data['is_registered'],
3357 'S_IS_BOT' => $user->data['is_bot'],
3358 'S_USER_PM_POPUP' => $user->optionget('popuppm'),
3359 'S_USER_LANG' => $user->lang['USER_LANG'],
3360 'S_USER_BROWSER' => (isset($user->data['session_browser'])) ? $user->data['session_browser'] : $user->lang['UNKNOWN_BROWSER'],
3361 'S_USERNAME' => $user->data['username'],
3362 'S_CONTENT_DIRECTION' => $user->lang['DIRECTION'],
3363 'S_CONTENT_FLOW_BEGIN' => ($user->lang['DIRECTION'] == 'ltr') ? 'left' : 'right',
3364 'S_CONTENT_FLOW_END' => ($user->lang['DIRECTION'] == 'ltr') ? 'right' : 'left',
3365 'S_CONTENT_ENCODING' => 'UTF-8',
3366 'S_TIMEZONE' => ($user->data['user_dst'] || ($user->data['user_id'] == ANONYMOUS && $config['board_dst'])) ? sprintf($user->lang['ALL_TIMES'], $user->lang['tz'][$tz], $user->lang['tz']['dst']) : sprintf($user->lang['ALL_TIMES'], $user->lang['tz'][$tz], ''),
3367 'S_DISPLAY_ONLINE_LIST' => ($l_online_time) ? 1 : 0,
3368 'S_DISPLAY_SEARCH' => (!$config['load_search']) ? 0 : (isset($auth) ? ($auth->acl_get('u_search') && $auth->acl_getf_global('f_search')) : 1),
3369 'S_DISPLAY_PM' => ($config['allow_privmsg'] && $user->data['is_registered'] && ($auth->acl_get('u_readpm') || $auth->acl_get('u_sendpm'))) ? true : false,
3370 'S_DISPLAY_MEMBERLIST' => (isset($auth)) ? $auth->acl_get('u_viewprofile') : 0,
3371 'S_NEW_PM' => ($s_privmsg_new) ? 1 : 0,
3372
3373 'T_THEME_PATH' => "{$phpbb_root_path}styles/" . $user->theme['theme_path'] . '/theme',
3374 'T_TEMPLATE_PATH' => "{$phpbb_root_path}styles/" . $user->theme['template_path'] . '/template',
3375 'T_IMAGESET_PATH' => "{$phpbb_root_path}styles/" . $user->theme['imageset_path'] . '/imageset',
3376 'T_IMAGESET_LANG_PATH' => "{$phpbb_root_path}styles/" . $user->theme['imageset_path'] . '/imageset/' . $user->data['user_lang'],
3377 'T_IMAGES_PATH' => "{$phpbb_root_path}images/",
3378 'T_SMILIES_PATH' => "{$phpbb_root_path}{$config['smilies_path']}/",
3379 'T_AVATAR_PATH' => "{$phpbb_root_path}{$config['avatar_path']}/",
3380 'T_AVATAR_GALLERY_PATH' => "{$phpbb_root_path}{$config['avatar_gallery_path']}/",
3381 'T_ICONS_PATH' => "{$phpbb_root_path}{$config['icons_path']}/",
3382 'T_RANKS_PATH' => "{$phpbb_root_path}{$config['ranks_path']}/",
3383 'T_UPLOAD_PATH' => "{$phpbb_root_path}{$config['upload_path']}/",
3384 'T_STYLESHEET_LINK' => (!$user->theme['theme_storedb']) ? "{$phpbb_root_path}styles/" . $user->theme['theme_path'] . '/theme/stylesheet.css' : "{$phpbb_root_path}style.$phpEx?sid=$user->session_id&id=" . $user->theme['style_id'] . '&lang=' . $user->data['user_lang'],
3385 'T_STYLESHEET_NAME' => $user->theme['theme_name'],
3386
3387 'SITE_LOGO_IMG' => $user->img('site_logo'))
3388 );
3389
3390 // application/xhtml+xml not used because of IE
3391 header('Content-type: text/html; charset=UTF-8');
3392
3393 header('Cache-Control: private, no-cache="set-cookie"');
3394 header('Expires: 0');
3395 header('Pragma: no-cache');
3396
3397 return;
3398 }
3399
3400 /**
3401 * Generate page footer
3402 */
3403 function page_footer($run_cron = true)
3404 {
3405 global $db, $config, $template, $user, $auth, $cache, $starttime, $phpbb_root_path, $phpEx;
3406
3407 // Output page creation time
3408 if (defined('DEBUG'))
3409 {
3410 $mtime = explode(' ', microtime());
3411 $totaltime = $mtime[0] + $mtime[1] - $starttime;
3412
3413 if (!empty($_REQUEST['explain']) && $auth->acl_get('a_') && defined('DEBUG_EXTRA') && method_exists($db, 'sql_report'))
3414 {
3415 $db->sql_report('display');
3416 }
3417
3418 $debug_output = sprintf('Time : %.3fs | ' . $db->sql_num_queries() . ' Queries | GZIP : ' . (($config['gzip_compress']) ? 'On' : 'Off') . (($user->load) ? ' | Load : ' . $user->load : ''), $totaltime);
3419
3420 if ($auth->acl_get('a_') && defined('DEBUG_EXTRA'))
3421 {
3422 if (function_exists('memory_get_usage'))
3423 {
3424 if ($memory_usage = memory_get_usage())
3425 {
3426 global $base_memory_usage;
3427 $memory_usage -= $base_memory_usage;
3428 $memory_usage = ($memory_usage >= 1048576) ? round((round($memory_usage / 1048576 * 100) / 100), 2) . ' ' . $user->lang['MB'] : (($memory_usage >= 1024) ? round((round($memory_usage / 1024 * 100) / 100), 2) . ' ' . $user->lang['KB'] : $memory_usage . ' ' . $user->lang['BYTES']);
3429
3430 $debug_output .= ' | Memory Usage: ' . $memory_usage;
3431 }
3432 }
3433
3434 $debug_output .= ' | <a href="' . build_url() . '&explain=1">Explain</a>';
3435 }
3436 }
3437
3438 $template->assign_vars(array(
3439 'DEBUG_OUTPUT' => (defined('DEBUG')) ? $debug_output : '',
3440 'TRANSLATION_INFO' => (!empty($user->lang['TRANSLATION_INFO'])) ? $user->lang['TRANSLATION_INFO'] : '',
3441
3442 'U_ACP' => ($auth->acl_get('a_') && $user->data['is_registered']) ? append_sid("{$phpbb_root_path}adm/index.$phpEx", false, true, $user->session_id) : '')
3443 );
3444
3445 // Call cron-type script
3446 if (!defined('IN_CRON') && $run_cron && !$config['board_disable'])
3447 {
3448 $cron_type = '';
3449
3450 if (time() - $config['queue_interval'] > $config['last_queue_run'] && !defined('IN_ADMIN') && file_exists($phpbb_root_path . 'cache/queue.' . $phpEx))
3451 {
3452 // Process email queue
3453 $cron_type = 'queue';
3454 }
3455 else if (method_exists($cache, 'tidy') && time() - $config['cache_gc'] > $config['cache_last_gc'])
3456 {
3457 // Tidy the cache
3458 $cron_type = 'tidy_cache';
3459 }
3460 else if (time() - $config['warnings_gc'] > $config['warnings_last_gc'])
3461 {
3462 $cron_type = 'tidy_warnings';
3463 }
3464 else if (time() - $config['database_gc'] > $config['database_last_gc'])
3465 {
3466 // Tidy the database
3467 $cron_type = 'tidy_database';
3468 }
3469 else if (time() - $config['search_gc'] > $config['search_last_gc'])
3470 {
3471 // Tidy the search
3472 $cron_type = 'tidy_search';
3473 }
3474 else if (time() - $config['session_gc'] > $config['session_last_gc'])
3475 {
3476 $cron_type = 'tidy_sessions';
3477 }
3478
3479 if ($cron_type)
3480 {
3481 $template->assign_var('RUN_CRON_TASK', '<img src="' . append_sid($phpbb_root_path . 'cron.' . $phpEx, 'cron_type=' . $cron_type) . '" width="1" height="1" alt="cron" />');
3482 }
3483 }
3484
3485 $template->display('body');
3486
3487 garbage_collection();
3488 exit_handler();
3489 }
3490
3491 /**
3492 * Closing the cache object and the database
3493 * Cool function name, eh? We might want to add operations to it later
3494 */
3495 function garbage_collection()
3496 {
3497 global $cache, $db;
3498
3499 // Unload cache, must be done before the DB connection if closed
3500 if (!empty($cache))
3501 {
3502 $cache->unload();
3503 }
3504
3505 // Close our DB connection.
3506 if (!empty($db))
3507 {
3508 $db->sql_close();
3509 }
3510 }
3511
3512 /**
3513 * Handler for exit calls in phpBB.
3514 * This function supports hooks.
3515 *
3516 * Note: This function is called after the template has been outputted.
3517 */
3518 function exit_handler()
3519 {
3520 global $phpbb_hook;
3521
3522 if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__))
3523 {
3524 if ($phpbb_hook->hook_return(__FUNCTION__))
3525 {
3526 return $phpbb_hook->hook_return_result(__FUNCTION__);
3527 }
3528 }
3529
3530 exit;
3531 }
3532
3533 /**
3534 * Handler for init calls in phpBB. This function is called in user::setup();
3535 * This function supports hooks.
3536 */
3537 function phpbb_user_session_handler()
3538 {
3539 global $phpbb_hook;
3540
3541 if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__))
3542 {
3543 if ($phpbb_hook->hook_return(__FUNCTION__))
3544 {
3545 return $phpbb_hook->hook_return_result(__FUNCTION__);
3546 }
3547 }
3548
3549 return;
3550 }
3551
3552 ?>