some changes/fixes
[phpbb.git] / phpBB / includes / functions.php
blob3278a54619690e66cf6625ac3b58b4057a154a22
1 <?php
2 /**
4 * @package phpBB3
5 * @version $Id$
6 * @copyright (c) 2005 phpBB Group
7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
9 */
11 // Common global functions
13 /**
14 * set_var
16 * Set variable, used by {@link request_var the request_var function}
18 * @access private
20 function set_var(&$result, $var, $type, $multibyte = false)
22 settype($var, $type);
23 $result = $var;
25 if ($type == 'string')
27 $result = trim(htmlspecialchars(str_replace(array("\r\n", "\r"), array("\n", "\n"), $result)));
28 $result = (STRIP) ? stripslashes($result) : $result;
30 // Check for possible multibyte characters to save a preg_replace call if nothing is in there...
31 if ($multibyte && strpos($result, '&amp;#') !== false)
33 $result = preg_replace('#&amp;(\#[0-9]+;)#', '&\1', $result);
38 /**
39 * request_var
41 * Used to get passed variable
43 function request_var($var_name, $default, $multibyte = false)
45 if (!isset($_REQUEST[$var_name]) || (is_array($_REQUEST[$var_name]) && !is_array($default)) || (is_array($default) && !is_array($_REQUEST[$var_name])))
47 return (is_array($default)) ? array() : $default;
50 $var = $_REQUEST[$var_name];
51 if (!is_array($default))
53 $type = gettype($default);
55 else
57 list($key_type, $type) = each($default);
58 $type = gettype($type);
59 $key_type = gettype($key_type);
62 if (is_array($var))
64 $_var = $var;
65 $var = array();
67 foreach ($_var as $k => $v)
69 if (is_array($v))
71 foreach ($v as $_k => $_v)
73 set_var($k, $k, $key_type);
74 set_var($_k, $_k, $key_type);
75 set_var($var[$k][$_k], $_v, $type, $multibyte);
78 else
80 set_var($k, $k, $key_type);
81 set_var($var[$k], $v, $type, $multibyte);
85 else
87 set_var($var, $var, $type, $multibyte);
90 return $var;
93 /**
94 * Set config value. Creates missing config entry.
96 function set_config($config_name, $config_value, $is_dynamic = false)
98 global $db, $cache, $config;
100 $sql = 'UPDATE ' . CONFIG_TABLE . "
101 SET config_value = '" . $db->sql_escape($config_value) . "'
102 WHERE config_name = '" . $db->sql_escape($config_name) . "'";
103 $db->sql_query($sql);
105 if (!$db->sql_affectedrows() && !isset($config[$config_name]))
107 $sql = 'INSERT INTO ' . CONFIG_TABLE . ' ' . $db->sql_build_array('INSERT', array(
108 'config_name' => $config_name,
109 'config_value' => $config_value,
110 'is_dynamic' => ($is_dynamic) ? 1 : 0));
111 $db->sql_query($sql);
114 $config[$config_name] = $config_value;
116 if (!$is_dynamic)
118 $cache->destroy('config');
123 * Generates an alphanumeric random string of given length
125 function gen_rand_string($num_chars = 8)
127 $rand_str = unique_id();
128 $rand_str = str_replace('0', 'Z', strtoupper(base_convert($rand_str, 16, 35)));
130 return substr($rand_str, 0, $num_chars);
134 * Return unique id
135 * @param $extra additional entropy
137 function unique_id($extra = 'c')
139 global $config;
140 static $dss_seeded;
142 $val = $config['rand_seed'] . microtime();
143 $val = md5($val);
144 $config['rand_seed'] = md5($config['rand_seed'] . $val . $extra);
146 if ($dss_seeded !== true)
148 set_config('rand_seed', $config['rand_seed'], true);
149 $dss_seeded = true;
152 return substr($val, 4, 16);
156 * Generate sort selection fields
158 function gen_sort_selects(&$limit_days, &$sort_by_text, &$sort_days, &$sort_key, &$sort_dir, &$s_limit_days, &$s_sort_key, &$s_sort_dir, &$u_sort_param)
160 global $user;
162 $sort_dir_text = array('a' => $user->lang['ASCENDING'], 'd' => $user->lang['DESCENDING']);
164 $s_limit_days = '<select name="st">';
165 foreach ($limit_days as $day => $text)
167 $selected = ($sort_days == $day) ? ' selected="selected"' : '';
168 $s_limit_days .= '<option value="' . $day . '"' . $selected . '>' . $text . '</option>';
170 $s_limit_days .= '</select>';
172 $s_sort_key = '<select name="sk">';
173 foreach ($sort_by_text as $key => $text)
175 $selected = ($sort_key == $key) ? ' selected="selected"' : '';
176 $s_sort_key .= '<option value="' . $key . '"' . $selected . '>' . $text . '</option>';
178 $s_sort_key .= '</select>';
180 $s_sort_dir = '<select name="sd">';
181 foreach ($sort_dir_text as $key => $value)
183 $selected = ($sort_dir == $key) ? ' selected="selected"' : '';
184 $s_sort_dir .= '<option value="' . $key . '"' . $selected . '>' . $value . '</option>';
186 $s_sort_dir .= '</select>';
188 $u_sort_param = "st=$sort_days&amp;sk=$sort_key&amp;sd=$sort_dir";
190 return;
194 * Generate Jumpbox
196 function make_jumpbox($action, $forum_id = false, $select_all = false, $acl_list = false)
198 global $config, $auth, $template, $user, $db, $phpEx;
200 if (!$config['load_jumpbox'])
202 return;
205 $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id
206 FROM ' . FORUMS_TABLE . '
207 ORDER BY left_id ASC';
208 $result = $db->sql_query($sql, 600);
210 $right = $padding = 0;
211 $padding_store = array('0' => 0);
212 $display_jumpbox = false;
213 $iteration = 0;
215 // Sometimes it could happen that forums will be displayed here not be displayed within the index page
216 // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
217 // If this happens, the padding could be "broken"
219 while ($row = $db->sql_fetchrow($result))
221 if ($row['left_id'] < $right)
223 $padding++;
224 $padding_store[$row['parent_id']] = $padding;
226 else if ($row['left_id'] > $right + 1)
228 $padding = $padding_store[$row['parent_id']];
231 $right = $row['right_id'];
233 if ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']))
235 // Non-postable forum with no subforums, don't display
236 continue;
239 if (!$auth->acl_get('f_list', $row['forum_id']))
241 // if the user does not have permissions to list this forum skip
242 continue;
245 if ($acl_list && !$auth->acl_gets($acl_list, $row['forum_id']))
247 continue;
250 if (!$display_jumpbox)
252 $template->assign_block_vars('jumpbox_forums', array(
253 'FORUM_ID' => ($select_all) ? 0 : -1,
254 'FORUM_NAME' => ($select_all) ? $user->lang['ALL_FORUMS'] : $user->lang['SELECT_FORUM'],
255 'S_FORUM_COUNT' => $iteration)
258 $iteration++;
259 $display_jumpbox = true;
262 $template->assign_block_vars('jumpbox_forums', array(
263 'FORUM_ID' => $row['forum_id'],
264 'FORUM_NAME' => $row['forum_name'],
265 'SELECTED' => ($row['forum_id'] == $forum_id) ? ' selected="selected"' : '',
266 'S_FORUM_COUNT' => $iteration,
267 'S_IS_CAT' => ($row['forum_type'] == FORUM_CAT) ? true : false,
268 'S_IS_LINK' => ($row['forum_type'] == FORUM_LINK) ? true : false,
269 'S_IS_POST' => ($row['forum_type'] == FORUM_POST) ? true : false)
272 for ($i = 0; $i < $padding; $i++)
274 $template->assign_block_vars('jumpbox_forums.level', array());
276 $iteration++;
278 $db->sql_freeresult($result);
279 unset($padding_store);
281 $template->assign_vars(array(
282 'S_DISPLAY_JUMPBOX' => $display_jumpbox,
283 'S_JUMPBOX_ACTION' => $action)
286 return;
290 // Compatibility functions
292 if (!function_exists('array_combine'))
295 * A wrapper for the PHP5 function array_combine()
296 * @param array $keys contains keys for the resulting array
297 * @param array $values contains values for the resulting array
299 * @return Returns an array by using the values from the keys array as keys and the
300 * values from the values array as the corresponding values. Returns false if the
301 * number of elements for each array isn't equal or if the arrays are empty.
303 function array_combine($keys, $values)
305 $keys = array_values($keys);
306 $values = array_values($values);
308 $n = sizeof($keys);
309 $m = sizeof($values);
310 if (!$n || !$m || ($n != $m))
312 return false;
315 $combined = array();
316 for ($i = 0; $i < $n; $i++)
318 $combined[$keys[$i]] = $values[$i];
320 return $combined;
324 if (!function_exists('str_split'))
327 * A wrapper for the PHP5 function str_split()
328 * @param array $string contains the string to be converted
329 * @param array $split_length contains the length of each chunk
331 * @return Converts a string to an array. If the optional split_length parameter is specified,
332 * the returned array will be broken down into chunks with each being split_length in length,
333 * otherwise each chunk will be one character in length. FALSE is returned if split_length is
334 * less than 1. If the split_length length exceeds the length of string, the entire string is
335 * returned as the first (and only) array element.
337 function str_split($string, $split_length = 1)
339 if ($split_length < 1)
341 return false;
343 else if ($split_length >= strlen($string))
345 return array($string);
347 else
349 preg_match_all('#.{1,' . $split_length . '}#s', $string, $matches);
350 return $matches[0];
355 if (!function_exists('stripos'))
358 * A wrapper for the PHP5 function stripos
359 * Find position of first occurrence of a case-insensitive string
361 * @param string $haystack is the string to search in
362 * @param string needle is the string to search for
364 * @return Returns the numeric position of the first occurrence of needle in the haystack string. Unlike strpos(), stripos() is case-insensitive.
365 * Note that the needle may be a string of one or more characters.
366 * If needle is not found, stripos() will return boolean FALSE.
368 function stripos($haystack, $needle)
370 if (preg_match('#' . preg_quote($needle, '#') . '#i', $haystack, $m))
372 return strpos($haystack, $m[0]);
375 return false;
379 if (!function_exists('realpath'))
381 if (substr(PHP_OS, 0, 3) != 'WIN' && !(bool) ini_get('safe_mode') && function_exists('shell_exec') && trim(`realpath .`))
384 * @author Chris Smith <chris@project-minerva.org>
385 * @copyright 2006 Project Minerva Team
386 * @param string $path The path which we should attempt to resolve.
387 * @return mixed
389 function phpbb_realpath($path)
391 $arg = escapeshellarg($path);
392 return trim(`realpath '$arg'`);
395 else
398 * Checks if a path ($path) is absolute or relative
400 * @param string $path Path to check absoluteness of
401 * @return boolean
403 function is_absolute($path)
405 return ($path[0] == '/' || (substr(PHP_OS, 0, 3) == 'WIN' && preg_match('#^[a-z]:/#i', $path))) ? true : false;
409 * @author Chris Smith <chris@project-minerva.org>
410 * @copyright 2006 Project Minerva Team
411 * @param string $path The path which we should attempt to resolve.
412 * @return mixed
414 function phpbb_realpath($path)
416 // Now to perform funky shizzle
418 // Switch to use UNIX slashes
419 $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
420 $path_prefix = '';
422 // Determine what sort of path we have
423 if (is_absolute($path))
425 $absolute = true;
427 if ($path[0] == '/')
429 // Absolute path, *NIX style
430 $path_prefix = '';
432 else
434 // Absolute path, Windows style
435 // Remove the drive letter and colon
436 $path_prefix = $path[0] . ':';
437 $path = substr($path, 2);
440 else
442 // Relative Path
443 // Prepend the current working directory
444 if (function_exists('getcwd'))
446 // This is the best method, hopefully it is enabled!
447 $path = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . '/' . $path;
448 $absolute = true;
449 if (preg_match('#^[a-z]:#i', $path))
451 $path_prefix = $path[0] . ':';
452 $path = substr($path, 2);
454 else
456 $path_prefix = '';
459 else if (isset($_SERVER['SCRIPT_FILENAME']) && !empty($_SERVER['SCRIPT_FILENAME']))
461 // Warning: If chdir() has been used this will lie!
462 // @todo This has some problems sometime (CLI can create them easily)
463 $path = str_replace(DIRECTORY_SEPARATOR, '/', dirname($_SERVER['SCRIPT_FILENAME'])) . '/' . $path;
464 $absolute = true;
465 $path_prefix = '';
467 else
469 // We have no way of getting the absolute path, just run on using relative ones.
470 $absolute = false;
471 $path_prefix = '.';
475 // Remove any repeated slashes
476 $path = preg_replace('#/{2,}#', '/', $path);
478 // Remove the slashes from the start and end of the path
479 $path = trim($path, '/');
481 // Break the string into little bits for us to nibble on
482 $bits = explode('/', $path);
484 // Remove any . in the path
485 $bits = array_diff($bits, array('.'));
487 // Lets get looping, run over and resolve any .. (up directory)
488 for ($i = 0, $max = sizeof($bits); $i < $max; $i++)
490 // @todo Optimise
491 if ($bits[$i] == '..' )
493 if (isset($bits[$i - 1]))
495 if ($bits[$i - 1] != '..')
497 // We found a .. and we are able to traverse upwards, lets do it!
498 unset($bits[$i]);
499 unset($bits[$i - 1]);
500 $i -= 2;
501 $max -= 2;
502 $bits = array_values($bits);
505 else if ($absolute) // ie. !isset($bits[$i - 1]) && $absolute
507 // We have an absolute path trying to descend above the root of the filesystem
508 // ... Error!
509 return false;
514 // Prepend the path prefix
515 array_unshift($bits, $path_prefix);
517 $resolved = '';
519 $max = sizeof($bits) - 1;
521 // Check if we are able to resolve symlinks, Windows cannot.
522 $symlink_resolve = (function_exists('readlink')) ? true : false;
524 foreach ($bits as $i => $bit)
526 if (@is_dir("$resolved/$bit") || ($i == $max && @is_file("$resolved/$bit")))
528 // Path Exists
529 if ($symlink_resolve && is_link("$resolved/$bit") && ($link = readlink("$resolved/$bit")))
531 // Resolved a symlink.
532 $resolved = $link . (($i == $max) ? '' : '/');
533 continue;
536 else
538 // Something doesn't exist here!
539 // This is correct realpath() behaviour but sadly open_basedir and safe_mode make this problematic
540 // return false;
542 $resolved .= $bit . (($i == $max) ? '' : '/');
545 // @todo If the file exists fine and open_basedir only has one path we should be able to prepend it
546 // because we must be inside that basedir, the question is where...
547 // @interal The slash in is_dir() gets around an open_basedir restriction
548 if (!@file_exists($resolved) || (!is_dir($resolved . '/') && !is_file($resolved)))
550 return false;
553 // Put the slashes back to the native operating systems slashes
554 $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved);
556 return $resolved; // We got here, in the end!
560 else
563 * A wrapper for realpath
565 function phpbb_realpath($path)
567 return realpath($path);
571 // functions used for building option fields
574 * Pick a language, any language ...
576 function language_select($default = '')
578 global $db;
580 $sql = 'SELECT lang_iso, lang_local_name
581 FROM ' . LANG_TABLE . '
582 ORDER BY lang_english_name';
583 $result = $db->sql_query($sql, 600);
585 $lang_options = '';
586 while ($row = $db->sql_fetchrow($result))
588 $selected = ($row['lang_iso'] == $default) ? ' selected="selected"' : '';
589 $lang_options .= '<option value="' . $row['lang_iso'] . '"' . $selected . '>' . $row['lang_local_name'] . '</option>';
591 $db->sql_freeresult($result);
593 return $lang_options;
596 /**
597 * Pick a template/theme combo,
599 function style_select($default = '', $all = false)
601 global $db;
603 $sql_where = (!$all) ? 'WHERE style_active = 1 ' : '';
604 $sql = 'SELECT style_id, style_name
605 FROM ' . STYLES_TABLE . "
606 $sql_where
607 ORDER BY style_name";
608 $result = $db->sql_query($sql);
610 $style_options = '';
611 while ($row = $db->sql_fetchrow($result))
613 $selected = ($row['style_id'] == $default) ? ' selected="selected"' : '';
614 $style_options .= '<option value="' . $row['style_id'] . '"' . $selected . '>' . $row['style_name'] . '</option>';
616 $db->sql_freeresult($result);
618 return $style_options;
622 * Pick a timezone
624 function tz_select($default = '', $truncate = false)
626 global $sys_timezone, $user;
628 $tz_select = '';
629 foreach ($user->lang['tz_zones'] as $offset => $zone)
631 if ($truncate)
633 $zone = (strlen($zone) > 70) ? substr($zone, 0, 70) . '...' : $zone;
636 if (is_numeric($offset))
638 $selected = ($offset == $default) ? ' selected="selected"' : '';
639 $tz_select .= '<option value="' . $offset . '"' . $selected . '>' . $zone . '</option>';
643 return $tz_select;
646 // Functions handling topic/post tracking/marking
649 * Marks a topic/forum as read
650 * Marks a topic as posted to
652 * @param int $user_id can only be used with $mode == 'post'
654 function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $user_id = 0)
656 global $db, $user, $config;
658 if ($mode == 'all')
660 if ($forum_id === false || !sizeof($forum_id))
662 if ($config['load_db_lastread'] && $user->data['is_registered'])
664 // Mark all forums read (index page)
665 $db->sql_query('DELETE FROM ' . TOPICS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
666 $db->sql_query('DELETE FROM ' . FORUMS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
667 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
669 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
671 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
672 $tracking_topics = ($tracking_topics) ? unserialize($tracking_topics) : array();
674 unset($tracking_topics['tf']);
675 unset($tracking_topics['t']);
676 unset($tracking_topics['f']);
677 $tracking_topics['l'] = base_convert(time() - $config['board_startdate'], 10, 36);
679 $user->set_cookie('track', serialize($tracking_topics), time() + 31536000);
680 unset($tracking_topics);
682 if ($user->data['is_registered'])
684 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
689 return;
691 else if ($mode == 'topics')
693 // Mark all topics in forums read
694 if (!is_array($forum_id))
696 $forum_id = array($forum_id);
699 // Add 0 to forums array to mark global announcements correctly
700 $forum_id[] = 0;
702 if ($config['load_db_lastread'] && $user->data['is_registered'])
704 $sql = 'DELETE FROM ' . TOPICS_TRACK_TABLE . "
705 WHERE user_id = {$user->data['user_id']}
706 AND " . $db->sql_in_set('forum_id', $forum_id);
707 $db->sql_query($sql);
709 $sql = 'SELECT forum_id
710 FROM ' . FORUMS_TRACK_TABLE . "
711 WHERE user_id = {$user->data['user_id']}
712 AND " . $db->sql_in_set('forum_id', $forum_id);
713 $result = $db->sql_query($sql);
715 $sql_update = array();
716 while ($row = $db->sql_fetchrow($result))
718 $sql_update[] = $row['forum_id'];
720 $db->sql_freeresult($result);
722 if (sizeof($sql_update))
724 $sql = 'UPDATE ' . FORUMS_TRACK_TABLE . '
725 SET mark_time = ' . time() . "
726 WHERE user_id = {$user->data['user_id']}
727 AND " . $db->sql_in_set('forum_id', $sql_update);
728 $db->sql_query($sql);
731 if ($sql_insert = array_diff($forum_id, $sql_update))
733 $sql_ary = array();
734 foreach ($sql_insert as $f_id)
736 $sql_ary[] = array(
737 'user_id' => $user->data['user_id'],
738 'forum_id' => $f_id,
739 'mark_time' => time()
743 if (sizeof($sql_ary))
745 switch (SQL_LAYER)
747 case 'mysql':
748 case 'mysql4':
749 case 'mysqli':
750 $db->sql_query('INSERT INTO ' . FORUMS_TRACK_TABLE . ' ' . $db->sql_build_array('MULTI_INSERT', $sql_ary));
751 break;
753 default:
754 foreach ($sql_ary as $ary)
756 $db->sql_query('INSERT INTO ' . FORUMS_TRACK_TABLE . ' ' . $db->sql_build_array('INSERT', $ary));
758 break;
763 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
765 $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
766 $tracking = ($tracking) ? unserialize($tracking) : array();
768 foreach ($forum_id as $f_id)
770 $topic_ids36 = (isset($tracking['tf'][$f_id])) ? $tracking['tf'][$f_id] : array();
772 if (isset($tracking['tf'][$f_id]))
774 unset($tracking['tf'][$f_id]);
777 foreach ($topic_ids36 as $topic_id36)
779 unset($tracking['t'][$topic_id36]);
782 if (isset($tracking['f'][$f_id]))
784 unset($tracking['f'][$f_id]);
787 $tracking['f'][$f_id] = base_convert(time() - $config['board_startdate'], 10, 36);
790 $user->set_cookie('track', serialize($tracking), time() + 31536000);
791 unset($tracking);
794 return;
796 else if ($mode == 'topic')
798 if ($topic_id === false || $forum_id === false)
800 return;
803 if ($config['load_db_lastread'] && $user->data['is_registered'])
805 $sql = 'UPDATE ' . TOPICS_TRACK_TABLE . '
806 SET mark_time = ' . (($post_time) ? $post_time : time()) . "
807 WHERE user_id = {$user->data['user_id']}
808 AND topic_id = $topic_id";
809 $db->sql_query($sql);
811 // insert row
812 if (!$db->sql_affectedrows())
814 $db->sql_return_on_error(true);
816 $sql_ary = array(
817 'user_id' => $user->data['user_id'],
818 'topic_id' => $topic_id,
819 'forum_id' => (int) $forum_id,
820 'mark_time' => ($post_time) ? $post_time : time(),
823 $db->sql_query('INSERT INTO ' . TOPICS_TRACK_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
825 $db->sql_return_on_error(false);
828 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
830 $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
831 $tracking = ($tracking) ? unserialize($tracking) : array();
833 $topic_id36 = base_convert($topic_id, 10, 36);
835 if (!isset($tracking['t'][$topic_id36]))
837 $tracking['tf'][$forum_id][$topic_id36] = true;
840 $post_time = ($post_time) ? $post_time : time();
841 $tracking['t'][$topic_id36] = base_convert($post_time - $config['board_startdate'], 10, 36);
843 // If the cookie grows larger than 10000 characters we will remove the smallest value
844 // This can result in old topics being unread - but most of the time it should be accurate...
845 if (isset($_COOKIE[$config['cookie_name'] . '_track']) && strlen($_COOKIE[$config['cookie_name'] . '_track']) > 10000)
847 //echo 'Cookie grown too large' . print_r($tracking, true);
849 // We get the ten most minimum stored time offsets and its associated topic ids
850 $time_keys = array();
851 for ($i = 0; $i < 10 && sizeof($tracking['t']); $i++)
853 $min_value = min($tracking['t']);
854 $m_tkey = array_search($min_value, $tracking['t']);
855 unset($tracking['t'][$m_tkey]);
857 $time_keys[$m_tkey] = $min_value;
860 // Now remove the topic ids from the array...
861 foreach ($tracking['tf'] as $f_id => $topic_id_ary)
863 foreach ($time_keys as $m_tkey => $min_value)
865 if (isset($topic_id_ary[$m_tkey]))
867 $tracking['f'][$f_id] = $min_value;
868 unset($tracking['tf'][$f_id][$m_tkey]);
873 if ($user->data['is_registered'])
875 $user->data['user_lastmark'] = intval(base_convert(max($time_keys) + $config['board_startdate'], 36, 10));
876 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . $user->data['user_lastmark'] . " WHERE user_id = {$user->data['user_id']}");
878 else
880 $tracking['l'] = max($time_keys);
884 $user->set_cookie('track', serialize($tracking), time() + 31536000);
887 return;
889 else if ($mode == 'post')
891 if ($topic_id === false)
893 return;
896 $use_user_id = (!$user_id) ? $user->data['user_id'] : $user_id;
898 if ($config['load_db_track'] && $use_user_id != ANONYMOUS)
900 $db->sql_return_on_error(true);
902 $sql_ary = array(
903 'user_id' => $use_user_id,
904 'topic_id' => $topic_id,
905 'topic_posted' => 1
908 $db->sql_query('INSERT INTO ' . TOPICS_POSTED_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
910 $db->sql_return_on_error(false);
913 return;
918 * Get topic tracking info by using already fetched info
920 function get_topic_tracking($forum_id, $topic_ids, &$rowset, $forum_mark_time, $global_announce_list = false)
922 global $config, $user;
924 $last_read = array();
926 if (!is_array($topic_ids))
928 $topic_ids = array($topic_ids);
931 foreach ($topic_ids as $topic_id)
933 if (!empty($rowset[$topic_id]['mark_time']))
935 $last_read[$topic_id] = $rowset[$topic_id]['mark_time'];
939 $topic_ids = array_diff($topic_ids, array_keys($last_read));
941 if (sizeof($topic_ids))
943 $mark_time = array();
945 // Get global announcement info
946 if ($global_announce_list && sizeof($global_announce_list))
948 if (!isset($forum_mark_time[0]))
950 global $db;
952 $sql = 'SELECT mark_time
953 FROM ' . FORUMS_TRACK_TABLE . "
954 WHERE user_id = {$user->data['user_id']}
955 AND forum_id = 0";
956 $result = $db->sql_query($sql);
957 $row = $db->sql_fetchrow($result);
958 $db->sql_freeresult($result);
960 if ($row)
962 $mark_time[0] = $row['mark_time'];
965 else
967 if ($forum_mark_time[0] !== false)
969 $mark_time[0] = $forum_mark_time[0];
974 if (!empty($forum_mark_time[$forum_id]) && $forum_mark_time[$forum_id] !== false)
976 $mark_time[$forum_id] = $forum_mark_time[$forum_id];
979 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
981 foreach ($topic_ids as $topic_id)
983 if ($global_announce_list && isset($global_announce_list[$topic_id]))
985 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
987 else
989 $last_read[$topic_id] = $user_lastmark;
994 return $last_read;
998 * Get topic tracking info from db (for cookie based tracking only this function is used)
1000 function get_complete_topic_tracking($forum_id, $topic_ids, $global_announce_list = false)
1002 global $config, $user;
1004 $last_read = array();
1006 if (!is_array($topic_ids))
1008 $topic_ids = array($topic_ids);
1011 if ($config['load_db_lastread'] && $user->data['is_registered'])
1013 global $db;
1015 $sql = 'SELECT topic_id, mark_time
1016 FROM ' . TOPICS_TRACK_TABLE . "
1017 WHERE user_id = {$user->data['user_id']}
1018 AND " . $db->sql_in_set('topic_id', $topic_ids);
1019 $result = $db->sql_query($sql);
1021 while ($row = $db->sql_fetchrow($result))
1023 $last_read[$row['topic_id']] = $row['mark_time'];
1025 $db->sql_freeresult($result);
1027 $topic_ids = array_diff($topic_ids, array_keys($last_read));
1029 if (sizeof($topic_ids))
1031 $sql = 'SELECT forum_id, mark_time
1032 FROM ' . FORUMS_TRACK_TABLE . "
1033 WHERE user_id = {$user->data['user_id']}
1034 AND forum_id " .
1035 (($global_announce_list && sizeof($global_announce_list)) ? "IN (0, $forum_id)" : "= $forum_id");
1036 $result = $db->sql_query($sql);
1038 $mark_time = array();
1039 while ($row = $db->sql_fetchrow($result))
1041 $mark_time[$row['forum_id']] = $row['mark_time'];
1043 $db->sql_freeresult($result);
1045 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
1047 foreach ($topic_ids as $topic_id)
1049 if ($global_announce_list && isset($global_announce_list[$topic_id]))
1051 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1053 else
1055 $last_read[$topic_id] = $user_lastmark;
1060 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1062 global $tracking_topics;
1064 if (!isset($tracking_topics) || !sizeof($tracking_topics))
1066 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1067 $tracking_topics = ($tracking_topics) ? unserialize($tracking_topics) : array();
1070 if (!$user->data['is_registered'])
1072 $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0;
1074 else
1076 $user_lastmark = $user->data['user_lastmark'];
1079 foreach ($topic_ids as $topic_id)
1081 $topic_id36 = base_convert($topic_id, 10, 36);
1083 if (isset($tracking_topics['t'][$topic_id36]))
1085 $last_read[$topic_id] = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate'];
1089 $topic_ids = array_diff($topic_ids, array_keys($last_read));
1091 if (sizeof($topic_ids))
1093 $mark_time = array();
1094 if ($global_announce_list && sizeof($global_announce_list))
1096 if (isset($tracking_topics['f'][0]))
1098 $mark_time[0] = base_convert($tracking_topics['f'][0], 36, 10) + $config['board_startdate'];
1102 if (isset($tracking_topics['f'][$forum_id]))
1104 $mark_time[$forum_id] = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate'];
1107 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user_lastmark;
1109 foreach ($topic_ids as $topic_id)
1111 if ($global_announce_list && isset($global_announce_list[$topic_id]))
1113 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1115 else
1117 $last_read[$topic_id] = $user_lastmark;
1123 return $last_read;
1127 * Check for read forums and update topic tracking info accordingly
1129 * @param int $forum_id the forum id to check
1130 * @param int $forum_last_post_time the forums last post time
1131 * @param int $f_mark_time the forums last mark time if user is registered and load_db_lastread enabled
1132 * @param int $mark_time_forum false if the mark time needs to be obtained, else the last users forum mark time
1134 * @return true if complete forum got marked read, else false.
1136 function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_time = false, $mark_time_forum = false)
1138 global $db, $tracking_topics, $user, $config;
1140 // Determine the users last forum mark time if not given.
1141 if ($mark_time_forum === false)
1143 if ($config['load_db_lastread'] && $user->data['is_registered'])
1145 $mark_time_forum = (!empty($f_mark_time)) ? $f_mark_time : $user->data['user_lastmark'];
1147 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1149 if (!isset($tracking_topics) || !sizeof($tracking_topics))
1151 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1152 $tracking_topics = ($tracking_topics) ? unserialize($tracking_topics) : array();
1155 if (!$user->data['is_registered'])
1157 $user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0;
1160 $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'];
1164 // Check the forum for any left unread topics.
1165 // If there are none, we mark the forum as read.
1166 if ($config['load_db_lastread'] && $user->data['is_registered'])
1168 if ($mark_time_forum >= $forum_last_post_time)
1170 // We do not need to mark read, this happened before. Therefore setting this to true
1171 $row = true;
1173 else
1175 $sql = 'SELECT t.forum_id FROM ' . TOPICS_TABLE . ' t
1176 LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt ON (tt.topic_id = t.topic_id AND tt.user_id = ' . $user->data['user_id'] . ')
1177 WHERE t.forum_id = ' . $forum_id . '
1178 AND t.topic_last_post_time > ' . $mark_time_forum . '
1179 AND t.topic_moved_id = 0
1180 AND tt.topic_id IS NULL
1181 GROUP BY t.forum_id';
1182 $result = $db->sql_query_limit($sql, 1);
1183 $row = $db->sql_fetchrow($result);
1184 $db->sql_freeresult($result);
1187 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1189 // Get information from cookie
1190 $row = false;
1192 if (!isset($tracking_topics['tf'][$forum_id]))
1194 // We do not need to mark read, this happened before. Therefore setting this to true
1195 $row = true;
1197 else
1199 $sql = 'SELECT topic_id
1200 FROM ' . TOPICS_TABLE . '
1201 WHERE forum_id = ' . $forum_id . '
1202 AND topic_last_post_time > ' . $mark_time_forum . '
1203 AND topic_moved_id = 0';
1204 $result = $db->sql_query($sql);
1206 $check_forum = $tracking_topics['tf'][$forum_id];
1207 $unread = false;
1208 while ($row = $db->sql_fetchrow($result))
1210 if (!in_array(base_convert($row['topic_id'], 10, 36), array_keys($check_forum)))
1212 $unread = true;
1213 break;
1216 $db->sql_freeresult($result);
1218 $row = $unread;
1221 else
1223 $row = true;
1226 if (!$row)
1228 markread('topics', $forum_id);
1229 return true;
1232 return false;
1235 // Pagination functions
1238 * Pagination routine, generates page number sequence
1239 * tpl_prefix is for using different pagination blocks at one page
1241 function generate_pagination($base_url, $num_items, $per_page, $start_item, $add_prevnext_text = false, $tpl_prefix = '')
1243 global $template, $user;
1245 $seperator = $user->theme['pagination_sep'];
1246 $total_pages = ceil($num_items/$per_page);
1248 if ($total_pages == 1 || !$num_items)
1250 return false;
1253 $on_page = floor($start_item / $per_page) + 1;
1254 $page_string = ($on_page == 1) ? '<strong>1</strong>' : '<a href="' . $base_url . '">1</a>';
1256 if ($total_pages > 5)
1258 $start_cnt = min(max(1, $on_page - 4), $total_pages - 5);
1259 $end_cnt = max(min($total_pages, $on_page + 4), 6);
1261 $page_string .= ($start_cnt > 1) ? ' ... ' : $seperator;
1263 for ($i = $start_cnt + 1; $i < $end_cnt; $i++)
1265 $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "&amp;start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
1266 if ($i < $end_cnt - 1)
1268 $page_string .= $seperator;
1272 $page_string .= ($end_cnt < $total_pages) ? ' ... ' : $seperator;
1274 else
1276 $page_string .= $seperator;
1278 for ($i = 2; $i < $total_pages; $i++)
1280 $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "&amp;start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
1281 if ($i < $total_pages)
1283 $page_string .= $seperator;
1288 $page_string .= ($on_page == $total_pages) ? '<strong>' . $total_pages . '</strong>' : '<a href="' . $base_url . '&amp;start=' . (($total_pages - 1) * $per_page) . '">' . $total_pages . '</a>';
1290 if ($add_prevnext_text)
1292 if ($on_page != 1)
1294 $page_string = '<a href="' . $base_url . '&amp;start=' . (($on_page - 2) * $per_page) . '">' . $user->lang['PREVIOUS'] . '</a>&nbsp;&nbsp;' . $page_string;
1297 if ($on_page != $total_pages)
1299 $page_string .= '&nbsp;&nbsp;<a href="' . $base_url . '&amp;start=' . ($on_page * $per_page) . '">' . $user->lang['NEXT'] . '</a>';
1303 $template->assign_vars(array(
1304 $tpl_prefix . 'BASE_URL' => $base_url,
1305 $tpl_prefix . 'PER_PAGE' => $per_page,
1307 $tpl_prefix . 'PREVIOUS_PAGE' => ($on_page == 1) ? '' : $base_url . '&amp;start=' . (($on_page - 2) * $per_page),
1308 $tpl_prefix . 'NEXT_PAGE' => ($on_page == $total_pages) ? '' : $base_url . '&amp;start=' . ($on_page * $per_page))
1311 return $page_string;
1315 * Return current page (pagination)
1317 function on_page($num_items, $per_page, $start)
1319 global $template, $user;
1321 $on_page = floor($start / $per_page) + 1;
1323 $template->assign_vars(array(
1324 'ON_PAGE' => $on_page)
1327 return sprintf($user->lang['PAGE_OF'], $on_page, max(ceil($num_items / $per_page), 1));
1330 // Server functions (building urls, redirecting...)
1333 * Append session id to url
1335 * @param string $url The url the session id needs to be appended to (can have params)
1336 * @param mixed $params String or array of additional url parameters
1337 * @param bool $is_amp Is url using &amp; (true) or & (false)
1338 * @param string $session_id Possibility to use a custom session id instead of the global one
1340 * Examples:
1341 * <code>
1342 * append_sid("{$phpbb_root_path}viewtopic.$phpEx?t=1&amp;f=2");
1343 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&amp;f=2');
1344 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2', false);
1345 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", array('t' => 1, 'f' => 2));
1346 * </code>
1348 function append_sid($url, $params = false, $is_amp = true, $session_id = false)
1350 global $_SID, $_EXTRA_URL;
1352 // Assign sid if session id is not specified
1353 if ($session_id === false)
1355 $session_id = $_SID;
1358 $amp_delim = ($is_amp) ? '&amp;' : '&';
1359 $url_delim = (strpos($url, '?') === false) ? '?' : $amp_delim;
1361 // Appending custom url parameter?
1362 $append_url = (!empty($_EXTRA_URL)) ? implode($amp_delim, $_EXTRA_URL) : '';
1364 // Use the short variant if possible ;)
1365 if ($params === false)
1367 // Append session id
1368 return (!$session_id) ? $url . (($append_url) ? $url_delim . $append_url : '') : $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . 'sid=' . $session_id;
1371 // Build string if parameters are specified as array
1372 if (is_array($params))
1374 $output = array();
1376 foreach ($params as $key => $item)
1378 if ($item === NULL)
1380 continue;
1383 $output[] = $key . '=' . $item;
1386 $params = implode($amp_delim, $output);
1389 // Append session id and parameters (even if they are empty)
1390 // If parameters are empty, the developer can still append his/her parameters without caring about the delimiter
1391 return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . $params . ((!$session_id) ? '' : $amp_delim . 'sid=' . $session_id);
1395 * Generate board url (example: http://www.foo.bar/phpBB)
1396 * @param bool $without_script_path if set to true the script path gets not appended (example: http://www.foo.bar)
1398 function generate_board_url($without_script_path = false)
1400 global $config, $user;
1402 $server_name = (!empty($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : getenv('SERVER_NAME');
1403 $server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT');
1405 // Forcing server vars is the only way to specify/override the protocol
1406 if ($config['force_server_vars'] || !$server_name)
1408 $server_protocol = ($config['server_protocol']) ? $config['server_protocol'] : (($config['cookie_secure']) ? 'https://' : 'http://');
1409 $server_name = $config['server_name'];
1410 $server_port = (int) $config['server_port'];
1412 $url = $server_protocol . $server_name;
1414 else
1416 // Do not rely on cookie_secure, users seem to think that it means a secured cookie instead of an encrypted connection
1417 $cookie_secure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 1 : 0;
1418 $url = (($cookie_secure) ? 'https://' : 'http://') . $server_name;
1421 if ($server_port && (($config['cookie_secure'] && $server_port <> 443) || (!$config['cookie_secure'] && $server_port <> 80)))
1423 $url .= ':' . $server_port;
1426 if ($without_script_path)
1428 return $url;
1431 // Strip / from the end
1432 return $url . substr($user->page['root_script_path'], 0, -1);
1436 * Redirects the user to another page then exits the script nicely
1438 function redirect($url)
1440 global $db, $cache, $config, $user;
1442 if (empty($user->lang))
1444 $user->add_lang('common');
1447 garbage_collection();
1449 // Make sure no &amp;'s are in, this will break the redirect
1450 $url = str_replace('&amp;', '&', $url);
1452 // Make sure no linebreaks are there... to prevent http response splitting for PHP < 4.4.2
1453 if (strpos(urldecode($url), "\n") !== false || strpos(urldecode($url), "\r") !== false)
1455 trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
1458 // Determine which type of redirect we need to handle...
1459 $url_parts = parse_url($url);
1461 if ($url_parts === false)
1463 // Malformed url, redirect to current page...
1464 $url = generate_board_url() . '/' . $user->page['page'];
1466 else if (!empty($url_parts['scheme']) && !empty($url_parts['host']))
1468 // Full URL
1470 else if ($url{0} == '/')
1472 // Absolute uri, prepend direct url...
1473 $url = generate_board_url(true) . $url;
1475 else
1477 // Relative uri
1478 $pathinfo = pathinfo($url);
1480 // Is the uri pointing to the current directory?
1481 if ($pathinfo['dirname'] == '.')
1483 if ($user->page['page_dir'])
1485 $url = generate_board_url() . '/' . $user->page['page_dir'] . '/' . str_replace('./', '', $url);
1487 else
1489 $url = generate_board_url() . '/' . str_replace('./', '', $url);
1492 else
1494 // Get the realpath of dirname
1495 $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath('./')));
1496 $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($pathinfo['dirname'])));
1497 $intersection = array_intersect_assoc($root_dirs, $page_dirs);
1499 $root_dirs = array_diff_assoc($root_dirs, $intersection);
1500 $page_dirs = array_diff_assoc($page_dirs, $intersection);
1502 $dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs);
1504 if ($dir && substr($dir, -1, 1) == '/')
1506 $dir = substr($dir, 0, -1);
1509 $url = $dir . '/' . str_replace($pathinfo['dirname'] . '/', '', $url);
1510 $url = generate_board_url() . '/' . $url;
1514 // Redirect via an HTML form for PITA webservers
1515 if (@preg_match('#Microsoft|WebSTAR|Xitami#', getenv('SERVER_SOFTWARE')))
1517 header('Refresh: 0; URL=' . $url);
1518 echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"><meta http-equiv="refresh" content="0; url=' . $url . '"><title>Redirect</title></head><body><div align="center">' . sprintf($user->lang['URL_REDIRECT'], '<a href="' . $url . '">', '</a>') . '</div></body></html>';
1520 exit;
1523 // Behave as per HTTP/1.1 spec for others
1524 header('Location: ' . $url);
1525 exit;
1529 * Re-Apply session id after page reloads
1531 function reapply_sid($url)
1533 global $phpEx, $phpbb_root_path;
1535 if ($url === "index.$phpEx")
1537 return append_sid("index.$phpEx");
1539 else if ($url === "{$phpbb_root_path}index.$phpEx")
1541 return append_sid("{$phpbb_root_path}index.$phpEx");
1544 // Remove previously added sid
1545 if (strpos($url, '?sid=') !== false)
1547 $url = preg_replace('/(\?)sid=[a-z0-9]+(&amp;|&)?/', '\1', $url);
1549 else if (strpos($url, '&sid=') !== false)
1551 $url = preg_replace('/&sid=[a-z0-9]+(&)?/', '\1', $url);
1553 else if (strpos($url, '&amp;sid=') !== false)
1555 $url = preg_replace('/&amp;sid=[a-z0-9]+(&amp;)?/', '\1', $url);
1558 return append_sid($url);
1562 * Returns url from the session/current page with an re-appended SID with optionally stripping vars from the url
1564 function build_url($strip_vars = false)
1566 global $user, $phpbb_root_path;
1568 // Append SID
1569 $redirect = (($user->page['page_dir']) ? $user->page['page_dir'] . '/' : '') . $user->page['page_name'] . (($user->page['query_string']) ? "?{$user->page['query_string']}" : '');
1570 $redirect = append_sid($redirect, false, false);
1572 // Add delimiter if not there...
1573 if (strpos($redirect, '?') === false)
1575 $redirect .= '?';
1578 // Strip vars...
1579 if ($strip_vars !== false && strpos($redirect, '?') !== false)
1581 if (!is_array($strip_vars))
1583 $strip_vars = array($strip_vars);
1586 $query = $_query = array();
1587 parse_str(substr($redirect, strpos($redirect, '?') + 1), $query);
1588 $redirect = substr($redirect, 0, strpos($redirect, '?'));
1590 // Strip the vars off
1591 foreach ($strip_vars as $strip)
1593 if (isset($query[$strip]))
1595 unset($query[$strip]);
1600 foreach ($query as $key => $value)
1602 $_query[] = $key . '=' . $value;
1604 $query = implode('&', $_query);
1606 $redirect .= ($query) ? '?' . $query : '';
1609 return $phpbb_root_path . str_replace('&', '&amp;', $redirect);
1613 * Meta refresh assignment
1615 function meta_refresh($time, $url)
1617 global $template;
1619 $template->assign_vars(array(
1620 'META' => '<meta http-equiv="refresh" content="' . $time . ';url=' . $url . '" />')
1624 // Message/Login boxes
1627 * Build Confirm box
1628 * @param boolean $check True for checking if confirmed (without any additional parameters) and false for displaying the confirm box
1629 * @param string $title Title/Message used for confirm box.
1630 * message text is _CONFIRM appended to title.
1631 * If title can not be found in user->lang a default one is displayed
1632 * If title_CONFIRM can not be found in user->lang the text given is used.
1633 * @param string $hidden Hidden variables
1634 * @param string $html_body Template used for confirm box
1635 * @param string $u_action Custom form action
1637 function confirm_box($check, $title = '', $hidden = '', $html_body = 'confirm_body.html', $u_action = '')
1639 global $user, $template, $db;
1640 global $phpEx, $phpbb_root_path;
1642 if (isset($_POST['cancel']))
1644 return false;
1647 $confirm = false;
1648 if (isset($_POST['confirm']))
1650 // language frontier
1651 if ($_POST['confirm'] == $user->lang['YES'])
1653 $confirm = true;
1657 if ($check && $confirm)
1659 $user_id = request_var('user_id', 0);
1660 $session_id = request_var('sess', '');
1661 $confirm_key = request_var('confirm_key', '');
1663 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'])
1665 return false;
1668 // Reset user_last_confirm_key
1669 $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = ''
1670 WHERE user_id = " . $user->data['user_id'];
1671 $db->sql_query($sql);
1673 return true;
1675 else if ($check)
1677 return false;
1680 $s_hidden_fields = build_hidden_fields(array(
1681 'user_id' => $user->data['user_id'],
1682 'sess' => $user->session_id,
1683 'sid' => $user->session_id)
1686 // generate activation key
1687 $confirm_key = gen_rand_string(10);
1689 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
1691 adm_page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
1693 else
1695 page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
1698 $template->set_filenames(array(
1699 'body' => $html_body)
1702 // If activation key already exist, we better do not re-use the key (something very strange is going on...)
1703 if (request_var('confirm_key', ''))
1705 // This should not occur, therefore we cancel the operation to safe the user
1706 return false;
1709 // re-add sid / transform & to &amp; for user->page (user->page is always using &)
1710 $use_page = ($u_action) ? $phpbb_root_path . $u_action : $phpbb_root_path . str_replace('&', '&amp;', $user->page['page']);
1711 $u_action = reapply_sid($use_page);
1712 $u_action .= ((strpos($u_action, '?') === false) ? '?' : '&amp;') . 'confirm_key=' . $confirm_key;
1714 $template->assign_vars(array(
1715 'MESSAGE_TITLE' => (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title],
1716 'MESSAGE_TEXT' => (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'],
1718 'YES_VALUE' => $user->lang['YES'],
1719 'S_CONFIRM_ACTION' => $u_action,
1720 'S_HIDDEN_FIELDS' => $hidden . $s_hidden_fields)
1723 $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = '" . $db->sql_escape($confirm_key) . "'
1724 WHERE user_id = " . $user->data['user_id'];
1725 $db->sql_query($sql);
1727 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
1729 adm_page_footer();
1731 else
1733 page_footer();
1738 * Generate login box or verify password
1740 function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = false, $s_display = true)
1742 global $db, $user, $template, $auth, $phpEx, $phpbb_root_path, $config;
1744 $err = '';
1746 // Make sure user->setup() has been called
1747 if (empty($user->lang))
1749 $user->setup();
1752 // Print out error if user tries to authenticate as an administrator without having the privileges...
1753 if ($admin && !$auth->acl_get('a_'))
1755 // Not authd
1756 // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
1757 if ($user->data['is_registered'])
1759 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
1761 trigger_error('NO_AUTH_ADMIN');
1764 if (isset($_POST['login']))
1766 $username = request_var('username', '');
1767 $password = request_var('password', '');
1768 $autologin = (!empty($_POST['autologin'])) ? true : false;
1769 $viewonline = (!empty($_POST['viewonline'])) ? 0 : 1;
1770 $admin = ($admin) ? 1 : 0;
1772 // Check if the supplied username is equal to the one stored within the database if re-authenticating
1773 if ($admin && strtolower($username) != strtolower($user->data['username']))
1775 // We log the attempt to use a different username...
1776 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
1777 trigger_error('NO_AUTH_ADMIN_USER_DIFFER');
1780 // If authentication is successful we redirect user to previous page
1781 $result = $auth->login($username, $password, $autologin, $viewonline, $admin);
1783 // If admin authentication and login, we will log if it was a success or not...
1784 // We also break the operation on the first non-success login - it could be argued that the user already knows
1785 if ($admin)
1787 if ($result['status'] == LOGIN_SUCCESS)
1789 add_log('admin', 'LOG_ADMIN_AUTH_SUCCESS');
1791 else
1793 // Only log the failed attempt if a real user tried to.
1794 // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
1795 if ($user->data['is_registered'])
1797 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
1802 // The result parameter is always an array, holding the relevant informations...
1803 if ($result['status'] == LOGIN_SUCCESS)
1805 $redirect = request_var('redirect', "{$phpbb_root_path}index.$phpEx");
1806 $message = ($l_success) ? $l_success : $user->lang['LOGIN_REDIRECT'];
1807 $l_redirect = ($admin) ? $user->lang['PROCEED_TO_ACP'] : (($redirect === "{$phpbb_root_path}index.$phpEx") ? $user->lang['RETURN_INDEX'] : $user->lang['RETURN_PAGE']);
1809 // append/replace SID (may change during the session for AOL users)
1810 $redirect = reapply_sid($redirect);
1812 meta_refresh(3, $redirect);
1813 trigger_error($message . '<br /><br />' . sprintf($l_redirect, '<a href="' . $redirect . '">', '</a>'));
1816 // Something failed, determine what...
1817 if ($result['status'] == LOGIN_BREAK)
1819 trigger_error($result['error_msg'], E_USER_ERROR);
1822 // Special cases... determine
1823 switch ($result['status'])
1825 case LOGIN_ERROR_ATTEMPTS:
1827 // Show confirm image
1828 $sql = 'DELETE FROM ' . CONFIRM_TABLE . "
1829 WHERE session_id = '" . $db->sql_escape($user->session_id) . "'
1830 AND confirm_type = " . CONFIRM_LOGIN;
1831 $db->sql_query($sql);
1833 // Generate code
1834 $code = gen_rand_string(mt_rand(5, 8));
1835 $confirm_id = md5(unique_id($user->ip));
1837 $sql = 'INSERT INTO ' . CONFIRM_TABLE . ' ' . $db->sql_build_array('INSERT', array(
1838 'confirm_id' => (string) $confirm_id,
1839 'session_id' => (string) $user->session_id,
1840 'confirm_type' => (int) CONFIRM_LOGIN,
1841 'code' => (string) $code)
1843 $db->sql_query($sql);
1845 $template->assign_vars(array(
1846 'S_CONFIRM_CODE' => true,
1847 'CONFIRM_ID' => $confirm_id,
1848 'CONFIRM_IMAGE' => '<img src="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=confirm&amp;id=' . $confirm_id . '&amp;type=' . CONFIRM_LOGIN) . '" alt="" title="" />',
1849 'L_LOGIN_CONFIRM_EXPLAIN' => sprintf($user->lang['LOGIN_CONFIRM_EXPLAIN'], '<a href="mailto:' . htmlentities($config['board_contact']) . '">', '</a>'),
1852 $err = $user->lang[$result['error_msg']];
1854 break;
1856 // Username, password, etc...
1857 default:
1858 $err = $user->lang[$result['error_msg']];
1860 // Assign admin contact to some error messages
1861 if ($result['error_msg'] == 'LOGIN_ERROR_USERNAME' || $result['error_msg'] == 'LOGIN_ERROR_PASSWORD')
1863 $err = (!$config['board_contact']) ? sprintf($user->lang[$result['error_msg']], '', '') : sprintf($user->lang[$result['error_msg']], '<a href="mailto:' . htmlentities($config['board_contact']) . '">', '</a>');
1865 break;
1869 if (!$redirect)
1871 // We just use what the session code determined...
1872 // If we are not within the admin directory we use the page dir...
1873 $redirect = '';
1875 if (!$admin)
1877 $redirect .= ($user->page['page_dir']) ? $user->page['page_dir'] . '/' : '';
1880 $redirect .= $user->page['page_name'] . (($user->page['query_string']) ? '?' . $user->page['query_string'] : '');
1883 $s_hidden_fields = build_hidden_fields(array('redirect' => $redirect, 'sid' => $user->session_id));
1885 $template->assign_vars(array(
1886 'LOGIN_ERROR' => $err,
1887 'LOGIN_EXPLAIN' => $l_explain,
1889 'U_SEND_PASSWORD' => ($config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') : '',
1890 'U_RESEND_ACTIVATION' => ($config['require_activation'] != USER_ACTIVATION_NONE && $config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=resend_act') : '',
1891 'U_TERMS_USE' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'),
1892 'U_PRIVACY' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'),
1894 'S_DISPLAY_FULL_LOGIN' => ($s_display) ? true : false,
1895 'S_LOGIN_ACTION' => (!$admin) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login') : append_sid("index.$phpEx"), // Needs to stay index.$phpEx because we are within the admin directory
1896 'S_HIDDEN_FIELDS' => $s_hidden_fields,
1898 'S_ADMIN_AUTH' => $admin,
1899 'USERNAME' => ($admin) ? $user->data['username'] : '')
1902 page_header($user->lang['LOGIN']);
1904 $template->set_filenames(array(
1905 'body' => 'login_body.html')
1907 make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx"));
1909 page_footer();
1913 * Generate forum login box
1915 function login_forum_box($forum_data)
1917 global $db, $config, $user, $template, $phpEx;
1919 $password = request_var('password', '');
1921 $sql = 'SELECT forum_id
1922 FROM ' . FORUMS_ACCESS_TABLE . '
1923 WHERE forum_id = ' . $forum_data['forum_id'] . '
1924 AND user_id = ' . $user->data['user_id'] . "
1925 AND session_id = '" . $db->sql_escape($user->session_id) . "'";
1926 $result = $db->sql_query($sql);
1927 $row = $db->sql_fetchrow($result);
1928 $db->sql_freeresult($result);
1930 if ($row)
1932 return true;
1935 if ($password)
1937 // Remove expired authorised sessions
1938 $sql = 'SELECT session_id
1939 FROM ' . SESSIONS_TABLE;
1940 $result = $db->sql_query($sql);
1942 if ($row = $db->sql_fetchrow($result))
1944 $sql_in = array();
1947 $sql_in[] = (string) $row['session_id'];
1949 while ($row = $db->sql_fetchrow($result));
1951 // Remove expired sessions
1952 $sql = 'DELETE FROM ' . FORUMS_ACCESS_TABLE . '
1953 WHERE ' . $db->sql_in_set('session_id', $sql_in, true);
1954 $db->sql_query($sql);
1956 $db->sql_freeresult($result);
1958 if ($password == $forum_data['forum_password'])
1960 $sql_ary = array(
1961 'forum_id' => (int) $forum_data['forum_id'],
1962 'user_id' => (int) $user->data['user_id'],
1963 'session_id' => (string) $user->session_id,
1966 $db->sql_query('INSERT INTO ' . FORUMS_ACCESS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1968 return true;
1971 $template->assign_var('LOGIN_ERROR', $user->lang['WRONG_PASSWORD']);
1974 page_header();
1976 $template->set_filenames(array(
1977 'body' => 'login_forum.html')
1980 page_footer();
1983 // Content related functions
1986 * Bump Topic Check - used by posting and viewtopic
1988 function bump_topic_allowed($forum_id, $topic_bumped, $last_post_time, $topic_poster, $last_topic_poster)
1990 global $config, $auth, $user;
1992 // Check permission and make sure the last post was not already bumped
1993 if (!$auth->acl_get('f_bump', $forum_id) || $topic_bumped)
1995 return false;
1998 // Check bump time range, is the user really allowed to bump the topic at this time?
1999 $bump_time = ($config['bump_type'] == 'm') ? $config['bump_interval'] * 60 : (($config['bump_type'] == 'h') ? $config['bump_interval'] * 3600 : $config['bump_interval'] * 86400);
2001 // Check bump time
2002 if ($last_post_time + $bump_time > time())
2004 return false;
2007 // Check bumper, only topic poster and last poster are allowed to bump
2008 if ($topic_poster != $user->data['user_id'] && $last_topic_poster != $user->data['user_id'] && !$auth->acl_get('m_', $forum_id))
2010 return false;
2013 // A bump time of 0 will completely disable the bump feature... not intended but might be useful.
2014 return $bump_time;
2018 * Generates a text with approx. the specified length which contains the specified words and their context
2020 * @param string $text The full text from which context shall be extracted
2021 * @param string $words An array of words which should be contained in the result, * is allowed as a wildcard
2022 * @param int $length The desired length of the resulting text, however the result might be shorter or longer than this value
2024 * @return string Context of the specified words seperated by "..."
2026 function get_context($text, $words, $length = 400)
2028 // first replace all whitespaces with single spaces
2029 $text = preg_replace('/\s+/', ' ', $text);
2031 $word_indizes = array();
2032 if (sizeof($words))
2034 $match = '';
2035 // find the starting indizes of all words
2036 foreach ($words as $word)
2038 if (preg_match('#(?: |^)(' . str_replace('\*', '\w*?', preg_quote($word, '#')) . ')(?: |$)#i', $text, $match))
2040 $pos = strpos($text, $match[1]);
2041 if ($pos !== false)
2043 $word_indizes[] = $pos;
2047 unset($match);
2049 if (sizeof($word_indizes))
2051 $word_indizes = array_unique($word_indizes);
2052 sort($word_indizes);
2054 $wordnum = sizeof($word_indizes);
2055 // number of characters on the right and left side of each word
2056 $sequence_length = (int) ($length / (2 * $wordnum)) - 2;
2057 $final_text = '';
2058 $word = $j = 0;
2059 $final_text_index = -1;
2061 // cycle through every character in the original text
2062 for ($i = $word_indizes[$word], $n = strlen($text); $i < $n; $i++)
2064 // if the current position is the start of one of the words then append $sequence_length characters to the final text
2065 if (isset($word_indizes[$word]) && ($i == $word_indizes[$word]))
2067 if ($final_text_index < $i - $sequence_length - 1)
2069 $final_text .= '... ' . preg_replace('#^([^ ]*)#', '', substr($text, $i - $sequence_length, $sequence_length));
2071 else
2073 // if the final text is already nearer to the current word than $sequence_length we only append the text
2074 // from its current index on and distribute the unused length to all other sequenes
2075 $sequence_length += (int) (($final_text_index - $i + $sequence_length + 1) / (2 * $wordnum));
2076 $final_text .= substr($text, $final_text_index + 1, $i - $final_text_index - 1);
2078 $final_text_index = $i - 1;
2080 // add the following characters to the final text (see below)
2081 $word++;
2082 $j = 1;
2085 if ($j > 0)
2087 // add the character to the final text and increment the sequence counter
2088 $final_text .= $text[$i];
2089 $final_text_index++;
2090 $j++;
2092 // if this is a whitespace then check whether we are done with this sequence
2093 if ($text[$i] == ' ')
2095 // only check whether we have to exit the context generation completely if we haven't already reached the end anyway
2096 if ($i + 4 < $n)
2098 if (($j > $sequence_length && $word >= $wordnum) || strlen($final_text) > $length)
2100 $final_text .= ' ...';
2101 break;
2104 else
2106 // make sure the text really reaches the end
2107 $j -= 4;
2110 // stop context generation and wait for the next word
2111 if ($j > $sequence_length)
2113 $j = 0;
2118 return $final_text;
2122 if (!sizeof($words) || !sizeof($word_indizes))
2124 return (strlen($text) >= $length + 3) ? substr($text, 0, $length) . '...' : $text;
2129 * Decode text whereby text is coming from the db and expected to be pre-parsed content
2130 * We are placing this outside of the message parser because we are often in need of it...
2132 function decode_message(&$message, $bbcode_uid = '')
2134 global $config;
2136 if ($bbcode_uid)
2138 $match = array('<br />', "[/*:m:$bbcode_uid]", ":u:$bbcode_uid", ":o:$bbcode_uid", ":$bbcode_uid");
2139 $replace = array("\n", '', '', '', '');
2141 else
2143 $match = array('<br />');
2144 $replace = array("\n");
2147 $message = str_replace($match, $replace, $message);
2149 $match = array(
2150 '#<!\-\- e \-\-><a href="mailto:(.*?)">.*?</a><!\-\- e \-\->#',
2151 '#<!\-\- m \-\-><a href="(.*?)" target="_blank">.*?</a><!\-\- m \-\->#',
2152 '#<!\-\- w \-\-><a href="http:\/\/(.*?)" target="_blank">.*?</a><!\-\- w \-\->#',
2153 '#<!\-\- l \-\-><a href="(.*?)">.*?</a><!\-\- l \-\->#',
2154 '#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#',
2155 '#<!\-\- .*? \-\->#s',
2156 '#<.*?>#s'
2159 $replace = array('\1', '\1', '\1', '\1', '\1', '', '');
2161 $message = preg_replace($match, $replace, $message);
2163 return;
2167 * Strips all bbcode from a text and returns the plain content
2169 function strip_bbcode(&$text, $uid = '')
2171 if (!$uid)
2173 $uid = '[0-9a-z]{5,}';
2176 $text = preg_replace("#\[\/?[a-z0-9\*\+\-]+(?:=.*?)?(?::[a-z])?(\:?$uid)\]#", ' ', $text);
2178 $match = array(
2179 '#<!\-\- e \-\-><a href="mailto:(.*?)">.*?</a><!\-\- e \-\->#',
2180 '#<!\-\- m \-\-><a href="(.*?)" target="_blank">.*?</a><!\-\- m \-\->#',
2181 '#<!\-\- w \-\-><a href="http:\/\/(.*?)" target="_blank">.*?</a><!\-\- w \-\->#',
2182 '#<!\-\- l \-\-><a href="(.*?)">.*?</a><!\-\- l \-\->#',
2183 '#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#',
2184 '#<!\-\- .*? \-\->#s',
2185 '#<.*?>#s'
2188 $replace = array('\1', '\1', '\1', '\1', '\1', '', '');
2190 $text = preg_replace($match, $replace, $text);
2194 * For display of custom parsed text on user-facing pages
2195 * Expects $text to be the value directly from the database (stored value)
2197 function generate_text_for_display($text, $uid, $bitfield, $flags)
2199 static $bbcode;
2201 if (!$text)
2203 return '';
2206 $text = str_replace("\n", '<br />', censor_text($text));
2208 // Parse bbcode if bbcode uid stored and bbcode enabled
2209 if ($uid && ($flags & 1))
2211 if (!class_exists('bbcode'))
2213 global $phpbb_root_path, $phpEx;
2214 include_once($phpbb_root_path . 'includes/bbcode.' . $phpEx);
2217 if (empty($bbcode))
2219 $bbcode = new bbcode($bitfield);
2221 else
2223 $bbcode->bbcode($bitfield);
2226 $bbcode->bbcode_second_pass($text, $uid);
2229 $text = smiley_text($text, !($flags & 2));
2231 return $text;
2235 * For parsing custom parsed text to be stored within the database.
2236 * This function additionally returns the uid and bitfield that needs to be stored.
2237 * Expects $text to be the value directly from request_var() and in it's non-parsed form
2239 function generate_text_for_storage(&$text, &$uid, &$bitfield, &$flags, $allow_bbcode = false, $allow_urls = false, $allow_smilies = false)
2241 global $phpbb_root_path, $phpEx;
2243 $uid = '';
2244 $bitfield = '';
2246 if (!$text)
2248 return;
2251 if (!class_exists('parse_message'))
2253 include_once($phpbb_root_path . 'includes/message_parser.' . $phpEx);
2256 $message_parser = new parse_message($text);
2257 $message_parser->parse($allow_bbcode, $allow_urls, $allow_smilies);
2259 $text = $message_parser->message;
2260 $uid = $message_parser->bbcode_uid;
2262 // If the bbcode_bitfield is empty, there is no need for the uid to be stored.
2263 if (!$message_parser->bbcode_bitfield)
2265 $uid = '';
2268 $flags = (($allow_bbcode) ? 1 : 0) + (($allow_smilies) ? 2 : 0) + (($allow_urls) ? 4 : 0);
2269 $bitfield = $message_parser->bbcode_bitfield;
2271 return;
2275 * For decoding custom parsed text for edits as well as extracting the flags
2276 * Expects $text to be the value directly from the database (pre-parsed content)
2278 function generate_text_for_edit($text, $uid, $flags)
2280 global $phpbb_root_path, $phpEx;
2282 decode_message($text, $uid);
2284 return array(
2285 'allow_bbcode' => ($flags & 1) ? 1 : 0,
2286 'allow_smilies' => ($flags & 2) ? 1 : 0,
2287 'allow_urls' => ($flags & 4) ? 1 : 0,
2288 'text' => $text
2293 * make_clickable function
2295 * Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx.
2296 * Cuts down displayed size of link if over 50 chars, turns absolute links
2297 * into relative versions when the server/script path matches the link
2299 function make_clickable($text, $server_url = false)
2301 if ($server_url === false)
2303 $server_url = generate_board_url();
2306 static $magic_url_match;
2307 static $magic_url_replace;
2309 if (!is_array($magic_url_match))
2311 $magic_url_match = $magic_url_replace = array();
2312 // Be sure to not let the matches cross over. ;)
2314 // relative urls for this board
2315 $magic_url_match[] = '#(^|[\n ]|\()(' . preg_quote($server_url, '#') . ')/(([^[ \t\n\r<"\'\)&]+|&(?!lt;|quot;))*)#ie';
2316 $magic_url_replace[] = "'\$1<!-- l --><a href=\"\$2/' . preg_replace('/(&amp;|\?)sid=[0-9a-f]{32}/', '\\1', '\$3') . '\">' . preg_replace('/(&amp;|\?)sid=[0-9a-f]{32}/', '\\1', '\$3') . '</a><!-- l -->'";
2318 // matches a xxxx://aaaaa.bbb.cccc. ...
2319 $magic_url_match[] = '#(^|[\n ]|\()([\w]+:/{2}.*?([^[ \t\n\r<"\'\)&]+|&(?!lt;|quot;))*)#ie';
2320 $magic_url_replace[] = "'\$1<!-- m --><a href=\"\$2\" target=\"_blank\">' . ((strlen('\$2') > 55) ? substr(str_replace('&amp;', '&', '\$2'), 0, 39) . ' ... ' . substr(str_replace('&amp;', '&', '\$2'), -10) : '\$2') . '</a><!-- m -->'";
2322 // matches a "www.xxxx.yyyy[/zzzz]" kinda lazy URL thing
2323 $magic_url_match[] = '#(^|[\n ]|\()(w{3}\.[\w\-]+\.[\w\-.\~]+(?:[^[ \t\n\r<"\'\)&]+|&(?!lt;|quot;))*)#ie';
2324 $magic_url_replace[] = "'\$1<!-- w --><a href=\"http://\$2\" target=\"_blank\">' . ((strlen('\$2') > 55) ? substr(str_replace('&amp;', '&', '\$2'), 0, 39) . ' ... ' . substr(str_replace('&amp;', '&', '\$2'), -10) : '\$2') . '</a><!-- w -->'";
2326 // matches an email@domain type address at the start of a line, or after a space or after what might be a BBCode.
2327 $magic_url_match[] = '/(^|[\n ]|\()(' . get_preg_expression('email') . ')/ie';
2328 $magic_url_replace[] = "'\$1<!-- e --><a href=\"mailto:\$2\">' . ((strlen('\$2') > 55) ? substr('\$2', 0, 39) . ' ... ' . substr('\$2', -10) : '\$2') . '</a><!-- e -->'";
2331 return preg_replace($magic_url_match, $magic_url_replace, $text);
2335 * Censoring
2337 function censor_text($text)
2339 global $censors, $user, $cache;
2341 if (!isset($censors))
2343 $censors = array();
2345 if ($user->optionget('viewcensors'))
2347 $cache->obtain_word_list($censors);
2351 if (sizeof($censors) && $user->optionget('viewcensors'))
2353 return preg_replace($censors['match'], $censors['replace'], $text);
2356 return $text;
2360 * Smiley processing
2362 function smiley_text($text, $force_option = false)
2364 global $config, $user, $phpbb_root_path;
2366 if ($force_option || !$config['allow_smilies'] || !$user->optionget('viewsmilies'))
2368 return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#', '\1', $text);
2370 else
2372 return str_replace('<img src="{SMILIES_PATH}', '<img src="' . $phpbb_root_path . $config['smilies_path'], $text);
2377 * Inline Attachment processing
2379 function parse_inline_attachments(&$text, &$attachments, &$update_count, $forum_id = 0, $preview = false)
2381 global $config, $user;
2383 $attachments = display_attachments($forum_id, NULL, $attachments, $update_count, false, true);
2384 $tpl_size = sizeof($attachments);
2386 $unset_tpl = array();
2388 preg_match_all('#<!\-\- ia([0-9]+) \-\->(.*?)<!\-\- ia\1 \-\->#', $text, $matches, PREG_PATTERN_ORDER);
2390 $replace = array();
2391 foreach ($matches[0] as $num => $capture)
2393 // Flip index if we are displaying the reverse way
2394 $index = ($config['display_order']) ? ($tpl_size-($matches[1][$num] + 1)) : $matches[1][$num];
2396 $replace['from'][] = $matches[0][$num];
2397 $replace['to'][] = (isset($attachments[$index])) ? $attachments[$index] : sprintf($user->lang['MISSING_INLINE_ATTACHMENT'], $matches[2][array_search($index, $matches[1])]);
2399 $unset_tpl[] = $index;
2402 if (isset($replace['from']))
2404 $text = str_replace($replace['from'], $replace['to'], $text);
2407 return array_unique($unset_tpl);
2411 * Check if extension is allowed to be posted within forum X (forum_id 0 == private messaging)
2413 function extension_allowed($forum_id, $extension, &$extensions)
2415 if (!sizeof($extensions))
2417 global $cache;
2419 $extensions = array();
2420 $cache->obtain_attach_extensions($extensions);
2423 if (!isset($extensions['_allowed_'][$extension]))
2425 return false;
2428 $check = $extensions['_allowed_'][$extension];
2430 if (is_array($check))
2432 // Check for private messaging AND all forums allowed
2433 if (sizeof($check) == 1 && $check[0] == 0)
2435 return true;
2438 return (!in_array($forum_id, $check)) ? false : true;
2441 return ($forum_id == 0) ? false : true;
2444 // Little helpers
2447 * Little helper for the build_hidden_fields function
2449 function _build_hidden_fields($key, $value, $specialchar)
2451 $hidden_fields = '';
2453 if (!is_array($value))
2455 $key = ($specialchar) ? htmlspecialchars($key) : $key;
2456 $value = ($specialchar) ? htmlspecialchars($value) : $value;
2458 $hidden_fields .= '<input type="hidden" name="' . $key . '" value="' . $value . '" />' . "\n";
2460 else
2462 foreach ($value as $_key => $_value)
2464 $hidden_fields .= _build_hidden_fields($key . '[' . $_key . ']', $_value, $specialchar);
2468 return $hidden_fields;
2472 * Build simple hidden fields from array
2474 function build_hidden_fields($field_ary, $specialchar = false)
2476 $s_hidden_fields = '';
2478 foreach ($field_ary as $name => $vars)
2480 $s_hidden_fields .= _build_hidden_fields($name, $vars, $specialchar);
2483 return $s_hidden_fields;
2487 * Parse cfg file
2489 function parse_cfg_file($filename, $lines = false)
2491 $parsed_items = array();
2493 if ($lines === false)
2495 $lines = file($filename);
2498 foreach ($lines as $line)
2500 $line = trim($line);
2502 if (!$line || $line{0} == '#' || ($delim_pos = strpos($line, '=')) === false)
2504 continue;
2507 // Determine first occurrence, since in values the equal sign is allowed
2508 $key = strtolower(trim(substr($line, 0, $delim_pos)));
2509 $value = trim(substr($line, $delim_pos + 1));
2511 if (in_array($value, array('off', 'false', '0')))
2513 $value = false;
2515 else if (in_array($value, array('on', 'true', '1')))
2517 $value = true;
2519 else if (!trim($value))
2521 $value = '';
2523 else if (($value{0} == "'" && $value{sizeof($value)-1} == "'") || ($value{0} == '"' && $value{sizeof($value)-1} == '"'))
2525 $value = substr($value, 1, sizeof($value)-2);
2528 $parsed_items[$key] = $value;
2531 return $parsed_items;
2535 * Add log event
2537 function add_log()
2539 global $db, $user;
2541 $args = func_get_args();
2543 $mode = array_shift($args);
2544 $reportee_id = ($mode == 'user') ? intval(array_shift($args)) : '';
2545 $forum_id = ($mode == 'mod') ? intval(array_shift($args)) : '';
2546 $topic_id = ($mode == 'mod') ? intval(array_shift($args)) : '';
2547 $action = array_shift($args);
2548 $data = (!sizeof($args)) ? '' : serialize($args);
2550 $sql_ary = array(
2551 'user_id' => (empty($user->data)) ? ANONYMOUS : $user->data['user_id'],
2552 'log_ip' => $user->ip,
2553 'log_time' => time(),
2554 'log_operation' => $action,
2555 'log_data' => $data,
2558 switch ($mode)
2560 case 'admin':
2561 $sql_ary['log_type'] = LOG_ADMIN;
2562 break;
2564 case 'mod':
2565 $sql_ary += array(
2566 'log_type' => LOG_MOD,
2567 'forum_id' => $forum_id,
2568 'topic_id' => $topic_id
2570 break;
2572 case 'user':
2573 $sql_ary += array(
2574 'log_type' => LOG_USERS,
2575 'reportee_id' => $reportee_id
2577 break;
2579 case 'critical':
2580 $sql_ary['log_type'] = LOG_CRITICAL;
2581 break;
2583 default:
2584 return false;
2587 $db->sql_query('INSERT INTO ' . LOG_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
2589 return $db->sql_nextid();
2593 * Return a nicely formatted backtrace (parts from the php manual by diz at ysagoon dot com)
2595 function get_backtrace()
2597 global $phpbb_root_path;
2599 $output = '<div style="font-family: monospace;">';
2600 $backtrace = debug_backtrace();
2601 $path = phpbb_realpath($phpbb_root_path);
2603 foreach ($backtrace as $number => $trace)
2605 // We skip the first one, because it only shows this file/function
2606 if ($number == 0)
2608 continue;
2611 // Strip the current directory from path
2612 $trace['file'] = str_replace(array($path, '\\'), array('', '/'), $trace['file']);
2613 $trace['file'] = substr($trace['file'], 1);
2614 $args = array();
2616 // If include/require/include_once is not called, do not show arguments - they may contain sensible informations
2617 if (!in_array($trace['function'], array('include', 'require', 'include_once')))
2619 unset($trace['args']);
2621 else
2623 // Path...
2624 if (!empty($trace['args'][0]))
2626 $argument = htmlspecialchars($trace['args'][0]);
2627 $argument = str_replace(array($path, '\\'), array('', '/'), $argument);
2628 $argument = substr($argument, 1);
2629 $args[] = "'{$argument}'";
2633 $trace['class'] = (!isset($trace['class'])) ? '' : $trace['class'];
2634 $trace['type'] = (!isset($trace['type'])) ? '' : $trace['type'];
2636 $output .= '<br />';
2637 $output .= '<b>FILE:</b> ' . htmlspecialchars($trace['file']) . '<br />';
2638 $output .= '<b>LINE:</b> ' . $trace['line'] . '<br />';
2640 $output .= '<b>CALL:</b> ' . htmlspecialchars($trace['class'] . $trace['type'] . $trace['function']) . '(' . ((sizeof($args)) ? implode(', ', $args) : '') . ')<br />';
2642 $output .= '</div>';
2643 return $output;
2647 * This function returns a regular expression pattern for commonly used expressions
2648 * Use with / as delimiter
2649 * mode can be: email|
2651 function get_preg_expression($mode)
2653 switch ($mode)
2655 case 'email':
2656 return '[a-z0-9&\'\.\-_\+]+@[a-z0-9\-]+\.([a-z0-9\-]+\.)*[a-z]+';
2657 break;
2660 return '';
2664 * Truncates string while retaining special characters if going over the max length
2665 * The default max length is 60 at the moment
2667 function truncate_string($string, $max_length = 60)
2669 $chars = array();
2671 // split the multibyte characters first
2672 $string_ary = preg_split('#(&\#[0-9]+;)#', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
2674 // Now go through the array and split the other characters
2675 foreach ($string_ary as $key => $value)
2677 if (strpos($value, '&#') === 0)
2679 $chars[] = $value;
2680 continue;
2683 // decode html entities and put them back later
2684 $_chars = str_split(html_entity_decode($value));
2685 $chars = array_merge($chars, array_map('htmlspecialchars', $_chars));
2688 // Now check the length ;)
2689 if (sizeof($chars) <= $max_length)
2691 return $string;
2694 // Cut off the last elements from the array
2695 return implode('', array_slice($chars, 0, $max_length));
2698 // Handler, header and footer
2701 * Error and message handler, call with trigger_error if reqd
2703 function msg_handler($errno, $msg_text, $errfile, $errline)
2705 global $cache, $db, $auth, $template, $config, $user;
2706 global $phpEx, $phpbb_root_path, $starttime, $msg_title, $msg_long_text;
2708 // Message handler is stripping text. In case we need it, we are possible to define long text...
2709 if (isset($msg_long_text) && $msg_long_text && !$msg_text)
2711 $msg_text = $msg_long_text;
2714 switch ($errno)
2716 case E_NOTICE:
2717 case E_WARNING:
2719 // Check the error reporting level and return if the error level does not match
2720 // Additionally do not display notices if we suppress them via @
2721 // If DEBUG_EXTRA is defined the default level is E_ALL
2722 if (($errno & ((defined('DEBUG_EXTRA') && error_reporting()) ? E_ALL : error_reporting())) == 0)
2724 return;
2728 * @todo Think about removing the if-condition within the final product, since we no longer enable DEBUG by default and we will maybe adjust the error reporting level
2730 if (defined('DEBUG'))
2732 if (strpos($errfile, 'cache') === false && strpos($errfile, 'template.') === false)
2734 // remove complete path to installation, with the risk of changing backslashes meant to be there
2735 $errfile = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $errfile);
2736 $msg_text = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $msg_text);
2738 echo '<b>[phpBB Debug] PHP Notice</b>: in file <b>' . $errfile . '</b> on line <b>' . $errline . '</b>: <b>' . $msg_text . '</b><br />' . "\n";
2742 break;
2744 case E_USER_ERROR:
2746 garbage_collection();
2748 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
2749 echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">';
2750 echo '<head>';
2751 echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
2752 echo '<title>' . $msg_title . '</title>';
2753 echo '<link href="' . $phpbb_root_path . 'adm/style/admin.css" rel="stylesheet" type="text/css" media="screen" />';
2754 echo '</head>';
2755 echo '<body id="errorpage">';
2756 echo '<div id="wrap">';
2757 echo ' <div id="page-header">';
2758 echo ' <a href="' . $phpbb_root_path . '">Return to forum index</a>';
2759 echo ' </div>';
2760 echo ' <div id="page-body">';
2761 echo ' <div class="panel">';
2762 echo ' <span class="corners-top"><span></span></span>';
2763 echo ' <div id="content">';
2764 echo ' <h1>General Error</h1>';
2766 echo ' <h2>' . $msg_text . '</h2>';
2768 if (!empty($config['board_contact']))
2770 echo ' <p>Please notify the board administrator or webmaster : <a href="mailto:' . $config['board_contact'] . '">' . $config['board_contact'] . '</a></p>';
2773 echo ' </div>';
2774 echo ' <span class="corners-bottom"><span></span></span>';
2775 echo ' </div>';
2776 echo ' </div>';
2777 echo ' <div id="page-footer">';
2778 echo ' Powered by phpBB &copy; ' . date('Y') . ' <a href="http://www.phpbb.com/">phpBB Group</a>';
2779 echo ' </div>';
2780 echo '</div>';
2781 echo '</body>';
2782 echo '</html>';
2784 exit;
2785 break;
2787 case E_USER_WARNING:
2788 case E_USER_NOTICE:
2790 define('IN_ERROR_HANDLER', true);
2792 if (empty($user->data))
2794 $user->session_begin();
2797 // We re-init the auth array to get correct results on login/logout
2798 $auth->acl($user->data);
2800 if (empty($user->lang))
2802 $user->setup();
2805 $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text;
2806 $msg_title = (!isset($msg_title)) ? $user->lang['INFORMATION'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title);
2808 if (!defined('HEADER_INC'))
2810 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
2812 adm_page_header($msg_title);
2814 else
2816 page_header($msg_title);
2820 $template->set_filenames(array(
2821 'body' => 'message_body.html')
2824 $template->assign_vars(array(
2825 'MESSAGE_TITLE' => $msg_title,
2826 'MESSAGE_TEXT' => $msg_text,
2827 'S_USER_WARNING' => ($errno == E_USER_WARNING) ? true : false,
2828 'S_USER_NOTICE' => ($errno == E_USER_NOTICE) ? true : false)
2831 // We do not want the cron script to be called on error messages
2832 define('IN_CRON', true);
2834 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
2836 adm_page_footer();
2838 else
2840 page_footer();
2843 exit;
2844 break;
2849 * Generate page header
2851 function page_header($page_title = '', $display_online_list = true)
2853 global $db, $config, $template, $SID, $_SID, $user, $auth, $phpEx, $phpbb_root_path;
2855 if (defined('HEADER_INC'))
2857 return;
2860 define('HEADER_INC', true);
2862 // gzip_compression
2863 if ($config['gzip_compress'])
2865 if (@extension_loaded('zlib') && !headers_sent())
2867 ob_start('ob_gzhandler');
2871 // Generate logged in/logged out status
2872 if ($user->data['user_id'] != ANONYMOUS)
2874 $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=logout');
2875 $l_login_logout = sprintf($user->lang['LOGOUT_USER'], $user->data['username']);
2877 else
2879 $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login');
2880 $l_login_logout = $user->lang['LOGIN'];
2883 // Last visit date/time
2884 $s_last_visit = ($user->data['user_id'] != ANONYMOUS) ? $user->format_date($user->data['session_last_visit']) : '';
2886 // Get users online list ... if required
2887 $l_online_users = $online_userlist = $l_online_record = '';
2889 if ($config['load_online'] && $config['load_online_time'] && $display_online_list)
2891 $userlist_ary = $userlist_visible = array();
2892 $logged_visible_online = $logged_hidden_online = $guests_online = $prev_user_id = 0;
2893 $prev_session_ip = $reading_sql = '';
2895 if (!empty($_REQUEST['f']))
2897 $f = request_var('f', 0);
2899 // Do not change this (it is defined as _f_={forum_id}x within session.php)
2900 $reading_sql = " AND s.session_page LIKE '%\_f\_={$f}x%'";
2902 // Specify escape character for MSSQL
2903 if (SQL_LAYER == 'mssql' || SQL_LAYER == 'mssql_odbc')
2905 $reading_sql .= " ESCAPE '\\'";
2909 // Get number of online guests
2910 if (!$config['load_online_guests'])
2912 $sql = 'SELECT COUNT(DISTINCT s.session_ip) as num_guests
2913 FROM ' . SESSIONS_TABLE . ' s
2914 WHERE s.session_user_id = ' . ANONYMOUS . '
2915 AND s.session_time >= ' . (time() - ($config['load_online_time'] * 60)) .
2916 $reading_sql;
2917 $result = $db->sql_query($sql);
2918 $guests_online = (int) $db->sql_fetchfield('num_guests');
2919 $db->sql_freeresult($result);
2922 $sql = 'SELECT u.username, u.user_id, u.user_type, u.user_allow_viewonline, u.user_colour, s.session_ip, s.session_viewonline
2923 FROM ' . USERS_TABLE . ' u, ' . SESSIONS_TABLE . ' s
2924 WHERE s.session_time >= ' . (time() - (intval($config['load_online_time']) * 60)) .
2925 $reading_sql .
2926 ((!$config['load_online_guests']) ? ' AND s.session_user_id <> ' . ANONYMOUS : '') . '
2927 AND u.user_id = s.session_user_id
2928 ORDER BY u.username ASC, s.session_ip ASC';
2929 $result = $db->sql_query($sql);
2931 while ($row = $db->sql_fetchrow($result))
2933 // User is logged in and therefore not a guest
2934 if ($row['user_id'] != ANONYMOUS)
2936 // Skip multiple sessions for one user
2937 if ($row['user_id'] != $prev_user_id)
2939 if ($row['user_colour'])
2941 $user_colour = ' style="color:#' . $row['user_colour'] . '"';
2942 $row['username'] = '<strong>' . $row['username'] . '</strong>';
2944 else
2946 $user_colour = '';
2949 if ($row['user_allow_viewonline'] && $row['session_viewonline'])
2951 $user_online_link = $row['username'];
2952 $logged_visible_online++;
2954 else
2956 $user_online_link = '<em>' . $row['username'] . '</em>';
2957 $logged_hidden_online++;
2960 if (($row['user_allow_viewonline'] && $row['session_viewonline']) || $auth->acl_get('u_viewonline'))
2962 if ($row['user_type'] <> USER_IGNORE)
2964 $user_online_link = '<a href="' . append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=viewprofile&amp;u=' . $row['user_id']) . '"' . $user_colour . '>' . $user_online_link . '</a>';
2966 else
2968 $user_online_link = ($user_colour) ? '<span' . $user_colour . '>' . $user_online_link . '</span>' : $user_online_link;
2971 $online_userlist .= ($online_userlist != '') ? ', ' . $user_online_link : $user_online_link;
2975 $prev_user_id = $row['user_id'];
2977 else
2979 // Skip multiple sessions for one user
2980 if ($row['session_ip'] != $prev_session_ip)
2982 $guests_online++;
2986 $prev_session_ip = $row['session_ip'];
2988 $db->sql_freeresult($result);
2990 if (!$online_userlist)
2992 $online_userlist = $user->lang['NO_ONLINE_USERS'];
2995 if (empty($_REQUEST['f']))
2997 $online_userlist = $user->lang['REGISTERED_USERS'] . ' ' . $online_userlist;
2999 else
3001 $l_online = ($guests_online == 1) ? $user->lang['BROWSING_FORUM_GUEST'] : $user->lang['BROWSING_FORUM_GUESTS'];
3002 $online_userlist = sprintf($l_online, $online_userlist, $guests_online);
3005 $total_online_users = $logged_visible_online + $logged_hidden_online + $guests_online;
3007 if ($total_online_users > $config['record_online_users'])
3009 set_config('record_online_users', $total_online_users, true);
3010 set_config('record_online_date', time(), true);
3013 // Build online listing
3014 $vars_online = array(
3015 'ONLINE' => array('total_online_users', 'l_t_user_s'),
3016 'REG' => array('logged_visible_online', 'l_r_user_s'),
3017 'HIDDEN' => array('logged_hidden_online', 'l_h_user_s'),
3018 'GUEST' => array('guests_online', 'l_g_user_s')
3021 foreach ($vars_online as $l_prefix => $var_ary)
3023 switch (${$var_ary[0]})
3025 case 0:
3026 ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_ZERO_TOTAL'];
3027 break;
3029 case 1:
3030 ${$var_ary[1]} = $user->lang[$l_prefix . '_USER_TOTAL'];
3031 break;
3033 default:
3034 ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_TOTAL'];
3035 break;
3038 unset($vars_online);
3040 $l_online_users = sprintf($l_t_user_s, $total_online_users);
3041 $l_online_users .= sprintf($l_r_user_s, $logged_visible_online);
3042 $l_online_users .= sprintf($l_h_user_s, $logged_hidden_online);
3043 $l_online_users .= sprintf($l_g_user_s, $guests_online);
3045 $l_online_record = sprintf($user->lang['RECORD_ONLINE_USERS'], $config['record_online_users'], $user->format_date($config['record_online_date']));
3047 $l_online_time = ($config['load_online_time'] == 1) ? 'VIEW_ONLINE_TIME' : 'VIEW_ONLINE_TIMES';
3048 $l_online_time = sprintf($user->lang[$l_online_time], $config['load_online_time']);
3050 else
3052 $l_online_time = '';
3055 $l_privmsgs_text = $l_privmsgs_text_unread = '';
3056 $s_privmsg_new = false;
3058 // Obtain number of new private messages if user is logged in
3059 if (isset($user->data['is_registered']) && $user->data['is_registered'])
3061 if ($user->data['user_new_privmsg'])
3063 $l_message_new = ($user->data['user_new_privmsg'] == 1) ? $user->lang['NEW_PM'] : $user->lang['NEW_PMS'];
3064 $l_privmsgs_text = sprintf($l_message_new, $user->data['user_new_privmsg']);
3066 if (!$user->data['user_last_privmsg'] || $user->data['user_last_privmsg'] > $user->data['session_last_visit'])
3068 $sql = 'UPDATE ' . USERS_TABLE . '
3069 SET user_last_privmsg = ' . $user->data['session_last_visit'] . '
3070 WHERE user_id = ' . $user->data['user_id'];
3071 $db->sql_query($sql);
3073 $s_privmsg_new = true;
3075 else
3077 $s_privmsg_new = false;
3080 else
3082 $l_privmsgs_text = $user->lang['NO_NEW_PM'];
3083 $s_privmsg_new = false;
3086 $l_privmsgs_text_unread = '';
3088 if ($user->data['user_unread_privmsg'] && $user->data['user_unread_privmsg'] != $user->data['user_new_privmsg'])
3090 $l_message_unread = ($user->data['user_unread_privmsg'] == 1) ? $user->lang['UNREAD_PM'] : $user->lang['UNREAD_PMS'];
3091 $l_privmsgs_text_unread = sprintf($l_message_unread, $user->data['user_unread_privmsg']);
3095 // Which timezone?
3096 $tz = ($user->data['user_id'] != ANONYMOUS) ? strval(doubleval($user->data['user_timezone'])) : strval(doubleval($config['board_timezone']));
3098 // The following assigns all _common_ variables that may be used at any point in a template.
3099 $template->assign_vars(array(
3100 'SITENAME' => $config['sitename'],
3101 'SITE_DESCRIPTION' => $config['site_desc'],
3102 'PAGE_TITLE' => $page_title,
3103 'SCRIPT_NAME' => str_replace('.' . $phpEx, '', $user->page['page_name']),
3104 'LAST_VISIT_DATE' => sprintf($user->lang['YOU_LAST_VISIT'], $s_last_visit),
3105 'CURRENT_TIME' => sprintf($user->lang['CURRENT_TIME'], $user->format_date(time(), false, true)),
3106 'TOTAL_USERS_ONLINE' => $l_online_users,
3107 'LOGGED_IN_USER_LIST' => $online_userlist,
3108 'RECORD_USERS' => $l_online_record,
3109 'PRIVATE_MESSAGE_INFO' => $l_privmsgs_text,
3110 'PRIVATE_MESSAGE_INFO_UNREAD' => $l_privmsgs_text_unread,
3112 'SID' => $SID,
3113 '_SID' => $_SID,
3114 'SESSION_ID' => $user->session_id,
3115 'ROOT_PATH' => $phpbb_root_path,
3117 'L_LOGIN_LOGOUT' => $l_login_logout,
3118 'L_INDEX' => $user->lang['FORUM_INDEX'],
3119 'L_ONLINE_EXPLAIN' => $l_online_time,
3121 'U_PRIVATEMSGS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=inbox'),
3122 'U_RETURN_INBOX' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=inbox'),
3123 'UA_RETURN_INBOX' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox', false),
3124 'U_POPUP_PM' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=popup'),
3125 'UA_POPUP_PM' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=popup', false),
3126 'U_MEMBERLIST' => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
3127 'U_MEMBERSLIST' => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
3128 'U_VIEWONLINE' => append_sid("{$phpbb_root_path}viewonline.$phpEx"),
3129 'U_LOGIN_LOGOUT' => $u_login_logout,
3130 'U_INDEX' => append_sid("{$phpbb_root_path}index.$phpEx"),
3131 'U_SEARCH' => append_sid("{$phpbb_root_path}search.$phpEx"),
3132 'U_REGISTER' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register'),
3133 'U_PROFILE' => append_sid("{$phpbb_root_path}ucp.$phpEx"),
3134 'U_MODCP' => append_sid("{$phpbb_root_path}mcp.$phpEx", false, true, $user->session_id),
3135 'U_FAQ' => append_sid("{$phpbb_root_path}faq.$phpEx"),
3136 'U_SEARCH_SELF' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=egosearch'),
3137 'U_SEARCH_NEW' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=newposts'),
3138 'U_SEARCH_UNANSWERED' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unanswered'),
3139 'U_SEARCH_ACTIVE_TOPICS'=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=active_topics'),
3140 'U_DELETE_COOKIES' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=delete_cookies'),
3141 'U_TEAM' => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=leaders'),
3142 'U_RESTORE_PERMISSIONS' => ($user->data['user_perm_from'] && $auth->acl_get('a_switchperm')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=restore_perm') : '',
3144 'S_USER_LOGGED_IN' => ($user->data['user_id'] != ANONYMOUS) ? true : false,
3145 'S_BOARD_DISABLED' => ($config['board_disable'] && !defined('IN_LOGIN') && $auth->acl_gets('a_', 'm_')) ? true : false,
3146 'S_REGISTERED_USER' => $user->data['is_registered'],
3147 'S_IS_BOT' => $user->data['is_bot'],
3148 'S_USER_PM_POPUP' => $user->optionget('popuppm'),
3149 'S_USER_LANG' => $user->data['user_lang'],
3150 'S_USER_BROWSER' => (isset($user->data['session_browser'])) ? $user->data['session_browser'] : $user->lang['UNKNOWN_BROWSER'],
3151 'S_USERNAME' => $user->data['username'],
3152 'S_CONTENT_DIRECTION' => $user->lang['DIRECTION'],
3153 'S_CONTENT_ENCODING' => $user->lang['ENCODING'],
3154 'S_CONTENT_DIR_LEFT' => $user->lang['LEFT'],
3155 'S_CONTENT_DIR_RIGHT' => $user->lang['RIGHT'],
3156 '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], ''),
3157 'S_DISPLAY_ONLINE_LIST' => ($config['load_online']) ? 1 : 0,
3158 'S_DISPLAY_SEARCH' => (!$config['load_search']) ? 0 : (isset($auth) ? ($auth->acl_get('u_search') && $auth->acl_getf_global('f_search')) : 1),
3159 'S_DISPLAY_PM' => ($config['allow_privmsg'] && $user->data['is_registered']) ? 1 : 0,
3160 'S_DISPLAY_MEMBERLIST' => (isset($auth)) ? $auth->acl_get('u_viewprofile') : 0,
3161 'S_NEW_PM' => ($s_privmsg_new) ? 1 : 0,
3163 'T_THEME_PATH' => "{$phpbb_root_path}styles/" . $user->theme['theme_path'] . '/theme',
3164 'T_TEMPLATE_PATH' => "{$phpbb_root_path}styles/" . $user->theme['template_path'] . '/template',
3165 'T_IMAGESET_PATH' => "{$phpbb_root_path}styles/" . $user->theme['imageset_path'] . '/imageset',
3166 'T_IMAGESET_LANG_PATH' => "{$phpbb_root_path}styles/" . $user->theme['imageset_path'] . '/imageset/' . $user->data['user_lang'],
3167 'T_IMAGES_PATH' => "{$phpbb_root_path}images/",
3168 'T_SMILIES_PATH' => "{$phpbb_root_path}{$config['smilies_path']}/",
3169 'T_AVATAR_PATH' => "{$phpbb_root_path}{$config['avatar_path']}/",
3170 'T_AVATAR_GALLERY_PATH' => "{$phpbb_root_path}{$config['avatar_gallery_path']}/",
3171 'T_ICONS_PATH' => "{$phpbb_root_path}{$config['icons_path']}/",
3172 'T_RANKS_PATH' => "{$phpbb_root_path}{$config['ranks_path']}/",
3173 'T_UPLOAD_PATH' => "{$phpbb_root_path}{$config['upload_path']}/",
3174 '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&amp;id=" . $user->theme['style_id'],
3175 'T_STYLESHEET_NAME' => $user->theme['theme_name'],
3176 'T_THEME_DATA' => (!$user->theme['theme_storedb']) ? '' : $user->theme['theme_data'],
3178 'SITE_LOGO_IMG' => $user->img('site_logo'))
3181 if ($config['send_encoding'])
3183 header('Content-type: text/html; charset=' . $user->lang['ENCODING']);
3185 header('Cache-Control: private, no-cache="set-cookie"');
3186 header('Expires: 0');
3187 header('Pragma: no-cache');
3189 return;
3193 * Generate page footer
3195 function page_footer()
3197 global $db, $config, $template, $user, $auth, $cache, $messenger, $starttime, $phpbb_root_path, $phpEx;
3199 // Output page creation time
3200 if (defined('DEBUG'))
3202 $mtime = explode(' ', microtime());
3203 $totaltime = $mtime[0] + $mtime[1] - $starttime;
3205 if (!empty($_REQUEST['explain']) && $auth->acl_get('a_') && defined('DEBUG_EXTRA') && method_exists($db, 'sql_report'))
3207 $db->sql_report('display');
3210 $debug_output = sprintf('Time : %.3fs | ' . $db->sql_num_queries() . ' Queries | GZIP : ' . (($config['gzip_compress']) ? 'On' : 'Off') . (($user->load) ? ' | Load : ' . $user->load : ''), $totaltime);
3212 if ($auth->acl_get('a_') && defined('DEBUG_EXTRA'))
3214 if (function_exists('memory_get_usage'))
3216 if ($memory_usage = memory_get_usage())
3218 global $base_memory_usage;
3219 $memory_usage -= $base_memory_usage;
3220 $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']);
3222 $debug_output .= ' | Memory Usage: ' . $memory_usage;
3226 $debug_output .= ' | <a href="' . build_url() . '&amp;explain=1">Explain</a>';
3230 $template->assign_vars(array(
3231 'DEBUG_OUTPUT' => (defined('DEBUG')) ? $debug_output : '',
3233 'U_ACP' => ($auth->acl_get('a_') && $user->data['is_registered']) ? "{$phpbb_root_path}adm/index.$phpEx?sid=" . $user->session_id : '')
3236 // Call cron-type script
3237 if (!defined('IN_CRON'))
3239 $cron_type = '';
3241 if (time() - $config['queue_interval'] > $config['last_queue_run'] && !defined('IN_ADMIN') && file_exists($phpbb_root_path . 'cache/queue.' . $phpEx))
3243 // Process email queue
3244 $cron_type = 'queue';
3246 else if (method_exists($cache, 'tidy') && time() - $config['cache_gc'] > $config['cache_last_gc'])
3248 // Tidy the cache
3249 $cron_type = 'tidy_cache';
3251 else if (time() - $config['warnings_gc'] > $config['warnings_last_gc'])
3253 $cron_type = 'tidy_warnings';
3255 else if (time() - $config['database_gc'] > $config['database_last_gc'])
3257 // Tidy the database
3258 $cron_type = 'tidy_database';
3260 else if (time() - $config['search_gc'] > $config['search_last_gc'])
3262 // Tidy the search
3263 $cron_type = 'tidy_search';
3265 else if (time() - $config['session_gc'] > $config['session_last_gc'])
3267 $cron_type = 'tidy_sessions';
3270 if ($cron_type)
3272 $template->assign_var('RUN_CRON_TASK', '<img src="' . $phpbb_root_path . 'cron.' . $phpEx . '?cron_type=' . $cron_type . '" width="1" height="1" alt="cron" />');
3276 $template->display('body');
3278 garbage_collection();
3280 exit;
3284 * Closing the cache object and the database
3285 * Cool function name, eh? We might want to add operations to it later
3287 function garbage_collection()
3289 global $cache, $db;
3291 // Unload cache, must be done before the DB connection if closed
3292 if (!empty($cache))
3294 $cache->unload();
3297 // Close our DB connection.
3298 if (!empty($db))
3300 $db->sql_close();
3306 class bitfield
3308 var $data;
3310 function bitfield($bitfield = '')
3312 $this->data = base64_decode($bitfield);
3317 function get($n)
3319 // Get the ($n / 8)th char
3320 $byte = $n >> 3;
3322 if (!isset($this->data[$byte]))
3324 // Of course, if it doesn't exist then the result if FALSE
3325 return false;
3328 $c = $this->data[$byte];
3330 // Lookup the ($n % 8)th bit of the byte
3331 $bit = 7 - ($n & 7);
3332 return (bool) (ord($c) & (1 << $bit));
3335 function set($n)
3337 $byte = $n >> 3;
3338 $bit = 7 - ($n & 7);
3340 if (isset($this->data[$byte]))
3342 $this->data[$byte] = $this->data[$byte] | chr(1 << $bit);
3344 else
3346 if ($byte - strlen($this->data) > 0)
3348 $this->data .= str_repeat("\0", $byte - strlen($this->data));
3350 $this->data .= chr(1 << $bit);
3354 function clear($n)
3356 $byte = $n >> 3;
3358 if (!isset($this->data[$byte]))
3360 return;
3363 $bit = 7 - ($n & 7);
3364 $this->data[$byte] = $this->data[$byte] &~ chr(1 << $bit);
3367 function get_blob()
3369 return $this->data;
3372 function get_base64()
3374 return base64_encode($this->data);
3377 function get_bin()
3379 $bin = '';
3380 $len = strlen($this->data);
3382 for ($i = 0; $i < $len; ++$i)
3384 $bin .= str_pad(decbin(ord($this->data[$i])), 8, '0', STR_PAD_LEFT);
3387 return $bin;
3390 function get_all_set()
3392 return array_keys(array_filter(str_split($this->get_bin())));
3395 function merge($bitfield)
3397 $this->data = $this->data | $bitfield->get_blob();