';
echo '';
echo '';
exit;
}
// Behave as per HTTP/1.1 spec for others
header('Location: ' . $url);
exit;
}
/**
* Re-Apply session id after page reloads
*/
function reapply_sid($url)
{
global $phpEx, $phpbb_root_path;
if ($url === "index.$phpEx")
{
return append_sid("index.$phpEx");
}
else if ($url === "{$phpbb_root_path}index.$phpEx")
{
return append_sid("{$phpbb_root_path}index.$phpEx");
}
// Remove previously added sid
if (strpos($url, 'sid=') !== false)
{
// All kind of links
$url = preg_replace('/(\?)?(&|&)?sid=[a-z0-9]+/', '', $url);
// if the sid was the first param, make the old second as first ones
$url = preg_replace("/$phpEx(&|&)+?/", "$phpEx?", $url);
}
return append_sid($url);
}
/**
* Returns url from the session/current page with an re-appended SID with optionally stripping vars from the url
*/
function build_url($strip_vars = false)
{
global $config, $user, $phpbb_path_helper;
$page = $phpbb_path_helper->get_valid_page($user->page['page'], $config['enable_mod_rewrite']);
// Append SID
$redirect = append_sid($page, false, false);
if ($strip_vars !== false)
{
$redirect = $phpbb_path_helper->strip_url_params($redirect, $strip_vars, false);
}
else
{
$redirect = str_replace('&', '&', $redirect);
}
return $redirect . ((strpos($redirect, '?') === false) ? '?' : '');
}
/**
* Meta refresh assignment
* Adds META template variable with meta http tag.
*
* @param int $time Time in seconds for meta refresh tag
* @param string $url URL to redirect to. The url will go through redirect() first before the template variable is assigned
* @param bool $disable_cd_check If true, meta_refresh() will redirect to an external domain. If false, the redirect point to the boards url if it does not match the current domain. Default is false.
*/
function meta_refresh($time, $url, $disable_cd_check = false)
{
global $template, $refresh_data, $request;
$url = redirect($url, true, $disable_cd_check);
if ($request->is_ajax())
{
$refresh_data = array(
'time' => $time,
'url' => $url,
);
}
else
{
// For XHTML compatibility we change back & to &
$url = str_replace('&', '&', $url);
$template->assign_vars(array(
'META' => '')
);
}
return $url;
}
/**
* Outputs correct status line header.
*
* Depending on php sapi one of the two following forms is used:
*
* Status: 404 Not Found
*
* HTTP/1.x 404 Not Found
*
* HTTP version is taken from HTTP_VERSION environment variable,
* and defaults to 1.0.
*
* Sample usage:
*
* send_status_line(404, 'Not Found');
*
* @param int $code HTTP status code
* @param string $message Message for the status code
* @return null
*/
function send_status_line($code, $message)
{
if (substr(strtolower(@php_sapi_name()), 0, 3) === 'cgi')
{
// in theory, we shouldn't need that due to php doing it. Reality offers a differing opinion, though
header("Status: $code $message", true, $code);
}
else
{
$version = phpbb_request_http_version();
header("$version $code $message", true, $code);
}
}
/**
* Returns the HTTP version used in the current request.
*
* Handles the case of being called before $request is present,
* in which case it falls back to the $_SERVER superglobal.
*
* @return string HTTP version
*/
function phpbb_request_http_version()
{
global $request;
$version = '';
if ($request && $request->server('SERVER_PROTOCOL'))
{
$version = $request->server('SERVER_PROTOCOL');
}
else if (isset($_SERVER['SERVER_PROTOCOL']))
{
$version = $_SERVER['SERVER_PROTOCOL'];
}
if (!empty($version) && is_string($version) && preg_match('#^HTTP/[0-9]\.[0-9]$#', $version))
{
return $version;
}
return 'HTTP/1.0';
}
//Form validation
/**
* Add a secret hash for use in links/GET requests
* @param string $link_name The name of the link; has to match the name used in check_link_hash, otherwise no restrictions apply
* @return string the hash
*/
function generate_link_hash($link_name)
{
global $user;
if (!isset($user->data["hash_$link_name"]))
{
$user->data["hash_$link_name"] = substr(sha1($user->data['user_form_salt'] . $link_name), 0, 8);
}
return $user->data["hash_$link_name"];
}
/**
* checks a link hash - for GET requests
* @param string $token the submitted token
* @param string $link_name The name of the link
* @return boolean true if all is fine
*/
function check_link_hash($token, $link_name)
{
return $token === generate_link_hash($link_name);
}
/**
* Add a secret token to the form (requires the S_FORM_TOKEN template variable)
* @param string $form_name The name of the form; has to match the name used in check_form_key, otherwise no restrictions apply
*/
function add_form_key($form_name)
{
global $config, $template, $user, $phpbb_dispatcher;
$now = time();
$token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : '';
$token = sha1($now . $user->data['user_form_salt'] . $form_name . $token_sid);
$s_fields = build_hidden_fields(array(
'creation_time' => $now,
'form_token' => $token,
));
/**
* Perform additional actions on creation of the form token
*
* @event core.add_form_key
* @var string form_name The form name
* @var int now Current time timestamp
* @var string s_fields Generated hidden fields
* @var string token Form token
* @var string token_sid User session ID
*
* @since 3.1.0-RC3
*/
$vars = array(
'form_name',
'now',
's_fields',
'token',
'token_sid',
);
extract($phpbb_dispatcher->trigger_event('core.add_form_key', compact($vars)));
$template->assign_vars(array(
'S_FORM_TOKEN' => $s_fields,
));
}
/**
* Check the form key. Required for all altering actions not secured by confirm_box
*
* @param string $form_name The name of the form; has to match the name used
* in add_form_key, otherwise no restrictions apply
* @param int $timespan The maximum acceptable age for a submitted form
* in seconds. Defaults to the config setting.
* @return bool True, if the form key was valid, false otherwise
*/
function check_form_key($form_name, $timespan = false)
{
global $config, $request, $user;
if ($timespan === false)
{
// we enforce a minimum value of half a minute here.
$timespan = ($config['form_token_lifetime'] == -1) ? -1 : max(30, $config['form_token_lifetime']);
}
if ($request->is_set_post('creation_time') && $request->is_set_post('form_token'))
{
$creation_time = abs($request->variable('creation_time', 0));
$token = $request->variable('form_token', '');
$diff = time() - $creation_time;
// If creation_time and the time() now is zero we can assume it was not a human doing this (the check for if ($diff)...
if (defined('DEBUG_TEST') || $diff && ($diff <= $timespan || $timespan === -1))
{
$token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : '';
$key = sha1($creation_time . $user->data['user_form_salt'] . $form_name . $token_sid);
if ($key === $token)
{
return true;
}
}
}
return false;
}
// Message/Login boxes
/**
* Build Confirm box
* @param boolean $check True for checking if confirmed (without any additional parameters) and false for displaying the confirm box
* @param string $title Title/Message used for confirm box.
* message text is _CONFIRM appended to title.
* If title cannot be found in user->lang a default one is displayed
* If title_CONFIRM cannot be found in user->lang the text given is used.
* @param string $hidden Hidden variables
* @param string $html_body Template used for confirm box
* @param string $u_action Custom form action
*/
function confirm_box($check, $title = '', $hidden = '', $html_body = 'confirm_body.html', $u_action = '')
{
global $user, $template, $db, $request;
global $config, $phpbb_path_helper;
if (isset($_POST['cancel']))
{
return false;
}
$confirm = ($user->lang['YES'] === $request->variable('confirm', '', true, \phpbb\request\request_interface::POST));
if ($check && $confirm)
{
$user_id = $request->variable('confirm_uid', 0);
$session_id = $request->variable('sess', '');
$confirm_key = $request->variable('confirm_key', '');
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'])
{
return false;
}
// Reset user_last_confirm_key
$sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = ''
WHERE user_id = " . $user->data['user_id'];
$db->sql_query($sql);
return true;
}
else if ($check)
{
return false;
}
$s_hidden_fields = build_hidden_fields(array(
'confirm_uid' => $user->data['user_id'],
'sess' => $user->session_id,
'sid' => $user->session_id,
));
// generate activation key
$confirm_key = gen_rand_string(10);
if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
{
adm_page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
}
else
{
page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
}
$template->set_filenames(array(
'body' => $html_body)
);
// If activation key already exist, we better do not re-use the key (something very strange is going on...)
if ($request->variable('confirm_key', ''))
{
// This should not occur, therefore we cancel the operation to safe the user
return false;
}
// re-add sid / transform & to & for user->page (user->page is always using &)
$use_page = ($u_action) ? $u_action : str_replace('&', '&', $user->page['page']);
$u_action = reapply_sid($phpbb_path_helper->get_valid_page($use_page, $config['enable_mod_rewrite']));
$u_action .= ((strpos($u_action, '?') === false) ? '?' : '&') . 'confirm_key=' . $confirm_key;
$template->assign_vars(array(
'MESSAGE_TITLE' => (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title],
'MESSAGE_TEXT' => (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'],
'YES_VALUE' => $user->lang['YES'],
'S_CONFIRM_ACTION' => $u_action,
'S_HIDDEN_FIELDS' => $hidden . $s_hidden_fields,
'S_AJAX_REQUEST' => $request->is_ajax(),
));
$sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = '" . $db->sql_escape($confirm_key) . "'
WHERE user_id = " . $user->data['user_id'];
$db->sql_query($sql);
if ($request->is_ajax())
{
$u_action .= '&confirm_uid=' . $user->data['user_id'] . '&sess=' . $user->session_id . '&sid=' . $user->session_id;
$json_response = new \phpbb\json_response;
$json_response->send(array(
'MESSAGE_BODY' => $template->assign_display('body'),
'MESSAGE_TITLE' => (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title],
'MESSAGE_TEXT' => (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'],
'YES_VALUE' => $user->lang['YES'],
'S_CONFIRM_ACTION' => str_replace('&', '&', $u_action), //inefficient, rewrite whole function
'S_HIDDEN_FIELDS' => $hidden . $s_hidden_fields
));
}
if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
{
adm_page_footer();
}
else
{
page_footer();
}
}
/**
* Generate login box or verify password
*/
function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = false, $s_display = true)
{
global $db, $user, $template, $auth, $phpEx, $phpbb_root_path, $config;
global $request, $phpbb_container, $phpbb_dispatcher, $phpbb_log;
$err = '';
// Make sure user->setup() has been called
if (!$user->is_setup())
{
$user->setup();
}
// Print out error if user tries to authenticate as an administrator without having the privileges...
if ($admin && !$auth->acl_get('a_'))
{
// Not authd
// anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
if ($user->data['is_registered'])
{
$phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_ADMIN_AUTH_FAIL');
}
trigger_error('NO_AUTH_ADMIN');
}
if ($request->is_set_post('login') || ($request->is_set('login') && $request->variable('login', '') == 'external'))
{
// Get credential
if ($admin)
{
$credential = $request->variable('credential', '');
if (strspn($credential, 'abcdef0123456789') !== strlen($credential) || strlen($credential) != 32)
{
if ($user->data['is_registered'])
{
$phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_ADMIN_AUTH_FAIL');
}
trigger_error('NO_AUTH_ADMIN');
}
$password = $request->untrimmed_variable('password_' . $credential, '', true);
}
else
{
$password = $request->untrimmed_variable('password', '', true);
}
$username = $request->variable('username', '', true);
$autologin = $request->is_set_post('autologin');
$viewonline = (int) !$request->is_set_post('viewonline');
$admin = ($admin) ? 1 : 0;
$viewonline = ($admin) ? $user->data['session_viewonline'] : $viewonline;
// Check if the supplied username is equal to the one stored within the database if re-authenticating
if ($admin && utf8_clean_string($username) != utf8_clean_string($user->data['username']))
{
// We log the attempt to use a different username...
$phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_ADMIN_AUTH_FAIL');
trigger_error('NO_AUTH_ADMIN_USER_DIFFER');
}
// If authentication is successful we redirect user to previous page
$result = $auth->login($username, $password, $autologin, $viewonline, $admin);
// If admin authentication and login, we will log if it was a success or not...
// We also break the operation on the first non-success login - it could be argued that the user already knows
if ($admin)
{
if ($result['status'] == LOGIN_SUCCESS)
{
$phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_ADMIN_AUTH_SUCCESS');
}
else
{
// Only log the failed attempt if a real user tried to.
// anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
if ($user->data['is_registered'])
{
$phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_ADMIN_AUTH_FAIL');
}
}
}
// The result parameter is always an array, holding the relevant information...
if ($result['status'] == LOGIN_SUCCESS)
{
$redirect = $request->variable('redirect', "{$phpbb_root_path}index.$phpEx");
/**
* This event allows an extension to modify the redirection when a user successfully logs in
*
* @event core.login_box_redirect
* @var string redirect Redirect string
* @var boolean admin Is admin?
* @var bool return If true, do not redirect but return the sanitized URL.
* @since 3.1.0-RC5
*/
$vars = array('redirect', 'admin', 'return');
extract($phpbb_dispatcher->trigger_event('core.login_box_redirect', compact($vars)));
// append/replace SID (may change during the session for AOL users)
$redirect = reapply_sid($redirect);
// Special case... the user is effectively banned, but we allow founders to login
if (defined('IN_CHECK_BAN') && $result['user_row']['user_type'] != USER_FOUNDER)
{
return;
}
redirect($redirect);
}
// Something failed, determine what...
if ($result['status'] == LOGIN_BREAK)
{
trigger_error($result['error_msg']);
}
// Special cases... determine
switch ($result['status'])
{
case LOGIN_ERROR_PASSWORD_CONVERT:
$err = sprintf(
$user->lang[$result['error_msg']],
($config['email_enable']) ? '' : '',
($config['email_enable']) ? '' : '',
'',
''
);
break;
case LOGIN_ERROR_ATTEMPTS:
$captcha = $phpbb_container->get('captcha.factory')->get_instance($config['captcha_plugin']);
$captcha->init(CONFIRM_LOGIN);
// $captcha->reset();
$template->assign_vars(array(
'CAPTCHA_TEMPLATE' => $captcha->get_template(),
));
// no break;
// Username, password, etc...
default:
$err = $user->lang[$result['error_msg']];
// Assign admin contact to some error messages
if ($result['error_msg'] == 'LOGIN_ERROR_USERNAME' || $result['error_msg'] == 'LOGIN_ERROR_PASSWORD')
{
$err = sprintf($user->lang[$result['error_msg']], '', '');
}
break;
}
/**
* This event allows an extension to process when a user fails a login attempt
*
* @event core.login_box_failed
* @var array result Login result data
* @var string username User name used to login
* @var string password Password used to login
* @var string err Error message
* @since 3.1.3-RC1
*/
$vars = array('result', 'username', 'password', 'err');
extract($phpbb_dispatcher->trigger_event('core.login_box_failed', compact($vars)));
}
// Assign credential for username/password pair
$credential = ($admin) ? md5(unique_id()) : false;
$s_hidden_fields = array(
'sid' => $user->session_id,
);
if ($redirect)
{
$s_hidden_fields['redirect'] = $redirect;
}
if ($admin)
{
$s_hidden_fields['credential'] = $credential;
}
/* @var $provider_collection \phpbb\auth\provider_collection */
$provider_collection = $phpbb_container->get('auth.provider_collection');
$auth_provider = $provider_collection->get_provider();
$auth_provider_data = $auth_provider->get_login_data();
if ($auth_provider_data)
{
if (isset($auth_provider_data['VARS']))
{
$template->assign_vars($auth_provider_data['VARS']);
}
if (isset($auth_provider_data['BLOCK_VAR_NAME']))
{
foreach ($auth_provider_data['BLOCK_VARS'] as $block_vars)
{
$template->assign_block_vars($auth_provider_data['BLOCK_VAR_NAME'], $block_vars);
}
}
$template->assign_vars(array(
'PROVIDER_TEMPLATE_FILE' => $auth_provider_data['TEMPLATE_FILE'],
));
}
$s_hidden_fields = build_hidden_fields($s_hidden_fields);
$template->assign_vars(array(
'LOGIN_ERROR' => $err,
'LOGIN_EXPLAIN' => $l_explain,
'U_SEND_PASSWORD' => ($config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') : '',
'U_RESEND_ACTIVATION' => ($config['require_activation'] == USER_ACTIVATION_SELF && $config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=resend_act') : '',
'U_TERMS_USE' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'),
'U_PRIVACY' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'),
'S_DISPLAY_FULL_LOGIN' => ($s_display) ? true : false,
'S_HIDDEN_FIELDS' => $s_hidden_fields,
'S_ADMIN_AUTH' => $admin,
'USERNAME' => ($admin) ? $user->data['username'] : '',
'USERNAME_CREDENTIAL' => 'username',
'PASSWORD_CREDENTIAL' => ($admin) ? 'password_' . $credential : 'password',
));
page_header($user->lang['LOGIN']);
$template->set_filenames(array(
'body' => 'login_body.html')
);
make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx"));
page_footer();
}
/**
* Generate forum login box
*/
function login_forum_box($forum_data)
{
global $db, $phpbb_container, $request, $template, $user, $phpbb_dispatcher;
$password = $request->variable('password', '', true);
$sql = 'SELECT forum_id
FROM ' . FORUMS_ACCESS_TABLE . '
WHERE forum_id = ' . $forum_data['forum_id'] . '
AND user_id = ' . $user->data['user_id'] . "
AND session_id = '" . $db->sql_escape($user->session_id) . "'";
$result = $db->sql_query($sql);
$row = $db->sql_fetchrow($result);
$db->sql_freeresult($result);
if ($row)
{
return true;
}
if ($password)
{
// Remove expired authorised sessions
$sql = 'SELECT f.session_id
FROM ' . FORUMS_ACCESS_TABLE . ' f
LEFT JOIN ' . SESSIONS_TABLE . ' s ON (f.session_id = s.session_id)
WHERE s.session_id IS NULL';
$result = $db->sql_query($sql);
if ($row = $db->sql_fetchrow($result))
{
$sql_in = array();
do
{
$sql_in[] = (string) $row['session_id'];
}
while ($row = $db->sql_fetchrow($result));
// Remove expired sessions
$sql = 'DELETE FROM ' . FORUMS_ACCESS_TABLE . '
WHERE ' . $db->sql_in_set('session_id', $sql_in);
$db->sql_query($sql);
}
$db->sql_freeresult($result);
/* @var $passwords_manager \phpbb\passwords\manager */
$passwords_manager = $phpbb_container->get('passwords.manager');
if ($passwords_manager->check($password, $forum_data['forum_password']))
{
$sql_ary = array(
'forum_id' => (int) $forum_data['forum_id'],
'user_id' => (int) $user->data['user_id'],
'session_id' => (string) $user->session_id,
);
$db->sql_query('INSERT INTO ' . FORUMS_ACCESS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
return true;
}
$template->assign_var('LOGIN_ERROR', $user->lang['WRONG_PASSWORD']);
}
/**
* Performing additional actions, load additional data on forum login
*
* @event core.login_forum_box
* @var array forum_data Array with forum data
* @var string password Password entered
* @since 3.1.0-RC3
*/
$vars = array('forum_data', 'password');
extract($phpbb_dispatcher->trigger_event('core.login_forum_box', compact($vars)));
page_header($user->lang['LOGIN']);
$template->assign_vars(array(
'FORUM_NAME' => isset($forum_data['forum_name']) ? $forum_data['forum_name'] : '',
'S_LOGIN_ACTION' => build_url(array('f')),
'S_HIDDEN_FIELDS' => build_hidden_fields(array('f' => $forum_data['forum_id'])))
);
$template->set_filenames(array(
'body' => 'login_forum.html')
);
page_footer();
}
// Little helpers
/**
* Little helper for the build_hidden_fields function
*/
function _build_hidden_fields($key, $value, $specialchar, $stripslashes)
{
$hidden_fields = '';
if (!is_array($value))
{
$value = ($stripslashes) ? stripslashes($value) : $value;
$value = ($specialchar) ? htmlspecialchars($value, ENT_COMPAT, 'UTF-8') : $value;
$hidden_fields .= '' . "\n";
}
else
{
foreach ($value as $_key => $_value)
{
$_key = ($stripslashes) ? stripslashes($_key) : $_key;
$_key = ($specialchar) ? htmlspecialchars($_key, ENT_COMPAT, 'UTF-8') : $_key;
$hidden_fields .= _build_hidden_fields($key . '[' . $_key . ']', $_value, $specialchar, $stripslashes);
}
}
return $hidden_fields;
}
/**
* Build simple hidden fields from array
*
* @param array $field_ary an array of values to build the hidden field from
* @param bool $specialchar if true, keys and values get specialchared
* @param bool $stripslashes if true, keys and values get stripslashed
*
* @return string the hidden fields
*/
function build_hidden_fields($field_ary, $specialchar = false, $stripslashes = false)
{
$s_hidden_fields = '';
foreach ($field_ary as $name => $vars)
{
$name = ($stripslashes) ? stripslashes($name) : $name;
$name = ($specialchar) ? htmlspecialchars($name, ENT_COMPAT, 'UTF-8') : $name;
$s_hidden_fields .= _build_hidden_fields($name, $vars, $specialchar, $stripslashes);
}
return $s_hidden_fields;
}
/**
* Parse cfg file
*/
function parse_cfg_file($filename, $lines = false)
{
$parsed_items = array();
if ($lines === false)
{
$lines = file($filename);
}
foreach ($lines as $line)
{
$line = trim($line);
if (!$line || $line[0] == '#' || ($delim_pos = strpos($line, '=')) === false)
{
continue;
}
// Determine first occurrence, since in values the equal sign is allowed
$key = htmlspecialchars(strtolower(trim(substr($line, 0, $delim_pos))));
$value = trim(substr($line, $delim_pos + 1));
if (in_array($value, array('off', 'false', '0')))
{
$value = false;
}
else if (in_array($value, array('on', 'true', '1')))
{
$value = true;
}
else if (!trim($value))
{
$value = '';
}
else if (($value[0] == "'" && $value[sizeof($value) - 1] == "'") || ($value[0] == '"' && $value[sizeof($value) - 1] == '"'))
{
$value = htmlspecialchars(substr($value, 1, sizeof($value)-2));
}
else
{
$value = htmlspecialchars($value);
}
$parsed_items[$key] = $value;
}
if (isset($parsed_items['parent']) && isset($parsed_items['name']) && $parsed_items['parent'] == $parsed_items['name'])
{
unset($parsed_items['parent']);
}
return $parsed_items;
}
/**
* Return a nicely formatted backtrace.
*
* Turns the array returned by debug_backtrace() into HTML markup.
* Also filters out absolute paths to phpBB root.
*
* @return string HTML markup
*/
function get_backtrace()
{
$output = '
';
$backtrace = debug_backtrace();
// We skip the first one, because it only shows this file/function
unset($backtrace[0]);
foreach ($backtrace as $trace)
{
// Strip the current directory from path
$trace['file'] = (empty($trace['file'])) ? '(not given by php)' : htmlspecialchars(phpbb_filter_root_path($trace['file']));
$trace['line'] = (empty($trace['line'])) ? '(not given by php)' : $trace['line'];
// Only show function arguments for include etc.
// Other parameters may contain sensible information
$argument = '';
if (!empty($trace['args'][0]) && in_array($trace['function'], array('include', 'require', 'include_once', 'require_once')))
{
$argument = htmlspecialchars(phpbb_filter_root_path($trace['args'][0]));
}
$trace['class'] = (!isset($trace['class'])) ? '' : $trace['class'];
$trace['type'] = (!isset($trace['type'])) ? '' : $trace['type'];
$output .= ' ';
$output .= 'FILE: ' . $trace['file'] . ' ';
$output .= 'LINE: ' . ((!empty($trace['line'])) ? $trace['line'] : '') . ' ';
$output .= 'CALL: ' . htmlspecialchars($trace['class'] . $trace['type'] . $trace['function']);
$output .= '(' . (($argument !== '') ? "'$argument'" : '') . ') ';
}
$output .= '
';
return $output;
}
/**
* This function returns a regular expression pattern for commonly used expressions
* Use with / as delimiter for email mode and # for url modes
* mode can be: email|bbcode_htm|url|url_inline|www_url|www_url_inline|relative_url|relative_url_inline|ipv4|ipv6
*/
function get_preg_expression($mode)
{
switch ($mode)
{
case 'email':
// Regex written by James Watts and Francisco Jose Martin Moreno
// http://fightingforalostcause.net/misc/2006/compare-email-regex.php
return '((?:[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*(?:[\w\!\#$\%\'\*\+\-\/\=\?\^\`{\|\}\~]|&)+)@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,63})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)';
break;
case 'bbcode_htm':
return array(
'#.*?#',
'#.*?#',
'#.*?#',
'# strlen($longest_match))
{
$longest_match = $match[0];
$longest_match_offset = $match[1];
}
}
$ret = substr_replace($ret, '', $longest_match_offset, strlen($longest_match));
if ($longest_match_offset == strlen($ret))
{
$ret .= ':';
}
if ($longest_match_offset == 0)
{
$ret = ':' . $ret;
}
return $ret;
default:
return false;
}
}
/**
* Wrapper for inet_pton()
*
* Converts a human readable IP address to its packed in_addr representation
* inet_pton() is supported by PHP since 5.1.0, since 5.3.0 also on Windows.
*
* @param string $address A human readable IPv4 or IPv6 address.
*
* @return mixed false if address is invalid,
* in_addr representation of the given address otherwise (string)
*/
function phpbb_inet_pton($address)
{
$ret = '';
if (preg_match(get_preg_expression('ipv4'), $address))
{
foreach (explode('.', $address) as $part)
{
$ret .= ($part <= 0xF ? '0' : '') . dechex($part);
}
return pack('H*', $ret);
}
if (preg_match(get_preg_expression('ipv6'), $address))
{
$parts = explode(':', $address);
$missing_parts = 8 - sizeof($parts) + 1;
if (substr($address, 0, 2) === '::')
{
++$missing_parts;
}
if (substr($address, -2) === '::')
{
++$missing_parts;
}
$embedded_ipv4 = false;
$last_part = end($parts);
if (preg_match(get_preg_expression('ipv4'), $last_part))
{
$parts[sizeof($parts) - 1] = '';
$last_part = phpbb_inet_pton($last_part);
$embedded_ipv4 = true;
--$missing_parts;
}
foreach ($parts as $i => $part)
{
if (strlen($part))
{
$ret .= str_pad($part, 4, '0', STR_PAD_LEFT);
}
else if ($i && $i < sizeof($parts) - 1)
{
$ret .= str_repeat('0000', $missing_parts);
}
}
$ret = pack('H*', $ret);
if ($embedded_ipv4)
{
$ret .= $last_part;
}
return $ret;
}
return false;
}
/**
* Wrapper for php's checkdnsrr function.
*
* @param string $host Fully-Qualified Domain Name
* @param string $type Resource record type to lookup
* Supported types are: MX (default), A, AAAA, NS, TXT, CNAME
* Other types may work or may not work
*
* @return mixed true if entry found,
* false if entry not found,
* null if this function is not supported by this environment
*
* Since null can also be returned, you probably want to compare the result
* with === true or === false,
*/
function phpbb_checkdnsrr($host, $type = 'MX')
{
// The dot indicates to search the DNS root (helps those having DNS prefixes on the same domain)
if (substr($host, -1) == '.')
{
$host_fqdn = $host;
$host = substr($host, 0, -1);
}
else
{
$host_fqdn = $host . '.';
}
// $host has format some.host.example.com
// $host_fqdn has format some.host.example.com.
// If we're looking for an A record we can use gethostbyname()
if ($type == 'A' && function_exists('gethostbyname'))
{
return (@gethostbyname($host_fqdn) == $host_fqdn) ? false : true;
}
if (function_exists('checkdnsrr'))
{
return checkdnsrr($host_fqdn, $type);
}
if (function_exists('dns_get_record'))
{
// dns_get_record() expects an integer as second parameter
// We have to convert the string $type to the corresponding integer constant.
$type_constant = 'DNS_' . $type;
$type_param = (defined($type_constant)) ? constant($type_constant) : DNS_ANY;
// dns_get_record() might throw E_WARNING and return false for records that do not exist
$resultset = @dns_get_record($host_fqdn, $type_param);
if (empty($resultset) || !is_array($resultset))
{
return false;
}
else if ($type_param == DNS_ANY)
{
// $resultset is a non-empty array
return true;
}
foreach ($resultset as $result)
{
if (
isset($result['host']) && $result['host'] == $host &&
isset($result['type']) && $result['type'] == $type
)
{
return true;
}
}
return false;
}
// If we're on Windows we can still try to call nslookup via exec() as a last resort
if (DIRECTORY_SEPARATOR == '\\' && function_exists('exec'))
{
@exec('nslookup -type=' . escapeshellarg($type) . ' ' . escapeshellarg($host_fqdn), $output);
// If output is empty, the nslookup failed
if (empty($output))
{
return NULL;
}
foreach ($output as $line)
{
$line = trim($line);
if (empty($line))
{
continue;
}
// Squash tabs and multiple whitespaces to a single whitespace.
$line = preg_replace('/\s+/', ' ', $line);
switch ($type)
{
case 'MX':
if (stripos($line, "$host MX") === 0)
{
return true;
}
break;
case 'NS':
if (stripos($line, "$host nameserver") === 0)
{
return true;
}
break;
case 'TXT':
if (stripos($line, "$host text") === 0)
{
return true;
}
break;
case 'CNAME':
if (stripos($line, "$host canonical name") === 0)
{
return true;
}
break;
default:
case 'AAAA':
// AAAA records returned by nslookup on Windows XP/2003 have this format.
// Later Windows versions use the A record format below for AAAA records.
if (stripos($line, "$host AAAA IPv6 address") === 0)
{
return true;
}
// No break
case 'A':
if (!empty($host_matches))
{
// Second line
if (stripos($line, "Address: ") === 0)
{
return true;
}
else
{
$host_matches = false;
}
}
else if (stripos($line, "Name: $host") === 0)
{
// First line
$host_matches = true;
}
break;
}
}
return false;
}
return NULL;
}
// Handler, header and footer
/**
* Error and message handler, call with trigger_error if read
*/
function msg_handler($errno, $msg_text, $errfile, $errline)
{
global $cache, $db, $auth, $template, $config, $user, $request;
global $phpEx, $phpbb_root_path, $msg_title, $msg_long_text, $phpbb_log;
// Do not display notices if we suppress them via @
if (error_reporting() == 0 && $errno != E_USER_ERROR && $errno != E_USER_WARNING && $errno != E_USER_NOTICE)
{
return;
}
// Message handler is stripping text. In case we need it, we are possible to define long text...
if (isset($msg_long_text) && $msg_long_text && !$msg_text)
{
$msg_text = $msg_long_text;
}
switch ($errno)
{
case E_NOTICE:
case E_WARNING:
// Check the error reporting level and return if the error level does not match
// If DEBUG is defined the default level is E_ALL
if (($errno & ((defined('DEBUG')) ? E_ALL : error_reporting())) == 0)
{
return;
}
if (strpos($errfile, 'cache') === false && strpos($errfile, 'template.') === false)
{
$errfile = phpbb_filter_root_path($errfile);
$msg_text = phpbb_filter_root_path($msg_text);
$error_name = ($errno === E_WARNING) ? 'PHP Warning' : 'PHP Notice';
echo '[phpBB Debug] ' . $error_name . ': in file ' . $errfile . ' on line ' . $errline . ': ' . $msg_text . ' ' . "\n";
// we are writing an image - the user won't see the debug, so let's place it in the log
if (defined('IMAGE_OUTPUT') || defined('IN_CRON'))
{
$phpbb_log->add('critical', $user->data['user_id'], $user->ip, 'LOG_IMAGE_GENERATION_ERROR', false, array($errfile, $errline, $msg_text));
}
// echo '
BACKTRACE ' . $backtrace;
}
if (defined('IN_INSTALL') || defined('DEBUG') || isset($auth) && $auth->acl_get('a_'))
{
$msg_text = $log_text;
// If this is defined there already was some output
// So let's not break it
if (defined('IN_DB_UPDATE'))
{
echo '
' . $msg_text . '
';
$db->sql_return_on_error(true);
phpbb_end_update($cache, $config);
}
}
if ((defined('IN_CRON') || defined('IMAGE_OUTPUT')) && isset($db))
{
// let's avoid loops
$db->sql_return_on_error(true);
$phpbb_log->add('critical', $user->data['user_id'], $user->ip, 'LOG_GENERAL_ERROR', false, array($msg_title, $log_text));
$db->sql_return_on_error(false);
}
// Do not send 200 OK, but service unavailable on errors
send_status_line(503, 'Service Unavailable');
garbage_collection();
// Try to not call the adm page data...
echo '';
echo '';
echo '';
echo '';
echo '' . $msg_title . '';
echo '';
echo '';
echo '';
echo '