PageRenderTime 84ms CodeModel.GetById 39ms RepoModel.GetById 1ms app.codeStats 1ms

/station/forum/includes/functions.php

https://github.com/bryanveloso/sayonarane
PHP | 3893 lines | 2772 code | 564 blank | 557 comment | 587 complexity | ff8bce027d29e55356a3aecdf1285153 MD5 | raw file
Possible License(s): AGPL-1.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. *
  4. * @package phpBB3
  5. * @version $Id: functions.php 9057 2008-11-10 16:24:18Z Kellanved $
  6. * @copyright (c) 2005 phpBB Group
  7. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  8. *
  9. */
  10. /**
  11. * @ignore
  12. */
  13. if (!defined('IN_PHPBB'))
  14. {
  15. exit;
  16. }
  17. // Common global functions
  18. /**
  19. * set_var
  20. *
  21. * Set variable, used by {@link request_var the request_var function}
  22. *
  23. * @access private
  24. */
  25. function set_var(&$result, $var, $type, $multibyte = false)
  26. {
  27. settype($var, $type);
  28. $result = $var;
  29. if ($type == 'string')
  30. {
  31. $result = trim(htmlspecialchars(str_replace(array("\r\n", "\r", "\0"), array("\n", "\n", ''), $result), ENT_COMPAT, 'UTF-8'));
  32. if (!empty($result))
  33. {
  34. // Make sure multibyte characters are wellformed
  35. if ($multibyte)
  36. {
  37. if (!preg_match('/^./u', $result))
  38. {
  39. $result = '';
  40. }
  41. }
  42. else
  43. {
  44. // no multibyte, allow only ASCII (0-127)
  45. $result = preg_replace('/[\x80-\xFF]/', '?', $result);
  46. }
  47. }
  48. $result = (STRIP) ? stripslashes($result) : $result;
  49. }
  50. }
  51. /**
  52. * request_var
  53. *
  54. * Used to get passed variable
  55. */
  56. function request_var($var_name, $default, $multibyte = false, $cookie = false)
  57. {
  58. if (!$cookie && isset($_COOKIE[$var_name]))
  59. {
  60. if (!isset($_GET[$var_name]) && !isset($_POST[$var_name]))
  61. {
  62. return (is_array($default)) ? array() : $default;
  63. }
  64. $_REQUEST[$var_name] = isset($_POST[$var_name]) ? $_POST[$var_name] : $_GET[$var_name];
  65. }
  66. if (!isset($_REQUEST[$var_name]) || (is_array($_REQUEST[$var_name]) && !is_array($default)) || (is_array($default) && !is_array($_REQUEST[$var_name])))
  67. {
  68. return (is_array($default)) ? array() : $default;
  69. }
  70. $var = $_REQUEST[$var_name];
  71. if (!is_array($default))
  72. {
  73. $type = gettype($default);
  74. }
  75. else
  76. {
  77. list($key_type, $type) = each($default);
  78. $type = gettype($type);
  79. $key_type = gettype($key_type);
  80. if ($type == 'array')
  81. {
  82. reset($default);
  83. $default = current($default);
  84. list($sub_key_type, $sub_type) = each($default);
  85. $sub_type = gettype($sub_type);
  86. $sub_type = ($sub_type == 'array') ? 'NULL' : $sub_type;
  87. $sub_key_type = gettype($sub_key_type);
  88. }
  89. }
  90. if (is_array($var))
  91. {
  92. $_var = $var;
  93. $var = array();
  94. foreach ($_var as $k => $v)
  95. {
  96. set_var($k, $k, $key_type);
  97. if ($type == 'array' && is_array($v))
  98. {
  99. foreach ($v as $_k => $_v)
  100. {
  101. if (is_array($_v))
  102. {
  103. $_v = null;
  104. }
  105. set_var($_k, $_k, $sub_key_type);
  106. set_var($var[$k][$_k], $_v, $sub_type, $multibyte);
  107. }
  108. }
  109. else
  110. {
  111. if ($type == 'array' || is_array($v))
  112. {
  113. $v = null;
  114. }
  115. set_var($var[$k], $v, $type, $multibyte);
  116. }
  117. }
  118. }
  119. else
  120. {
  121. set_var($var, $var, $type, $multibyte);
  122. }
  123. return $var;
  124. }
  125. /**
  126. * Set config value. Creates missing config entry.
  127. */
  128. function set_config($config_name, $config_value, $is_dynamic = false)
  129. {
  130. global $db, $cache, $config;
  131. $sql = 'UPDATE ' . CONFIG_TABLE . "
  132. SET config_value = '" . $db->sql_escape($config_value) . "'
  133. WHERE config_name = '" . $db->sql_escape($config_name) . "'";
  134. $db->sql_query($sql);
  135. if (!$db->sql_affectedrows() && !isset($config[$config_name]))
  136. {
  137. $sql = 'INSERT INTO ' . CONFIG_TABLE . ' ' . $db->sql_build_array('INSERT', array(
  138. 'config_name' => $config_name,
  139. 'config_value' => $config_value,
  140. 'is_dynamic' => ($is_dynamic) ? 1 : 0));
  141. $db->sql_query($sql);
  142. }
  143. $config[$config_name] = $config_value;
  144. if (!$is_dynamic)
  145. {
  146. $cache->destroy('config');
  147. }
  148. }
  149. /**
  150. * Generates an alphanumeric random string of given length
  151. */
  152. function gen_rand_string($num_chars = 8)
  153. {
  154. $rand_str = unique_id();
  155. $rand_str = str_replace('0', 'Z', strtoupper(base_convert($rand_str, 16, 35)));
  156. return substr($rand_str, 0, $num_chars);
  157. }
  158. /**
  159. * Return unique id
  160. * @param string $extra additional entropy
  161. */
  162. function unique_id($extra = 'c')
  163. {
  164. static $dss_seeded = false;
  165. global $config;
  166. $val = $config['rand_seed'] . microtime();
  167. $val = md5($val);
  168. $config['rand_seed'] = md5($config['rand_seed'] . $val . $extra);
  169. if ($dss_seeded !== true && ($config['rand_seed_last_update'] < time() - rand(1,10)))
  170. {
  171. set_config('rand_seed', $config['rand_seed'], true);
  172. set_config('rand_seed_last_update', time(), true);
  173. $dss_seeded = true;
  174. }
  175. return substr($val, 4, 16);
  176. }
  177. /**
  178. * Return formatted string for filesizes
  179. */
  180. function get_formatted_filesize($bytes, $add_size_lang = true)
  181. {
  182. global $user;
  183. if ($bytes >= pow(2, 20))
  184. {
  185. return ($add_size_lang) ? round($bytes / 1024 / 1024, 2) . ' ' . $user->lang['MIB'] : round($bytes / 1024 / 1024, 2);
  186. }
  187. if ($bytes >= pow(2, 10))
  188. {
  189. return ($add_size_lang) ? round($bytes / 1024, 2) . ' ' . $user->lang['KIB'] : round($bytes / 1024, 2);
  190. }
  191. return ($add_size_lang) ? ($bytes) . ' ' . $user->lang['BYTES'] : ($bytes);
  192. }
  193. /**
  194. * Determine whether we are approaching the maximum execution time. Should be called once
  195. * at the beginning of the script in which it's used.
  196. * @return bool Either true if the maximum execution time is nearly reached, or false
  197. * if some time is still left.
  198. */
  199. function still_on_time($extra_time = 15)
  200. {
  201. static $max_execution_time, $start_time;
  202. $time = explode(' ', microtime());
  203. $current_time = $time[0] + $time[1];
  204. if (empty($max_execution_time))
  205. {
  206. $max_execution_time = (function_exists('ini_get')) ? (int) @ini_get('max_execution_time') : (int) @get_cfg_var('max_execution_time');
  207. // If zero, then set to something higher to not let the user catch the ten seconds barrier.
  208. if ($max_execution_time === 0)
  209. {
  210. $max_execution_time = 50 + $extra_time;
  211. }
  212. $max_execution_time = min(max(10, ($max_execution_time - $extra_time)), 50);
  213. // For debugging purposes
  214. // $max_execution_time = 10;
  215. global $starttime;
  216. $start_time = (empty($starttime)) ? $current_time : $starttime;
  217. }
  218. return (ceil($current_time - $start_time) < $max_execution_time) ? true : false;
  219. }
  220. /**
  221. *
  222. * @version Version 0.1 / slightly modified for phpBB 3.0.x (using $H$ as hash type identifier)
  223. *
  224. * Portable PHP password hashing framework.
  225. *
  226. * Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
  227. * the public domain.
  228. *
  229. * There's absolutely no warranty.
  230. *
  231. * The homepage URL for this framework is:
  232. *
  233. * http://www.openwall.com/phpass/
  234. *
  235. * Please be sure to update the Version line if you edit this file in any way.
  236. * It is suggested that you leave the main version number intact, but indicate
  237. * your project name (after the slash) and add your own revision information.
  238. *
  239. * Please do not change the "private" password hashing method implemented in
  240. * here, thereby making your hashes incompatible. However, if you must, please
  241. * change the hash type identifier (the "$P$") to something different.
  242. *
  243. * Obviously, since this code is in the public domain, the above are not
  244. * requirements (there can be none), but merely suggestions.
  245. *
  246. *
  247. * Hash the password
  248. */
  249. function phpbb_hash($password)
  250. {
  251. $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  252. $random_state = unique_id();
  253. $random = '';
  254. $count = 6;
  255. if (($fh = @fopen('/dev/urandom', 'rb')))
  256. {
  257. $random = fread($fh, $count);
  258. fclose($fh);
  259. }
  260. if (strlen($random) < $count)
  261. {
  262. $random = '';
  263. for ($i = 0; $i < $count; $i += 16)
  264. {
  265. $random_state = md5(unique_id() . $random_state);
  266. $random .= pack('H*', md5($random_state));
  267. }
  268. $random = substr($random, 0, $count);
  269. }
  270. $hash = _hash_crypt_private($password, _hash_gensalt_private($random, $itoa64), $itoa64);
  271. if (strlen($hash) == 34)
  272. {
  273. return $hash;
  274. }
  275. return md5($password);
  276. }
  277. /**
  278. * Check for correct password
  279. *
  280. * @param string $password The password in plain text
  281. * @param string $hash The stored password hash
  282. *
  283. * @return bool Returns true if the password is correct, false if not.
  284. */
  285. function phpbb_check_hash($password, $hash)
  286. {
  287. $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  288. if (strlen($hash) == 34)
  289. {
  290. return (_hash_crypt_private($password, $hash, $itoa64) === $hash) ? true : false;
  291. }
  292. return (md5($password) === $hash) ? true : false;
  293. }
  294. /**
  295. * Generate salt for hash generation
  296. */
  297. function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6)
  298. {
  299. if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
  300. {
  301. $iteration_count_log2 = 8;
  302. }
  303. $output = '$H$';
  304. $output .= $itoa64[min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)];
  305. $output .= _hash_encode64($input, 6, $itoa64);
  306. return $output;
  307. }
  308. /**
  309. * Encode hash
  310. */
  311. function _hash_encode64($input, $count, &$itoa64)
  312. {
  313. $output = '';
  314. $i = 0;
  315. do
  316. {
  317. $value = ord($input[$i++]);
  318. $output .= $itoa64[$value & 0x3f];
  319. if ($i < $count)
  320. {
  321. $value |= ord($input[$i]) << 8;
  322. }
  323. $output .= $itoa64[($value >> 6) & 0x3f];
  324. if ($i++ >= $count)
  325. {
  326. break;
  327. }
  328. if ($i < $count)
  329. {
  330. $value |= ord($input[$i]) << 16;
  331. }
  332. $output .= $itoa64[($value >> 12) & 0x3f];
  333. if ($i++ >= $count)
  334. {
  335. break;
  336. }
  337. $output .= $itoa64[($value >> 18) & 0x3f];
  338. }
  339. while ($i < $count);
  340. return $output;
  341. }
  342. /**
  343. * The crypt function/replacement
  344. */
  345. function _hash_crypt_private($password, $setting, &$itoa64)
  346. {
  347. $output = '*';
  348. // Check for correct hash
  349. if (substr($setting, 0, 3) != '$H$')
  350. {
  351. return $output;
  352. }
  353. $count_log2 = strpos($itoa64, $setting[3]);
  354. if ($count_log2 < 7 || $count_log2 > 30)
  355. {
  356. return $output;
  357. }
  358. $count = 1 << $count_log2;
  359. $salt = substr($setting, 4, 8);
  360. if (strlen($salt) != 8)
  361. {
  362. return $output;
  363. }
  364. /**
  365. * We're kind of forced to use MD5 here since it's the only
  366. * cryptographic primitive available in all versions of PHP
  367. * currently in use. To implement our own low-level crypto
  368. * in PHP would result in much worse performance and
  369. * consequently in lower iteration counts and hashes that are
  370. * quicker to crack (by non-PHP code).
  371. */
  372. if (PHP_VERSION >= 5)
  373. {
  374. $hash = md5($salt . $password, true);
  375. do
  376. {
  377. $hash = md5($hash . $password, true);
  378. }
  379. while (--$count);
  380. }
  381. else
  382. {
  383. $hash = pack('H*', md5($salt . $password));
  384. do
  385. {
  386. $hash = pack('H*', md5($hash . $password));
  387. }
  388. while (--$count);
  389. }
  390. $output = substr($setting, 0, 12);
  391. $output .= _hash_encode64($hash, 16, $itoa64);
  392. return $output;
  393. }
  394. /**
  395. * Global function for chmodding directories and files for internal use
  396. * This function determines owner and group whom the file belongs to and user and group of PHP and then set safest possible file permissions.
  397. * The function determines owner and group from common.php file and sets the same to the provided file.
  398. * The function uses bit fields to build the permissions.
  399. * The function sets the appropiate execute bit on directories.
  400. *
  401. * Supported constants representing bit fields are:
  402. *
  403. * CHMOD_ALL - all permissions (7)
  404. * CHMOD_READ - read permission (4)
  405. * CHMOD_WRITE - write permission (2)
  406. * CHMOD_EXECUTE - execute permission (1)
  407. *
  408. * NOTE: The function uses POSIX extension and fileowner()/filegroup() functions. If any of them is disabled, this function tries to build proper permissions, by calling is_readable() and is_writable() functions.
  409. *
  410. * @param $filename The file/directory to be chmodded
  411. * @param $perms Permissions to set
  412. * @return true on success, otherwise false
  413. *
  414. * @author faw, phpBB Group
  415. */
  416. function phpbb_chmod($filename, $perms = CHMOD_READ)
  417. {
  418. // Return if the file no longer exists.
  419. if (!file_exists($filename))
  420. {
  421. return false;
  422. }
  423. if (!function_exists('fileowner') || !function_exists('filegroup'))
  424. {
  425. $file_uid = $file_gid = false;
  426. $common_php_owner = $common_php_group = false;
  427. }
  428. else
  429. {
  430. global $phpbb_root_path, $phpEx;
  431. // Determine owner/group of common.php file and the filename we want to change here
  432. $common_php_owner = fileowner($phpbb_root_path . 'common.' . $phpEx);
  433. $common_php_group = filegroup($phpbb_root_path . 'common.' . $phpEx);
  434. $file_uid = fileowner($filename);
  435. $file_gid = filegroup($filename);
  436. // Try to set the owner to the same common.php has
  437. if ($common_php_owner !== $file_uid && $common_php_owner !== false && $file_uid !== false)
  438. {
  439. // Will most likely not work
  440. if (@chown($filename, $common_php_owner));
  441. {
  442. $file_uid = fileowner($filename);
  443. }
  444. }
  445. // Try to set the group to the same common.php has
  446. if ($common_php_group !== $file_gid && $common_php_group !== false && $file_gid !== false)
  447. {
  448. if (@chgrp($filename, $common_php_group));
  449. {
  450. $file_gid = filegroup($filename);
  451. }
  452. }
  453. }
  454. // And the owner and the groups PHP is running under.
  455. $php_uid = (function_exists('posix_getuid')) ? @posix_getuid() : false;
  456. $php_gids = (function_exists('posix_getgroups')) ? @posix_getgroups() : false;
  457. // Who is PHP?
  458. if ($file_uid === false || $file_gid === false || $php_uid === false || $php_gids === false)
  459. {
  460. $php = null;
  461. }
  462. else if ($file_uid == $php_uid /* && $common_php_owner !== false && $common_php_owner === $file_uid*/)
  463. {
  464. $php = 'owner';
  465. }
  466. else if (in_array($file_gid, $php_gids))
  467. {
  468. $php = 'group';
  469. }
  470. else
  471. {
  472. $php = 'other';
  473. }
  474. // Owner always has read/write permission
  475. $owner = CHMOD_READ | CHMOD_WRITE;
  476. if (is_dir($filename))
  477. {
  478. $owner |= CHMOD_EXECUTE;
  479. // Only add execute bit to the permission if the dir needs to be readable
  480. if ($perms & CHMOD_READ)
  481. {
  482. $perms |= CHMOD_EXECUTE;
  483. }
  484. }
  485. switch ($php)
  486. {
  487. case null:
  488. case 'owner':
  489. $result = @chmod($filename, ($owner << 6) + (0 << 3) + (0 << 0));
  490. if (!is_null($php) || (is_readable($filename) && is_writable($filename)))
  491. {
  492. break;
  493. }
  494. case 'group':
  495. $result = @chmod($filename, ($owner << 6) + ($perms << 3) + (0 << 0));
  496. if (!is_null($php) || ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || is_writable($filename))))
  497. {
  498. break;
  499. }
  500. case 'other':
  501. $result = @chmod($filename, ($owner << 6) + ($perms << 3) + ($perms << 0));
  502. if (!is_null($php) || ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || is_writable($filename))))
  503. {
  504. break;
  505. }
  506. default:
  507. return false;
  508. break;
  509. }
  510. return $result;
  511. }
  512. // Compatibility functions
  513. if (!function_exists('array_combine'))
  514. {
  515. /**
  516. * A wrapper for the PHP5 function array_combine()
  517. * @param array $keys contains keys for the resulting array
  518. * @param array $values contains values for the resulting array
  519. *
  520. * @return Returns an array by using the values from the keys array as keys and the
  521. * values from the values array as the corresponding values. Returns false if the
  522. * number of elements for each array isn't equal or if the arrays are empty.
  523. */
  524. function array_combine($keys, $values)
  525. {
  526. $keys = array_values($keys);
  527. $values = array_values($values);
  528. $n = sizeof($keys);
  529. $m = sizeof($values);
  530. if (!$n || !$m || ($n != $m))
  531. {
  532. return false;
  533. }
  534. $combined = array();
  535. for ($i = 0; $i < $n; $i++)
  536. {
  537. $combined[$keys[$i]] = $values[$i];
  538. }
  539. return $combined;
  540. }
  541. }
  542. if (!function_exists('str_split'))
  543. {
  544. /**
  545. * A wrapper for the PHP5 function str_split()
  546. * @param array $string contains the string to be converted
  547. * @param array $split_length contains the length of each chunk
  548. *
  549. * @return Converts a string to an array. If the optional split_length parameter is specified,
  550. * the returned array will be broken down into chunks with each being split_length in length,
  551. * otherwise each chunk will be one character in length. FALSE is returned if split_length is
  552. * less than 1. If the split_length length exceeds the length of string, the entire string is
  553. * returned as the first (and only) array element.
  554. */
  555. function str_split($string, $split_length = 1)
  556. {
  557. if ($split_length < 1)
  558. {
  559. return false;
  560. }
  561. else if ($split_length >= strlen($string))
  562. {
  563. return array($string);
  564. }
  565. else
  566. {
  567. preg_match_all('#.{1,' . $split_length . '}#s', $string, $matches);
  568. return $matches[0];
  569. }
  570. }
  571. }
  572. if (!function_exists('stripos'))
  573. {
  574. /**
  575. * A wrapper for the PHP5 function stripos
  576. * Find position of first occurrence of a case-insensitive string
  577. *
  578. * @param string $haystack is the string to search in
  579. * @param string $needle is the string to search for
  580. *
  581. * @return mixed Returns the numeric position of the first occurrence of needle in the haystack string. Unlike strpos(), stripos() is case-insensitive.
  582. * Note that the needle may be a string of one or more characters.
  583. * If needle is not found, stripos() will return boolean FALSE.
  584. */
  585. function stripos($haystack, $needle)
  586. {
  587. if (preg_match('#' . preg_quote($needle, '#') . '#i', $haystack, $m))
  588. {
  589. return strpos($haystack, $m[0]);
  590. }
  591. return false;
  592. }
  593. }
  594. /**
  595. * Checks if a path ($path) is absolute or relative
  596. *
  597. * @param string $path Path to check absoluteness of
  598. * @return boolean
  599. */
  600. function is_absolute($path)
  601. {
  602. return ($path[0] == '/' || (DIRECTORY_SEPARATOR == '\\' && preg_match('#^[a-z]:/#i', $path))) ? true : false;
  603. }
  604. /**
  605. * @author Chris Smith <chris@project-minerva.org>
  606. * @copyright 2006 Project Minerva Team
  607. * @param string $path The path which we should attempt to resolve.
  608. * @return mixed
  609. */
  610. function phpbb_own_realpath($path)
  611. {
  612. // Now to perform funky shizzle
  613. // Switch to use UNIX slashes
  614. $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
  615. $path_prefix = '';
  616. // Determine what sort of path we have
  617. if (is_absolute($path))
  618. {
  619. $absolute = true;
  620. if ($path[0] == '/')
  621. {
  622. // Absolute path, *NIX style
  623. $path_prefix = '';
  624. }
  625. else
  626. {
  627. // Absolute path, Windows style
  628. // Remove the drive letter and colon
  629. $path_prefix = $path[0] . ':';
  630. $path = substr($path, 2);
  631. }
  632. }
  633. else
  634. {
  635. // Relative Path
  636. // Prepend the current working directory
  637. if (function_exists('getcwd'))
  638. {
  639. // This is the best method, hopefully it is enabled!
  640. $path = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . '/' . $path;
  641. $absolute = true;
  642. if (preg_match('#^[a-z]:#i', $path))
  643. {
  644. $path_prefix = $path[0] . ':';
  645. $path = substr($path, 2);
  646. }
  647. else
  648. {
  649. $path_prefix = '';
  650. }
  651. }
  652. else if (isset($_SERVER['SCRIPT_FILENAME']) && !empty($_SERVER['SCRIPT_FILENAME']))
  653. {
  654. // Warning: If chdir() has been used this will lie!
  655. // Warning: This has some problems sometime (CLI can create them easily)
  656. $path = str_replace(DIRECTORY_SEPARATOR, '/', dirname($_SERVER['SCRIPT_FILENAME'])) . '/' . $path;
  657. $absolute = true;
  658. $path_prefix = '';
  659. }
  660. else
  661. {
  662. // We have no way of getting the absolute path, just run on using relative ones.
  663. $absolute = false;
  664. $path_prefix = '.';
  665. }
  666. }
  667. // Remove any repeated slashes
  668. $path = preg_replace('#/{2,}#', '/', $path);
  669. // Remove the slashes from the start and end of the path
  670. $path = trim($path, '/');
  671. // Break the string into little bits for us to nibble on
  672. $bits = explode('/', $path);
  673. // Remove any . in the path, renumber array for the loop below
  674. $bits = array_values(array_diff($bits, array('.')));
  675. // Lets get looping, run over and resolve any .. (up directory)
  676. for ($i = 0, $max = sizeof($bits); $i < $max; $i++)
  677. {
  678. // @todo Optimise
  679. if ($bits[$i] == '..' )
  680. {
  681. if (isset($bits[$i - 1]))
  682. {
  683. if ($bits[$i - 1] != '..')
  684. {
  685. // We found a .. and we are able to traverse upwards, lets do it!
  686. unset($bits[$i]);
  687. unset($bits[$i - 1]);
  688. $i -= 2;
  689. $max -= 2;
  690. $bits = array_values($bits);
  691. }
  692. }
  693. else if ($absolute) // ie. !isset($bits[$i - 1]) && $absolute
  694. {
  695. // We have an absolute path trying to descend above the root of the filesystem
  696. // ... Error!
  697. return false;
  698. }
  699. }
  700. }
  701. // Prepend the path prefix
  702. array_unshift($bits, $path_prefix);
  703. $resolved = '';
  704. $max = sizeof($bits) - 1;
  705. // Check if we are able to resolve symlinks, Windows cannot.
  706. $symlink_resolve = (function_exists('readlink')) ? true : false;
  707. foreach ($bits as $i => $bit)
  708. {
  709. if (@is_dir("$resolved/$bit") || ($i == $max && @is_file("$resolved/$bit")))
  710. {
  711. // Path Exists
  712. if ($symlink_resolve && is_link("$resolved/$bit") && ($link = readlink("$resolved/$bit")))
  713. {
  714. // Resolved a symlink.
  715. $resolved = $link . (($i == $max) ? '' : '/');
  716. continue;
  717. }
  718. }
  719. else
  720. {
  721. // Something doesn't exist here!
  722. // This is correct realpath() behaviour but sadly open_basedir and safe_mode make this problematic
  723. // return false;
  724. }
  725. $resolved .= $bit . (($i == $max) ? '' : '/');
  726. }
  727. // @todo If the file exists fine and open_basedir only has one path we should be able to prepend it
  728. // because we must be inside that basedir, the question is where...
  729. // @internal The slash in is_dir() gets around an open_basedir restriction
  730. if (!@file_exists($resolved) || (!is_dir($resolved . '/') && !is_file($resolved)))
  731. {
  732. return false;
  733. }
  734. // Put the slashes back to the native operating systems slashes
  735. $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved);
  736. // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
  737. if (substr($resolved, -1) == DIRECTORY_SEPARATOR)
  738. {
  739. return substr($resolved, 0, -1);
  740. }
  741. return $resolved; // We got here, in the end!
  742. }
  743. if (!function_exists('realpath'))
  744. {
  745. /**
  746. * A wrapper for realpath
  747. * @ignore
  748. */
  749. function phpbb_realpath($path)
  750. {
  751. return phpbb_own_realpath($path);
  752. }
  753. }
  754. else
  755. {
  756. /**
  757. * A wrapper for realpath
  758. */
  759. function phpbb_realpath($path)
  760. {
  761. $realpath = realpath($path);
  762. // Strangely there are provider not disabling realpath but returning strange values. :o
  763. // We at least try to cope with them.
  764. if ($realpath === $path || $realpath === false)
  765. {
  766. return phpbb_own_realpath($path);
  767. }
  768. // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
  769. if (substr($realpath, -1) == DIRECTORY_SEPARATOR)
  770. {
  771. $realpath = substr($realpath, 0, -1);
  772. }
  773. return $realpath;
  774. }
  775. }
  776. if (!function_exists('htmlspecialchars_decode'))
  777. {
  778. /**
  779. * A wrapper for htmlspecialchars_decode
  780. * @ignore
  781. */
  782. function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT)
  783. {
  784. return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
  785. }
  786. }
  787. // functions used for building option fields
  788. /**
  789. * Pick a language, any language ...
  790. */
  791. function language_select($default = '')
  792. {
  793. global $db;
  794. $sql = 'SELECT lang_iso, lang_local_name
  795. FROM ' . LANG_TABLE . '
  796. ORDER BY lang_english_name';
  797. $result = $db->sql_query($sql);
  798. $lang_options = '';
  799. while ($row = $db->sql_fetchrow($result))
  800. {
  801. $selected = ($row['lang_iso'] == $default) ? ' selected="selected"' : '';
  802. $lang_options .= '<option value="' . $row['lang_iso'] . '"' . $selected . '>' . $row['lang_local_name'] . '</option>';
  803. }
  804. $db->sql_freeresult($result);
  805. return $lang_options;
  806. }
  807. /**
  808. * Pick a template/theme combo,
  809. */
  810. function style_select($default = '', $all = false)
  811. {
  812. global $db;
  813. $sql_where = (!$all) ? 'WHERE style_active = 1 ' : '';
  814. $sql = 'SELECT style_id, style_name
  815. FROM ' . STYLES_TABLE . "
  816. $sql_where
  817. ORDER BY style_name";
  818. $result = $db->sql_query($sql);
  819. $style_options = '';
  820. while ($row = $db->sql_fetchrow($result))
  821. {
  822. $selected = ($row['style_id'] == $default) ? ' selected="selected"' : '';
  823. $style_options .= '<option value="' . $row['style_id'] . '"' . $selected . '>' . $row['style_name'] . '</option>';
  824. }
  825. $db->sql_freeresult($result);
  826. return $style_options;
  827. }
  828. /**
  829. * Pick a timezone
  830. */
  831. function tz_select($default = '', $truncate = false)
  832. {
  833. global $user;
  834. $tz_select = '';
  835. foreach ($user->lang['tz_zones'] as $offset => $zone)
  836. {
  837. if ($truncate)
  838. {
  839. $zone_trunc = truncate_string($zone, 50, 255, false, '...');
  840. }
  841. else
  842. {
  843. $zone_trunc = $zone;
  844. }
  845. if (is_numeric($offset))
  846. {
  847. $selected = ($offset == $default) ? ' selected="selected"' : '';
  848. $tz_select .= '<option title="'.$zone.'" value="' . $offset . '"' . $selected . '>' . $zone_trunc . '</option>';
  849. }
  850. }
  851. return $tz_select;
  852. }
  853. // Functions handling topic/post tracking/marking
  854. /**
  855. * Marks a topic/forum as read
  856. * Marks a topic as posted to
  857. *
  858. * @param int $user_id can only be used with $mode == 'post'
  859. */
  860. function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $user_id = 0)
  861. {
  862. global $db, $user, $config;
  863. if ($mode == 'all')
  864. {
  865. if ($forum_id === false || !sizeof($forum_id))
  866. {
  867. if ($config['load_db_lastread'] && $user->data['is_registered'])
  868. {
  869. // Mark all forums read (index page)
  870. $db->sql_query('DELETE FROM ' . TOPICS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
  871. $db->sql_query('DELETE FROM ' . FORUMS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
  872. $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
  873. }
  874. else if ($config['load_anon_lastread'] || $user->data['is_registered'])
  875. {
  876. $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
  877. $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
  878. unset($tracking_topics['tf']);
  879. unset($tracking_topics['t']);
  880. unset($tracking_topics['f']);
  881. $tracking_topics['l'] = base_convert(time() - $config['board_startdate'], 10, 36);
  882. $user->set_cookie('track', tracking_serialize($tracking_topics), time() + 31536000);
  883. $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking_topics)) : tracking_serialize($tracking_topics);
  884. unset($tracking_topics);
  885. if ($user->data['is_registered'])
  886. {
  887. $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
  888. }
  889. }
  890. }
  891. return;
  892. }
  893. else if ($mode == 'topics')
  894. {
  895. // Mark all topics in forums read
  896. if (!is_array($forum_id))
  897. {
  898. $forum_id = array($forum_id);
  899. }
  900. // Add 0 to forums array to mark global announcements correctly
  901. $forum_id[] = 0;
  902. if ($config['load_db_lastread'] && $user->data['is_registered'])
  903. {
  904. $sql = 'DELETE FROM ' . TOPICS_TRACK_TABLE . "
  905. WHERE user_id = {$user->data['user_id']}
  906. AND " . $db->sql_in_set('forum_id', $forum_id);
  907. $db->sql_query($sql);
  908. $sql = 'SELECT forum_id
  909. FROM ' . FORUMS_TRACK_TABLE . "
  910. WHERE user_id = {$user->data['user_id']}
  911. AND " . $db->sql_in_set('forum_id', $forum_id);
  912. $result = $db->sql_query($sql);
  913. $sql_update = array();
  914. while ($row = $db->sql_fetchrow($result))
  915. {
  916. $sql_update[] = $row['forum_id'];
  917. }
  918. $db->sql_freeresult($result);
  919. if (sizeof($sql_update))
  920. {
  921. $sql = 'UPDATE ' . FORUMS_TRACK_TABLE . '
  922. SET mark_time = ' . time() . "
  923. WHERE user_id = {$user->data['user_id']}
  924. AND " . $db->sql_in_set('forum_id', $sql_update);
  925. $db->sql_query($sql);
  926. }
  927. if ($sql_insert = array_diff($forum_id, $sql_update))
  928. {
  929. $sql_ary = array();
  930. foreach ($sql_insert as $f_id)
  931. {
  932. $sql_ary[] = array(
  933. 'user_id' => (int) $user->data['user_id'],
  934. 'forum_id' => (int) $f_id,
  935. 'mark_time' => time()
  936. );
  937. }
  938. $db->sql_multi_insert(FORUMS_TRACK_TABLE, $sql_ary);
  939. }
  940. }
  941. else if ($config['load_anon_lastread'] || $user->data['is_registered'])
  942. {
  943. $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
  944. $tracking = ($tracking) ? tracking_unserialize($tracking) : array();
  945. foreach ($forum_id as $f_id)
  946. {
  947. $topic_ids36 = (isset($tracking['tf'][$f_id])) ? $tracking['tf'][$f_id] : array();
  948. if (isset($tracking['tf'][$f_id]))
  949. {
  950. unset($tracking['tf'][$f_id]);
  951. }
  952. foreach ($topic_ids36 as $topic_id36)
  953. {
  954. unset($tracking['t'][$topic_id36]);
  955. }
  956. if (isset($tracking['f'][$f_id]))
  957. {
  958. unset($tracking['f'][$f_id]);
  959. }
  960. $tracking['f'][$f_id] = base_convert(time() - $config['board_startdate'], 10, 36);
  961. }
  962. if (isset($tracking['tf']) && empty($tracking['tf']))
  963. {
  964. unset($tracking['tf']);
  965. }
  966. $user->set_cookie('track', tracking_serialize($tracking), time() + 31536000);
  967. $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking);
  968. unset($tracking);
  969. }
  970. return;
  971. }
  972. else if ($mode == 'topic')
  973. {
  974. if ($topic_id === false || $forum_id === false)
  975. {
  976. return;
  977. }
  978. if ($config['load_db_lastread'] && $user->data['is_registered'])
  979. {
  980. $sql = 'UPDATE ' . TOPICS_TRACK_TABLE . '
  981. SET mark_time = ' . (($post_time) ? $post_time : time()) . "
  982. WHERE user_id = {$user->data['user_id']}
  983. AND topic_id = $topic_id";
  984. $db->sql_query($sql);
  985. // insert row
  986. if (!$db->sql_affectedrows())
  987. {
  988. $db->sql_return_on_error(true);
  989. $sql_ary = array(
  990. 'user_id' => (int) $user->data['user_id'],
  991. 'topic_id' => (int) $topic_id,
  992. 'forum_id' => (int) $forum_id,
  993. 'mark_time' => ($post_time) ? (int) $post_time : time(),
  994. );
  995. $db->sql_query('INSERT INTO ' . TOPICS_TRACK_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
  996. $db->sql_return_on_error(false);
  997. }
  998. }
  999. else if ($config['load_anon_lastread'] || $user->data['is_registered'])
  1000. {
  1001. $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
  1002. $tracking = ($tracking) ? tracking_unserialize($tracking) : array();
  1003. $topic_id36 = base_convert($topic_id, 10, 36);
  1004. if (!isset($tracking['t'][$topic_id36]))
  1005. {
  1006. $tracking['tf'][$forum_id][$topic_id36] = true;
  1007. }
  1008. $post_time = ($post_time) ? $post_time : time();
  1009. $tracking['t'][$topic_id36] = base_convert($post_time - $config['board_startdate'], 10, 36);
  1010. // If the cookie grows larger than 10000 characters we will remove the smallest value
  1011. // This can result in old topics being unread - but most of the time it should be accurate...
  1012. if (isset($_COOKIE[$config['cookie_name'] . '_track']) && strlen($_COOKIE[$config['cookie_name'] . '_track']) > 10000)
  1013. {
  1014. //echo 'Cookie grown too large' . print_r($tracking, true);
  1015. // We get the ten most minimum stored time offsets and its associated topic ids
  1016. $time_keys = array();
  1017. for ($i = 0; $i < 10 && sizeof($tracking['t']); $i++)
  1018. {
  1019. $min_value = min($tracking['t']);
  1020. $m_tkey = array_search($min_value, $tracking['t']);
  1021. unset($tracking['t'][$m_tkey]);
  1022. $time_keys[$m_tkey] = $min_value;
  1023. }
  1024. // Now remove the topic ids from the array...
  1025. foreach ($tracking['tf'] as $f_id => $topic_id_ary)
  1026. {
  1027. foreach ($time_keys as $m_tkey => $min_value)
  1028. {
  1029. if (isset($topic_id_ary[$m_tkey]))
  1030. {
  1031. $tracking['f'][$f_id] = $min_value;
  1032. unset($tracking['tf'][$f_id][$m_tkey]);
  1033. }
  1034. }
  1035. }
  1036. if ($user->data['is_registered'])
  1037. {
  1038. $user->data['user_lastmark'] = intval(base_convert(max($time_keys) + $config['board_startdate'], 36, 10));
  1039. $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . $user->data['user_lastmark'] . " WHERE user_id = {$user->data['user_id']}");
  1040. }
  1041. else
  1042. {
  1043. $tracking['l'] = max($time_keys);
  1044. }
  1045. }
  1046. $user->set_cookie('track', tracking_serialize($tracking), time() + 31536000);
  1047. $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking);
  1048. }
  1049. return;
  1050. }
  1051. else if ($mode == 'post')
  1052. {
  1053. if ($topic_id === false)
  1054. {
  1055. return;
  1056. }
  1057. $use_user_id = (!$user_id) ? $user->data['user_id'] : $user_id;
  1058. if ($config['load_db_track'] && $use_user_id != ANONYMOUS)
  1059. {
  1060. $db->sql_return_on_error(true);
  1061. $sql_ary = array(
  1062. 'user_id' => (int) $use_user_id,
  1063. 'topic_id' => (int) $topic_id,
  1064. 'topic_posted' => 1
  1065. );
  1066. $db->sql_query('INSERT INTO ' . TOPICS_POSTED_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
  1067. $db->sql_return_on_error(false);
  1068. }
  1069. return;
  1070. }
  1071. }
  1072. /**
  1073. * Get topic tracking info by using already fetched info
  1074. */
  1075. function get_topic_tracking($forum_id, $topic_ids, &$rowset, $forum_mark_time, $global_announce_list = false)
  1076. {
  1077. global $config, $user;
  1078. $last_read = array();
  1079. if (!is_array($topic_ids))
  1080. {
  1081. $topic_ids = array($topic_ids);
  1082. }
  1083. foreach ($topic_ids as $topic_id)
  1084. {
  1085. if (!empty($rowset[$topic_id]['mark_time']))
  1086. {
  1087. $last_read[$topic_id] = $rowset[$topic_id]['mark_time'];
  1088. }
  1089. }
  1090. $topic_ids = array_diff($topic_ids, array_keys($last_read));
  1091. if (sizeof($topic_ids))
  1092. {
  1093. $mark_time = array();
  1094. // Get global announcement info
  1095. if ($global_announce_list && sizeof($global_announce_list))
  1096. {
  1097. if (!isset($forum_mark_time[0]))
  1098. {
  1099. global $db;
  1100. $sql = 'SELECT mark_time
  1101. FROM ' . FORUMS_TRACK_TABLE . "
  1102. WHERE user_id = {$user->data['user_id']}
  1103. AND forum_id = 0";
  1104. $result = $db->sql_query($sql);
  1105. $row = $db->sql_fetchrow($result);
  1106. $db->sql_freeresult($result);
  1107. if ($row)
  1108. {
  1109. $mark_time[0] = $row['mark_time'];
  1110. }
  1111. }
  1112. else
  1113. {
  1114. if ($forum_mark_time[0] !== false)
  1115. {
  1116. $mark_time[0] = $forum_mark_time[0];
  1117. }
  1118. }
  1119. }
  1120. if (!empty($forum_mark_time[$forum_id]) && $forum_mark_time[$forum_id] !== false)
  1121. {
  1122. $mark_time[$forum_id] = $forum_mark_time[$forum_id];
  1123. }
  1124. $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
  1125. foreach ($topic_ids as $topic_id)
  1126. {
  1127. if ($global_announce_list && isset($global_announce_list[$topic_id]))
  1128. {
  1129. $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
  1130. }
  1131. else
  1132. {
  1133. $last_read[$topic_id] = $user_lastmark;
  1134. }
  1135. }
  1136. }
  1137. return $last_read;
  1138. }
  1139. /**
  1140. * Get topic tracking info from db (for cookie based tracking only this function is used)
  1141. */
  1142. function get_complete_topic_tracking($forum_id, $topic_ids, $global_announce_list = false)
  1143. {
  1144. global $config, $user;
  1145. $last_read = array();
  1146. if (!is_array($topic_ids))
  1147. {
  1148. $topic_ids = array($topic_ids);
  1149. }
  1150. if ($config['load_db_lastread'] && $user->data['is_registered'])
  1151. {
  1152. global $db;
  1153. $sql = 'SELECT topic_id, mark_time
  1154. FROM ' . TOPICS_TRACK_TABLE . "
  1155. WHERE user_id = {$user->data['user_id']}
  1156. AND " . $db->sql_in_set('topic_id', $topic_ids);
  1157. $result = $db->sql_query($sql);
  1158. while ($row = $db->sql_fetchrow($result))
  1159. {
  1160. $last_read[$row['topic_id']] = $row['mark_time'];
  1161. }
  1162. $db->sql_freeresult($result);
  1163. $topic_ids = array_diff($topic_ids, array_keys($last_read));
  1164. if (sizeof($topic_ids))
  1165. {
  1166. $sql = 'SELECT forum_id, mark_time
  1167. FROM ' . FORUMS_TRACK_TABLE . "
  1168. WHERE user_id = {$user->data['user_id']}
  1169. AND forum_id " .
  1170. (($global_announce_list && sizeof($global_announce_list)) ? "IN (0, $forum_id)" : "= $forum_id");
  1171. $result = $db->sql_query($sql);
  1172. $mark_time = array();
  1173. while ($row = $db->sql_fetchrow($result))
  1174. {
  1175. $mark_time[$row['forum_id']] = $row['mark_time'];
  1176. }
  1177. $db->sql_freeresult($result);
  1178. $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
  1179. foreach ($topic_ids as $topic_id)
  1180. {
  1181. if ($global_announce_list && isset($global_announce_list[$topic_id]))
  1182. {
  1183. $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
  1184. }
  1185. else
  1186. {
  1187. $last_read[$topic_id] = $user_lastmark;
  1188. }
  1189. }
  1190. }
  1191. }
  1192. else if ($config['load_anon_lastread'] || $user->data['is_registered'])
  1193. {
  1194. global $tracking_topics;
  1195. if (!isset($tracking_topics) || !sizeof($tracking_topics))
  1196. {
  1197. $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
  1198. $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
  1199. }
  1200. if (!$user->data['is_registered'])
  1201. {
  1202. $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0;
  1203. }
  1204. else
  1205. {
  1206. $user_lastmark = $user->data['user_lastmark'];
  1207. }
  1208. foreach ($topic_ids as $topic_id)
  1209. {
  1210. $topic_id36 = base_convert($topic_id, 10, 36);
  1211. if (isset($tracking_topics['t'][$topic_id36]))
  1212. {
  1213. $last_read[$topic_id] = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate'];
  1214. }
  1215. }
  1216. $topic_ids = array_diff($topic_ids, array_keys($last_read));
  1217. if (sizeof($topic_ids))
  1218. {
  1219. $mark_time = array();
  1220. if ($global_announce_list && sizeof($global_announce_list))
  1221. {
  1222. if (isset($tracking_topics['f'][0]))
  1223. {
  1224. $mark_time[0] = base_convert($tracking_topics['f'][0], 36, 10) + $config['board_startdate'];
  1225. }
  1226. }
  1227. if (isset($tracking_topics['f'][$forum_id]))
  1228. {
  1229. $mark_time[$forum_id] = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate'];
  1230. }
  1231. $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user_lastmark;
  1232. foreach ($topic_ids as $topic_id)
  1233. {
  1234. if ($global_announce_list && isset($global_announce_list[$topic_id]))
  1235. {
  1236. $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
  1237. }
  1238. else
  1239. {
  1240. $last_read[$topic_id] = $user_lastmark;
  1241. }
  1242. }
  1243. }
  1244. }
  1245. return $last_read;
  1246. }
  1247. /**
  1248. * Check for read forums and update topic tracking info accordingly
  1249. *
  1250. * @param int $forum_id the forum id to check
  1251. * @param int $forum_last_post_time the forums last post time
  1252. * @param int $f_mark_time the forums last mark time if user is registered and load_db_lastread enabled
  1253. * @param int $mark_time_forum false if the mark time needs to be obtained, else the last users forum mark time
  1254. *
  1255. * @return true if complete forum got marked read, else false.
  1256. */
  1257. function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_time = false, $mark_time_forum = false)
  1258. {
  1259. global $db, $tracking_topics, $user, $config;
  1260. // Determine the users last forum mark time if not given.
  1261. if ($mark_time_forum === false)
  1262. {
  1263. if ($config['load_db_lastread'] && $user->data['is_registered'])
  1264. {
  1265. $mark_time_forum = (!empty($f_mark_time)) ? $f_mark_time : $user->data['user_lastmark'];
  1266. }
  1267. else if ($config['load_anon_lastread'] || $user->data['is_registered'])
  1268. {
  1269. $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
  1270. $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
  1271. if (!$user->data['is_registered'])
  1272. {
  1273. $user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0;
  1274. }
  1275. $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'];
  1276. }
  1277. }
  1278. // Check the forum for any left unread topics.
  1279. // If there are none, we mark the forum as read.
  1280. if ($config['load_db_lastread'] && $user->data['is_registered'])
  1281. {
  1282. if ($mark_time_forum >= $forum_last_post_time)
  1283. {
  1284. // We do not need to mark read, this happened before. Therefore setting this to true
  1285. $row = true;
  1286. }
  1287. else
  1288. {
  1289. $sql = 'SELECT t.forum_id FROM ' . TOPICS_TABLE . ' t
  1290. LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt ON (tt.topic_id = t.topic_id AND tt.user_id = ' . $user->data['user_id'] . ')
  1291. WHERE t.forum_id = ' . $forum_id . '
  1292. AND t.topic_last_post_time > ' . $mark_time_forum . '
  1293. AND t.topic_moved_id = 0
  1294. AND (tt.topic_id IS NULL OR tt.mark_time < t.topic_last_post_time)
  1295. GROUP BY t.forum_id';
  1296. $result = $db->sql_query_limit($sql, 1);
  1297. $row = $db->sql_fetchrow($result);
  1298. $db->sql_freeresult($result);
  1299. }
  1300. }
  1301. else if ($config['load_anon_lastread'] || $user->data['is_registered'])
  1302. {
  1303. // Get information from cookie
  1304. $row = false;
  1305. if (!isset($tracking_topics['tf'][$forum_id]))
  1306. {
  1307. // We do not need to mark read, this happened before. Therefore setting this to true
  1308. $row = true;
  1309. }
  1310. else
  1311. {
  1312. $sql = 'SELECT topic_id
  1313. FROM ' . TOPICS_TABLE . '
  1314. WHERE forum_id = ' . $forum_id . '
  1315. AND topic_last_post_time > ' . $mark_time_forum . '
  1316. AND topic_moved_id = 0';
  1317. $result = $db->sql_query($sql);
  1318. $check_forum = $tracking_topics['tf'][$forum_id];
  1319. $unread = false;
  1320. while ($row = $db->sql_fetchrow($result))
  1321. {
  1322. if (!isset($check_forum[base_convert($row['topic_id'], 10, 36)]))
  1323. {
  1324. $unread = true;
  1325. break;
  1326. }
  1327. }
  1328. $db->sql_freeresult($result);
  1329. $row = $unread;
  1330. }
  1331. }
  1332. else
  1333. {
  1334. $row = true;
  1335. }
  1336. if (!$row)
  1337. {
  1338. markread('topics', $forum_id);
  1339. return true;
  1340. }
  1341. return false;
  1342. }
  1343. /**
  1344. * Transform an array into a serialized format
  1345. */
  1346. function tracking_serialize($input)
  1347. {
  1348. $out = '';
  1349. foreach ($input as $key => $value)
  1350. {
  1351. if (is_array($value))
  1352. {
  1353. $out .= $key . ':(' . tracking_serialize($value) . ');';
  1354. }
  1355. else
  1356. {
  1357. $out .= $key . ':' . $value . ';';
  1358. }
  1359. }
  1360. return $out;
  1361. }
  1362. /**
  1363. * Transform a serialized array into an actual array
  1364. */
  1365. function tracking_unserialize($string, $max_depth = 3)
  1366. {
  1367. $n = strlen($string);
  1368. if ($n > 10010)
  1369. {
  1370. die('Invalid data supplied');
  1371. }
  1372. $data = $stack = array();
  1373. $key = '';
  1374. $mode = 0;
  1375. $level = &$data;
  1376. for ($i = 0; $i < $n; ++$i)
  1377. {
  1378. switch ($mode)
  1379. {
  1380. case 0:
  1381. switch ($string[$i])
  1382. {
  1383. case ':':
  1384. $level[$key] = 0;
  1385. $mode = 1;
  1386. break;
  1387. case ')':
  1388. unset($level);
  1389. $level = array_pop($stack);
  1390. $mode = 3;
  1391. break;
  1392. default:
  1393. $key .= $string[$i];
  1394. }
  1395. break;
  1396. case 1:
  1397. switch ($string[$i])
  1398. {
  1399. case '(':
  1400. if (sizeof($stack) >= $max_depth)
  1401. {
  1402. die('Invalid data supplied');
  1403. }
  1404. $stack[] = &$level;
  1405. $level[$key] = array();
  1406. $level = &$level[$key];
  1407. $key = '';
  1408. $mode = 0;
  1409. break;
  1410. default:
  1411. $level[$key] = $string[$i];
  1412. $mode = 2;
  1413. break;
  1414. }
  1415. break;
  1416. case 2:
  1417. switch ($string[$i])
  1418. {
  1419. case ')':
  1420. unset($level);
  1421. $level = array_pop($stack);
  1422. $mode = 3;
  1423. break;
  1424. case ';':
  1425. $key = '';
  1426. $mode = 0;
  1427. break;
  1428. default:
  1429. $level[$key] .= $string[$i];
  1430. break;
  1431. }
  1432. break;
  1433. case 3:
  1434. switch ($string[$i])
  1435. {
  1436. case ')':
  1437. unset($level);
  1438. $level = array_pop($stack);
  1439. break;
  1440. case ';':
  1441. $key = '';
  1442. $mode = 0;
  1443. break;
  1444. default:
  1445. die('Invalid data supplied');
  1446. break;
  1447. }
  1448. break;
  1449. }
  1450. }
  1451. if (sizeof($stack) != 0 || ($mode != 0 && $mode != 3))
  1452. {
  1453. die('Invalid data supplied');
  1454. }
  1455. return $level;
  1456. }
  1457. // Pagination functions
  1458. /**
  1459. * Pagination routine, generates page number sequence
  1460. * tpl_prefix is for using different pagination blocks at one page
  1461. */
  1462. function generate_pagination($base_url, $num_items, $per_page, $start_item, $add_prevnext_text = false, $tpl_prefix = '')
  1463. {
  1464. global $template, $user;
  1465. // Make sure $per_page is a valid value
  1466. $per_page = ($per_page <= 0) ? 1 : $per_page;
  1467. $seperator = '<span class="page-sep">' . $user->lang['COMMA_SEPARATOR'] . '</span>';
  1468. $total_pages = ceil($num_items / $per_page);
  1469. if ($total_pages == 1 || !$num_items)
  1470. {
  1471. return false;
  1472. }
  1473. $on_page = floor($start_item / $per_page) + 1;
  1474. $url_delim = (strpos($base_url, '?') === false) ? '?' : '&amp;';
  1475. $page_string = ($on_page == 1) ? '<strong>1</strong>' : '<a href="' . $base_url . '">1</a>';
  1476. if ($total_pages > 5)
  1477. {
  1478. $start_cnt = min(max(1, $on_page - 4), $total_pages - 5);
  1479. $end_cnt = max(min($total_pages, $on_page + 4), 6);
  1480. $page_string .= ($start_cnt > 1) ? ' ... ' : $seperator;
  1481. for ($i = $start_cnt + 1; $i < $end_cnt; $i++)
  1482. {
  1483. $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
  1484. if ($i < $end_cnt - 1)
  1485. {
  1486. $page_string .= $seperator;
  1487. }
  1488. }
  1489. $page_string .= ($end_cnt < $total_pages) ? ' ... ' : $seperator;
  1490. }
  1491. else
  1492. {
  1493. $page_string .= $seperator;
  1494. for ($i = 2; $i < $total_pages; $i++)
  1495. {
  1496. $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
  1497. if ($i < $total_pages)
  1498. {
  1499. $page_string .= $seperator;
  1500. }
  1501. }
  1502. }
  1503. $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>';
  1504. if ($add_prevnext_text)
  1505. {
  1506. if ($on_page != 1)
  1507. {
  1508. $page_string = '<a href="' . $base_url . "{$url_delim}start=" . (($on_page - 2) * $per_page) . '">' . $user->lang['PREVIOUS'] . '</a>&nbsp;&nbsp;' . $page_string;
  1509. }
  1510. if ($on_page != $total_pages)
  1511. {
  1512. $page_string .= '&nbsp;&nbsp;<a href="' . $base_url . "{$url_delim}start=" . ($on_page * $per_page) . '">' . $user->lang['NEXT'] . '</a>';
  1513. }
  1514. }
  1515. $template->assign_vars(array(
  1516. $tpl_prefix . 'BASE_URL' => $base_url,
  1517. 'A_' . $tpl_prefix . 'BASE_URL' => addslashes($base_url),
  1518. $tpl_prefix . 'PER_PAGE' => $per_page,
  1519. $tpl_prefix . 'PREVIOUS_PAGE' => ($on_page == 1) ? '' : $base_url . "{$url_delim}start=" . (($on_page - 2) * $per_page),
  1520. $tpl_prefix . 'NEXT_PAGE' => ($on_page == $total_pages) ? '' : $base_url . "{$url_delim}start=" . ($on_page * $per_page),
  1521. $tpl_prefix . 'TOTAL_PAGES' => $total_pages,
  1522. ));
  1523. return $page_string;
  1524. }
  1525. /**
  1526. * Return current page (pagination)
  1527. */
  1528. function on_page($num_items, $per_page, $start)
  1529. {
  1530. global $template, $user;
  1531. // Make sure $per_page is a valid value
  1532. $per_page = ($per_page <= 0) ? 1 : $per_page;
  1533. $on_page = floor($start / $per_page) + 1;
  1534. $template->assign_vars(array(
  1535. 'ON_PAGE' => $on_page)
  1536. );
  1537. return sprintf($user->lang['PAGE_OF'], $on_page, max(ceil($num_items / $per_page), 1));
  1538. }
  1539. // Server functions (building urls, redirecting...)
  1540. /**
  1541. * Append session id to url.
  1542. * This function supports hooks.
  1543. *
  1544. * @param string $url The url the session id needs to be appended to (can have params)
  1545. * @param mixed $params String or array of additional url parameters
  1546. * @param bool $is_amp Is url using &amp; (true) or & (false)
  1547. * @param string $session_id Possibility to use a custom session id instead of the global one
  1548. *
  1549. * Examples:
  1550. * <code>
  1551. * append_sid("{$phpbb_root_path}viewtopic.$phpEx?t=1&amp;f=2");
  1552. * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&amp;f=2');
  1553. * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2', false);
  1554. * append_sid("{$phpbb_root_path}viewtopic.$phpEx", array('t' => 1, 'f' => 2));
  1555. * </code>
  1556. *
  1557. */
  1558. function append_sid($url, $params = false, $is_amp = true, $session_id = false)
  1559. {
  1560. global $_SID, $_EXTRA_URL, $phpbb_hook;
  1561. // Developers using the hook function need to globalise the $_SID and $_EXTRA_URL on their own and also handle it appropiatly.
  1562. // They could mimick most of what is within this function
  1563. if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__, $url, $params, $is_amp, $session_id))
  1564. {
  1565. if ($phpbb_hook->hook_return(__FUNCTION__))
  1566. {
  1567. return $phpbb_hook->hook_return_result(__FUNCTION__);
  1568. }
  1569. }
  1570. // Assign sid if session id is not specified
  1571. if ($session_id === false)
  1572. {
  1573. $session_id = $_SID;
  1574. }
  1575. $amp_delim = ($is_amp) ? '&amp;' : '&';
  1576. $url_delim = (strpos($url, '?') === false) ? '?' : $amp_delim;
  1577. // Appending custom url parameter?
  1578. $append_url = (!empty($_EXTRA_URL)) ? implode($amp_delim, $_EXTRA_URL) : '';
  1579. $anchor = '';
  1580. if (strpos($url, '#') !== false)
  1581. {
  1582. list($url, $anchor) = explode('#', $url, 2);
  1583. $anchor = '#' . $anchor;
  1584. }
  1585. else if (!is_array($params) && strpos($params, '#') !== false)
  1586. {
  1587. list($params, $anchor) = explode('#', $params, 2);
  1588. $anchor = '#' . $anchor;
  1589. }
  1590. // Use the short variant if possible ;)
  1591. if ($params === false)
  1592. {
  1593. // Append session id
  1594. if (!$session_id)
  1595. {
  1596. return $url . (($append_url) ? $url_delim . $append_url : '') . $anchor;
  1597. }
  1598. else
  1599. {
  1600. return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . 'sid=' . $session_id . $anchor;
  1601. }
  1602. }
  1603. // Build string if parameters are specified as array
  1604. if (is_array($params))
  1605. {
  1606. $output = array();
  1607. foreach ($params as $key => $item)
  1608. {
  1609. if ($item === NULL)
  1610. {
  1611. continue;
  1612. }
  1613. if ($key == '#')
  1614. {
  1615. $anchor = '#' . $item;
  1616. continue;
  1617. }
  1618. $output[] = $key . '=' . $item;
  1619. }
  1620. $params = implode($amp_delim, $output);
  1621. }
  1622. // Append session id and parameters (even if they are empty)
  1623. // If parameters are empty, the developer can still append his/her parameters without caring about the delimiter
  1624. return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . $params . ((!$session_id) ? '' : $amp_delim . 'sid=' . $session_id) . $anchor;
  1625. }
  1626. /**
  1627. * Generate board url (example: http://www.example.com/phpBB)
  1628. * @param bool $without_script_path if set to true the script path gets not appended (example: http://www.example.com)
  1629. */
  1630. function generate_board_url($without_script_path = false)
  1631. {
  1632. global $config, $user;
  1633. $server_name = $user->host;
  1634. $server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT');
  1635. // Forcing server vars is the only way to specify/override the protocol
  1636. if ($config['force_server_vars'] || !$server_name)
  1637. {
  1638. $server_protocol = ($config['server_protocol']) ? $config['server_protocol'] : (($config['cookie_secure']) ? 'https://' : 'http://');
  1639. $server_name = $config['server_name'];
  1640. $server_port = (int) $config['server_port'];
  1641. $script_path = $config['script_path'];
  1642. $url = $server_protocol . $server_name;
  1643. $cookie_secure = $config['cookie_secure'];
  1644. }
  1645. else
  1646. {
  1647. // Do not rely on cookie_secure, users seem to think that it means a secured cookie instead of an encrypted connection
  1648. $cookie_secure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 1 : 0;
  1649. $url = (($cookie_secure) ? 'https://' : 'http://') . $server_name;
  1650. $script_path = $user->page['root_script_path'];
  1651. }
  1652. if ($server_port && (($cookie_secure && $server_port <> 443) || (!$cookie_secure && $server_port <> 80)))
  1653. {
  1654. // HTTP HOST can carry a port number (we fetch $user->host, b…

Large files files are truncated, but you can click here to view the full file