- no more encoding mixture, say hello to UTF-8 (I'll add a validation solution for...
[phpbb.git] / phpBB / includes / functions.php
blobd7a594eeeb0a928b4e0d8a760bf1ceb2afd5d136
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), ENT_QUOTES, 'UTF-8'));
29 if (!empty($result))
31 // Make sure multibyte characters are wellformed
32 if ($multibyte)
34 if (!preg_match('/^./u', $result))
36 $result = '';
39 else
41 // no multibyte, allow only ASCII (0-127)
42 $result = preg_replace('/[\x80-\xFF]/', '?', $result);
46 $result = (STRIP) ? stripslashes($result) : $result;
50 /**
51 * request_var
53 * Used to get passed variable
55 function request_var($var_name, $default, $multibyte = false)
57 if (!isset($_REQUEST[$var_name]) || (is_array($_REQUEST[$var_name]) && !is_array($default)) || (is_array($default) && !is_array($_REQUEST[$var_name])))
59 return (is_array($default)) ? array() : $default;
62 $var = $_REQUEST[$var_name];
63 if (!is_array($default))
65 $type = gettype($default);
67 else
69 list($key_type, $type) = each($default);
70 $type = gettype($type);
71 $key_type = gettype($key_type);
74 if (is_array($var))
76 $_var = $var;
77 $var = array();
79 foreach ($_var as $k => $v)
81 if (is_array($v))
83 foreach ($v as $_k => $_v)
85 set_var($k, $k, $key_type);
86 set_var($_k, $_k, $key_type);
87 set_var($var[$k][$_k], $_v, $type, $multibyte);
90 else
92 set_var($k, $k, $key_type);
93 set_var($var[$k], $v, $type, $multibyte);
97 else
99 set_var($var, $var, $type, $multibyte);
102 return $var;
106 * Set config value. Creates missing config entry.
108 function set_config($config_name, $config_value, $is_dynamic = false)
110 global $db, $cache, $config;
112 $sql = 'UPDATE ' . CONFIG_TABLE . "
113 SET config_value = '" . $db->sql_escape($config_value) . "'
114 WHERE config_name = '" . $db->sql_escape($config_name) . "'";
115 $db->sql_query($sql);
117 if (!$db->sql_affectedrows() && !isset($config[$config_name]))
119 $sql = 'INSERT INTO ' . CONFIG_TABLE . ' ' . $db->sql_build_array('INSERT', array(
120 'config_name' => $config_name,
121 'config_value' => $config_value,
122 'is_dynamic' => ($is_dynamic) ? 1 : 0));
123 $db->sql_query($sql);
126 $config[$config_name] = $config_value;
128 if (!$is_dynamic)
130 $cache->destroy('config');
135 * Generates an alphanumeric random string of given length
137 function gen_rand_string($num_chars = 8)
139 $rand_str = unique_id();
140 $rand_str = str_replace('0', 'Z', strtoupper(base_convert($rand_str, 16, 35)));
142 return substr($rand_str, 0, $num_chars);
146 * Return unique id
147 * @param $extra additional entropy
149 function unique_id($extra = 'c')
151 global $config;
152 static $dss_seeded;
154 $val = $config['rand_seed'] . microtime();
155 $val = md5($val);
156 $config['rand_seed'] = md5($config['rand_seed'] . $val . $extra);
158 if ($dss_seeded !== true)
160 set_config('rand_seed', $config['rand_seed'], true);
161 $dss_seeded = true;
164 return substr($val, 4, 16);
168 * Generate sort selection fields
170 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)
172 global $user;
174 $sort_dir_text = array('a' => $user->lang['ASCENDING'], 'd' => $user->lang['DESCENDING']);
176 $s_limit_days = '<select name="st">';
177 foreach ($limit_days as $day => $text)
179 $selected = ($sort_days == $day) ? ' selected="selected"' : '';
180 $s_limit_days .= '<option value="' . $day . '"' . $selected . '>' . $text . '</option>';
182 $s_limit_days .= '</select>';
184 $s_sort_key = '<select name="sk">';
185 foreach ($sort_by_text as $key => $text)
187 $selected = ($sort_key == $key) ? ' selected="selected"' : '';
188 $s_sort_key .= '<option value="' . $key . '"' . $selected . '>' . $text . '</option>';
190 $s_sort_key .= '</select>';
192 $s_sort_dir = '<select name="sd">';
193 foreach ($sort_dir_text as $key => $value)
195 $selected = ($sort_dir == $key) ? ' selected="selected"' : '';
196 $s_sort_dir .= '<option value="' . $key . '"' . $selected . '>' . $value . '</option>';
198 $s_sort_dir .= '</select>';
200 $u_sort_param = "st=$sort_days&amp;sk=$sort_key&amp;sd=$sort_dir";
202 return;
206 * Generate Jumpbox
208 function make_jumpbox($action, $forum_id = false, $select_all = false, $acl_list = false)
210 global $config, $auth, $template, $user, $db, $phpEx;
212 if (!$config['load_jumpbox'])
214 return;
217 $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id
218 FROM ' . FORUMS_TABLE . '
219 ORDER BY left_id ASC';
220 $result = $db->sql_query($sql, 600);
222 $right = $padding = 0;
223 $padding_store = array('0' => 0);
224 $display_jumpbox = false;
225 $iteration = 0;
227 // Sometimes it could happen that forums will be displayed here not be displayed within the index page
228 // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
229 // If this happens, the padding could be "broken"
231 while ($row = $db->sql_fetchrow($result))
233 if ($row['left_id'] < $right)
235 $padding++;
236 $padding_store[$row['parent_id']] = $padding;
238 else if ($row['left_id'] > $right + 1)
240 $padding = $padding_store[$row['parent_id']];
243 $right = $row['right_id'];
245 if ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']))
247 // Non-postable forum with no subforums, don't display
248 continue;
251 if (!$auth->acl_get('f_list', $row['forum_id']))
253 // if the user does not have permissions to list this forum skip
254 continue;
257 if ($acl_list && !$auth->acl_gets($acl_list, $row['forum_id']))
259 continue;
262 if (!$display_jumpbox)
264 $template->assign_block_vars('jumpbox_forums', array(
265 'FORUM_ID' => ($select_all) ? 0 : -1,
266 'FORUM_NAME' => ($select_all) ? $user->lang['ALL_FORUMS'] : $user->lang['SELECT_FORUM'],
267 'S_FORUM_COUNT' => $iteration)
270 $iteration++;
271 $display_jumpbox = true;
274 $template->assign_block_vars('jumpbox_forums', array(
275 'FORUM_ID' => $row['forum_id'],
276 'FORUM_NAME' => $row['forum_name'],
277 'SELECTED' => ($row['forum_id'] == $forum_id) ? ' selected="selected"' : '',
278 'S_FORUM_COUNT' => $iteration,
279 'S_IS_CAT' => ($row['forum_type'] == FORUM_CAT) ? true : false,
280 'S_IS_LINK' => ($row['forum_type'] == FORUM_LINK) ? true : false,
281 'S_IS_POST' => ($row['forum_type'] == FORUM_POST) ? true : false)
284 for ($i = 0; $i < $padding; $i++)
286 $template->assign_block_vars('jumpbox_forums.level', array());
288 $iteration++;
290 $db->sql_freeresult($result);
291 unset($padding_store);
293 $template->assign_vars(array(
294 'S_DISPLAY_JUMPBOX' => $display_jumpbox,
295 'S_JUMPBOX_ACTION' => $action)
298 return;
302 // Compatibility functions
304 if (!function_exists('array_combine'))
307 * A wrapper for the PHP5 function array_combine()
308 * @param array $keys contains keys for the resulting array
309 * @param array $values contains values for the resulting array
311 * @return Returns an array by using the values from the keys array as keys and the
312 * values from the values array as the corresponding values. Returns false if the
313 * number of elements for each array isn't equal or if the arrays are empty.
315 function array_combine($keys, $values)
317 $keys = array_values($keys);
318 $values = array_values($values);
320 $n = sizeof($keys);
321 $m = sizeof($values);
322 if (!$n || !$m || ($n != $m))
324 return false;
327 $combined = array();
328 for ($i = 0; $i < $n; $i++)
330 $combined[$keys[$i]] = $values[$i];
332 return $combined;
336 if (!function_exists('str_split'))
339 * A wrapper for the PHP5 function str_split()
340 * @param array $string contains the string to be converted
341 * @param array $split_length contains the length of each chunk
343 * @return Converts a string to an array. If the optional split_length parameter is specified,
344 * the returned array will be broken down into chunks with each being split_length in length,
345 * otherwise each chunk will be one character in length. FALSE is returned if split_length is
346 * less than 1. If the split_length length exceeds the length of string, the entire string is
347 * returned as the first (and only) array element.
349 function str_split($string, $split_length = 1)
351 if ($split_length < 1)
353 return false;
355 else if ($split_length >= strlen($string))
357 return array($string);
359 else
361 preg_match_all('#.{1,' . $split_length . '}#s', $string, $matches);
362 return $matches[0];
367 if (!function_exists('stripos'))
370 * A wrapper for the PHP5 function stripos
371 * Find position of first occurrence of a case-insensitive string
373 * @param string $haystack is the string to search in
374 * @param string needle is the string to search for
376 * @return Returns the numeric position of the first occurrence of needle in the haystack string. Unlike strpos(), stripos() is case-insensitive.
377 * Note that the needle may be a string of one or more characters.
378 * If needle is not found, stripos() will return boolean FALSE.
380 function stripos($haystack, $needle)
382 if (preg_match('#' . preg_quote($needle, '#') . '#i', $haystack, $m))
384 return strpos($haystack, $m[0]);
387 return false;
391 if (!function_exists('realpath'))
393 if (substr(PHP_OS, 0, 3) != 'WIN' && !(bool) ini_get('safe_mode') && function_exists('shell_exec') && trim(`realpath .`))
396 * @author Chris Smith <chris@project-minerva.org>
397 * @copyright 2006 Project Minerva Team
398 * @param string $path The path which we should attempt to resolve.
399 * @return mixed
401 function phpbb_realpath($path)
403 $arg = escapeshellarg($path);
404 return trim(`realpath '$arg'`);
407 else
410 * Checks if a path ($path) is absolute or relative
412 * @param string $path Path to check absoluteness of
413 * @return boolean
415 function is_absolute($path)
417 return ($path[0] == '/' || (substr(PHP_OS, 0, 3) == 'WIN' && preg_match('#^[a-z]:/#i', $path))) ? true : false;
421 * @author Chris Smith <chris@project-minerva.org>
422 * @copyright 2006 Project Minerva Team
423 * @param string $path The path which we should attempt to resolve.
424 * @return mixed
426 function phpbb_realpath($path)
428 // Now to perform funky shizzle
430 // Switch to use UNIX slashes
431 $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
432 $path_prefix = '';
434 // Determine what sort of path we have
435 if (is_absolute($path))
437 $absolute = true;
439 if ($path[0] == '/')
441 // Absolute path, *NIX style
442 $path_prefix = '';
444 else
446 // Absolute path, Windows style
447 // Remove the drive letter and colon
448 $path_prefix = $path[0] . ':';
449 $path = substr($path, 2);
452 else
454 // Relative Path
455 // Prepend the current working directory
456 if (function_exists('getcwd'))
458 // This is the best method, hopefully it is enabled!
459 $path = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . '/' . $path;
460 $absolute = true;
461 if (preg_match('#^[a-z]:#i', $path))
463 $path_prefix = $path[0] . ':';
464 $path = substr($path, 2);
466 else
468 $path_prefix = '';
471 else if (isset($_SERVER['SCRIPT_FILENAME']) && !empty($_SERVER['SCRIPT_FILENAME']))
473 // Warning: If chdir() has been used this will lie!
474 // @todo This has some problems sometime (CLI can create them easily)
475 $path = str_replace(DIRECTORY_SEPARATOR, '/', dirname($_SERVER['SCRIPT_FILENAME'])) . '/' . $path;
476 $absolute = true;
477 $path_prefix = '';
479 else
481 // We have no way of getting the absolute path, just run on using relative ones.
482 $absolute = false;
483 $path_prefix = '.';
487 // Remove any repeated slashes
488 $path = preg_replace('#/{2,}#', '/', $path);
490 // Remove the slashes from the start and end of the path
491 $path = trim($path, '/');
493 // Break the string into little bits for us to nibble on
494 $bits = explode('/', $path);
496 // Remove any . in the path
497 $bits = array_diff($bits, array('.'));
499 // Lets get looping, run over and resolve any .. (up directory)
500 for ($i = 0, $max = sizeof($bits); $i < $max; $i++)
502 // @todo Optimise
503 if ($bits[$i] == '..' )
505 if (isset($bits[$i - 1]))
507 if ($bits[$i - 1] != '..')
509 // We found a .. and we are able to traverse upwards, lets do it!
510 unset($bits[$i]);
511 unset($bits[$i - 1]);
512 $i -= 2;
513 $max -= 2;
514 $bits = array_values($bits);
517 else if ($absolute) // ie. !isset($bits[$i - 1]) && $absolute
519 // We have an absolute path trying to descend above the root of the filesystem
520 // ... Error!
521 return false;
526 // Prepend the path prefix
527 array_unshift($bits, $path_prefix);
529 $resolved = '';
531 $max = sizeof($bits) - 1;
533 // Check if we are able to resolve symlinks, Windows cannot.
534 $symlink_resolve = (function_exists('readlink')) ? true : false;
536 foreach ($bits as $i => $bit)
538 if (@is_dir("$resolved/$bit") || ($i == $max && @is_file("$resolved/$bit")))
540 // Path Exists
541 if ($symlink_resolve && is_link("$resolved/$bit") && ($link = readlink("$resolved/$bit")))
543 // Resolved a symlink.
544 $resolved = $link . (($i == $max) ? '' : '/');
545 continue;
548 else
550 // Something doesn't exist here!
551 // This is correct realpath() behaviour but sadly open_basedir and safe_mode make this problematic
552 // return false;
554 $resolved .= $bit . (($i == $max) ? '' : '/');
557 // @todo If the file exists fine and open_basedir only has one path we should be able to prepend it
558 // because we must be inside that basedir, the question is where...
559 // @interal The slash in is_dir() gets around an open_basedir restriction
560 if (!@file_exists($resolved) || (!is_dir($resolved . '/') && !is_file($resolved)))
562 return false;
565 // Put the slashes back to the native operating systems slashes
566 $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved);
568 return $resolved; // We got here, in the end!
572 else
575 * A wrapper for realpath
577 function phpbb_realpath($path)
579 return realpath($path);
583 // functions used for building option fields
586 * Pick a language, any language ...
588 function language_select($default = '')
590 global $db;
592 $sql = 'SELECT lang_iso, lang_local_name
593 FROM ' . LANG_TABLE . '
594 ORDER BY lang_english_name';
595 $result = $db->sql_query($sql, 600);
597 $lang_options = '';
598 while ($row = $db->sql_fetchrow($result))
600 $selected = ($row['lang_iso'] == $default) ? ' selected="selected"' : '';
601 $lang_options .= '<option value="' . $row['lang_iso'] . '"' . $selected . '>' . $row['lang_local_name'] . '</option>';
603 $db->sql_freeresult($result);
605 return $lang_options;
608 /**
609 * Pick a template/theme combo,
611 function style_select($default = '', $all = false)
613 global $db;
615 $sql_where = (!$all) ? 'WHERE style_active = 1 ' : '';
616 $sql = 'SELECT style_id, style_name
617 FROM ' . STYLES_TABLE . "
618 $sql_where
619 ORDER BY style_name";
620 $result = $db->sql_query($sql);
622 $style_options = '';
623 while ($row = $db->sql_fetchrow($result))
625 $selected = ($row['style_id'] == $default) ? ' selected="selected"' : '';
626 $style_options .= '<option value="' . $row['style_id'] . '"' . $selected . '>' . $row['style_name'] . '</option>';
628 $db->sql_freeresult($result);
630 return $style_options;
634 * Pick a timezone
636 function tz_select($default = '', $truncate = false)
638 global $sys_timezone, $user;
640 $tz_select = '';
641 foreach ($user->lang['tz_zones'] as $offset => $zone)
643 if ($truncate)
645 $zone = (strlen($zone) > 70) ? substr($zone, 0, 70) . '...' : $zone;
648 if (is_numeric($offset))
650 $selected = ($offset == $default) ? ' selected="selected"' : '';
651 $tz_select .= '<option value="' . $offset . '"' . $selected . '>' . $zone . '</option>';
655 return $tz_select;
658 // Functions handling topic/post tracking/marking
661 * Marks a topic/forum as read
662 * Marks a topic as posted to
664 * @param int $user_id can only be used with $mode == 'post'
666 function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $user_id = 0)
668 global $db, $user, $config;
670 if ($mode == 'all')
672 if ($forum_id === false || !sizeof($forum_id))
674 if ($config['load_db_lastread'] && $user->data['is_registered'])
676 // Mark all forums read (index page)
677 $db->sql_query('DELETE FROM ' . TOPICS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
678 $db->sql_query('DELETE FROM ' . FORUMS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
679 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
681 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
683 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
684 $tracking_topics = ($tracking_topics) ? unserialize($tracking_topics) : array();
686 unset($tracking_topics['tf']);
687 unset($tracking_topics['t']);
688 unset($tracking_topics['f']);
689 $tracking_topics['l'] = base_convert(time() - $config['board_startdate'], 10, 36);
691 $user->set_cookie('track', serialize($tracking_topics), time() + 31536000);
692 unset($tracking_topics);
694 if ($user->data['is_registered'])
696 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
701 return;
703 else if ($mode == 'topics')
705 // Mark all topics in forums read
706 if (!is_array($forum_id))
708 $forum_id = array($forum_id);
711 // Add 0 to forums array to mark global announcements correctly
712 $forum_id[] = 0;
714 if ($config['load_db_lastread'] && $user->data['is_registered'])
716 $sql = 'DELETE FROM ' . TOPICS_TRACK_TABLE . "
717 WHERE user_id = {$user->data['user_id']}
718 AND " . $db->sql_in_set('forum_id', $forum_id);
719 $db->sql_query($sql);
721 $sql = 'SELECT forum_id
722 FROM ' . FORUMS_TRACK_TABLE . "
723 WHERE user_id = {$user->data['user_id']}
724 AND " . $db->sql_in_set('forum_id', $forum_id);
725 $result = $db->sql_query($sql);
727 $sql_update = array();
728 while ($row = $db->sql_fetchrow($result))
730 $sql_update[] = $row['forum_id'];
732 $db->sql_freeresult($result);
734 if (sizeof($sql_update))
736 $sql = 'UPDATE ' . FORUMS_TRACK_TABLE . '
737 SET mark_time = ' . time() . "
738 WHERE user_id = {$user->data['user_id']}
739 AND " . $db->sql_in_set('forum_id', $sql_update);
740 $db->sql_query($sql);
743 if ($sql_insert = array_diff($forum_id, $sql_update))
745 $sql_ary = array();
746 foreach ($sql_insert as $f_id)
748 $sql_ary[] = array(
749 'user_id' => $user->data['user_id'],
750 'forum_id' => $f_id,
751 'mark_time' => time()
755 if (sizeof($sql_ary))
757 switch (SQL_LAYER)
759 case 'mysql':
760 case 'mysql4':
761 case 'mysqli':
762 $db->sql_query('INSERT INTO ' . FORUMS_TRACK_TABLE . ' ' . $db->sql_build_array('MULTI_INSERT', $sql_ary));
763 break;
765 default:
766 foreach ($sql_ary as $ary)
768 $db->sql_query('INSERT INTO ' . FORUMS_TRACK_TABLE . ' ' . $db->sql_build_array('INSERT', $ary));
770 break;
775 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
777 $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
778 $tracking = ($tracking) ? unserialize($tracking) : array();
780 foreach ($forum_id as $f_id)
782 $topic_ids36 = (isset($tracking['tf'][$f_id])) ? $tracking['tf'][$f_id] : array();
784 if (isset($tracking['tf'][$f_id]))
786 unset($tracking['tf'][$f_id]);
789 foreach ($topic_ids36 as $topic_id36)
791 unset($tracking['t'][$topic_id36]);
794 if (isset($tracking['f'][$f_id]))
796 unset($tracking['f'][$f_id]);
799 $tracking['f'][$f_id] = base_convert(time() - $config['board_startdate'], 10, 36);
802 $user->set_cookie('track', serialize($tracking), time() + 31536000);
803 unset($tracking);
806 return;
808 else if ($mode == 'topic')
810 if ($topic_id === false || $forum_id === false)
812 return;
815 if ($config['load_db_lastread'] && $user->data['is_registered'])
817 $sql = 'UPDATE ' . TOPICS_TRACK_TABLE . '
818 SET mark_time = ' . (($post_time) ? $post_time : time()) . "
819 WHERE user_id = {$user->data['user_id']}
820 AND topic_id = $topic_id";
821 $db->sql_query($sql);
823 // insert row
824 if (!$db->sql_affectedrows())
826 $db->sql_return_on_error(true);
828 $sql_ary = array(
829 'user_id' => $user->data['user_id'],
830 'topic_id' => $topic_id,
831 'forum_id' => (int) $forum_id,
832 'mark_time' => ($post_time) ? $post_time : time(),
835 $db->sql_query('INSERT INTO ' . TOPICS_TRACK_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
837 $db->sql_return_on_error(false);
840 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
842 $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
843 $tracking = ($tracking) ? unserialize($tracking) : array();
845 $topic_id36 = base_convert($topic_id, 10, 36);
847 if (!isset($tracking['t'][$topic_id36]))
849 $tracking['tf'][$forum_id][$topic_id36] = true;
852 $post_time = ($post_time) ? $post_time : time();
853 $tracking['t'][$topic_id36] = base_convert($post_time - $config['board_startdate'], 10, 36);
855 // If the cookie grows larger than 10000 characters we will remove the smallest value
856 // This can result in old topics being unread - but most of the time it should be accurate...
857 if (isset($_COOKIE[$config['cookie_name'] . '_track']) && strlen($_COOKIE[$config['cookie_name'] . '_track']) > 10000)
859 //echo 'Cookie grown too large' . print_r($tracking, true);
861 // We get the ten most minimum stored time offsets and its associated topic ids
862 $time_keys = array();
863 for ($i = 0; $i < 10 && sizeof($tracking['t']); $i++)
865 $min_value = min($tracking['t']);
866 $m_tkey = array_search($min_value, $tracking['t']);
867 unset($tracking['t'][$m_tkey]);
869 $time_keys[$m_tkey] = $min_value;
872 // Now remove the topic ids from the array...
873 foreach ($tracking['tf'] as $f_id => $topic_id_ary)
875 foreach ($time_keys as $m_tkey => $min_value)
877 if (isset($topic_id_ary[$m_tkey]))
879 $tracking['f'][$f_id] = $min_value;
880 unset($tracking['tf'][$f_id][$m_tkey]);
885 if ($user->data['is_registered'])
887 $user->data['user_lastmark'] = intval(base_convert(max($time_keys) + $config['board_startdate'], 36, 10));
888 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . $user->data['user_lastmark'] . " WHERE user_id = {$user->data['user_id']}");
890 else
892 $tracking['l'] = max($time_keys);
896 $user->set_cookie('track', serialize($tracking), time() + 31536000);
899 return;
901 else if ($mode == 'post')
903 if ($topic_id === false)
905 return;
908 $use_user_id = (!$user_id) ? $user->data['user_id'] : $user_id;
910 if ($config['load_db_track'] && $use_user_id != ANONYMOUS)
912 $db->sql_return_on_error(true);
914 $sql_ary = array(
915 'user_id' => $use_user_id,
916 'topic_id' => $topic_id,
917 'topic_posted' => 1
920 $db->sql_query('INSERT INTO ' . TOPICS_POSTED_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
922 $db->sql_return_on_error(false);
925 return;
930 * Get topic tracking info by using already fetched info
932 function get_topic_tracking($forum_id, $topic_ids, &$rowset, $forum_mark_time, $global_announce_list = false)
934 global $config, $user;
936 $last_read = array();
938 if (!is_array($topic_ids))
940 $topic_ids = array($topic_ids);
943 foreach ($topic_ids as $topic_id)
945 if (!empty($rowset[$topic_id]['mark_time']))
947 $last_read[$topic_id] = $rowset[$topic_id]['mark_time'];
951 $topic_ids = array_diff($topic_ids, array_keys($last_read));
953 if (sizeof($topic_ids))
955 $mark_time = array();
957 // Get global announcement info
958 if ($global_announce_list && sizeof($global_announce_list))
960 if (!isset($forum_mark_time[0]))
962 global $db;
964 $sql = 'SELECT mark_time
965 FROM ' . FORUMS_TRACK_TABLE . "
966 WHERE user_id = {$user->data['user_id']}
967 AND forum_id = 0";
968 $result = $db->sql_query($sql);
969 $row = $db->sql_fetchrow($result);
970 $db->sql_freeresult($result);
972 if ($row)
974 $mark_time[0] = $row['mark_time'];
977 else
979 if ($forum_mark_time[0] !== false)
981 $mark_time[0] = $forum_mark_time[0];
986 if (!empty($forum_mark_time[$forum_id]) && $forum_mark_time[$forum_id] !== false)
988 $mark_time[$forum_id] = $forum_mark_time[$forum_id];
991 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
993 foreach ($topic_ids as $topic_id)
995 if ($global_announce_list && isset($global_announce_list[$topic_id]))
997 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
999 else
1001 $last_read[$topic_id] = $user_lastmark;
1006 return $last_read;
1010 * Get topic tracking info from db (for cookie based tracking only this function is used)
1012 function get_complete_topic_tracking($forum_id, $topic_ids, $global_announce_list = false)
1014 global $config, $user;
1016 $last_read = array();
1018 if (!is_array($topic_ids))
1020 $topic_ids = array($topic_ids);
1023 if ($config['load_db_lastread'] && $user->data['is_registered'])
1025 global $db;
1027 $sql = 'SELECT topic_id, mark_time
1028 FROM ' . TOPICS_TRACK_TABLE . "
1029 WHERE user_id = {$user->data['user_id']}
1030 AND " . $db->sql_in_set('topic_id', $topic_ids);
1031 $result = $db->sql_query($sql);
1033 while ($row = $db->sql_fetchrow($result))
1035 $last_read[$row['topic_id']] = $row['mark_time'];
1037 $db->sql_freeresult($result);
1039 $topic_ids = array_diff($topic_ids, array_keys($last_read));
1041 if (sizeof($topic_ids))
1043 $sql = 'SELECT forum_id, mark_time
1044 FROM ' . FORUMS_TRACK_TABLE . "
1045 WHERE user_id = {$user->data['user_id']}
1046 AND forum_id " .
1047 (($global_announce_list && sizeof($global_announce_list)) ? "IN (0, $forum_id)" : "= $forum_id");
1048 $result = $db->sql_query($sql);
1050 $mark_time = array();
1051 while ($row = $db->sql_fetchrow($result))
1053 $mark_time[$row['forum_id']] = $row['mark_time'];
1055 $db->sql_freeresult($result);
1057 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
1059 foreach ($topic_ids as $topic_id)
1061 if ($global_announce_list && isset($global_announce_list[$topic_id]))
1063 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1065 else
1067 $last_read[$topic_id] = $user_lastmark;
1072 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1074 global $tracking_topics;
1076 if (!isset($tracking_topics) || !sizeof($tracking_topics))
1078 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1079 $tracking_topics = ($tracking_topics) ? unserialize($tracking_topics) : array();
1082 if (!$user->data['is_registered'])
1084 $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0;
1086 else
1088 $user_lastmark = $user->data['user_lastmark'];
1091 foreach ($topic_ids as $topic_id)
1093 $topic_id36 = base_convert($topic_id, 10, 36);
1095 if (isset($tracking_topics['t'][$topic_id36]))
1097 $last_read[$topic_id] = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate'];
1101 $topic_ids = array_diff($topic_ids, array_keys($last_read));
1103 if (sizeof($topic_ids))
1105 $mark_time = array();
1106 if ($global_announce_list && sizeof($global_announce_list))
1108 if (isset($tracking_topics['f'][0]))
1110 $mark_time[0] = base_convert($tracking_topics['f'][0], 36, 10) + $config['board_startdate'];
1114 if (isset($tracking_topics['f'][$forum_id]))
1116 $mark_time[$forum_id] = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate'];
1119 $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user_lastmark;
1121 foreach ($topic_ids as $topic_id)
1123 if ($global_announce_list && isset($global_announce_list[$topic_id]))
1125 $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1127 else
1129 $last_read[$topic_id] = $user_lastmark;
1135 return $last_read;
1139 * Check for read forums and update topic tracking info accordingly
1141 * @param int $forum_id the forum id to check
1142 * @param int $forum_last_post_time the forums last post time
1143 * @param int $f_mark_time the forums last mark time if user is registered and load_db_lastread enabled
1144 * @param int $mark_time_forum false if the mark time needs to be obtained, else the last users forum mark time
1146 * @return true if complete forum got marked read, else false.
1148 function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_time = false, $mark_time_forum = false)
1150 global $db, $tracking_topics, $user, $config;
1152 // Determine the users last forum mark time if not given.
1153 if ($mark_time_forum === false)
1155 if ($config['load_db_lastread'] && $user->data['is_registered'])
1157 $mark_time_forum = (!empty($f_mark_time)) ? $f_mark_time : $user->data['user_lastmark'];
1159 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1161 if (!isset($tracking_topics) || !sizeof($tracking_topics))
1163 $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1164 $tracking_topics = ($tracking_topics) ? unserialize($tracking_topics) : array();
1167 if (!$user->data['is_registered'])
1169 $user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0;
1172 $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'];
1176 // Check the forum for any left unread topics.
1177 // If there are none, we mark the forum as read.
1178 if ($config['load_db_lastread'] && $user->data['is_registered'])
1180 if ($mark_time_forum >= $forum_last_post_time)
1182 // We do not need to mark read, this happened before. Therefore setting this to true
1183 $row = true;
1185 else
1187 $sql = 'SELECT t.forum_id FROM ' . TOPICS_TABLE . ' t
1188 LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt ON (tt.topic_id = t.topic_id AND tt.user_id = ' . $user->data['user_id'] . ')
1189 WHERE t.forum_id = ' . $forum_id . '
1190 AND t.topic_last_post_time > ' . $mark_time_forum . '
1191 AND t.topic_moved_id = 0
1192 AND tt.topic_id IS NULL
1193 GROUP BY t.forum_id';
1194 $result = $db->sql_query_limit($sql, 1);
1195 $row = $db->sql_fetchrow($result);
1196 $db->sql_freeresult($result);
1199 else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1201 // Get information from cookie
1202 $row = false;
1204 if (!isset($tracking_topics['tf'][$forum_id]))
1206 // We do not need to mark read, this happened before. Therefore setting this to true
1207 $row = true;
1209 else
1211 $sql = 'SELECT topic_id
1212 FROM ' . TOPICS_TABLE . '
1213 WHERE forum_id = ' . $forum_id . '
1214 AND topic_last_post_time > ' . $mark_time_forum . '
1215 AND topic_moved_id = 0';
1216 $result = $db->sql_query($sql);
1218 $check_forum = $tracking_topics['tf'][$forum_id];
1219 $unread = false;
1220 while ($row = $db->sql_fetchrow($result))
1222 if (!in_array(base_convert($row['topic_id'], 10, 36), array_keys($check_forum)))
1224 $unread = true;
1225 break;
1228 $db->sql_freeresult($result);
1230 $row = $unread;
1233 else
1235 $row = true;
1238 if (!$row)
1240 markread('topics', $forum_id);
1241 return true;
1244 return false;
1247 // Pagination functions
1250 * Pagination routine, generates page number sequence
1251 * tpl_prefix is for using different pagination blocks at one page
1253 function generate_pagination($base_url, $num_items, $per_page, $start_item, $add_prevnext_text = false, $tpl_prefix = '')
1255 global $template, $user;
1257 $seperator = $user->theme['pagination_sep'];
1258 $total_pages = ceil($num_items/$per_page);
1260 if ($total_pages == 1 || !$num_items)
1262 return false;
1265 $on_page = floor($start_item / $per_page) + 1;
1266 $page_string = ($on_page == 1) ? '<strong>1</strong>' : '<a href="' . $base_url . '">1</a>';
1268 if ($total_pages > 5)
1270 $start_cnt = min(max(1, $on_page - 4), $total_pages - 5);
1271 $end_cnt = max(min($total_pages, $on_page + 4), 6);
1273 $page_string .= ($start_cnt > 1) ? ' ... ' : $seperator;
1275 for ($i = $start_cnt + 1; $i < $end_cnt; $i++)
1277 $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "&amp;start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
1278 if ($i < $end_cnt - 1)
1280 $page_string .= $seperator;
1284 $page_string .= ($end_cnt < $total_pages) ? ' ... ' : $seperator;
1286 else
1288 $page_string .= $seperator;
1290 for ($i = 2; $i < $total_pages; $i++)
1292 $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "&amp;start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
1293 if ($i < $total_pages)
1295 $page_string .= $seperator;
1300 $page_string .= ($on_page == $total_pages) ? '<strong>' . $total_pages . '</strong>' : '<a href="' . $base_url . '&amp;start=' . (($total_pages - 1) * $per_page) . '">' . $total_pages . '</a>';
1302 if ($add_prevnext_text)
1304 if ($on_page != 1)
1306 $page_string = '<a href="' . $base_url . '&amp;start=' . (($on_page - 2) * $per_page) . '">' . $user->lang['PREVIOUS'] . '</a>&nbsp;&nbsp;' . $page_string;
1309 if ($on_page != $total_pages)
1311 $page_string .= '&nbsp;&nbsp;<a href="' . $base_url . '&amp;start=' . ($on_page * $per_page) . '">' . $user->lang['NEXT'] . '</a>';
1315 $template->assign_vars(array(
1316 $tpl_prefix . 'BASE_URL' => $base_url,
1317 $tpl_prefix . 'PER_PAGE' => $per_page,
1319 $tpl_prefix . 'PREVIOUS_PAGE' => ($on_page == 1) ? '' : $base_url . '&amp;start=' . (($on_page - 2) * $per_page),
1320 $tpl_prefix . 'NEXT_PAGE' => ($on_page == $total_pages) ? '' : $base_url . '&amp;start=' . ($on_page * $per_page))
1323 return $page_string;
1327 * Return current page (pagination)
1329 function on_page($num_items, $per_page, $start)
1331 global $template, $user;
1333 $on_page = floor($start / $per_page) + 1;
1335 $template->assign_vars(array(
1336 'ON_PAGE' => $on_page)
1339 return sprintf($user->lang['PAGE_OF'], $on_page, max(ceil($num_items / $per_page), 1));
1342 // Server functions (building urls, redirecting...)
1345 * Append session id to url
1347 * @param string $url The url the session id needs to be appended to (can have params)
1348 * @param mixed $params String or array of additional url parameters
1349 * @param bool $is_amp Is url using &amp; (true) or & (false)
1350 * @param string $session_id Possibility to use a custom session id instead of the global one
1352 * Examples:
1353 * <code>
1354 * append_sid("{$phpbb_root_path}viewtopic.$phpEx?t=1&amp;f=2");
1355 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&amp;f=2');
1356 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2', false);
1357 * append_sid("{$phpbb_root_path}viewtopic.$phpEx", array('t' => 1, 'f' => 2));
1358 * </code>
1360 function append_sid($url, $params = false, $is_amp = true, $session_id = false)
1362 global $_SID, $_EXTRA_URL;
1364 // Assign sid if session id is not specified
1365 if ($session_id === false)
1367 $session_id = $_SID;
1370 $amp_delim = ($is_amp) ? '&amp;' : '&';
1371 $url_delim = (strpos($url, '?') === false) ? '?' : $amp_delim;
1373 // Appending custom url parameter?
1374 $append_url = (!empty($_EXTRA_URL)) ? implode($amp_delim, $_EXTRA_URL) : '';
1376 // Use the short variant if possible ;)
1377 if ($params === false)
1379 // Append session id
1380 return (!$session_id) ? $url . (($append_url) ? $url_delim . $append_url : '') : $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . 'sid=' . $session_id;
1383 // Build string if parameters are specified as array
1384 if (is_array($params))
1386 $output = array();
1388 foreach ($params as $key => $item)
1390 if ($item === NULL)
1392 continue;
1395 $output[] = $key . '=' . $item;
1398 $params = implode($amp_delim, $output);
1401 // Append session id and parameters (even if they are empty)
1402 // If parameters are empty, the developer can still append his/her parameters without caring about the delimiter
1403 return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . $params . ((!$session_id) ? '' : $amp_delim . 'sid=' . $session_id);
1407 * Generate board url (example: http://www.foo.bar/phpBB)
1408 * @param bool $without_script_path if set to true the script path gets not appended (example: http://www.foo.bar)
1410 function generate_board_url($without_script_path = false)
1412 global $config, $user;
1414 $server_name = (!empty($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : getenv('SERVER_NAME');
1415 $server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT');
1417 // Forcing server vars is the only way to specify/override the protocol
1418 if ($config['force_server_vars'] || !$server_name)
1420 $server_protocol = ($config['server_protocol']) ? $config['server_protocol'] : (($config['cookie_secure']) ? 'https://' : 'http://');
1421 $server_name = $config['server_name'];
1422 $server_port = (int) $config['server_port'];
1424 $url = $server_protocol . $server_name;
1426 else
1428 // Do not rely on cookie_secure, users seem to think that it means a secured cookie instead of an encrypted connection
1429 $cookie_secure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 1 : 0;
1430 $url = (($cookie_secure) ? 'https://' : 'http://') . $server_name;
1433 if ($server_port && (($config['cookie_secure'] && $server_port <> 443) || (!$config['cookie_secure'] && $server_port <> 80)))
1435 $url .= ':' . $server_port;
1438 if ($without_script_path)
1440 return $url;
1443 // Strip / from the end
1444 return $url . substr($user->page['root_script_path'], 0, -1);
1448 * Redirects the user to another page then exits the script nicely
1450 function redirect($url)
1452 global $db, $cache, $config, $user;
1454 if (empty($user->lang))
1456 $user->add_lang('common');
1459 garbage_collection();
1461 // Make sure no &amp;'s are in, this will break the redirect
1462 $url = str_replace('&amp;', '&', $url);
1464 // Make sure no linebreaks are there... to prevent http response splitting for PHP < 4.4.2
1465 if (strpos(urldecode($url), "\n") !== false || strpos(urldecode($url), "\r") !== false)
1467 trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
1470 // Determine which type of redirect we need to handle...
1471 $url_parts = parse_url($url);
1473 if ($url_parts === false)
1475 // Malformed url, redirect to current page...
1476 $url = generate_board_url() . '/' . $user->page['page'];
1478 else if (!empty($url_parts['scheme']) && !empty($url_parts['host']))
1480 // Full URL
1482 else if ($url{0} == '/')
1484 // Absolute uri, prepend direct url...
1485 $url = generate_board_url(true) . $url;
1487 else
1489 // Relative uri
1490 $pathinfo = pathinfo($url);
1492 // Is the uri pointing to the current directory?
1493 if ($pathinfo['dirname'] == '.')
1495 if ($user->page['page_dir'])
1497 $url = generate_board_url() . '/' . $user->page['page_dir'] . '/' . str_replace('./', '', $url);
1499 else
1501 $url = generate_board_url() . '/' . str_replace('./', '', $url);
1504 else
1506 // Get the realpath of dirname
1507 $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath('./')));
1508 $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($pathinfo['dirname'])));
1509 $intersection = array_intersect_assoc($root_dirs, $page_dirs);
1511 $root_dirs = array_diff_assoc($root_dirs, $intersection);
1512 $page_dirs = array_diff_assoc($page_dirs, $intersection);
1514 $dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs);
1516 if ($dir && substr($dir, -1, 1) == '/')
1518 $dir = substr($dir, 0, -1);
1521 $url = $dir . '/' . str_replace($pathinfo['dirname'] . '/', '', $url);
1522 $url = generate_board_url() . '/' . $url;
1526 // Redirect via an HTML form for PITA webservers
1527 if (@preg_match('#Microsoft|WebSTAR|Xitami#', getenv('SERVER_SOFTWARE')))
1529 header('Refresh: 0; URL=' . $url);
1530 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>';
1532 exit;
1535 // Behave as per HTTP/1.1 spec for others
1536 header('Location: ' . $url);
1537 exit;
1541 * Re-Apply session id after page reloads
1543 function reapply_sid($url)
1545 global $phpEx, $phpbb_root_path;
1547 if ($url === "index.$phpEx")
1549 return append_sid("index.$phpEx");
1551 else if ($url === "{$phpbb_root_path}index.$phpEx")
1553 return append_sid("{$phpbb_root_path}index.$phpEx");
1556 // Remove previously added sid
1557 if (strpos($url, '?sid=') !== false)
1559 $url = preg_replace('/(\?)sid=[a-z0-9]+(&amp;|&)?/', '\1', $url);
1561 else if (strpos($url, '&sid=') !== false)
1563 $url = preg_replace('/&sid=[a-z0-9]+(&)?/', '\1', $url);
1565 else if (strpos($url, '&amp;sid=') !== false)
1567 $url = preg_replace('/&amp;sid=[a-z0-9]+(&amp;)?/', '\1', $url);
1570 return append_sid($url);
1574 * Returns url from the session/current page with an re-appended SID with optionally stripping vars from the url
1576 function build_url($strip_vars = false)
1578 global $user, $phpbb_root_path;
1580 // Append SID
1581 $redirect = (($user->page['page_dir']) ? $user->page['page_dir'] . '/' : '') . $user->page['page_name'] . (($user->page['query_string']) ? "?{$user->page['query_string']}" : '');
1582 $redirect = append_sid($redirect, false, false);
1584 // Add delimiter if not there...
1585 if (strpos($redirect, '?') === false)
1587 $redirect .= '?';
1590 // Strip vars...
1591 if ($strip_vars !== false && strpos($redirect, '?') !== false)
1593 if (!is_array($strip_vars))
1595 $strip_vars = array($strip_vars);
1598 $query = $_query = array();
1599 parse_str(substr($redirect, strpos($redirect, '?') + 1), $query);
1600 $redirect = substr($redirect, 0, strpos($redirect, '?'));
1602 // Strip the vars off
1603 foreach ($strip_vars as $strip)
1605 if (isset($query[$strip]))
1607 unset($query[$strip]);
1612 foreach ($query as $key => $value)
1614 $_query[] = $key . '=' . $value;
1616 $query = implode('&', $_query);
1618 $redirect .= ($query) ? '?' . $query : '';
1621 return $phpbb_root_path . str_replace('&', '&amp;', $redirect);
1625 * Meta refresh assignment
1627 function meta_refresh($time, $url)
1629 global $template;
1631 $template->assign_vars(array(
1632 'META' => '<meta http-equiv="refresh" content="' . $time . ';url=' . $url . '" />')
1636 // Message/Login boxes
1639 * Build Confirm box
1640 * @param boolean $check True for checking if confirmed (without any additional parameters) and false for displaying the confirm box
1641 * @param string $title Title/Message used for confirm box.
1642 * message text is _CONFIRM appended to title.
1643 * If title can not be found in user->lang a default one is displayed
1644 * If title_CONFIRM can not be found in user->lang the text given is used.
1645 * @param string $hidden Hidden variables
1646 * @param string $html_body Template used for confirm box
1647 * @param string $u_action Custom form action
1649 function confirm_box($check, $title = '', $hidden = '', $html_body = 'confirm_body.html', $u_action = '')
1651 global $user, $template, $db;
1652 global $phpEx, $phpbb_root_path;
1654 if (isset($_POST['cancel']))
1656 return false;
1659 $confirm = false;
1660 if (isset($_POST['confirm']))
1662 // language frontier
1663 if ($_POST['confirm'] == $user->lang['YES'])
1665 $confirm = true;
1669 if ($check && $confirm)
1671 $user_id = request_var('user_id', 0);
1672 $session_id = request_var('sess', '');
1673 $confirm_key = request_var('confirm_key', '');
1675 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'])
1677 return false;
1680 // Reset user_last_confirm_key
1681 $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = ''
1682 WHERE user_id = " . $user->data['user_id'];
1683 $db->sql_query($sql);
1685 return true;
1687 else if ($check)
1689 return false;
1692 $s_hidden_fields = build_hidden_fields(array(
1693 'user_id' => $user->data['user_id'],
1694 'sess' => $user->session_id,
1695 'sid' => $user->session_id)
1698 // generate activation key
1699 $confirm_key = gen_rand_string(10);
1701 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
1703 adm_page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
1705 else
1707 page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
1710 $template->set_filenames(array(
1711 'body' => $html_body)
1714 // If activation key already exist, we better do not re-use the key (something very strange is going on...)
1715 if (request_var('confirm_key', ''))
1717 // This should not occur, therefore we cancel the operation to safe the user
1718 return false;
1721 // re-add sid / transform & to &amp; for user->page (user->page is always using &)
1722 $use_page = ($u_action) ? $phpbb_root_path . $u_action : $phpbb_root_path . str_replace('&', '&amp;', $user->page['page']);
1723 $u_action = reapply_sid($use_page);
1724 $u_action .= ((strpos($u_action, '?') === false) ? '?' : '&amp;') . 'confirm_key=' . $confirm_key;
1726 $template->assign_vars(array(
1727 'MESSAGE_TITLE' => (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title],
1728 'MESSAGE_TEXT' => (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'],
1730 'YES_VALUE' => $user->lang['YES'],
1731 'S_CONFIRM_ACTION' => $u_action,
1732 'S_HIDDEN_FIELDS' => $hidden . $s_hidden_fields)
1735 $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = '" . $db->sql_escape($confirm_key) . "'
1736 WHERE user_id = " . $user->data['user_id'];
1737 $db->sql_query($sql);
1739 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
1741 adm_page_footer();
1743 else
1745 page_footer();
1750 * Generate login box or verify password
1752 function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = false, $s_display = true)
1754 global $db, $user, $template, $auth, $phpEx, $phpbb_root_path, $config;
1756 $err = '';
1758 // Make sure user->setup() has been called
1759 if (empty($user->lang))
1761 $user->setup();
1764 // Print out error if user tries to authenticate as an administrator without having the privileges...
1765 if ($admin && !$auth->acl_get('a_'))
1767 // Not authd
1768 // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
1769 if ($user->data['is_registered'])
1771 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
1773 trigger_error('NO_AUTH_ADMIN');
1776 if (isset($_POST['login']))
1778 $username = request_var('username', '');
1779 $password = request_var('password', '');
1780 $autologin = (!empty($_POST['autologin'])) ? true : false;
1781 $viewonline = (!empty($_POST['viewonline'])) ? 0 : 1;
1782 $admin = ($admin) ? 1 : 0;
1784 // Check if the supplied username is equal to the one stored within the database if re-authenticating
1785 if ($admin && strtolower($username) != strtolower($user->data['username']))
1787 // We log the attempt to use a different username...
1788 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
1789 trigger_error('NO_AUTH_ADMIN_USER_DIFFER');
1792 // If authentication is successful we redirect user to previous page
1793 $result = $auth->login($username, $password, $autologin, $viewonline, $admin);
1795 // If admin authentication and login, we will log if it was a success or not...
1796 // We also break the operation on the first non-success login - it could be argued that the user already knows
1797 if ($admin)
1799 if ($result['status'] == LOGIN_SUCCESS)
1801 add_log('admin', 'LOG_ADMIN_AUTH_SUCCESS');
1803 else
1805 // Only log the failed attempt if a real user tried to.
1806 // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
1807 if ($user->data['is_registered'])
1809 add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
1814 // The result parameter is always an array, holding the relevant informations...
1815 if ($result['status'] == LOGIN_SUCCESS)
1817 $redirect = request_var('redirect', "{$phpbb_root_path}index.$phpEx");
1818 $message = ($l_success) ? $l_success : $user->lang['LOGIN_REDIRECT'];
1819 $l_redirect = ($admin) ? $user->lang['PROCEED_TO_ACP'] : (($redirect === "{$phpbb_root_path}index.$phpEx") ? $user->lang['RETURN_INDEX'] : $user->lang['RETURN_PAGE']);
1821 // append/replace SID (may change during the session for AOL users)
1822 $redirect = reapply_sid($redirect);
1824 meta_refresh(3, $redirect);
1825 trigger_error($message . '<br /><br />' . sprintf($l_redirect, '<a href="' . $redirect . '">', '</a>'));
1828 // Something failed, determine what...
1829 if ($result['status'] == LOGIN_BREAK)
1831 trigger_error($result['error_msg'], E_USER_ERROR);
1834 // Special cases... determine
1835 switch ($result['status'])
1837 case LOGIN_ERROR_ATTEMPTS:
1839 // Show confirm image
1840 $sql = 'DELETE FROM ' . CONFIRM_TABLE . "
1841 WHERE session_id = '" . $db->sql_escape($user->session_id) . "'
1842 AND confirm_type = " . CONFIRM_LOGIN;
1843 $db->sql_query($sql);
1845 // Generate code
1846 $code = gen_rand_string(mt_rand(5, 8));
1847 $confirm_id = md5(unique_id($user->ip));
1849 $sql = 'INSERT INTO ' . CONFIRM_TABLE . ' ' . $db->sql_build_array('INSERT', array(
1850 'confirm_id' => (string) $confirm_id,
1851 'session_id' => (string) $user->session_id,
1852 'confirm_type' => (int) CONFIRM_LOGIN,
1853 'code' => (string) $code)
1855 $db->sql_query($sql);
1857 $template->assign_vars(array(
1858 'S_CONFIRM_CODE' => true,
1859 'CONFIRM_ID' => $confirm_id,
1860 'CONFIRM_IMAGE' => '<img src="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=confirm&amp;id=' . $confirm_id . '&amp;type=' . CONFIRM_LOGIN) . '" alt="" title="" />',
1861 'L_LOGIN_CONFIRM_EXPLAIN' => sprintf($user->lang['LOGIN_CONFIRM_EXPLAIN'], '<a href="mailto:' . htmlentities($config['board_contact']) . '">', '</a>'),
1864 $err = $user->lang[$result['error_msg']];
1866 break;
1868 // Username, password, etc...
1869 default:
1870 $err = $user->lang[$result['error_msg']];
1872 // Assign admin contact to some error messages
1873 if ($result['error_msg'] == 'LOGIN_ERROR_USERNAME' || $result['error_msg'] == 'LOGIN_ERROR_PASSWORD')
1875 $err = (!$config['board_contact']) ? sprintf($user->lang[$result['error_msg']], '', '') : sprintf($user->lang[$result['error_msg']], '<a href="mailto:' . htmlentities($config['board_contact']) . '">', '</a>');
1877 break;
1881 if (!$redirect)
1883 // We just use what the session code determined...
1884 // If we are not within the admin directory we use the page dir...
1885 $redirect = '';
1887 if (!$admin)
1889 $redirect .= ($user->page['page_dir']) ? $user->page['page_dir'] . '/' : '';
1892 $redirect .= $user->page['page_name'] . (($user->page['query_string']) ? '?' . $user->page['query_string'] : '');
1895 $s_hidden_fields = build_hidden_fields(array('redirect' => $redirect, 'sid' => $user->session_id));
1897 $template->assign_vars(array(
1898 'LOGIN_ERROR' => $err,
1899 'LOGIN_EXPLAIN' => $l_explain,
1901 'U_SEND_PASSWORD' => ($config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') : '',
1902 'U_RESEND_ACTIVATION' => ($config['require_activation'] != USER_ACTIVATION_NONE && $config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=resend_act') : '',
1903 'U_TERMS_USE' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'),
1904 'U_PRIVACY' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'),
1906 'S_DISPLAY_FULL_LOGIN' => ($s_display) ? true : false,
1907 '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
1908 'S_HIDDEN_FIELDS' => $s_hidden_fields,
1910 'S_ADMIN_AUTH' => $admin,
1911 'USERNAME' => ($admin) ? $user->data['username'] : '')
1914 page_header($user->lang['LOGIN']);
1916 $template->set_filenames(array(
1917 'body' => 'login_body.html')
1919 make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx"));
1921 page_footer();
1925 * Generate forum login box
1927 function login_forum_box($forum_data)
1929 global $db, $config, $user, $template, $phpEx;
1931 $password = request_var('password', '');
1933 $sql = 'SELECT forum_id
1934 FROM ' . FORUMS_ACCESS_TABLE . '
1935 WHERE forum_id = ' . $forum_data['forum_id'] . '
1936 AND user_id = ' . $user->data['user_id'] . "
1937 AND session_id = '" . $db->sql_escape($user->session_id) . "'";
1938 $result = $db->sql_query($sql);
1939 $row = $db->sql_fetchrow($result);
1940 $db->sql_freeresult($result);
1942 if ($row)
1944 return true;
1947 if ($password)
1949 // Remove expired authorised sessions
1950 $sql = 'SELECT session_id
1951 FROM ' . SESSIONS_TABLE;
1952 $result = $db->sql_query($sql);
1954 if ($row = $db->sql_fetchrow($result))
1956 $sql_in = array();
1959 $sql_in[] = (string) $row['session_id'];
1961 while ($row = $db->sql_fetchrow($result));
1963 // Remove expired sessions
1964 $sql = 'DELETE FROM ' . FORUMS_ACCESS_TABLE . '
1965 WHERE ' . $db->sql_in_set('session_id', $sql_in, true);
1966 $db->sql_query($sql);
1968 $db->sql_freeresult($result);
1970 if ($password == $forum_data['forum_password'])
1972 $sql_ary = array(
1973 'forum_id' => (int) $forum_data['forum_id'],
1974 'user_id' => (int) $user->data['user_id'],
1975 'session_id' => (string) $user->session_id,
1978 $db->sql_query('INSERT INTO ' . FORUMS_ACCESS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1980 return true;
1983 $template->assign_var('LOGIN_ERROR', $user->lang['WRONG_PASSWORD']);
1986 page_header($user->lang['LOGIN']);
1988 $template->assign_vars(array(
1989 'S_HIDDEN_FIELDS' => build_hidden_fields(array('f' => $forum_data['forum_id'])))
1992 $template->set_filenames(array(
1993 'body' => 'login_forum.html')
1996 page_footer();
1999 // Content related functions
2002 * Bump Topic Check - used by posting and viewtopic
2004 function bump_topic_allowed($forum_id, $topic_bumped, $last_post_time, $topic_poster, $last_topic_poster)
2006 global $config, $auth, $user;
2008 // Check permission and make sure the last post was not already bumped
2009 if (!$auth->acl_get('f_bump', $forum_id) || $topic_bumped)
2011 return false;
2014 // Check bump time range, is the user really allowed to bump the topic at this time?
2015 $bump_time = ($config['bump_type'] == 'm') ? $config['bump_interval'] * 60 : (($config['bump_type'] == 'h') ? $config['bump_interval'] * 3600 : $config['bump_interval'] * 86400);
2017 // Check bump time
2018 if ($last_post_time + $bump_time > time())
2020 return false;
2023 // Check bumper, only topic poster and last poster are allowed to bump
2024 if ($topic_poster != $user->data['user_id'] && $last_topic_poster != $user->data['user_id'] && !$auth->acl_get('m_', $forum_id))
2026 return false;
2029 // A bump time of 0 will completely disable the bump feature... not intended but might be useful.
2030 return $bump_time;
2034 * Generates a text with approx. the specified length which contains the specified words and their context
2036 * @param string $text The full text from which context shall be extracted
2037 * @param string $words An array of words which should be contained in the result, * is allowed as a wildcard
2038 * @param int $length The desired length of the resulting text, however the result might be shorter or longer than this value
2040 * @return string Context of the specified words seperated by "..."
2042 function get_context($text, $words, $length = 400)
2044 // first replace all whitespaces with single spaces
2045 $text = preg_replace('/\s+/', ' ', $text);
2047 $word_indizes = array();
2048 if (sizeof($words))
2050 $match = '';
2051 // find the starting indizes of all words
2052 foreach ($words as $word)
2054 if (preg_match('#(?:[^\w]|^)(' . str_replace('\*', '\w*?', preg_quote($word, '#')) . ')(?:[^\w]|$)#i', $text, $match))
2056 $pos = strpos($text, $match[1]);
2057 if ($pos !== false)
2059 $word_indizes[] = $pos;
2063 unset($match);
2065 if (sizeof($word_indizes))
2067 $word_indizes = array_unique($word_indizes);
2068 sort($word_indizes);
2070 $wordnum = sizeof($word_indizes);
2071 // number of characters on the right and left side of each word
2072 $sequence_length = (int) ($length / (2 * $wordnum)) - 2;
2073 $final_text = '';
2074 $word = $j = 0;
2075 $final_text_index = -1;
2077 // cycle through every character in the original text
2078 for ($i = $word_indizes[$word], $n = strlen($text); $i < $n; $i++)
2080 // if the current position is the start of one of the words then append $sequence_length characters to the final text
2081 if (isset($word_indizes[$word]) && ($i == $word_indizes[$word]))
2083 if ($final_text_index < $i - $sequence_length - 1)
2085 $final_text .= '... ' . preg_replace('#^([^ ]*)#', '', substr($text, $i - $sequence_length, $sequence_length));
2087 else
2089 // if the final text is already nearer to the current word than $sequence_length we only append the text
2090 // from its current index on and distribute the unused length to all other sequenes
2091 $sequence_length += (int) (($final_text_index - $i + $sequence_length + 1) / (2 * $wordnum));
2092 $final_text .= substr($text, $final_text_index + 1, $i - $final_text_index - 1);
2094 $final_text_index = $i - 1;
2096 // add the following characters to the final text (see below)
2097 $word++;
2098 $j = 1;
2101 if ($j > 0)
2103 // add the character to the final text and increment the sequence counter
2104 $final_text .= $text[$i];
2105 $final_text_index++;
2106 $j++;
2108 // if this is a whitespace then check whether we are done with this sequence
2109 if ($text[$i] == ' ')
2111 // only check whether we have to exit the context generation completely if we haven't already reached the end anyway
2112 if ($i + 4 < $n)
2114 if (($j > $sequence_length && $word >= $wordnum) || strlen($final_text) > $length)
2116 $final_text .= ' ...';
2117 break;
2120 else
2122 // make sure the text really reaches the end
2123 $j -= 4;
2126 // stop context generation and wait for the next word
2127 if ($j > $sequence_length)
2129 $j = 0;
2134 return $final_text;
2138 if (!sizeof($words) || !sizeof($word_indizes))
2140 return (strlen($text) >= $length + 3) ? substr($text, 0, $length) . '...' : $text;
2145 * Decode text whereby text is coming from the db and expected to be pre-parsed content
2146 * We are placing this outside of the message parser because we are often in need of it...
2148 function decode_message(&$message, $bbcode_uid = '')
2150 global $config;
2152 if ($bbcode_uid)
2154 $match = array('<br />', "[/*:m:$bbcode_uid]", ":u:$bbcode_uid", ":o:$bbcode_uid", ":$bbcode_uid");
2155 $replace = array("\n", '', '', '', '');
2157 else
2159 $match = array('<br />');
2160 $replace = array("\n");
2163 $message = str_replace($match, $replace, $message);
2165 $match = get_preg_expression('bbcode_htm');
2166 $replace = array('\1', '\2', '\1', '', '');
2168 $message = preg_replace($match, $replace, $message);
2172 * Strips all bbcode from a text and returns the plain content
2174 function strip_bbcode(&$text, $uid = '')
2176 if (!$uid)
2178 $uid = '[0-9a-z]{5,}';
2181 $text = preg_replace("#\[\/?[a-z0-9\*\+\-]+(?:=.*?)?(?::[a-z])?(\:?$uid)\]#", ' ', $text);
2183 $match = get_preg_expression('bbcode_htm');
2184 $replace = array('\1', '\2', '\1', '', '');
2186 $text = preg_replace($match, $replace, $text);
2190 * For display of custom parsed text on user-facing pages
2191 * Expects $text to be the value directly from the database (stored value)
2193 function generate_text_for_display($text, $uid, $bitfield, $flags)
2195 static $bbcode;
2197 if (!$text)
2199 return '';
2202 $text = str_replace("\n", '<br />', censor_text($text));
2204 // Parse bbcode if bbcode uid stored and bbcode enabled
2205 if ($uid && ($flags & OPTION_FLAG_BBCODE))
2207 if (!class_exists('bbcode'))
2209 global $phpbb_root_path, $phpEx;
2210 include_once($phpbb_root_path . 'includes/bbcode.' . $phpEx);
2213 if (empty($bbcode))
2215 $bbcode = new bbcode($bitfield);
2217 else
2219 $bbcode->bbcode($bitfield);
2222 $bbcode->bbcode_second_pass($text, $uid);
2225 $text = smiley_text($text, !($flags & OPTION_FLAG_SMILIES));
2227 return $text;
2231 * For parsing custom parsed text to be stored within the database.
2232 * This function additionally returns the uid and bitfield that needs to be stored.
2233 * Expects $text to be the value directly from request_var() and in it's non-parsed form
2235 function generate_text_for_storage(&$text, &$uid, &$bitfield, &$flags, $allow_bbcode = false, $allow_urls = false, $allow_smilies = false)
2237 global $phpbb_root_path, $phpEx;
2239 $uid = '';
2240 $bitfield = '';
2242 if (!$text)
2244 return;
2247 if (!class_exists('parse_message'))
2249 include_once($phpbb_root_path . 'includes/message_parser.' . $phpEx);
2252 $message_parser = new parse_message($text);
2253 $message_parser->parse($allow_bbcode, $allow_urls, $allow_smilies);
2255 $text = $message_parser->message;
2256 $uid = $message_parser->bbcode_uid;
2258 // If the bbcode_bitfield is empty, there is no need for the uid to be stored.
2259 if (!$message_parser->bbcode_bitfield)
2261 $uid = '';
2264 $flags = (($allow_bbcode) ? 1 : 0) + (($allow_smilies) ? 2 : 0) + (($allow_urls) ? 4 : 0);
2265 $bitfield = $message_parser->bbcode_bitfield;
2267 return;
2271 * For decoding custom parsed text for edits as well as extracting the flags
2272 * Expects $text to be the value directly from the database (pre-parsed content)
2274 function generate_text_for_edit($text, $uid, $flags)
2276 global $phpbb_root_path, $phpEx;
2278 decode_message($text, $uid);
2280 return array(
2281 'allow_bbcode' => ($flags & OPTION_FLAG_BBCODE) ? 1 : 0,
2282 'allow_smilies' => ($flags & OPTION_FLAG_SMILIES) ? 1 : 0,
2283 'allow_urls' => ($flags & OPTION_FLAG_LINKS) ? 1 : 0,
2284 'text' => $text
2289 * make_clickable function
2291 * Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx.
2292 * Cuts down displayed size of link if over 50 chars, turns absolute links
2293 * into relative versions when the server/script path matches the link
2295 function make_clickable($text, $server_url = false)
2297 if ($server_url === false)
2299 $server_url = generate_board_url();
2302 static $magic_url_match;
2303 static $magic_url_replace;
2305 if (!is_array($magic_url_match))
2307 $magic_url_match = $magic_url_replace = array();
2308 // Be sure to not let the matches cross over. ;)
2310 // relative urls for this board
2311 $magic_url_match[] = '#(^|[\n ]|\()(' . preg_quote($server_url, '#') . ')/(([^[ \t\n\r<"\'\)&]+|&(?!lt;|quot;))*)#ie';
2312 $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 -->'";
2314 // matches a xxxx://aaaaa.bbb.cccc. ...
2315 $magic_url_match[] = '#(^|[\n ]|\()([\w]+:/{2}.*?([^[ \t\n\r<"\'\)&]+|&(?!lt;|quot;))*)#ie';
2316 $magic_url_replace[] = "'\$1<!-- m --><a href=\"\$2\">' . ((strlen('\$2') > 55) ? substr(str_replace('&amp;', '&', '\$2'), 0, 39) . ' ... ' . substr(str_replace('&amp;', '&', '\$2'), -10) : '\$2') . '</a><!-- m -->'";
2318 // matches a "www.xxxx.yyyy[/zzzz]" kinda lazy URL thing
2319 $magic_url_match[] = '#(^|[\n ]|\()(w{3}\.[\w\-]+\.[\w\-.\~]+(?:[^[ \t\n\r<"\'\)&]+|&(?!lt;|quot;))*)#ie';
2320 $magic_url_replace[] = "'\$1<!-- w --><a href=\"http://\$2\">' . ((strlen('\$2') > 55) ? substr(str_replace('&amp;', '&', '\$2'), 0, 39) . ' ... ' . substr(str_replace('&amp;', '&', '\$2'), -10) : '\$2') . '</a><!-- w -->'";
2322 // matches an email@domain type address at the start of a line, or after a space or after what might be a BBCode.
2323 $magic_url_match[] = '/(^|[\n ]|\()(' . get_preg_expression('email') . ')/ie';
2324 $magic_url_replace[] = "'\$1<!-- e --><a href=\"mailto:\$2\">' . ((strlen('\$2') > 55) ? substr('\$2', 0, 39) . ' ... ' . substr('\$2', -10) : '\$2') . '</a><!-- e -->'";
2327 return preg_replace($magic_url_match, $magic_url_replace, $text);
2331 * Censoring
2333 function censor_text($text)
2335 static $censors;
2336 global $cache;
2338 if (!isset($censors) || !is_array($censors))
2340 $censors = array();
2342 // obtain_word_list is taking care of the users censor option and the board-wide option
2343 $cache->obtain_word_list($censors);
2346 if (sizeof($censors))
2348 return preg_replace($censors['match'], $censors['replace'], $text);
2351 return $text;
2355 * Smiley processing
2357 function smiley_text($text, $force_option = false)
2359 global $config, $user, $phpbb_root_path;
2361 if ($force_option || !$config['allow_smilies'] || !$user->optionget('viewsmilies'))
2363 return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#', '\1', $text);
2365 else
2367 return str_replace('<img src="{SMILIES_PATH}', '<img src="' . $phpbb_root_path . $config['smilies_path'], $text);
2372 * Inline Attachment processing
2374 function parse_inline_attachments(&$text, &$attachments, &$update_count, $forum_id = 0, $preview = false)
2376 global $config, $user;
2378 if (!function_exists('display_attachments'))
2380 global $phpbb_root_path, $phpEx;
2381 include_once("{$phpbb_root_path}includes/functions_display.$phpEx");
2384 $attachments = display_attachments($forum_id, NULL, $attachments, $update_count, false, true);
2385 $tpl_size = sizeof($attachments);
2387 $unset_tpl = array();
2389 preg_match_all('#<!\-\- ia([0-9]+) \-\->(.*?)<!\-\- ia\1 \-\->#', $text, $matches, PREG_PATTERN_ORDER);
2391 $replace = array();
2392 foreach ($matches[0] as $num => $capture)
2394 // Flip index if we are displaying the reverse way
2395 $index = ($config['display_order']) ? ($tpl_size-($matches[1][$num] + 1)) : $matches[1][$num];
2397 $replace['from'][] = $matches[0][$num];
2398 $replace['to'][] = (isset($attachments[$index])) ? $attachments[$index] : sprintf($user->lang['MISSING_INLINE_ATTACHMENT'], $matches[2][array_search($index, $matches[1])]);
2400 $unset_tpl[] = $index;
2403 if (isset($replace['from']))
2405 $text = str_replace($replace['from'], $replace['to'], $text);
2408 return array_unique($unset_tpl);
2412 * Check if extension is allowed to be posted within forum X (forum_id 0 == private messaging)
2414 function extension_allowed($forum_id, $extension, &$extensions)
2416 if (!sizeof($extensions))
2418 global $cache;
2420 $extensions = array();
2421 $cache->obtain_attach_extensions($extensions);
2424 if (!isset($extensions['_allowed_'][$extension]))
2426 return false;
2429 $check = $extensions['_allowed_'][$extension];
2431 if (is_array($check))
2433 // Check for private messaging AND all forums allowed
2434 if (sizeof($check) == 1 && $check[0] == 0)
2436 return true;
2439 return (!in_array($forum_id, $check)) ? false : true;
2442 return ($forum_id == 0) ? false : true;
2445 // Little helpers
2448 * Little helper for the build_hidden_fields function
2450 function _build_hidden_fields($key, $value, $specialchar)
2452 $hidden_fields = '';
2454 if (!is_array($value))
2456 $key = ($specialchar) ? htmlspecialchars($key) : $key;
2457 $value = ($specialchar) ? htmlspecialchars($value) : $value;
2459 $hidden_fields .= '<input type="hidden" name="' . $key . '" value="' . $value . '" />' . "\n";
2461 else
2463 foreach ($value as $_key => $_value)
2465 $hidden_fields .= _build_hidden_fields($key . '[' . $_key . ']', $_value, $specialchar);
2469 return $hidden_fields;
2473 * Build simple hidden fields from array
2475 function build_hidden_fields($field_ary, $specialchar = false)
2477 $s_hidden_fields = '';
2479 foreach ($field_ary as $name => $vars)
2481 $s_hidden_fields .= _build_hidden_fields($name, $vars, $specialchar);
2484 return $s_hidden_fields;
2488 * Parse cfg file
2490 function parse_cfg_file($filename, $lines = false)
2492 $parsed_items = array();
2494 if ($lines === false)
2496 $lines = file($filename);
2499 foreach ($lines as $line)
2501 $line = trim($line);
2503 if (!$line || $line{0} == '#' || ($delim_pos = strpos($line, '=')) === false)
2505 continue;
2508 // Determine first occurrence, since in values the equal sign is allowed
2509 $key = strtolower(trim(substr($line, 0, $delim_pos)));
2510 $value = trim(substr($line, $delim_pos + 1));
2512 if (in_array($value, array('off', 'false', '0')))
2514 $value = false;
2516 else if (in_array($value, array('on', 'true', '1')))
2518 $value = true;
2520 else if (!trim($value))
2522 $value = '';
2524 else if (($value{0} == "'" && $value{sizeof($value)-1} == "'") || ($value{0} == '"' && $value{sizeof($value)-1} == '"'))
2526 $value = substr($value, 1, sizeof($value)-2);
2529 $parsed_items[$key] = $value;
2532 return $parsed_items;
2536 * Add log event
2538 function add_log()
2540 global $db, $user;
2542 $args = func_get_args();
2544 $mode = array_shift($args);
2545 $reportee_id = ($mode == 'user') ? intval(array_shift($args)) : '';
2546 $forum_id = ($mode == 'mod') ? intval(array_shift($args)) : '';
2547 $topic_id = ($mode == 'mod') ? intval(array_shift($args)) : '';
2548 $action = array_shift($args);
2549 $data = (!sizeof($args)) ? '' : serialize($args);
2551 $sql_ary = array(
2552 'user_id' => (empty($user->data)) ? ANONYMOUS : $user->data['user_id'],
2553 'log_ip' => $user->ip,
2554 'log_time' => time(),
2555 'log_operation' => $action,
2556 'log_data' => $data,
2559 switch ($mode)
2561 case 'admin':
2562 $sql_ary['log_type'] = LOG_ADMIN;
2563 break;
2565 case 'mod':
2566 $sql_ary += array(
2567 'log_type' => LOG_MOD,
2568 'forum_id' => $forum_id,
2569 'topic_id' => $topic_id
2571 break;
2573 case 'user':
2574 $sql_ary += array(
2575 'log_type' => LOG_USERS,
2576 'reportee_id' => $reportee_id
2578 break;
2580 case 'critical':
2581 $sql_ary['log_type'] = LOG_CRITICAL;
2582 break;
2584 default:
2585 return false;
2588 $db->sql_query('INSERT INTO ' . LOG_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
2590 return $db->sql_nextid();
2594 * Return a nicely formatted backtrace (parts from the php manual by diz at ysagoon dot com)
2596 function get_backtrace()
2598 global $phpbb_root_path;
2600 $output = '<div style="font-family: monospace;">';
2601 $backtrace = debug_backtrace();
2602 $path = phpbb_realpath($phpbb_root_path);
2604 foreach ($backtrace as $number => $trace)
2606 // We skip the first one, because it only shows this file/function
2607 if ($number == 0)
2609 continue;
2612 // Strip the current directory from path
2613 $trace['file'] = str_replace(array($path, '\\'), array('', '/'), $trace['file']);
2614 $trace['file'] = substr($trace['file'], 1);
2615 $args = array();
2617 // If include/require/include_once is not called, do not show arguments - they may contain sensible informations
2618 if (!in_array($trace['function'], array('include', 'require', 'include_once')))
2620 unset($trace['args']);
2622 else
2624 // Path...
2625 if (!empty($trace['args'][0]))
2627 $argument = htmlspecialchars($trace['args'][0]);
2628 $argument = str_replace(array($path, '\\'), array('', '/'), $argument);
2629 $argument = substr($argument, 1);
2630 $args[] = "'{$argument}'";
2634 $trace['class'] = (!isset($trace['class'])) ? '' : $trace['class'];
2635 $trace['type'] = (!isset($trace['type'])) ? '' : $trace['type'];
2637 $output .= '<br />';
2638 $output .= '<b>FILE:</b> ' . htmlspecialchars($trace['file']) . '<br />';
2639 $output .= '<b>LINE:</b> ' . $trace['line'] . '<br />';
2641 $output .= '<b>CALL:</b> ' . htmlspecialchars($trace['class'] . $trace['type'] . $trace['function']) . '(' . ((sizeof($args)) ? implode(', ', $args) : '') . ')<br />';
2643 $output .= '</div>';
2644 return $output;
2648 * This function returns a regular expression pattern for commonly used expressions
2649 * Use with / as delimiter for email mode
2650 * mode can be: email|bbcode_htm
2652 function get_preg_expression($mode)
2654 switch ($mode)
2656 case 'email':
2657 return '[a-z0-9&\'\.\-_\+]+@[a-z0-9\-]+\.([a-z0-9\-]+\.)*[a-z]+';
2658 break;
2660 case 'bbcode_htm':
2661 return array(
2662 '#<!\-\- e \-\-><a href="mailto:(.*?)">.*?</a><!\-\- e \-\->#',
2663 '#<!\-\- (l|m|w) \-\-><a href="(.*?)">.*?</a><!\-\- \1 \-\->#',
2664 '#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#',
2665 '#<!\-\- .*? \-\->#s',
2666 '#<.*?>#s',
2668 break;
2671 return '';
2675 * Truncates string while retaining special characters if going over the max length
2676 * The default max length is 60 at the moment
2678 function truncate_string($string, $max_length = 60)
2680 $chars = array();
2682 // split the multibyte characters first
2683 $string_ary = preg_split('/(&#[0-9]+;)/', $string, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
2685 // Now go through the array and split the other characters
2686 foreach ($string_ary as $key => $value)
2688 if (strpos($value, '&#') === 0)
2690 $chars[] = $value;
2691 continue;
2694 // decode html entities and put them back later
2695 $_chars = str_split(html_entity_decode($value));
2696 $chars = array_merge($chars, array_map('htmlspecialchars', $_chars));
2699 // Now check the length ;)
2700 if (sizeof($chars) <= $max_length)
2702 return $string;
2705 // Cut off the last elements from the array
2706 return implode('', array_slice($chars, 0, $max_length));
2709 // Handler, header and footer
2712 * Error and message handler, call with trigger_error if reqd
2714 function msg_handler($errno, $msg_text, $errfile, $errline)
2716 global $cache, $db, $auth, $template, $config, $user;
2717 global $phpEx, $phpbb_root_path, $starttime, $msg_title, $msg_long_text;
2719 // Message handler is stripping text. In case we need it, we are possible to define long text...
2720 if (isset($msg_long_text) && $msg_long_text && !$msg_text)
2722 $msg_text = $msg_long_text;
2725 switch ($errno)
2727 case E_NOTICE:
2728 case E_WARNING:
2730 // Check the error reporting level and return if the error level does not match
2731 // Additionally do not display notices if we suppress them via @
2732 // If DEBUG_EXTRA is defined the default level is E_ALL
2733 if (($errno & ((defined('DEBUG_EXTRA') && error_reporting()) ? E_ALL : error_reporting())) == 0)
2735 return;
2739 * @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
2741 if (defined('DEBUG'))
2743 if (strpos($errfile, 'cache') === false && strpos($errfile, 'template.') === false)
2745 // remove complete path to installation, with the risk of changing backslashes meant to be there
2746 $errfile = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $errfile);
2747 $msg_text = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $msg_text);
2749 echo '<b>[phpBB Debug] PHP Notice</b>: in file <b>' . $errfile . '</b> on line <b>' . $errline . '</b>: <b>' . $msg_text . '</b><br />' . "\n";
2753 break;
2755 case E_USER_ERROR:
2757 garbage_collection();
2759 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
2760 echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">';
2761 echo '<head>';
2762 echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
2763 echo '<title>' . $msg_title . '</title>';
2764 echo '<link href="' . $phpbb_root_path . 'adm/style/admin.css" rel="stylesheet" type="text/css" media="screen" />';
2765 echo '</head>';
2766 echo '<body id="errorpage">';
2767 echo '<div id="wrap">';
2768 echo ' <div id="page-header">';
2769 echo ' <a href="' . $phpbb_root_path . '">Return to forum index</a>';
2770 echo ' </div>';
2771 echo ' <div id="page-body">';
2772 echo ' <div class="panel">';
2773 echo ' <span class="corners-top"><span></span></span>';
2774 echo ' <div id="content">';
2775 echo ' <h1>General Error</h1>';
2777 echo ' <h2>' . $msg_text . '</h2>';
2779 if (!empty($config['board_contact']))
2781 echo ' <p>Please notify the board administrator or webmaster : <a href="mailto:' . $config['board_contact'] . '">' . $config['board_contact'] . '</a></p>';
2784 echo ' </div>';
2785 echo ' <span class="corners-bottom"><span></span></span>';
2786 echo ' </div>';
2787 echo ' </div>';
2788 echo ' <div id="page-footer">';
2789 echo ' Powered by phpBB &copy; ' . date('Y') . ' <a href="http://www.phpbb.com/">phpBB Group</a>';
2790 echo ' </div>';
2791 echo '</div>';
2792 echo '</body>';
2793 echo '</html>';
2795 exit;
2796 break;
2798 case E_USER_WARNING:
2799 case E_USER_NOTICE:
2801 define('IN_ERROR_HANDLER', true);
2803 if (empty($user->data))
2805 $user->session_begin();
2808 // We re-init the auth array to get correct results on login/logout
2809 $auth->acl($user->data);
2811 if (empty($user->lang))
2813 $user->setup();
2816 $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text;
2817 $msg_title = (!isset($msg_title)) ? $user->lang['INFORMATION'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title);
2819 if (!defined('HEADER_INC'))
2821 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
2823 adm_page_header($msg_title);
2825 else
2827 page_header($msg_title);
2831 $template->set_filenames(array(
2832 'body' => 'message_body.html')
2835 $template->assign_vars(array(
2836 'MESSAGE_TITLE' => $msg_title,
2837 'MESSAGE_TEXT' => $msg_text,
2838 'S_USER_WARNING' => ($errno == E_USER_WARNING) ? true : false,
2839 'S_USER_NOTICE' => ($errno == E_USER_NOTICE) ? true : false)
2842 // We do not want the cron script to be called on error messages
2843 define('IN_CRON', true);
2845 if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
2847 adm_page_footer();
2849 else
2851 page_footer();
2854 exit;
2855 break;
2860 * Generate page header
2862 function page_header($page_title = '', $display_online_list = true)
2864 global $db, $config, $template, $SID, $_SID, $user, $auth, $phpEx, $phpbb_root_path;
2866 if (defined('HEADER_INC'))
2868 return;
2871 define('HEADER_INC', true);
2873 // gzip_compression
2874 if ($config['gzip_compress'])
2876 if (@extension_loaded('zlib') && !headers_sent())
2878 ob_start('ob_gzhandler');
2882 // Generate logged in/logged out status
2883 if ($user->data['user_id'] != ANONYMOUS)
2885 $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=logout');
2886 $l_login_logout = sprintf($user->lang['LOGOUT_USER'], $user->data['username']);
2888 else
2890 $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login');
2891 $l_login_logout = $user->lang['LOGIN'];
2894 // Last visit date/time
2895 $s_last_visit = ($user->data['user_id'] != ANONYMOUS) ? $user->format_date($user->data['session_last_visit']) : '';
2897 // Get users online list ... if required
2898 $l_online_users = $online_userlist = $l_online_record = '';
2900 if ($config['load_online'] && $config['load_online_time'] && $display_online_list)
2902 $userlist_ary = $userlist_visible = array();
2903 $logged_visible_online = $logged_hidden_online = $guests_online = $prev_user_id = 0;
2904 $prev_session_ip = $reading_sql = '';
2906 if (!empty($_REQUEST['f']))
2908 $f = request_var('f', 0);
2910 // Do not change this (it is defined as _f_={forum_id}x within session.php)
2911 $reading_sql = " AND s.session_page LIKE '%\_f\_={$f}x%'";
2913 // Specify escape character for MSSQL
2914 if (SQL_LAYER == 'mssql' || SQL_LAYER == 'mssql_odbc')
2916 $reading_sql .= " ESCAPE '\\'";
2920 // Get number of online guests
2921 if (!$config['load_online_guests'])
2923 $sql = 'SELECT COUNT(DISTINCT s.session_ip) as num_guests
2924 FROM ' . SESSIONS_TABLE . ' s
2925 WHERE s.session_user_id = ' . ANONYMOUS . '
2926 AND s.session_time >= ' . (time() - ($config['load_online_time'] * 60)) .
2927 $reading_sql;
2928 $result = $db->sql_query($sql);
2929 $guests_online = (int) $db->sql_fetchfield('num_guests');
2930 $db->sql_freeresult($result);
2933 $sql = 'SELECT u.username, u.user_id, u.user_type, u.user_allow_viewonline, u.user_colour, s.session_ip, s.session_viewonline
2934 FROM ' . USERS_TABLE . ' u, ' . SESSIONS_TABLE . ' s
2935 WHERE s.session_time >= ' . (time() - (intval($config['load_online_time']) * 60)) .
2936 $reading_sql .
2937 ((!$config['load_online_guests']) ? ' AND s.session_user_id <> ' . ANONYMOUS : '') . '
2938 AND u.user_id = s.session_user_id
2939 ORDER BY u.username ASC, s.session_ip ASC';
2940 $result = $db->sql_query($sql);
2942 while ($row = $db->sql_fetchrow($result))
2944 // User is logged in and therefore not a guest
2945 if ($row['user_id'] != ANONYMOUS)
2947 // Skip multiple sessions for one user
2948 if ($row['user_id'] != $prev_user_id)
2950 if ($row['user_colour'])
2952 $user_colour = ' style="color:#' . $row['user_colour'] . '"';
2953 $row['username'] = '<strong>' . $row['username'] . '</strong>';
2955 else
2957 $user_colour = '';
2960 if ($row['user_allow_viewonline'] && $row['session_viewonline'])
2962 $user_online_link = $row['username'];
2963 $logged_visible_online++;
2965 else
2967 $user_online_link = '<em>' . $row['username'] . '</em>';
2968 $logged_hidden_online++;
2971 if (($row['user_allow_viewonline'] && $row['session_viewonline']) || $auth->acl_get('u_viewonline'))
2973 if ($row['user_type'] <> USER_IGNORE)
2975 $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>';
2977 else
2979 $user_online_link = ($user_colour) ? '<span' . $user_colour . '>' . $user_online_link . '</span>' : $user_online_link;
2982 $online_userlist .= ($online_userlist != '') ? ', ' . $user_online_link : $user_online_link;
2986 $prev_user_id = $row['user_id'];
2988 else
2990 // Skip multiple sessions for one user
2991 if ($row['session_ip'] != $prev_session_ip)
2993 $guests_online++;
2997 $prev_session_ip = $row['session_ip'];
2999 $db->sql_freeresult($result);
3001 if (!$online_userlist)
3003 $online_userlist = $user->lang['NO_ONLINE_USERS'];
3006 if (empty($_REQUEST['f']))
3008 $online_userlist = $user->lang['REGISTERED_USERS'] . ' ' . $online_userlist;
3010 else
3012 $l_online = ($guests_online == 1) ? $user->lang['BROWSING_FORUM_GUEST'] : $user->lang['BROWSING_FORUM_GUESTS'];
3013 $online_userlist = sprintf($l_online, $online_userlist, $guests_online);
3016 $total_online_users = $logged_visible_online + $logged_hidden_online + $guests_online;
3018 if ($total_online_users > $config['record_online_users'])
3020 set_config('record_online_users', $total_online_users, true);
3021 set_config('record_online_date', time(), true);
3024 // Build online listing
3025 $vars_online = array(
3026 'ONLINE' => array('total_online_users', 'l_t_user_s'),
3027 'REG' => array('logged_visible_online', 'l_r_user_s'),
3028 'HIDDEN' => array('logged_hidden_online', 'l_h_user_s'),
3029 'GUEST' => array('guests_online', 'l_g_user_s')
3032 foreach ($vars_online as $l_prefix => $var_ary)
3034 switch (${$var_ary[0]})
3036 case 0:
3037 ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_ZERO_TOTAL'];
3038 break;
3040 case 1:
3041 ${$var_ary[1]} = $user->lang[$l_prefix . '_USER_TOTAL'];
3042 break;
3044 default:
3045 ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_TOTAL'];
3046 break;
3049 unset($vars_online);
3051 $l_online_users = sprintf($l_t_user_s, $total_online_users);
3052 $l_online_users .= sprintf($l_r_user_s, $logged_visible_online);
3053 $l_online_users .= sprintf($l_h_user_s, $logged_hidden_online);
3054 $l_online_users .= sprintf($l_g_user_s, $guests_online);
3056 $l_online_record = sprintf($user->lang['RECORD_ONLINE_USERS'], $config['record_online_users'], $user->format_date($config['record_online_date']));
3058 $l_online_time = ($config['load_online_time'] == 1) ? 'VIEW_ONLINE_TIME' : 'VIEW_ONLINE_TIMES';
3059 $l_online_time = sprintf($user->lang[$l_online_time], $config['load_online_time']);
3061 else
3063 $l_online_time = '';
3066 $l_privmsgs_text = $l_privmsgs_text_unread = '';
3067 $s_privmsg_new = false;
3069 // Obtain number of new private messages if user is logged in
3070 if (isset($user->data['is_registered']) && $user->data['is_registered'])
3072 if ($user->data['user_new_privmsg'])
3074 $l_message_new = ($user->data['user_new_privmsg'] == 1) ? $user->lang['NEW_PM'] : $user->lang['NEW_PMS'];
3075 $l_privmsgs_text = sprintf($l_message_new, $user->data['user_new_privmsg']);
3077 if (!$user->data['user_last_privmsg'] || $user->data['user_last_privmsg'] > $user->data['session_last_visit'])
3079 $sql = 'UPDATE ' . USERS_TABLE . '
3080 SET user_last_privmsg = ' . $user->data['session_last_visit'] . '
3081 WHERE user_id = ' . $user->data['user_id'];
3082 $db->sql_query($sql);
3084 $s_privmsg_new = true;
3086 else
3088 $s_privmsg_new = false;
3091 else
3093 $l_privmsgs_text = $user->lang['NO_NEW_PM'];
3094 $s_privmsg_new = false;
3097 $l_privmsgs_text_unread = '';
3099 if ($user->data['user_unread_privmsg'] && $user->data['user_unread_privmsg'] != $user->data['user_new_privmsg'])
3101 $l_message_unread = ($user->data['user_unread_privmsg'] == 1) ? $user->lang['UNREAD_PM'] : $user->lang['UNREAD_PMS'];
3102 $l_privmsgs_text_unread = sprintf($l_message_unread, $user->data['user_unread_privmsg']);
3106 // Which timezone?
3107 $tz = ($user->data['user_id'] != ANONYMOUS) ? strval(doubleval($user->data['user_timezone'])) : strval(doubleval($config['board_timezone']));
3109 // The following assigns all _common_ variables that may be used at any point in a template.
3110 $template->assign_vars(array(
3111 'SITENAME' => $config['sitename'],
3112 'SITE_DESCRIPTION' => $config['site_desc'],
3113 'PAGE_TITLE' => $page_title,
3114 'SCRIPT_NAME' => str_replace('.' . $phpEx, '', $user->page['page_name']),
3115 'LAST_VISIT_DATE' => sprintf($user->lang['YOU_LAST_VISIT'], $s_last_visit),
3116 'CURRENT_TIME' => sprintf($user->lang['CURRENT_TIME'], $user->format_date(time(), false, true)),
3117 'TOTAL_USERS_ONLINE' => $l_online_users,
3118 'LOGGED_IN_USER_LIST' => $online_userlist,
3119 'RECORD_USERS' => $l_online_record,
3120 'PRIVATE_MESSAGE_INFO' => $l_privmsgs_text,
3121 'PRIVATE_MESSAGE_INFO_UNREAD' => $l_privmsgs_text_unread,
3123 'SID' => $SID,
3124 '_SID' => $_SID,
3125 'SESSION_ID' => $user->session_id,
3126 'ROOT_PATH' => $phpbb_root_path,
3128 'L_LOGIN_LOGOUT' => $l_login_logout,
3129 'L_INDEX' => $user->lang['FORUM_INDEX'],
3130 'L_ONLINE_EXPLAIN' => $l_online_time,
3132 'U_PRIVATEMSGS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=inbox'),
3133 'U_RETURN_INBOX' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=inbox'),
3134 'UA_RETURN_INBOX' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox', false),
3135 'U_POPUP_PM' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=popup'),
3136 'UA_POPUP_PM' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=popup', false),
3137 'U_MEMBERLIST' => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
3138 'U_MEMBERSLIST' => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
3139 'U_VIEWONLINE' => append_sid("{$phpbb_root_path}viewonline.$phpEx"),
3140 'U_LOGIN_LOGOUT' => $u_login_logout,
3141 'U_INDEX' => append_sid("{$phpbb_root_path}index.$phpEx"),
3142 'U_SEARCH' => append_sid("{$phpbb_root_path}search.$phpEx"),
3143 'U_REGISTER' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register'),
3144 'U_PROFILE' => append_sid("{$phpbb_root_path}ucp.$phpEx"),
3145 'U_MODCP' => append_sid("{$phpbb_root_path}mcp.$phpEx", false, true, $user->session_id),
3146 'U_FAQ' => append_sid("{$phpbb_root_path}faq.$phpEx"),
3147 'U_SEARCH_SELF' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=egosearch'),
3148 'U_SEARCH_NEW' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=newposts'),
3149 'U_SEARCH_UNANSWERED' => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unanswered'),
3150 'U_SEARCH_ACTIVE_TOPICS'=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=active_topics'),
3151 'U_DELETE_COOKIES' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=delete_cookies'),
3152 'U_TEAM' => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=leaders'),
3153 'U_RESTORE_PERMISSIONS' => ($user->data['user_perm_from'] && $auth->acl_get('a_switchperm')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=restore_perm') : '',
3155 'S_USER_LOGGED_IN' => ($user->data['user_id'] != ANONYMOUS) ? true : false,
3156 'S_BOARD_DISABLED' => ($config['board_disable'] && !defined('IN_LOGIN') && !$auth->acl_gets('a_', 'm_')) ? true : false,
3157 'S_REGISTERED_USER' => $user->data['is_registered'],
3158 'S_IS_BOT' => $user->data['is_bot'],
3159 'S_USER_PM_POPUP' => $user->optionget('popuppm'),
3160 'S_USER_LANG' => $user->data['user_lang'],
3161 'S_USER_BROWSER' => (isset($user->data['session_browser'])) ? $user->data['session_browser'] : $user->lang['UNKNOWN_BROWSER'],
3162 'S_USERNAME' => $user->data['username'],
3163 'S_CONTENT_DIRECTION' => $user->lang['DIRECTION'],
3164 'S_CONTENT_ENCODING' => 'UTF-8',
3165 'S_CONTENT_DIR_LEFT' => $user->lang['LEFT'],
3166 'S_CONTENT_DIR_RIGHT' => $user->lang['RIGHT'],
3167 '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], ''),
3168 'S_DISPLAY_ONLINE_LIST' => ($l_online_time) ? 1 : 0,
3169 'S_DISPLAY_SEARCH' => (!$config['load_search']) ? 0 : (isset($auth) ? ($auth->acl_get('u_search') && $auth->acl_getf_global('f_search')) : 1),
3170 'S_DISPLAY_PM' => ($config['allow_privmsg'] && $user->data['is_registered']) ? 1 : 0,
3171 'S_DISPLAY_MEMBERLIST' => (isset($auth)) ? $auth->acl_get('u_viewprofile') : 0,
3172 'S_NEW_PM' => ($s_privmsg_new) ? 1 : 0,
3174 'T_THEME_PATH' => "{$phpbb_root_path}styles/" . $user->theme['theme_path'] . '/theme',
3175 'T_TEMPLATE_PATH' => "{$phpbb_root_path}styles/" . $user->theme['template_path'] . '/template',
3176 'T_IMAGESET_PATH' => "{$phpbb_root_path}styles/" . $user->theme['imageset_path'] . '/imageset',
3177 'T_IMAGESET_LANG_PATH' => "{$phpbb_root_path}styles/" . $user->theme['imageset_path'] . '/imageset/' . $user->data['user_lang'],
3178 'T_IMAGES_PATH' => "{$phpbb_root_path}images/",
3179 'T_SMILIES_PATH' => "{$phpbb_root_path}{$config['smilies_path']}/",
3180 'T_AVATAR_PATH' => "{$phpbb_root_path}{$config['avatar_path']}/",
3181 'T_AVATAR_GALLERY_PATH' => "{$phpbb_root_path}{$config['avatar_gallery_path']}/",
3182 'T_ICONS_PATH' => "{$phpbb_root_path}{$config['icons_path']}/",
3183 'T_RANKS_PATH' => "{$phpbb_root_path}{$config['ranks_path']}/",
3184 'T_UPLOAD_PATH' => "{$phpbb_root_path}{$config['upload_path']}/",
3185 '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'] . '&amp;lang=' . $user->data['user_lang'],
3186 'T_STYLESHEET_NAME' => $user->theme['theme_name'],
3187 'T_THEME_DATA' => (!$user->theme['theme_storedb']) ? '' : $user->theme['theme_data'],
3189 'SITE_LOGO_IMG' => $user->img('site_logo'))
3192 if ($config['send_encoding'])
3194 header('Content-type: text/html; charset=UTF-8');
3196 header('Cache-Control: private, no-cache="set-cookie"');
3197 header('Expires: 0');
3198 header('Pragma: no-cache');
3200 return;
3204 * Generate page footer
3206 function page_footer()
3208 global $db, $config, $template, $user, $auth, $cache, $messenger, $starttime, $phpbb_root_path, $phpEx;
3210 // Output page creation time
3211 if (defined('DEBUG'))
3213 $mtime = explode(' ', microtime());
3214 $totaltime = $mtime[0] + $mtime[1] - $starttime;
3216 if (!empty($_REQUEST['explain']) && $auth->acl_get('a_') && defined('DEBUG_EXTRA') && method_exists($db, 'sql_report'))
3218 $db->sql_report('display');
3221 $debug_output = sprintf('Time : %.3fs | ' . $db->sql_num_queries() . ' Queries | GZIP : ' . (($config['gzip_compress']) ? 'On' : 'Off') . (($user->load) ? ' | Load : ' . $user->load : ''), $totaltime);
3223 if ($auth->acl_get('a_') && defined('DEBUG_EXTRA'))
3225 if (function_exists('memory_get_usage'))
3227 if ($memory_usage = memory_get_usage())
3229 global $base_memory_usage;
3230 $memory_usage -= $base_memory_usage;
3231 $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']);
3233 $debug_output .= ' | Memory Usage: ' . $memory_usage;
3237 $debug_output .= ' | <a href="' . build_url() . '&amp;explain=1">Explain</a>';
3241 $template->assign_vars(array(
3242 'DEBUG_OUTPUT' => (defined('DEBUG')) ? $debug_output : '',
3244 'U_ACP' => ($auth->acl_get('a_') && $user->data['is_registered']) ? "{$phpbb_root_path}adm/index.$phpEx?sid=" . $user->session_id : '')
3247 // Call cron-type script
3248 if (!defined('IN_CRON'))
3250 $cron_type = '';
3252 if (time() - $config['queue_interval'] > $config['last_queue_run'] && !defined('IN_ADMIN') && file_exists($phpbb_root_path . 'cache/queue.' . $phpEx))
3254 // Process email queue
3255 $cron_type = 'queue';
3257 else if (method_exists($cache, 'tidy') && time() - $config['cache_gc'] > $config['cache_last_gc'])
3259 // Tidy the cache
3260 $cron_type = 'tidy_cache';
3262 else if (time() - $config['warnings_gc'] > $config['warnings_last_gc'])
3264 $cron_type = 'tidy_warnings';
3266 else if (time() - $config['database_gc'] > $config['database_last_gc'])
3268 // Tidy the database
3269 $cron_type = 'tidy_database';
3271 else if (time() - $config['search_gc'] > $config['search_last_gc'])
3273 // Tidy the search
3274 $cron_type = 'tidy_search';
3276 else if (time() - $config['session_gc'] > $config['session_last_gc'])
3278 $cron_type = 'tidy_sessions';
3281 if ($cron_type)
3283 $template->assign_var('RUN_CRON_TASK', '<img src="' . $phpbb_root_path . 'cron.' . $phpEx . '?cron_type=' . $cron_type . '" width="1" height="1" alt="cron" />');
3287 $template->display('body');
3289 garbage_collection();
3291 exit;
3295 * Closing the cache object and the database
3296 * Cool function name, eh? We might want to add operations to it later
3298 function garbage_collection()
3300 global $cache, $db;
3302 // Unload cache, must be done before the DB connection if closed
3303 if (!empty($cache))
3305 $cache->unload();
3308 // Close our DB connection.
3309 if (!empty($db))
3311 $db->sql_close();
3316 * @package phpBB3
3318 class bitfield
3320 var $data;
3322 function bitfield($bitfield = '')
3324 $this->data = base64_decode($bitfield);
3329 function get($n)
3331 // Get the ($n / 8)th char
3332 $byte = $n >> 3;
3334 if (!isset($this->data[$byte]))
3336 // Of course, if it doesn't exist then the result if FALSE
3337 return false;
3340 $c = $this->data[$byte];
3342 // Lookup the ($n % 8)th bit of the byte
3343 $bit = 7 - ($n & 7);
3344 return (bool) (ord($c) & (1 << $bit));
3347 function set($n)
3349 $byte = $n >> 3;
3350 $bit = 7 - ($n & 7);
3352 if (isset($this->data[$byte]))
3354 $this->data[$byte] = $this->data[$byte] | chr(1 << $bit);
3356 else
3358 if ($byte - strlen($this->data) > 0)
3360 $this->data .= str_repeat("\0", $byte - strlen($this->data));
3362 $this->data .= chr(1 << $bit);
3366 function clear($n)
3368 $byte = $n >> 3;
3370 if (!isset($this->data[$byte]))
3372 return;
3375 $bit = 7 - ($n & 7);
3376 $this->data[$byte] = $this->data[$byte] &~ chr(1 << $bit);
3379 function get_blob()
3381 return $this->data;
3384 function get_base64()
3386 return base64_encode($this->data);
3389 function get_bin()
3391 $bin = '';
3392 $len = strlen($this->data);
3394 for ($i = 0; $i < $len; ++$i)
3396 $bin .= str_pad(decbin(ord($this->data[$i])), 8, '0', STR_PAD_LEFT);
3399 return $bin;
3402 function get_all_set()
3404 return array_keys(array_filter(str_split($this->get_bin())));
3407 function merge($bitfield)
3409 $this->data = $this->data | $bitfield->get_blob();