/include/functions.php
PHP | 1581 lines | 1062 code | 270 blank | 249 comment | 274 complexity | 09fbdd9c99226d1834a19a55f91f6307 MD5 | raw file
- <?php
- /**
- * Copyright (C) 2015 Panther (https://www.pantherforum.org)
- * based on code by FluxBB copyright (C) 2008-2012 FluxBB
- * License: http://www.gnu.org/licenses/gpl.html GPL version 3 or higher
- */
- //
- // Cookie stuff!
- //
- function check_cookie(&$panther_user)
- {
- global $db, $panther_config;
- $now = time();
- // If the cookie is set and it matches the correct pattern, then read the values from it
- if (isset($_COOKIE[$panther_config['o_cookie_name']]) && preg_match('%^(\d+)\|([0-9a-fA-F]+)\|(\d+)\|([0-9a-fA-F]+)$%', $_COOKIE[$panther_config['o_cookie_name']], $matches))
- {
- $cookie = array(
- 'user_id' => intval($matches[1]),
- 'password_hash' => $matches[2],
- 'expiration_time' => intval($matches[3]),
- 'cookie_hash' => $matches[4],
- );
- }
- // If it has a non-guest user, and hasn't expired
- if (isset($cookie) && $cookie['user_id'] > 1 && $cookie['expiration_time'] > $now)
- {
- // If the cookie has been tampered with
- if (!panther_hash_equals(hash_hmac('sha512', $cookie['user_id'].'|'.$cookie['expiration_time'], $panther_config['o_cookie_seed'].'_cookie_hash'), $cookie['cookie_hash']))
- {
- $expire = $now + 31536000; // The cookie expires after a year
- panther_setcookie(1, panther_hash(uniqid(rand(), true)), $expire);
- set_default_user();
- return;
- }
- $data = array(
- ':id' => $cookie['user_id'],
- );
- // Check if there's a user with the user ID and password hash from the cookie
- $ps = $db->run('SELECT u.*, g.*, o.logged, o.idle FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$db->prefix.'online AS o ON o.user_id=u.id WHERE u.id=:id', $data);
- $panther_user = $ps->fetch();
- // If user authorisation failed
- if (!isset($panther_user['id']) || !panther_hash_equals(hash_hmac('sha512', $panther_user['login_key'], $panther_config['o_cookie_seed'].'_password_hash'), $cookie['password_hash']))
- {
- $expire = $now + 31536000; // The cookie expires after a year
- panther_setcookie(1, panther_hash(uniqid(rand(), true)), $expire);
- set_default_user();
- return;
- }
- // Send a new, updated cookie with a new expiration timestamp
- $expire = ($cookie['expiration_time'] > $now + $panther_config['o_timeout_visit']) ? $now + 1209600 : $now + $panther_config['o_timeout_visit'];
- panther_setcookie($panther_user['id'], $panther_user['login_key'], $expire);
- // Set a default language if the user selected language no longer exists
- if (!file_exists(PANTHER_ROOT.'lang/'.$panther_user['language']))
- $panther_user['language'] = $panther_config['o_default_lang'];
- $style_root = (($panther_config['o_style_path'] != 'style') ? $panther_config['o_style_path'] : PANTHER_ROOT.$panther_config['o_style_path']).'/';
- // Set a default style if the user selected style no longer exists
- if (!file_exists($style_root.$panther_user['style'].'.css'))
- $panther_user['style'] = $panther_config['o_default_style'];
- if (!$panther_user['disp_topics'])
- $panther_user['disp_topics'] = $panther_config['o_disp_topics_default'];
- if (!$panther_user['disp_posts'])
- $panther_user['disp_posts'] = $panther_config['o_disp_posts_default'];
- // Define this if you want this visit to affect the online list and the users last visit data
- if (!defined('PANTHER_QUIET_VISIT'))
- {
- // Update the online list
- if (!$panther_user['logged'])
- {
- $panther_user['logged'] = $now;
- $data = array(
- ':id' => $panther_user['id'],
- ':ident' => $panther_user['username'],
- ':logged' => $panther_user['logged'],
- );
- // REPLACE INTO avoids a user having two rows in the online table
- $db->run('REPLACE INTO '.$db->prefix.'online (user_id, ident, logged) VALUES (:id, :ident, :logged)', $data);
- // Reset tracked topics
- set_tracked_topics(null);
- }
- else
- {
- $data = array(
- ':id' => $panther_user['id'],
- );
- // Special case: We've timed out, but no other user has browsed the forums since we timed out
- if ($panther_user['logged'] < ($now-$panther_config['o_timeout_visit']))
- {
- $update = array(
- 'last_visit' => $panther_user['logged'],
- );
- $db->update('users', $update, 'id=:id', $data);
- $panther_user['last_visit'] = $panther_user['logged'];
- }
- $update = array(
- 'logged' => $now,
- );
- if ($panther_user['idle'] == '1')
- $update['idle'] = 0;
- $db->update('online', $update, 'user_id=:id', $data);
- // Update tracked topics with the current expire time
- if (isset($_COOKIE[$panther_config['o_cookie_name'].'_track']))
- forum_setcookie($panther_config['o_cookie_name'].'_track', $_COOKIE[$panther_config['o_cookie_name'].'_track'], $now + $panther_config['o_timeout_visit']);
- }
- }
- else
- {
- if (!$panther_user['logged'])
- $panther_user['logged'] = $panther_user['last_visit'];
- }
- $panther_user['is_guest'] = false;
- $panther_user['is_admmod'] = $panther_user['g_id'] == PANTHER_ADMIN || $panther_user['g_moderator'] == '1';
- $panther_user['is_admin'] = $panther_user['g_id'] == PANTHER_ADMIN || $panther_user['g_moderator'] == '1' && $panther_user['g_admin'] == '1';
- $panther_user['is_bot'] = false;
- }
- else
- set_default_user();
- }
- function panther_hash_equals($hash, $input)
- {
- if (function_exists('hash_equals'))
- return hash_equals((string)$hash, $input);
- $input_length = strlen($input);
- if ($input_length !== strlen($hash))
- return false;
- $result = 0;
- for ($i = 0; $i < $input_length; $i++)
- $result |= ord($input[$i]) ^ ord($hash[$i]);
- return $result === 0;
- }
- //
- // Try to determine the current URL
- //
- function get_current_url($max_length = 0)
- {
- $protocol = get_current_protocol();
- $port = (isset($_SERVER['SERVER_PORT']) && (($_SERVER['SERVER_PORT'] != '80' && $protocol == 'http') || ($_SERVER['SERVER_PORT'] != '443' && $protocol == 'https')) && strpos($_SERVER['HTTP_HOST'], ':') === false) ? ':'.$_SERVER['SERVER_PORT'] : '';
- $url = urldecode($protocol.'://'.$_SERVER['HTTP_HOST'].$port.$_SERVER['REQUEST_URI']);
- if (strlen($url) <= $max_length || $max_length == 0)
- return $url;
- // We can't find a short enough url
- return null;
- }
- //
- // Fetch the current protocol in use - http or https
- //
- function get_current_protocol()
- {
- $protocol = 'http';
- // Check if the server is claiming to using HTTPS
- if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off')
- $protocol = 'https';
- // If we are behind a reverse proxy try to decide which protocol it is using
- if (defined('FORUM_BEHIND_REVERSE_PROXY'))
- {
- // Check if we are behind a Microsoft based reverse proxy
- if (!empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) != 'off')
- $protocol = 'https';
- // Check if we're behind a "proper" reverse proxy, and what protocol it's using
- if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']))
- $protocol = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']);
- }
- return $protocol;
- }
- function check_ssl_state()
- {
- global $panther_config;
- if ($panther_config['o_force_ssl'] == '1' && get_current_protocol() == 'http')
- {
- header('Location: '.str_replace('http://', 'https://', get_current_url()));
- exit;
- }
- }
- //
- // Fetch the base_url, optionally support HTTPS and HTTP
- //
- function get_base_url($support_https = true)
- {
- global $panther_config;
- static $base_url;
- if (!$support_https)
- return $panther_config['o_base_url'];
- if (!isset($base_url))
- {
- // Make sure we are using the correct protocol
- $base_url = str_replace(array('http://', 'https://'), get_current_protocol().'://', $panther_config['o_base_url']);
- }
- return $base_url;
- }
- //
- // Fetch admin IDs
- //
- function get_admin_ids()
- {
- if (file_exists(FORUM_CACHE_DIR.'cache_admins.php'))
- include FORUM_CACHE_DIR.'cache_admins.php';
- if (!defined('PANTHER_ADMINS_LOADED'))
- {
- if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
- require PANTHER_ROOT.'include/cache.php';
- generate_admins_cache();
- require FORUM_CACHE_DIR.'cache_admins.php';
- }
- return $panther_admins;
- }
- //
- // Fill $panther_user with default values (for guests)
- //
- function set_default_user()
- {
- global $db, $panther_user, $panther_config;
- $remote_addr = get_remote_address();
- $remote_addr = isbotex($remote_addr);
- $data = array(
- ':ident' => $remote_addr,
- );
- // Fetch guest user
- $ps = $db->run('SELECT u.*, g.*, o.logged, o.last_post, o.last_search FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$db->prefix.'online AS o ON o.ident=:ident WHERE u.id=1', $data);
- if (!$ps->rowCount())
- error_handler(E_ERROR, 'Unable to fetch guest information. Your database must contain both a guest user and a guest user group.', __FILE__, __LINE__);
- $panther_user = $ps->fetch();
- // Update online list
- if (!$panther_user['logged'])
- {
- $panther_user['logged'] = time();
- $data = array(
- ':ident' => $remote_addr,
- ':logged' => $panther_user['logged'],
- );
- // REPLACE INTO avoids a user having two rows in the online table
- $db->run('REPLACE INTO '.$db->prefix.'online (user_id, ident, logged) VALUES(1, :ident, :logged)', $data);
- }
- else
- {
- $update = array(
- 'logged' => time(),
- );
- $data = array(
- ':ident' => $remote_addr,
- );
- $db->update('online', $update, 'ident=:ident', $data);
- }
- $panther_user['disp_topics'] = $panther_config['o_disp_topics_default'];
- $panther_user['disp_posts'] = $panther_config['o_disp_posts_default'];
- $panther_user['timezone'] = $panther_config['o_default_timezone'];
- $panther_user['dst'] = $panther_config['o_default_dst'];
- $panther_user['language'] = $panther_config['o_default_lang'];
- $panther_user['style'] = $panther_config['o_default_style'];
- $panther_user['is_guest'] = true;
- $panther_user['is_admmod'] = false;
- $panther_user['is_admin'] = false;
- $panther_user['is_bot'] = (strpos($remote_addr, '[Bot]') !== false);
- }
- //
- // Set a cookie, Panther style!
- // Wrapper for forum_setcookie
- //
- function panther_setcookie($user_id, $password_hash, $expire)
- {
- global $panther_config;
- forum_setcookie($panther_config['o_cookie_name'], $user_id.'|'.hash_hmac('sha512', $password_hash, $panther_config['o_cookie_seed'].'_password_hash').'|'.$expire.'|'.hash_hmac('sha512', $user_id.'|'.$expire, $panther_config['o_cookie_seed'].'_cookie_hash'), $expire);
- }
- //
- // Set a cookie, Panther style!
- //
- function forum_setcookie($name, $value, $expire)
- {
- global $panther_config;
- if ($expire - time() - $panther_config['o_timeout_visit'] < 1)
- $expire = 0;
- // Enable sending of a P3P header
- header('P3P: CP="CUR ADM"');
- setcookie($name, $value, $expire, $panther_config['o_cookie_path'], $panther_config['o_cookie_domain'], $panther_config['o_cookie_secure'], true);
- }
- //
- // Check whether the connecting user is banned (and delete any expired bans while we're at it)
- //
- function check_bans()
- {
- global $db, $panther_config, $lang_common, $panther_user, $panther_bans;
- // Admins and moderators aren't affected
- if ($panther_user['is_admmod'] || !$panther_bans)
- return;
- // Add a dot or a colon (depending on IPv4/IPv6) at the end of the IP address to prevent banned address
- // 192.168.0.5 from matching e.g. 192.168.0.50
- $user_ip = get_remote_address();
- $user_ip .= (strpos($user_ip, '.') !== false) ? '.' : ':';
- $bans_altered = false;
- $is_banned = false;
- foreach ($panther_bans as $cur_ban)
- {
- // Has this ban expired?
- if ($cur_ban['expire'] != '' && $cur_ban['expire'] <= time())
- {
- $data = array(
- ':id' => $cur_ban['id'],
- );
- $db->delete('bans', 'id=:id', $data);
- $bans_altered = true;
- continue;
- }
- if ($cur_ban['username'] != '' && utf8_strtolower($panther_user['username']) == utf8_strtolower($cur_ban['username']))
- $is_banned = true;
- if ($cur_ban['ip'] != '')
- {
- $cur_ban_ips = explode(' ', $cur_ban['ip']);
- $num_ips = count($cur_ban_ips);
- for ($i = 0; $i < $num_ips; ++$i)
- {
- // Add the proper ending to the ban
- if (strpos($user_ip, '.') !== false)
- $cur_ban_ips[$i] = $cur_ban_ips[$i].'.';
- else
- $cur_ban_ips[$i] = $cur_ban_ips[$i].':';
- if (substr($user_ip, 0, strlen($cur_ban_ips[$i])) == $cur_ban_ips[$i])
- {
- $is_banned = true;
- break;
- }
- }
- }
- if ($is_banned)
- {
- $data = array(
- ':ident' => $panther_user['username'],
- );
- $db->delete('online', 'ident=:ident', $data);
- message($lang_common['Ban message'].' '.(($cur_ban['expire'] != '') ? $lang_common['Ban message 2'].' '.strtolower(format_time($cur_ban['expire'], true)).'. ' : '').(($cur_ban['message'] != '') ? $lang_common['Ban message 3'].'<br /><br /><strong>'.$cur_ban['message'].'</strong><br /><br />' : '<br /><br />').$lang_common['Ban message 4'].' '.$panther_config['o_admin_email'], true);
- }
- }
- // If we removed any expired bans during our run-through, we need to regenerate the bans cache
- if ($bans_altered)
- {
- if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
- require PANTHER_ROOT.'include/cache.php';
- generate_bans_cache();
- }
- }
- //
- // Check username
- //
- function check_username($username, $exclude_id = null)
- {
- global $db, $panther_config, $errors, $lang_prof_reg, $lang_register, $lang_common, $panther_bans;
- // Include UTF-8 function
- require_once PANTHER_ROOT.'include/utf8/strcasecmp.php';
- // Convert multiple whitespace characters into one (to prevent people from registering with indistinguishable usernames)
- $username = preg_replace('%\s+%s', ' ', $username);
- // Validate username
- if (panther_strlen($username) < 2)
- $errors[] = $lang_prof_reg['Username too short'];
- else if (panther_strlen($username) > 25) // This usually doesn't happen since the form element only accepts 25 characters
- $errors[] = $lang_prof_reg['Username too long'];
- else if (!strcasecmp($username, 'Guest') || !utf8_strcasecmp($username, $lang_common['Guest']))
- $errors[] = $lang_prof_reg['Username guest'];
- else if (preg_match('%[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}%', $username) || preg_match('%((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))%', $username))
- $errors[] = $lang_prof_reg['Username IP'];
- else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
- $errors[] = $lang_prof_reg['Username reserved chars'];
- else if (preg_match('%(?:\[/?(?:b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|\*|topic|post|forum|user)\]|\[(?:img|url|quote|list)=)%i', $username))
- $errors[] = $lang_prof_reg['Username BBCode'];
- // Check username for any censored words
- if ($panther_config['o_censoring'] == '1' && censor_words($username) != $username)
- $errors[] = $lang_register['Username censor'];
-
- $where_cond = '(UPPER(username)=UPPER(:username) OR UPPER(username)=UPPER(:username2)) AND id>1';
-
- $data = array(
- ':username' => $username,
- ':username2' => ucp_preg_replace('%[^\p{L}\p{N}]%u', '', $username),
- );
- // Check that the username (or a too similar username) is not already registered
- if (!is_null($exclude_id))
- {
- $where_cond .= ' AND id!=:id';
- $data[':id'] = $exclude_id;
- }
- $ps = $db->select('users', 'username', $data, $where_cond);
- if ($ps->rowCount())
- {
- $busy = $ps->fetchColumn();
- $errors[] = $lang_register['Username dupe 1'].' '.$busy.'. '.$lang_register['Username dupe 2'];
- }
- // Check username for any banned usernames
- foreach ($panther_bans as $cur_ban)
- {
- if ($cur_ban['username'] != '' && utf8_strtolower($username) == utf8_strtolower($cur_ban['username']))
- {
- $errors[] = $lang_prof_reg['Banned username'];
- break;
- }
- }
- }
- //
- // Update "Users online"
- //
- function update_users_online()
- {
- global $db, $panther_config, $panther_user;
- $cur_position = substr($_SERVER['REQUEST_URI'], 1);
- $server_base = dirname($_SERVER['PHP_SELF']);
- if ($server_base !== '/')
- $cur_position = substr($cur_position, strlen($server_base));
- $cur_position = ($cur_position == '') ? 'index.php' : $cur_position;
- $now = time();
- $online['users'] = $online['guests'] = array();
- // Fetch all online list entries that are older than "o_timeout_online"
- $ps = $db->run('SELECT o.user_id, o.ident, o.logged, o.idle, u.group_id FROM '.$db->prefix.'online AS o LEFT JOIN '.$db->prefix.'users AS u ON o.user_id=u.id');
- foreach ($ps as $cur_user)
- {
- if ($cur_user['logged'] < ($now - $panther_config['o_timeout_online']))
- {
- // If the entry is a guest, delete it
- if ($cur_user['user_id'] == '1')
- {
- $data = array(
- ':ident' => $cur_user['ident']
- );
- $db->delete('online', 'ident=:ident', $data);
- }
- else
- {
- // If the entry is older than "o_timeout_visit", update last_visit for the user in question, then delete him/her from the online list
- if ($cur_user['logged'] < ($now - $panther_config['o_timeout_visit']))
- {
- $update = array(
- 'last_visit' => $cur_user['logged'],
- );
-
- $data = array(
- ':id' => $cur_user['user_id'],
- );
-
- $db->update('users', $update, 'id=:id', $data);
- $db->delete('online', 'user_id=:id', $data);
- }
- }
- }
- else
- {
- if ($cur_user['user_id'] == 1)
- $online['guests'][] = array('ident' => $cur_user['ident'], 'group_id' => PANTHER_GUEST);
- else
- $online['users'][$cur_user['user_id']] = array('username' => $cur_user['ident'], 'group_id' => $cur_user['group_id'], 'id' => $cur_user['user_id']);
- }
- }
-
- if (!$panther_user['is_bot'])
- {
- $update = array(
- 'currently' => $cur_position,
- );
-
- $data = array();
- if ($panther_user['is_guest'])
- {
- $field = 'ident';
- $data[':ident'] = get_remote_address();
- }
- else
- {
- $field = 'user_id';
- $data[':ident'] = $panther_user['id'];
- }
- $db->update('online', $update, $field.'=:ident', $data);
- }
- return $online;
- }
- //
- // Display the profile navigation menu
- //
- function generate_profile_menu($page = '')
- {
- global $lang_profile, $panther_config, $panther_user, $id, $panther_url;
-
- $sections = array(
- array('page' => 'essentials', 'link' => panther_link($panther_url['profile_essentials'], array($id)), 'lang' => $lang_profile['Section essentials']),
- array('page' => 'personal', 'link' => panther_link($panther_url['profile_personal'], array($id)), 'lang' => $lang_profile['Section personal']),
- array('page' => 'messaging', 'link' => panther_link($panther_url['profile_messaging'], array($id)), 'lang' => $lang_profile['Section messaging']),
- );
-
- if ($panther_config['o_avatars'] == '1' || $panther_config['o_signatures'] == '1')
- $sections[] = array('page' => 'personality', 'link' => panther_link($panther_url['profile_personality'], array($id)), 'lang' => $lang_profile['Section personality']);
-
- $sections[] = array('page' => 'display', 'link' => panther_link($panther_url['profile_display'], array($id)), 'lang' => $lang_profile['Section display']);
- $sections[] = array('page' => 'privacy', 'link' => panther_link($panther_url['profile_privacy'], array($id)), 'lang' => $lang_profile['Section privacy']);
- $sections[] = array('page' => 'view', 'link' => panther_link($panther_url['profile_view'], array($id)), 'lang' => $lang_profile['Section view']);
- if ($panther_user['is_admin'] || ($panther_user['g_moderator'] == '1' && $panther_user['g_mod_ban_users'] == '1'))
- $sections[] = array('page' => 'admin', 'link' => panther_link($panther_url['profile_admin'], array($id)), 'lang' => $lang_profile['Section admin']);
- $tpl = load_template('profile_sidebar.tpl');
- echo $tpl->render(
- array(
- 'lang_profile' => $lang_profile,
- 'sections' => $sections,
- 'page' => $page,
- )
- );
- }
- //
- // Display PM menu
- //
- function generate_pm_menu($page = 2) // Default to inbox
- {
- global $panther_url, $lang_pm, $panther_user, $db;
- static $folders;
- $percent = ($panther_user['g_pm_limit'] != '0') ? round(ceil($panther_user['num_pms'] / $panther_user['g_pm_limit']*100), 0) : 0;
- $limit = ($panther_user['g_pm_limit'] == '0') ? '∞' : $panther_user['g_pm_limit'];
-
- $data = array(
- ':uid' => $panther_user['id'],
- );
- $folders = array();
- $ps = $db->select('folders', 'name, id', $data, 'user_id=:uid OR user_id=1', 'id, user_id ASC');
- foreach ($ps as $folder)
- {
- $data = array(
- ':id' => $folder['id'],
- ':uid' => $panther_user['id'],
- );
- $ps1 = $db->select('pms_data', 'COUNT(topic_id)', $data, 'user_id=:uid AND deleted=0 AND (folder_id=:id '.(($folder['id'] == 1) ? 'OR viewed=0)' : ')'));
- $amount = $ps1->fetchColumn();
- $folders[] = array(
- 'id' => $folder['id'],
- 'link' => panther_link($panther_url['box'], array($folder['id'])),
- 'name' => $folder['name'],
- 'amount' => $amount,
- );
- }
-
- $tpl = load_template('pm_sidebar.tpl');
- return $tpl->render(
- array(
- 'lang_pm' => $lang_pm,
- 'folders' => $folders,
- 'percent' => $percent,
- 'num_pms' => forum_number_format($panther_user['num_pms']),
- 'limit' => forum_number_format($limit),
- 'blocked_link' => panther_link($panther_url['pms_blocked']),
- 'folders_link' => panther_link($panther_url['pms_folders']),
- 'page' => $page,
- )
- );
- }
- //
- // Outputs markup to display a user's avatar
- //
- function generate_avatar_markup($user_id, $user_email, $use_gravatar = 0, $size = array())
- {
- global $panther_config;
- static $user_avatar_cache = array();
- $avatar_path = ($panther_config['o_avatars_dir'] != '') ? $panther_config['o_avatars_path'] : PANTHER_ROOT.$panther_config['o_avatars_path'];
- $avatar_dir = ($panther_config['o_avatars_dir'] != '') ? $panther_config['o_avatars_dir'] : get_base_url(true).'/'.$panther_config['o_avatars_path'];
- if (!isset($user_avatar_cache[$user_id]))
- {
- if ($use_gravatar == 1)
- {
- $params = (count($size) == 2) ? array($size[0], $size[1]) : array($panther_config['o_avatars_width'], $panther_config['o_avatars_height']);
- $user_avatar_cache[$user_id] = '<img src="https://www.gravatar.com/avatar.php?gravatar_id='.md5(strtolower($user_email)).'&size='.$params[0].'" width="'.$params[0].'" height="'.$params[1].'" alt="" />';
- }
- else if ($panther_config['o_avatar_upload'] == '1')
- {
- $filetypes = array('jpg', 'gif', 'png');
- foreach ($filetypes as $cur_type)
- {
- $path = $avatar_path.$user_id.'.'.$cur_type;
- $url = $avatar_dir.$user_id.'.'.$cur_type;
- if (file_exists($path) && $img_size = getimagesize($path))
- {
- $size = (count($size) == 2 ? 'width="'.$size[0].'" height="'.$size[1].'"' : $img_size[3]);
- $user_avatar_cache[$user_id] = '<img src="'.$url.'?m='.filemtime($path).'" '.$size.' alt="" />';
- break;
- }
- }
- }
- // If there's no avatar set, we mustn't have one uploaded. Set the default!
- if (!isset($user_avatar_cache[$user_id]))
- {
- $path = $avatar_path.'1.'.$panther_config['o_avatar'];
- $url = $avatar_dir.'1.'.$panther_config['o_avatar'];
- $img_size = getimagesize($path);
- $size = (count($size) == 2 ? 'width="'.$size[0].'" height="'.$size[1].'"' : $img_size[3]);
- $user_avatar_cache[$user_id] = '<img src="'.$url.'?m='.filemtime($path).'" '.$size.' alt="" />';
- }
- }
- return $user_avatar_cache[$user_id];
- }
- //
- // Generate browser's title
- //
- function generate_page_title($page_title, $p = null)
- {
- global $lang_common;
- if (!is_array($page_title))
- $page_title = array($page_title);
- $page_title = array_reverse($page_title);
- if ($p > 1)
- $page_title[0] .= ' ('.sprintf($lang_common['Page'], forum_number_format($p)).')';
- $crumbs = implode($lang_common['Title separator'], $page_title);
- return $crumbs;
- }
- //
- // Save array of tracked topics in cookie
- //
- function set_tracked_topics($tracked_topics)
- {
- global $panther_config;
- $cookie_data = '';
- if (!empty($tracked_topics))
- {
- // Sort the arrays (latest read first)
- arsort($tracked_topics['topics'], SORT_NUMERIC);
- arsort($tracked_topics['forums'], SORT_NUMERIC);
- // Homebrew serialization (to avoid having to run unserialize() on cookie data)
- foreach ($tracked_topics['topics'] as $id => $timestamp)
- $cookie_data .= 't'.$id.'='.$timestamp.';';
- foreach ($tracked_topics['forums'] as $id => $timestamp)
- $cookie_data .= 'f'.$id.'='.$timestamp.';';
- // Enforce a byte size limit (4096 minus some space for the cookie name - defaults to 4048)
- if (strlen($cookie_data) > FORUM_MAX_COOKIE_SIZE)
- {
- $cookie_data = substr($cookie_data, 0, FORUM_MAX_COOKIE_SIZE);
- $cookie_data = substr($cookie_data, 0, strrpos($cookie_data, ';')).';';
- }
- }
- forum_setcookie($panther_config['o_cookie_name'].'_track', $cookie_data, time() + $panther_config['o_timeout_visit']);
- $_COOKIE[$panther_config['o_cookie_name'].'_track'] = $cookie_data; // Set it directly in $_COOKIE as well
- }
- //
- // Extract array of tracked topics from cookie
- //
- function get_tracked_topics()
- {
- global $panther_config;
- $cookie_data = isset($_COOKIE[$panther_config['o_cookie_name'].'_track']) ? $_COOKIE[$panther_config['o_cookie_name'].'_track'] : false;
- if (!$cookie_data)
- return array('topics' => array(), 'forums' => array());
- if (strlen($cookie_data) > FORUM_MAX_COOKIE_SIZE)
- return array('topics' => array(), 'forums' => array());
- // Unserialize data from cookie
- $tracked_topics = array('topics' => array(), 'forums' => array());
- $temp = explode(';', $cookie_data);
- foreach ($temp as $t)
- {
- $type = substr($t, 0, 1) == 'f' ? 'forums' : 'topics';
- $id = intval(substr($t, 1));
- $timestamp = intval(substr($t, strpos($t, '=') + 1));
- if ($id > 0 && $timestamp > 0)
- $tracked_topics[$type][$id] = $timestamp;
- }
- return $tracked_topics;
- }
- //
- // Update posts, topics, last_post, last_post_id and last_poster for a forum
- //
- function update_forum($forum_id)
- {
- global $db;
- $data = array(
- ':id' => $forum_id,
- );
- $ps = $db->select('topics', 'COUNT(id), SUM(num_replies)', $data, 'forum_id=:id AND approved=1 AND deleted=0');
- list($num_topics, $num_posts) = $ps->fetch(PDO::FETCH_NUM);
- $num_posts = $num_posts + $num_topics; // $num_posts is only the sum of all replies (we have to add the topic posts)
- $data = array(
- ':id' => $forum_id
- );
- $ps = $db->select('topics', 'last_post, last_post_id, last_poster, subject, id', $data, 'forum_id=:id AND approved=1 AND deleted=0 AND moved_to IS NULL ORDER BY last_post DESC LIMIT 1');
- if ($ps->rowCount()) // There are topics in the forum
- {
- list($last_post, $last_post_id, $last_poster, $last_topic, $last_topic_id) = $ps->fetch(PDO::FETCH_NUM);
- $update = array(
- 'num_topics' => $num_topics,
- 'num_posts' => $num_posts,
- 'last_post' => $last_post,
- 'last_post_id' => $last_post_id,
- 'last_topic' => $last_topic,
- 'last_topic_id' => $last_topic_id,
- 'last_poster' => $last_poster,
- );
- $data = array(
- ':id' => $forum_id,
- );
- $db->update('forums', $update, 'id=:id', $data);
- }
- else // There are no topics
- {
- $data = array(
- ':num_topics' => $num_topics,
- ':num_posts' => $num_posts,
- ':id' => $forum_id,
- );
- // Annoyingly PDO does not allow NULL values to be added in prepared statements. When added it becomes 'NULL', so we have to run the query manually instead.
- $db->run('UPDATE '.$db->prefix.'forums SET num_topics=:num_topics, num_posts=:num_posts, last_post=NULL, last_post_id=NULL, last_poster=NULL, last_topic=\'\', last_topic_id=0 WHERE id=:id', $data);
- }
- }
- //
- // Deletes any avatars owned by the specified user ID
- //
- function delete_avatar($user_id)
- {
- global $panther_config;
- $filetypes = array('jpg', 'gif', 'png');
- $avatar_path = ($panther_config['o_avatars_dir'] != '') ? $panther_config['o_avatars_path'] : PANTHER_ROOT.$panther_config['o_avatars_path'];
- // Delete user avatar
- foreach ($filetypes as $cur_type)
- {
- if (file_exists($avatar_path.$user_id.'.'.$cur_type))
- @unlink($avatar_path.$user_id.'.'.$cur_type);
- }
- }
- //
- // Delete a topic and all of its posts
- //
- function delete_topic($topic_id)
- {
- global $db, $panther_config;
- // Delete the topic and any redirect topics
- attach_delete_thread($topic_id);
- $data = array(
- ':id' => $topic_id,
- );
- $topic = array(
- ':id' => $topic_id,
- ':moved_to' => $topic_id,
- );
- $update = array(
- 'deleted' => 1,
- );
- $post_ids = array();
- $db->update('topics', $update, 'id=:id OR moved_to=:moved_to', $topic);
- $db->delete('polls', 'topic_id=:id', $data);
- // Get all post IDs from this topic.
- $ps = $db->select('posts', 'id', $data, 'topic_id=:id');
- foreach ($ps as $cur_post)
- $post_ids[] = $cur_post['id'];
- // Make sure we have a list of post IDs
- if (!empty($post_ids))
- {
- strip_search_index($post_ids); // Should be an array
- $db->update('posts', $update, 'topic_id=:id', $data);
- }
- if ($panther_config['o_delete_full'] == '1')
- permanently_delete_topic($topic_id);
- }
- //
- // Delete a single post
- //
- function delete_post($post_id, $topic_id)
- {
- global $db, $panther_config;
- $topic_data = array(
- ':id' => $topic_id,
- );
- $post_data = array(
- ':id' => $post_id,
- );
- $ps = $db->select('posts', 'id, poster, posted', $topic_data, 'topic_id=:id AND approved=1 AND deleted=0', 'id DESC LIMIT 2');
- list($last_id, ,) = $ps->fetch(PDO::FETCH_NUM);
- list($second_last_id, $second_poster, $second_posted) = $ps->fetch(PDO::FETCH_NUM);
- // Delete the post
- attach_delete_post($post_id);
- $update = array(
- 'deleted' => 1,
- );
- $db->update('posts', $update, 'id=:id', $post_data);
- strip_search_index(array($post_id));
- // Count number of replies in the topic
- $ps = $db->select('posts', 'COUNT(id)', $topic_data, 'topic_id=:id AND approved=1 AND deleted=0');
- $num_replies = $ps->fetchColumn() - 1; // Decrement the deleted post
- // If the message we deleted is the most recent in the topic (at the end of the topic)
- if ($last_id == $post_id)
- {
- // If there is a $second_last_id there is more than 1 reply to the topic
- if (!empty($second_last_id))
- {
- $update = array(
- 'last_post' => $second_posted,
- 'last_post_id' => $second_last_id,
- 'last_poster' => $second_poster,
- 'num_replies' => $num_replies,
- );
- $data = array(
- ':id' => $topic_id,
- );
- $db->update('topics', $update, 'id=:id', $data);
- }
- else
- {
- $data = array(
- ':id' => $topic_id,
- ':num_replies' => $num_replies-1,
- );
- // We deleted the only reply, so now last_post/last_post_id/last_poster is posted/id/poster from the topic itself
- $db->run('UPDATE '.$db->prefix.'topics SET last_post=posted, last_post_id=id, last_poster=poster, num_replies=:num_replies WHERE id=:id', $data);
- }
- }
- else // Otherwise we just decrement the reply counter
- {
- $update = array(
- 'num_replies' => $num_replies,
- );
- $db->update('topics', $update, 'id=:id', $topic_data);
- }
- if ($panther_config['o_delete_full'] == '1')
- permanently_delete_post($post_id);
- }
- //
- // Permanently delete a single post
- //
- function permanently_delete_post($id)
- {
- global $db;
- $data = array(
- ':id' => $id,
- );
- $db->delete('posts', 'id=:id AND deleted=1', $data); // Since we've already stripped the search index, all we need to do is delete the row
- }
- //
- // Permanently delete a topic
- //
- function permanently_delete_topic($id)
- {
- global $db;
- $data = array(
- ':id' => $id,
- ':moved_to' => $id,
- );
- $db->delete('topics', '(id=:id OR moved_to=:moved_to) AND deleted=1', $data);
- unset($data[':moved_to']);
- $db->delete('posts', 'topic_id=? AND deleted=1', array_values($data));
- // Delete any subscriptions for this topic
- $db->delete('topic_subscriptions', 'topic_id=?', array_values($data));
- }
- //
- // Delete every .php file in the forum's cache directory
- //
- function forum_clear_cache()
- {
- $files = array_diff(scandir(FORUM_CACHE_DIR), array('.', '..'));
- foreach ($files as $file)
- {
- if (substr($file, -4) == '.php')
- @unlink(FORUM_CACHE_DIR.$file);
- }
- }
- //
- // Replace censored words in $text
- //
- function censor_words($text)
- {
- global $db;
- static $search_for, $replace_with;
- // If not already built in a previous call, build an array of censor words and their replacement text
- if (!isset($search_for))
- {
- if (file_exists(FORUM_CACHE_DIR.'cache_censoring.php'))
- include FORUM_CACHE_DIR.'cache_censoring.php';
- if (!defined('PANTHER_CENSOR_LOADED'))
- {
- if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
- require PANTHER_ROOT.'include/cache.php';
- generate_censoring_cache();
- require FORUM_CACHE_DIR.'cache_censoring.php';
- }
- }
- if (!empty($search_for))
- $text = substr(ucp_preg_replace($search_for, $replace_with, ' '.$text.' '), 1, -1);
- return $text;
- }
- //
- // Determines the correct title for $user
- // $user must contain the elements 'username', 'title', 'posts', 'g_id' and 'g_user_title'
- //
- function get_title($user)
- {
- global $panther_bans, $lang_common, $panther_config;
- static $ban_list, $panther_ranks;
- // If not already built in a previous call, build an array of lowercase banned usernames
- if (empty($ban_list))
- {
- $ban_list = array();
- foreach ($panther_bans as $cur_ban)
- $ban_list[] = utf8_strtolower($cur_ban['username']);
- }
- // If not already loaded in a previous call, load the cached ranks
- if ($panther_config['o_ranks'] == '1' && !defined('PANTHER_RANKS_LOADED'))
- {
- if (file_exists(FORUM_CACHE_DIR.'cache_ranks.php'))
- include FORUM_CACHE_DIR.'cache_ranks.php';
- if (!defined('PANTHER_RANKS_LOADED'))
- {
- if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
- require PANTHER_ROOT.'include/cache.php';
- generate_ranks_cache();
- require FORUM_CACHE_DIR.'cache_ranks.php';
- }
- }
- // If the user has a custom title
- if ($user['title'] != '')
- $user_title = $user['title'];
- // If the user is banned
- else if (in_array(utf8_strtolower($user['username']), $ban_list))
- $user_title = $lang_common['Banned'];
- // If the user group has a default user title
- else if ($user['g_user_title'] != '')
- $user_title = $user['g_user_title'];
- // If the user is a guest
- else if ($user['g_id'] == PANTHER_GUEST)
- $user_title = $lang_common['Guest'];
- // If nothing else helps, we assign the default
- else
- {
- // Are there any ranks?
- if ($panther_config['o_ranks'] == '1' && !empty($panther_ranks))
- {
- foreach ($panther_ranks as $cur_rank)
- {
- if ($user['num_posts'] >= $cur_rank['min_posts'])
- $user_title = $cur_rank['rank'];
- }
- }
- // If the user didn't "reach" any rank (or if ranks are disabled), we assign the default
- if (!isset($user_title))
- $user_title = $lang_common['Member'];
- }
- return $user_title;
- }
- //
- // Generate a string with numbered links (for multipage scripts)
- //
- function paginate($num_pages, $cur_page, $link, $args = null)
- {
- global $lang_common, $panther_config, $panther_url;
- $pages = array();
- $link_to_all = false;
- // If $cur_page == -1, we link to all pages (used in viewforum.php)
- if ($cur_page == -1)
- {
- $cur_page = 1;
- $link_to_all = true;
- }
- if ($num_pages > 1)
- {
- if ($cur_page > 1)
- $pages[] = array('item' => true, 'href' => str_replace('#', '', get_sublink($link, $panther_url['page'], ($cur_page - 1), $args)), 'current' => $lang_common['Previous']);
-
- if ($cur_page > 3)
- {
- $pages[] = array('item' => (empty($pages) ? true : false), 'href' => $link, 'current' => 1);
- if ($cur_page > 5)
- $pages[] = $lang_common['Spacer'];
- }
- // Don't ask me how the following works. It just does, OK? =)
- for ($current = ($cur_page == 5) ? $cur_page - 3 : $cur_page - 2, $stop = ($cur_page + 4 == $num_pages) ? $cur_page + 4 : $cur_page + 3; $current < $stop; ++$current)
- {
- if ($current < 1 || $current > $num_pages)
- continue;
- else if ($current != $cur_page || $link_to_all)
- $pages[] = array('item' => (empty($pages) ? true : false), 'href' => str_replace('#', '', get_sublink($link, $panther_url['page'], $current, $args)), 'current' => forum_number_format($current));
- else
- $pages[] = array('item' => (empty($pages) ? true : false), 'current' => forum_number_format($current));
- }
- if ($cur_page <= ($num_pages-3))
- {
- if ($cur_page != ($num_pages-3) && $cur_page != ($num_pages-4))
- $pages[] = $lang_common['Spacer'];
- $pages[] = array('item' => (empty($pages) ? true : false), 'href' => get_sublink($link, $panther_url['page'], $num_pages, $args), 'current' => forum_number_format($num_pages));
- }
- // Add a next page link
- if ($num_pages > 1 && !$link_to_all && $cur_page < $num_pages)
- $pages[] = array('item' => (empty($pages) ? true : false), 'rel' => 'next', 'href' => get_sublink($link, $panther_url['page'], ($cur_page + 1), $args), 'current' => $lang_common['Next']);
- }
- $tpl = load_template('pagination.tpl');
- return $tpl->render(
- array(
- 'num_pages' => $num_pages,
- 'cur_page' => $cur_page,
- 'pages' => $pages,
- 'link' => $link,
- )
- );
- }
- //
- // Display a message
- //
- function message($message, $no_back_link = false, $http_status = null)
- {
- global $db, $lang_common, $panther_config, $panther_start, $tpl_main, $panther_user, $panther_url;
- // Did we receive a custom header?
- if (!is_null($http_status))
- header('HTTP/1.1 '.$http_status);
- if (!defined('PANTHER_HEADER'))
- {
- $page_title = array($panther_config['o_board_title'], $lang_common['Info']);
- define('PANTHER_ACTIVE_PAGE', 'index');
- require PANTHER_ROOT.'header.php';
- }
- $tpl = load_template('message.tpl');
- echo $tpl->render(
- array(
- 'lang_common' => $lang_common,
- 'message' => $message,
- 'no_back_link' => $no_back_link,
- )
- );
- require PANTHER_ROOT.'footer.php';
- }
- //
- // Format a time string according to $time_format and time zones
- //
- function format_time($timestamp, $date_only = false, $date_format = null, $time_format = null, $time_only = false, $no_text = false)
- {
- global $lang_common, $panther_user, $forum_date_formats, $forum_time_formats;
- if ($timestamp == '')
- return $lang_common['Never'];
- $diff = ($panther_user['timezone'] + $panther_user['dst']) * 3600;
- $timestamp += $diff;
- $now = time();
- if(is_null($date_format))
- $date_format = $forum_date_formats[$panther_user['date_format']];
- if(is_null($time_format))
- $time_format = $forum_time_formats[$panther_user['time_format']];
- $date = gmdate($date_format, $timestamp);
- $today = gmdate($date_format, $now+$diff);
- $yesterday = gmdate($date_format, $now+$diff-86400);
- if (!$no_text)
- {
- if ($date == $today)
- $date = $lang_common['Today'];
- else if ($date == $yesterday)
- $date = $lang_common['Yesterday'];
- }
- if ($date_only)
- return $date;
- else if ($time_only)
- return gmdate($time_format, $timestamp);
- else
- return $date.' '.gmdate($time_format, $timestamp);
- }
- //
- // A wrapper for PHP's number_format function
- //
- function forum_number_format($number, $decimals = 0)
- {
- global $lang_common;
- return is_numeric($number) ? number_format($number, $decimals, $lang_common['lang_decimal_point'], $lang_common['lang_thousands_sep']) : $number;
- }
- //
- // Generate a random key of length $len
- //
- function random_key($len, $readable = false, $hash = false)
- {
- if (!function_exists('secure_random_bytes'))
- include PANTHER_ROOT.'include/srand.php';
- $key = secure_random_bytes($len);
- if ($hash)
- return substr(bin2hex($key), 0, $len);
- else if ($readable)
- {
- $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
- $result = '';
- for ($i = 0; $i < $len; ++$i)
- $result .= substr($chars, (ord($key[$i]) % strlen($chars)), 1);
- return $result;
- }
- return $key;
- }
- //
- // Make sure that user is using a valid token
- //
- function confirm_referrer($script, $use_ip = true)
- {
- global $lang_common, $panther_user;
- // Yeah, pretty complex ternary =)
- $sent_hash = ((isset($_POST['csrf_token'])) ? panther_trim($_POST['csrf_token']) : (isset($_GET['csrf_token']) ? panther_trim($_GET['csrf_token']) : ''));
- if (!panther_hash_equals(generate_csrf_token($script, $use_ip), $sent_hash))
- message($lang_common['Bad referrer']);
- }
- //
- // Generate a csrf token
- //
- function generate_csrf_token($script = 'nothing', $use_ip = true)
- {
- global $panther_user;
- $script = ($script != 'nothing') ? $script : panther_trim(basename($_SERVER['SCRIPT_NAME']));
- return panther_hash($panther_user['id'].$script.$panther_user['salt'].(($use_ip ? get_remote_address() : '')).$panther_user['login_key']);
- }
- //
- // Validate the given redirect URL, use the fallback otherwise
- //
- function validate_redirect($redirect_url, $fallback_url)
- {
- $referrer = parse_url(strtolower($redirect_url));
- // Make sure the host component exists
- if (!isset($referrer['host']))
- $referrer['host'] = '';
- // Remove www subdomain if it exists
- if (strpos($referrer['host'], 'www.') === 0)
- $referrer['host'] = substr($referrer['host'], 4);
- // Make sure the path component exists
- if (!isset($referrer['path']))
- $referrer['path'] = '';
- $valid = parse_url(strtolower(get_base_url()));
- // Remove www subdomain if it exists
- if (strpos($valid['host'], 'www.') === 0)
- $valid['host'] = substr($valid['host'], 4);
- // Make sure the path component exists
- if (!isset($valid['path']))
- $valid['path'] = '';
- if ($referrer['host'] == $valid['host'] && preg_match('%^'.preg_quote($valid['path'], '%').'/(.*?)\.php%i', $referrer['path']))
- return $redirect_url;
- else
- return $fallback_url;
- }
- //
- // Generate a random password of length $len
- // Compatibility wrapper for random_key
- //
- function random_pass($len)
- {
- return random_key($len, true);
- }
- //
- // Compute a hash of $str
- //
- function panther_hash($str)
- {
- return hash('sha512', $str);
- }
- //
- // Try to determine the correct remote IP-address
- //
- function get_remote_address()
- {
- $remote_addr = $_SERVER['REMOTE_ADDR'];
- // If we are behind a reverse proxy try to find the real users IP
- if (defined('FORUM_BEHIND_REVERSE_PROXY'))
- {
- if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
- {
- // The general format of the field is:
- // X-Forwarded-For: client1, proxy1, proxy2
- // where the value is a comma+space separated list of IP addresses, the left-most being the farthest downstream client,
- // and each successive proxy that passed the request adding the IP address where it received the request from.
- $forwarded_for = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
- $forwarded_for = trim($forwarded_for[0]);
- if (@preg_match('%^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$%', $forwarded_for) || @preg_match('%^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$%', $forwarded_for))
- $remote_addr = $forwarded_for;
- }
- }
- return $remote_addr;
- }
- //
- // Calls htmlspecialchars with a few options already set
- // As of 1.1.0, this has been deprecated and will be removed soon. Use Twig instead.
- //
- function panther_htmlspecialchars($str)
- {
- return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
- }
- //
- // Calls htmlspecialchars_decode with a few options already set
- //
- function panther_htmlspecialchars_decode($str)
- {
- if (function_exists('htmlspecialchars_decode'))
- return htmlspecialchars_decode($str, ENT_QUOTES);
- static $translations;
- if (!isset($translations))
- {
- $translations = get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES);
- $translations['''] = '\''; // get_html_translation_table doesn't include ' which is what htmlspecialchars translates ' to, but apparently that is okay?! http://bugs.php.net/bug.php?id=25927
- $translations = array_flip($translations);
- }
- return strtr($str, $translations);
- }
- //
- // A wrapper for utf8_strlen for compatibility
- //
- function panther_strlen($str)
- {
- return utf8_strlen($str);
- }
- //
- // Convert \r\n and \r to \n
- //
- function panther_linebreaks($str)
- {
- return str_replace(array("\r\n", "\r"), "\n", $str);
- }
- //
- // A wrapper for utf8_trim for compatibility
- //
- function panther_trim($str, $charlist = false)
- {
- return is_string($str) ? utf8_trim($str, $charlist) : '';
- }
- //
- // Checks if a string is in all uppercase
- //
- function is_all_uppercase($string)
- {
- return utf8_strtoupper($string) == $string && utf8_strtolower($string) != $string;
- }
- //
- // Inserts $element into $input at $offset
- // $offset can be either a numerical offset to insert at (eg: 0 inserts at the beginning of the array)
- // or a string, which is the key that the new element should be inserted before
- // $key is optional: it's used when inserting a new key/value pair into an associative array
- //
- function array_insert(&$input, $offset, $element, $key = null)
- {
- if (is_null($key))
- $key = $offset;
- // Determine the proper offset if we're using a string
- if (!is_int($offset))
- $offset = array_search($offset, array_keys($input), true);
- // Out of bounds checks
- if ($offset > count($input))
- $offset = count($input);
- else if ($offset < 0)
- $offset = 0;
- $input = array_merge(array_slice($input, 0, $offset), array($key => $element), array_slice($input, $offset));
- }
- //
- // Return a template object from Twig
- //
- function load_template($tpl_file)
- {
- global $panther_user, $panther_config, $tpl_manager;
-
- $style_root = (($panther_config['o_style_path'] != 'style') ? $panther_config['o_style_path'] : PANTHER_ROOT.$panther_config['o_style_path']).'/'.$panther_user['style'].'/templates/';
- if (file_exists($style_root.$tpl_file))
- $tpl_file = $tpl_manager->loadTemplate('@style/'.$tpl_file);
- else
- $tpl_file = $tpl_manager->loadTemplate('@core/'.$tpl_file);
- return $tpl_file;
- }
- //
- // Display a message when board is in maintenance mode
- //
- function maintenance_message()
- {
- global $db, $panther_config, $lang_common, $panther_user;
- // Send no-cache headers
- header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
- header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
- header('Cache-Control: post-check=0, pre-check=0', false);
- header('Pragma: no-cache'); // For HTTP/1.0 compatibility
- // Send the Content-type header in case the web server is setup to send something else
- header('Content-type: text/html; charset=utf-8');
- // Deal with newlines, tabs and multiple spaces
- $pattern = array("\t", ' ', ' ');
- $replace = array('    ', '  ', '  ');
- $message = str_replace($pattern, $replace, $panther_config['o_maintenance_message']);
- $tpl = load_template('maintenance.tpl');
- echo $tpl->render(
- array(
- 'lang_common' => $lang_common,
- 'message' => $message,
- 'page_title' => generate_page_title(array($panther_config['o_board_title'], $lang_common['Maintenance'])),
- 'style' => (($panther_config['o_style_dir']) != '' ? $panther_config['o_style_dir'] : get_base_url().'/style/').$panther_user['style'],
- 'panther_config' => $panther_config,
- )
- );
- // End the transaction
- $db->end_transaction();
- exit;
- }
- //
- // Display $message and redirect user to $destination_url
- //
- function redirect($destination_url, $message)
- {
- global $db, $panther_config, $lang_common, $panther_user;
- // Prefix with base_url (unless there's already a valid URI)
- if (strpos($destination_url, 'http://') !== 0 && strpos($destination_url, 'https://') !== 0 && strpos($destination_url, '/') !== 0)
- $destination_url = get_base_url(true).'/'.$destination_url;
- // Do a little spring cleaning
- $destination_url = preg_replace('%([\r\n])|(\%0[ad])|(;\s*data\s*:)%i', '', $destination_url);
- // If the delay is 0 seconds, we might as well skip the redirect all together
- if ($panther_config['o_redirect_delay'] == '0')
- {
- $db->end_transaction();
- header('Location: '.str_replace('&', '&', $destination_url));
- exit;
- }
- // Send no-cache headers
- header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
- header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
- header('Cache-Control: post-check=0, pre-check=0', false);
- header('Pragma: no-cache'); // For HTTP/1.0 compatibility
- // Send the Content-type header in case the web server is setup to send something else
- header('Content-type: text/html; charset=utf-8');
- $tpl = load_template('redirect.tpl');
- echo $tpl->render(
- array(
- 'lang_common' => $lang_common,
- 'destination_url' => $destination_url,
- 'message' => $message,
- 'queries' => ($panther_config['o_show_queries'] == '1') ? display_saved_queries() : '',
- 'panther_config' => $panther_config,
- 'page_title' => generate_page_title(array($panther_config['o_board_title'], $lang_common['Redirecting'])),
- 'css_url' => (($panther_config['o_style_dir']) != '' ? $panther_config['o_style_dir'] : get_base_url().'/style/').$panther_user['style'],
- )
- );
-
- $db->end_transaction();
- exit;
- }
- //
- // Handle PHP errors
- //
- function error_handler($errno = 0, $errstr = 'Error', $errfile = 'unknown', $errline = 0)
- {
- global $panther_config, $lang_common, $panther_user, $panther_url;
- if (!is_int($errno)) // Make sure set_exception_handler doesn't intefere
- {
- $errstr = $errno;
- $errno = 1;
- }
- // Needed to ensure it doesn't appear after completion on every page
- if ($errno < 1)
- exit;
- $error = error_get_last();
-
- //