/** * Step 0.5: Does the login work? */ function checkLogin() { global $modSettings, $upcontext, $disable_security, $db_type, $support_js; // Login checks require hard database work :P $db = database(); // Are we trying to login? if (isset($_POST['contbutt']) && (!empty($_POST['user']) || $disable_security)) { // If we've disabled security pick a suitable name! if (empty($_POST['user'])) { $_POST['user'] = '******'; } // Before SMF 2.0 these column names were different! $oldDB = false; if (empty($db_type) || $db_type == 'mysql') { $request = $db->query('', ' SHOW COLUMNS FROM {db_prefix}members LIKE {string:member_name}', array('member_name' => 'memberName', 'db_error_skip' => true)); if ($db->num_rows($request) != 0) { $oldDB = true; } $db->free_result($request); } // Get what we believe to be their details. if (!$disable_security) { if ($oldDB) { $request = $db->query('', ' SELECT id_member, memberName AS member_name, passwd, id_group, additionalGroups AS additional_groups, lngfile FROM {db_prefix}members WHERE memberName = {string:member_name}', array('member_name' => $_POST['user'], 'db_error_skip' => true)); } else { $request = $db->query('', ' SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile FROM {db_prefix}members WHERE member_name = {string:member_name}', array('member_name' => $_POST['user'], 'db_error_skip' => true)); } if ($db->num_rows($request) != 0) { list($id_member, $name, $password, $id_group, $addGroups, $user_language) = $db->fetch_row($request); // These will come in handy, if you want to login require_once SOURCEDIR . '/Security.php'; require_once SUBSDIR . '/Auth.subs.php'; $groups = explode(',', $addGroups); $groups[] = $id_group; foreach ($groups as $k => $v) { $groups[$k] = (int) $v; } // Figure out if the password is using our encryption - if what they typed is right. if (isset($_REQUEST['hash_passwrd']) && strlen($_REQUEST['hash_passwrd']) === 64) { validateToken('login'); $valid_password = validateLoginPassword($_REQUEST['hash_passwrd'], $password); // Challenge passed. if ($valid_password) { $sha_passwd = $_REQUEST['hash_passwrd']; $valid_password = true; } elseif (preg_match('/^[0-9a-f]{40}$/i', $password)) { // Might Need to update so we will need to ask for the password again. $upcontext['disable_login_hashing'] = true; $upcontext['login_hash_error'] = true; } } else { // validateLoginPassword will convert this to a SHA-256 pw and check it $sha_passwd = $_POST['passwrd']; $valid_password = validateLoginPassword($sha_passwd, $password, $_POST['user']); } // Password still not working? if ($valid_password === false && !empty($_POST['passwrd'])) { // SHA-1 from SMF? $sha_passwd = sha1(Util::strtolower($_POST['user']) . $_POST['passwrd']); $valid_password = $sha_passwd === $password; // Lets upgrade this to our new password if ($valid_password) { $password = validateLoginPassword($_POST['passwrd'], '', $_POST['user'], true); $password_salt = substr(md5(mt_rand()), 0, 4); // Update the password hash and set up the salt. updateMemberData($id_member, array('passwd' => $password, 'password_salt' => $password_salt, 'passwd_flood' => '')); } } } else { $upcontext['username_incorrect'] = true; } $db->free_result($request); } $upcontext['username'] = $_POST['user']; // Track whether javascript works! if (!empty($_POST['js_works'])) { $upcontext['upgrade_status']['js'] = 1; $support_js = 1; } else { $support_js = 0; } // Note down the version we are coming from. if (!empty($modSettings['elkVersion']) && empty($upcontext['user']['version'])) { $upcontext['user']['version'] = $modSettings['elkVersion']; } // Didn't get anywhere? if (empty($valid_password) && empty($upcontext['username_incorrect']) && !$disable_security) { // MD5? $md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user'])); if ($md5pass != $password) { $upcontext['password_failed'] = true; // Disable the hashing this time. $upcontext['disable_login_hashing'] = true; } } if (empty($upcontext['password_failed']) && !empty($name) || $disable_security) { // Set the password. if (!$disable_security) { // Do we actually have permission? if (!in_array(1, $groups)) { $request = $db->query('', ' SELECT permission FROM {db_prefix}permissions WHERE id_group IN ({array_int:groups}) AND permission = {string:admin_forum}', array('groups' => $groups, 'admin_forum' => 'admin_forum', 'db_error_skip' => true)); if ($db->num_rows($request) == 0) { return throw_error('You need to be an admin to perform an upgrade!'); } $db->free_result($request); } $upcontext['user']['id'] = $id_member; $upcontext['user']['name'] = $name; } else { $upcontext['user']['id'] = 1; $upcontext['user']['name'] = 'Administrator'; } $upcontext['user']['pass'] = mt_rand(0, 60000); // This basically is used to match the GET variables to Settings.php. $upcontext['upgrade_status']['pass'] = $upcontext['user']['pass']; // Set the language to that of the user? if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/' . basename($user_language, '.lng') . '/index.' . basename($user_language, '.lng') . '.php')) { $user_language = basename($user_language, '.lng'); $temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/' . $user_language . '/index.' . $user_language . '.php')), 0, 4096); preg_match('~(?://|/\\*)\\s*Version:\\s+(.+?);\\s*index(?:[\\s]{2}|\\*/)~i', $temp, $match); if (empty($match[1]) || $match[1] != CURRENT_LANG_VERSION) { $upcontext['upgrade_options_warning'] = 'The language files for your selected language, ' . $user_language . ', have not been updated to the latest version. Upgrade will continue with the forum default, ' . $upcontext['language'] . '.'; } elseif (!file_exists($modSettings['theme_dir'] . '/languages/' . $user_language . '/Install.' . $user_language . '.php')) { $upcontext['upgrade_options_warning'] = 'The language files for your selected language, ' . $user_language . ', have not been uploaded/updated as the "Install" language file is missing. Upgrade will continue with the forum default, ' . $upcontext['language'] . '.'; } else { // Set this as the new language. $upcontext['language'] = $user_language; $upcontext['upgrade_status']['lang'] = $upcontext['language']; // Include the file. require_once $modSettings['theme_dir'] . '/languages/' . $user_language . '/Install.' . $user_language . '.php'; } } // If we're resuming set the step and substep to be correct. if (isset($_POST['cont'])) { $upcontext['current_step'] = $upcontext['user']['step']; $_GET['substep'] = $upcontext['user']['substep']; } return true; } } return false; }
/** * Verify the activation code, and activate the user if correct. * Accessed by ?action=activate */ public function action_activate() { global $context, $txt, $modSettings, $scripturl, $language, $user_info; require_once SUBSDIR . '/Auth.subs.php'; // Logged in users should not bother to activate their accounts if (!empty($user_info['id'])) { redirectexit(); } loadLanguage('Login'); loadTemplate('Login'); loadJavascriptFile('sha256.js', array('defer' => true)); if (empty($_REQUEST['u']) && empty($_POST['user'])) { if (empty($modSettings['registration_method']) || $modSettings['registration_method'] == '3') { fatal_lang_error('no_access', false); } $context['member_id'] = 0; $context['sub_template'] = 'resend'; $context['page_title'] = $txt['invalid_activation_resend']; $context['can_activate'] = empty($modSettings['registration_method']) || $modSettings['registration_method'] == '1'; $context['default_username'] = isset($_GET['user']) ? $_GET['user'] : ''; return; } // Get the code from the database... $row = findUser(empty($_REQUEST['u']) ? ' member_name = {string:email_address} OR email_address = {string:email_address}' : ' id_member = {int:id_member}', array('id_member' => isset($_REQUEST['u']) ? (int) $_REQUEST['u'] : 0, 'email_address' => isset($_POST['user']) ? $_POST['user'] : ''), false); // Does this user exist at all? if (empty($row)) { $context['sub_template'] = 'retry_activate'; $context['page_title'] = $txt['invalid_userid']; $context['member_id'] = 0; return; } // Change their email address? (they probably tried a fake one first :P.) require_once SUBSDIR . '/Auth.subs.php'; if (isset($_POST['new_email'], $_REQUEST['passwd']) && validateLoginPassword($_REQUEST['passwd'], $row['passwd'], $row['member_name'], true) && ($row['is_activated'] == 0 || $row['is_activated'] == 2)) { if (empty($modSettings['registration_method']) || $modSettings['registration_method'] == 3) { fatal_lang_error('no_access', false); } // @todo Separate the sprintf? require_once SUBSDIR . '/DataValidator.class.php'; if (!Data_Validator::is_valid($_POST, array('new_email' => 'valid_email|required|max_length[255]'), array('new_email' => 'trim'))) { fatal_error(sprintf($txt['valid_email_needed'], htmlspecialchars($_POST['new_email'], ENT_COMPAT, 'UTF-8')), false); } // Make sure their email isn't banned. isBannedEmail($_POST['new_email'], 'cannot_register', $txt['ban_register_prohibited']); // Ummm... don't even dare try to take someone else's email!! // @todo Separate the sprintf? if (userByEmail($_POST['new_email'])) { fatal_lang_error('email_in_use', false, array(htmlspecialchars($_POST['new_email'], ENT_COMPAT, 'UTF-8'))); } updateMemberData($row['id_member'], array('email_address' => $_POST['new_email'])); $row['email_address'] = $_POST['new_email']; $email_change = true; } // Resend the password, but only if the account wasn't activated yet. if (!empty($_REQUEST['sa']) && $_REQUEST['sa'] == 'resend' && ($row['is_activated'] == 0 || $row['is_activated'] == 2) && (!isset($_REQUEST['code']) || $_REQUEST['code'] == '')) { require_once SUBSDIR . '/Mail.subs.php'; $replacements = array('REALNAME' => $row['real_name'], 'USERNAME' => $row['member_name'], 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $row['id_member'] . ';code=' . $row['validation_code'], 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $row['id_member'], 'ACTIVATIONCODE' => $row['validation_code'], 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder'); $emaildata = loadEmailTemplate('resend_activate_message', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']); sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); $context['page_title'] = $txt['invalid_activation_resend']; // This will ensure we don't actually get an error message if it works! $context['error_title'] = ''; fatal_lang_error(!empty($email_change) ? 'change_email_success' : 'resend_email_success', false); } // Quit if this code is not right. if (empty($_REQUEST['code']) || $row['validation_code'] != $_REQUEST['code']) { if (!empty($row['is_activated'])) { fatal_lang_error('already_activated', false); } elseif ($row['validation_code'] == '') { loadLanguage('Profile'); fatal_error($txt['registration_not_approved'] . ' <a href="' . $scripturl . '?action=activate;user='******'member_name'] . '">' . $txt['here'] . '</a>.', false); } $context['sub_template'] = 'retry_activate'; $context['page_title'] = $txt['invalid_activation_code']; $context['member_id'] = $row['id_member']; return; } // Let the integration know that they've been activated! call_integration_hook('integrate_activate', array($row['member_name'])); // Validation complete - update the database! updateMemberData($row['id_member'], array('is_activated' => 1, 'validation_code' => '')); // Also do a proper member stat re-evaluation. updateStats('member', false); if (!isset($_POST['new_email'])) { require_once SUBSDIR . '/Notification.subs.php'; sendAdminNotifications('activation', $row['id_member'], $row['member_name']); } $context += array('page_title' => $txt['registration_successful'], 'sub_template' => 'login', 'default_username' => $row['member_name'], 'default_password' => '', 'never_expire' => false, 'description' => $txt['activate_success']); }
/** * Check the passed id_member/password. * If $is_username is true, treats $id as a username. * * @param int|null $id * @param string|null $password * @param bool $is_username */ function ssi_checkPassword($id = null, $password = null, $is_username = false) { // If $id is null, this was most likely called from a query string and should do nothing. if ($id === null) { return; } require_once SUBSDIR . '/Auth.subs.php'; $member = loadExistingMember($id, !$is_username); return validateLoginPassword($password, $member['passwd'], $member['member_name']) && $member['is_activated'] == 1; }
/** * Ask for the administrator login information. */ function action_adminAccount() { global $txt, $db_type, $db_connection, $databases, $incontext, $db_prefix, $db_passwd; $incontext['sub_template'] = 'admin_account'; $incontext['page_title'] = $txt['user_settings']; $incontext['continue'] = 1; // Need this to check whether we need the database password. require dirname(__FILE__) . '/Settings.php'; if (!defined('ELK')) { define('ELK', 1); } definePaths(); // These files may be or may not be already included, better safe than sorry for now require_once SOURCEDIR . '/Subs.php'; require_once SUBSDIR . '/Util.class.php'; $db = load_database(); if (!isset($_POST['username'])) { $_POST['username'] = ''; } if (!isset($_POST['email'])) { $_POST['email'] = ''; } $incontext['username'] = htmlspecialchars(stripslashes($_POST['username']), ENT_COMPAT, 'UTF-8'); $incontext['email'] = htmlspecialchars(stripslashes($_POST['email']), ENT_COMPAT, 'UTF-8'); $incontext['require_db_confirm'] = empty($db_type) || !empty($databases[$db_type]['require_db_confirm']); // Only allow create an admin account if they don't have one already. $request = $db->query('', ' SELECT id_member FROM {db_prefix}members WHERE id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0 LIMIT 1', array('db_error_skip' => true, 'admin_group' => 1)); // Skip the step if an admin already exists if ($db->num_rows($request) != 0) { return true; } $db->free_result($request); // Trying to create an account? if (isset($_POST['password1']) && !empty($_POST['contbutt'])) { // Wrong password? if ($incontext['require_db_confirm'] && $_POST['password3'] != $db_passwd) { $incontext['error'] = $txt['error_db_connect']; return false; } // Not matching passwords? if ($_POST['password1'] != $_POST['password2']) { $incontext['error'] = $txt['error_user_settings_again_match']; return false; } // No password? if (strlen($_POST['password1']) < 4) { $incontext['error'] = $txt['error_user_settings_no_password']; return false; } if (!file_exists(SOURCEDIR . '/Subs.php')) { $incontext['error'] = $txt['error_subs_missing']; return false; } // Update the main contact email? if (!empty($_POST['email']) && (empty($webmaster_email) || $webmaster_email == '*****@*****.**')) { updateSettingsFile(array('webmaster_email' => $_POST['email'])); } // Work out whether we're going to have dodgy characters and remove them. $invalid_characters = preg_match('~[<>&"\'=\\\\]~', $_POST['username']) != 0; $_POST['username'] = preg_replace('~[<>&"\'=\\\\]~', '', $_POST['username']); $result = $db->query('', ' SELECT id_member, password_salt FROM {db_prefix}members WHERE member_name = {string:username} OR email_address = {string:email} LIMIT 1', array('username' => stripslashes($_POST['username']), 'email' => stripslashes($_POST['email']), 'db_error_skip' => true)); if ($db->num_rows($result) != 0) { list($incontext['member_id'], $incontext['member_salt']) = $db->fetch_row($result); $db->free_result($result); $incontext['account_existed'] = $txt['error_user_settings_taken']; } elseif ($_POST['username'] == '' || strlen($_POST['username']) > 25) { // Try the previous step again. $incontext['error'] = $_POST['username'] == '' ? $txt['error_username_left_empty'] : $txt['error_username_too_long']; return false; } elseif ($invalid_characters || $_POST['username'] == '_' || $_POST['username'] == '|' || strpos($_POST['username'], '[code') !== false || strpos($_POST['username'], '[/code') !== false) { // Try the previous step again. $incontext['error'] = $txt['error_invalid_characters_username']; return false; } elseif (empty($_POST['email']) || !filter_var(stripslashes($_POST['email']), FILTER_VALIDATE_EMAIL) || strlen(stripslashes($_POST['email'])) > 255) { // One step back, this time fill out a proper email address. $incontext['error'] = sprintf($txt['error_valid_email_needed'], $_POST['username']); return false; } elseif ($_POST['username'] != '') { require_once SUBSDIR . '/Auth.subs.php'; $incontext['member_salt'] = substr(md5(mt_rand()), 0, 4); // Format the username properly. $_POST['username'] = preg_replace('~[\\t\\n\\r\\x0B\\0\\xA0]+~', ' ', $_POST['username']); $ip = isset($_SERVER['REMOTE_ADDR']) ? substr($_SERVER['REMOTE_ADDR'], 0, 255) : ''; // Get a security hash for this combination $password = stripslashes($_POST['password1']); $incontext['passwd'] = validateLoginPassword($password, '', $_POST['username'], true); $request = $db->insert('', $db_prefix . 'members', array('member_name' => 'string-25', 'real_name' => 'string-25', 'passwd' => 'string', 'email_address' => 'string', 'id_group' => 'int', 'posts' => 'int', 'date_registered' => 'int', 'hide_email' => 'int', 'password_salt' => 'string', 'lngfile' => 'string', 'personal_text' => 'string', 'avatar' => 'string', 'member_ip' => 'string', 'member_ip2' => 'string', 'buddy_list' => 'string', 'pm_ignore_list' => 'string', 'message_labels' => 'string', 'website_title' => 'string', 'website_url' => 'string', 'location' => 'string', 'signature' => 'string', 'usertitle' => 'string', 'secret_question' => 'string', 'additional_groups' => 'string', 'ignore_boards' => 'string', 'openid_uri' => 'string'), array(stripslashes($_POST['username']), stripslashes($_POST['username']), $incontext['passwd'], stripslashes($_POST['email']), 1, 0, time(), 0, $incontext['member_salt'], '', '', '', $ip, $ip, '', '', '', '', '', '', '', '', '', '', '', ''), array('id_member')); // Awww, crud! if ($request === false) { $incontext['error'] = $txt['error_user_settings_query'] . '<br /> <div style="margin: 2ex;">' . nl2br(htmlspecialchars($db->last_error($db_connection), ENT_COMPAT, 'UTF-8')) . '</div>'; return false; } $incontext['member_id'] = $db->insert_id("{$db_prefix}members", 'id_member'); } // If we're here we're good. return true; } return false; }
/** * Actually logs you in. * * What it does: * - checks credentials and checks that login was successful. * - it employs protection against a specific IP or user trying to brute force * a login to an account. * - upgrades password encryption on login, if necessary. * - after successful login, redirects you to $_SESSION['login_url']. * - accessed from ?action=login2, by forms. * * On error, uses the same templates action_login() uses. */ public function action_login2() { global $txt, $scripturl, $user_info, $user_settings, $modSettings, $context, $sc; // Load cookie authentication and all stuff. require_once SUBSDIR . '/Auth.subs.php'; // Beyond this point you are assumed to be a guest trying to login. if (!$user_info['is_guest']) { redirectexit(); } // Are you guessing with a script? checkSession('post'); validateToken('login'); spamProtection('login'); // Set the login_url if it's not already set (but careful not to send us to an attachment). if (empty($_SESSION['login_url']) && isset($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'dlattach') === false && preg_match('~(board|topic)[=,]~', $_SESSION['old_url']) != 0 || isset($_GET['quicklogin']) && isset($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'login') === false) { $_SESSION['login_url'] = $_SESSION['old_url']; } // Been guessing a lot, haven't we? if (isset($_SESSION['failed_login']) && $_SESSION['failed_login'] >= $modSettings['failed_login_threshold'] * 3) { fatal_lang_error('login_threshold_fail', 'critical'); } // Set up the cookie length. (if it's invalid, just fall through and use the default.) if (isset($_POST['cookieneverexp']) || !empty($_POST['cookielength']) && $_POST['cookielength'] == -1) { $modSettings['cookieTime'] = 3153600; } elseif (!empty($_POST['cookielength']) && ($_POST['cookielength'] >= 1 || $_POST['cookielength'] <= 525600)) { $modSettings['cookieTime'] = (int) $_POST['cookielength']; } loadLanguage('Login'); // Load the template stuff loadTemplate('Login'); loadJavascriptFile('sha256.js', array('defer' => true)); $context['sub_template'] = 'login'; // Set up the default/fallback stuff. $context['default_username'] = isset($_POST['user']) ? preg_replace('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($_POST['user'], ENT_COMPAT, 'UTF-8')) : ''; $context['default_password'] = ''; $context['never_expire'] = $modSettings['cookieTime'] == 525600 || $modSettings['cookieTime'] == 3153600; $context['login_errors'] = array($txt['error_occurred']); $context['page_title'] = $txt['login']; // Add the login chain to the link tree. $context['linktree'][] = array('url' => $scripturl . '?action=login', 'name' => $txt['login']); // This is an OpenID login. Let's validate... if (!empty($_POST['openid_identifier']) && !empty($modSettings['enableOpenID'])) { require_once SUBSDIR . '/OpenID.subs.php'; $open_id = new OpenID(); if ($open_id->validate($_POST['openid_identifier']) !== 'no_data') { return $open_id; } else { $context['login_errors'] = array($txt['openid_not_found']); return; } } // You forgot to type your username, dummy! if (!isset($_POST['user']) || $_POST['user'] == '') { $context['login_errors'] = array($txt['need_username']); return; } // No one needs a username that long, plus we only support 80 chars in the db if (Util::strlen($_POST['user']) > 80) { $_POST['user'] = Util::substr($_POST['user'], 0, 80); } // Can't use a password > 64 characters sorry, to long and only good for a DoS attack // Plus we expect a 64 character one from SHA-256 if (isset($_POST['passwrd']) && strlen($_POST['passwrd']) > 64 || isset($_POST['hash_passwrd']) && strlen($_POST['hash_passwrd']) > 64) { $context['login_errors'] = array($txt['improper_password']); return; } // Hmm... maybe 'admin' will login with no password. Uhh... NO! if ((!isset($_POST['passwrd']) || $_POST['passwrd'] == '') && (!isset($_POST['hash_passwrd']) || strlen($_POST['hash_passwrd']) != 64)) { $context['login_errors'] = array($txt['no_password']); return; } // No funky symbols either. if (preg_match('~[<>&"\'=\\\\]~', preg_replace('~(&#(\\d{1,7}|x[0-9a-fA-F]{1,6});)~', '', $_POST['user'])) != 0) { $context['login_errors'] = array($txt['error_invalid_characters_username']); return; } // Are we using any sort of integration to validate the login? if (in_array('retry', call_integration_hook('integrate_validate_login', array($_POST['user'], isset($_POST['hash_passwrd']) && strlen($_POST['hash_passwrd']) == 40 ? $_POST['hash_passwrd'] : null, $modSettings['cookieTime'])), true)) { $context['login_errors'] = array($txt['login_hash_error']); $context['disable_login_hashing'] = true; return; } // Find them... if we can $user_settings = loadExistingMember($_POST['user']); // Let them try again, it didn't match anything... if (empty($user_settings)) { $context['login_errors'] = array($txt['username_no_exist']); return; } // Figure out if the password is using Elk's encryption - if what they typed is right. if (isset($_POST['hash_passwrd']) && strlen($_POST['hash_passwrd']) === 64) { // Challenge what was passed $valid_password = validateLoginPassword($_POST['hash_passwrd'], $user_settings['passwd']); // Let them in if ($valid_password) { $sha_passwd = $_POST['hash_passwrd']; $valid_password = true; } elseif (preg_match('/^[0-9a-f]{40}$/i', $user_settings['passwd']) && isset($_POST['old_hash_passwrd']) && $_POST['old_hash_passwrd'] === hash('sha1', $user_settings['passwd'] . $sc)) { // Old password passed, turn off hashing and ask for it again so we can update the db to something more secure. $context['login_errors'] = array($txt['login_hash_error']); $context['disable_login_hashing'] = true; unset($user_settings); return; } else { // Don't allow this! validatePasswordFlood($user_settings['id_member'], $user_settings['passwd_flood']); $_SESSION['failed_login'] = isset($_SESSION['failed_login']) ? $_SESSION['failed_login'] + 1 : 1; // To many tries, maybe they need a reminder if ($_SESSION['failed_login'] >= $modSettings['failed_login_threshold']) { redirectexit('action=reminder'); } else { log_error($txt['incorrect_password'] . ' - <span class="remove">' . $user_settings['member_name'] . '</span>', 'user'); // Wrong password, lets enable plain text responses in case form hashing is causing problems $context['disable_login_hashing'] = true; $context['login_errors'] = array($txt['incorrect_password']); unset($user_settings); return; } } } else { // validateLoginPassword will hash this like the form normally would and check its valid $sha_passwd = $_POST['passwrd']; $valid_password = validateLoginPassword($sha_passwd, $user_settings['passwd'], $user_settings['member_name']); } // Bad password! Thought you could fool the database?! if ($valid_password === false) { // Let's be cautious, no hacking please. thanx. validatePasswordFlood($user_settings['id_member'], $user_settings['passwd_flood']); // Maybe we were too hasty... let's try some other authentication methods. $other_passwords = $this->_other_passwords($user_settings); // Whichever encryption it was using, let's make it use ElkArte's now ;). if (in_array($user_settings['passwd'], $other_passwords)) { $user_settings['passwd'] = validateLoginPassword($sha_passwd, '', '', true); $user_settings['password_salt'] = substr(md5(mt_rand()), 0, 4); // Update the password hash and set up the salt. updateMemberData($user_settings['id_member'], array('passwd' => $user_settings['passwd'], 'password_salt' => $user_settings['password_salt'], 'passwd_flood' => '')); } else { // They've messed up again - keep a count to see if they need a hand. $_SESSION['failed_login'] = isset($_SESSION['failed_login']) ? $_SESSION['failed_login'] + 1 : 1; // Hmm... don't remember it, do you? Here, try the password reminder ;). if ($_SESSION['failed_login'] >= $modSettings['failed_login_threshold']) { redirectexit('action=reminder'); } else { // Log an error so we know that it didn't go well in the error log. log_error($txt['incorrect_password'] . ' - <span class="remove">' . $user_settings['member_name'] . '</span>', 'user'); $context['login_errors'] = array($txt['incorrect_password']); return; } } } elseif (!empty($user_settings['passwd_flood'])) { // Let's be sure they weren't a little hacker. validatePasswordFlood($user_settings['id_member'], $user_settings['passwd_flood'], true); // If we got here then we can reset the flood counter. updateMemberData($user_settings['id_member'], array('passwd_flood' => '')); } // Correct password, but they've got no salt; fix it! if ($user_settings['password_salt'] == '') { $user_settings['password_salt'] = substr(md5(mt_rand()), 0, 4); updateMemberData($user_settings['id_member'], array('password_salt' => $user_settings['password_salt'])); } // Check their activation status. if (!checkActivation()) { return; } doLogin(); }
return substr($method, 0, 2) !== '__' && strpos($method, 'insert_') !== 0 && strpos($method, 'table_') !== 0; }); foreach ($tables as $table_method) { $table_name = substr($table_method, 6); // Copied from DbTable class // Strip out the table name, we might not need it in some cases $real_prefix = preg_match('~^("?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix; // With or without the database name, the fullname looks like this. $full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name); $result = $install_instance->{$table_method}(); if ($result === false) { $incontext['failures'][$table_method] = $db->last_error(); } } foreach ($inserts as $insert_method) { $table_name = substr($insert_method, 6); if (in_array($table_name, $exists)) { $db_wrapper->countMode(); $incontext['sql_results']['insert_dups'] += $install_instance->{$insert_method}(); $db_wrapper->countMode(false); continue; } $result = $install_instance->{$insert_method}(); } // Add the admin user account require_once SOURCEDIR . '/Subs.php'; require_once SUBSDIR . '/Auth.subs.php'; require_once SUBSDIR . '/Util.class.php'; $request = $db->insert('', $db_prefix . 'members', array('member_name' => 'string-25', 'real_name' => 'string-25', 'passwd' => 'string', 'email_address' => 'string', 'id_group' => 'int', 'posts' => 'int', 'date_registered' => 'int', 'hide_email' => 'int', 'password_salt' => 'string', 'lngfile' => 'string', 'personal_text' => 'string', 'avatar' => 'string', 'member_ip' => 'string', 'member_ip2' => 'string', 'buddy_list' => 'string', 'pm_ignore_list' => 'string', 'message_labels' => 'string', 'website_title' => 'string', 'website_url' => 'string', 'location' => 'string', 'signature' => 'string', 'usertitle' => 'string', 'secret_question' => 'string', 'additional_groups' => 'string', 'ignore_boards' => 'string', 'openid_uri' => 'string'), array('admin', 'admin', validateLoginPassword($password = '******', '', 'admin', true), 'admin@localhost', 1, 0, time(), 0, substr(md5(mt_rand()), 0, 4), '', '', '', '127.0.0.1', '127.0.0.1', '', '', '', '', '', '', '', '', '', '', '', ''), array('id_member')); // Add some stats $db->insert('ignore', '{db_prefix}log_activity', array('date' => 'date', 'topics' => 'int', 'posts' => 'int', 'registers' => 'int'), array(strftime('%Y-%m-%d', time()), 1, 1, !empty($incontext['member_id']) ? 1 : 0), array('date'));
/** * Verify the answer to the secret question. * Accessed with sa=secret2 */ public function action_secret2() { global $txt, $context; checkSession(); validateToken('remind-sai'); // Hacker? How did you get this far without an email or username? if (empty($_REQUEST['uid'])) { fatal_lang_error('username_no_exist', false); } loadLanguage('Login'); // Get the information from the database. require_once SUBSDIR . '/Members.subs.php'; $member = getBasicMemberData((int) $_REQUEST['uid'], array('authentication' => true)); if (empty($member)) { fatal_lang_error('username_no_exist', false); } // Check if the secret answer is correct. if ($member['secret_question'] == '' || $member['secret_answer'] == '' || md5($_POST['secret_answer']) !== $member['secret_answer']) { log_error(sprintf($txt['reminder_error'], $member['member_name']), 'user'); fatal_lang_error('incorrect_answer', false); } // If it's OpenID this is where the music ends. if (!empty($member['openid_uri'])) { $context['sub_template'] = 'sent'; $context['description'] = sprintf($txt['reminder_openid_is'], $member['openid_uri']); return; } // You can't use a blank one! if (strlen(trim($_POST['passwrd1'])) === 0) { fatal_lang_error('no_password', false); } // They have to be the same too. if ($_POST['passwrd1'] != $_POST['passwrd2']) { fatal_lang_error('passwords_dont_match', false); } // Make sure they have a strong enough password. require_once SUBSDIR . '/Auth.subs.php'; $passwordError = validatePassword($_POST['passwrd1'], $member['member_name'], array($member['email_address'])); // Invalid? if ($passwordError != null) { fatal_lang_error('profile_error_password_' . $passwordError, false); } // Alright, so long as 'yer sure. require_once SUBSDIR . '/Auth.subs.php'; $sha_passwd = $_POST['passwrd1']; updateMemberData($member['id_member'], array('passwd' => validateLoginPassword($sha_passwd, '', $member['member_name'], true))); call_integration_hook('integrate_reset_pass', array($member['member_name'], $member['member_name'], $_POST['passwrd1'])); // Tell them it went fine. loadTemplate('Login'); loadJavascriptFile('sha256.js', array('defer' => true)); $context += array('page_title' => $txt['reminder_password_set'], 'sub_template' => 'login', 'default_username' => $member['member_name'], 'default_password' => $_POST['passwrd1'], 'never_expire' => false, 'description' => $txt['reminder_password_set']); createToken('login'); }
/** * Allow the change or view of profiles. * Loads the profile menu. * * @see Action_Controller::action_index() */ public function action_index() { global $txt, $scripturl, $user_info, $context, $user_profile, $cur_profile; global $modSettings, $memberContext, $profile_vars, $post_errors, $user_settings; // Don't reload this as we may have processed error strings. if (empty($post_errors)) { loadLanguage('Profile+Drafts'); } loadTemplate('Profile'); require_once SUBSDIR . '/Menu.subs.php'; require_once SUBSDIR . '/Profile.subs.php'; $memID = currentMemberID(); $context['id_member'] = $memID; $cur_profile = $user_profile[$memID]; // Let's have some information about this member ready, too. loadMemberContext($memID); $context['member'] = $memberContext[$memID]; // Is this the profile of the user himself or herself? $context['user']['is_owner'] = $memID == $user_info['id']; /** * Define all the sections within the profile area! * We start by defining the permission required - then we take this and turn * it into the relevant context ;) * * Possible fields: * For Section: * - string $title: Section title. * - array $areas: Array of areas within this section. * * For Areas: * - string $label: Text string that will be used to show the area in the menu. * - string $file: Optional text string that may contain a file name that's needed for inclusion in order to display the area properly. * - string $custom_url: Optional href for area. * - string $function: Function to execute for this section. * - bool $enabled: Should area be shown? * - string $sc: Session check validation to do on save - note without this save will get unset - if set. * - bool $hidden: Does this not actually appear on the menu? * - bool $password: Whether to require the user's password in order to save the data in the area. * - array $subsections: Array of subsections, in order of appearance. * - array $permission: Array of permissions to determine who can access this area. Should contain arrays $own and $any. */ $profile_areas = array('info' => array('title' => $txt['profileInfo'], 'areas' => array('summary' => array('label' => $txt['summary'], 'file' => 'ProfileInfo.controller.php', 'controller' => 'ProfileInfo_Controller', 'function' => 'action_summary', 'token' => 'profile-aa%u', 'token_type' => 'get', 'permission' => array('own' => 'profile_view_own', 'any' => 'profile_view_any')), 'statistics' => array('label' => $txt['statPanel'], 'file' => 'ProfileInfo.controller.php', 'controller' => 'ProfileInfo_Controller', 'function' => 'action_statPanel', 'permission' => array('own' => 'profile_view_own', 'any' => 'profile_view_any')), 'showposts' => array('label' => $txt['showPosts'], 'file' => 'ProfileInfo.controller.php', 'controller' => 'ProfileInfo_Controller', 'function' => 'action_showPosts', 'subsections' => array('messages' => array($txt['showMessages'], array('profile_view_own', 'profile_view_any')), 'topics' => array($txt['showTopics'], array('profile_view_own', 'profile_view_any')), 'unwatchedtopics' => array($txt['showUnwatched'], array('profile_view_own', 'profile_view_any'), 'enabled' => $modSettings['enable_unwatch'] && $context['user']['is_owner']), 'attach' => array($txt['showAttachments'], array('profile_view_own', 'profile_view_any'))), 'permission' => array('own' => 'profile_view_own', 'any' => 'profile_view_any')), 'showdrafts' => array('label' => $txt['drafts_show'], 'file' => 'Draft.controller.php', 'controller' => 'Draft_Controller', 'function' => 'action_showProfileDrafts', 'enabled' => !empty($modSettings['drafts_enabled']) && $context['user']['is_owner'], 'permission' => array('own' => 'profile_view_own', 'any' => array())), 'showlikes' => array('label' => $txt['likes_show'], 'file' => 'Likes.controller.php', 'controller' => 'Likes_Controller', 'function' => 'action_showProfileLikes', 'enabled' => !empty($modSettings['likes_enabled']) && $context['user']['is_owner'], 'subsections' => array('given' => array($txt['likes_given'], array('profile_view_own')), 'received' => array($txt['likes_received'], array('profile_view_own'))), 'permission' => array('own' => 'profile_view_own', 'any' => array())), 'permissions' => array('label' => $txt['showPermissions'], 'file' => 'ProfileInfo.controller.php', 'controller' => 'ProfileInfo_Controller', 'function' => 'action_showPermissions', 'permission' => array('own' => 'manage_permissions', 'any' => 'manage_permissions')), 'history' => array('label' => $txt['history'], 'file' => 'ProfileHistory.controller.php', 'controller' => 'ProfileHistory_Controller', 'function' => 'action_index', 'subsections' => array('activity' => array($txt['trackActivity'], 'moderate_forum'), 'ip' => array($txt['trackIP'], 'moderate_forum'), 'edits' => array($txt['trackEdits'], 'moderate_forum'), 'logins' => array($txt['trackLogins'], array('profile_view_own', 'moderate_forum'))), 'permission' => array('own' => 'moderate_forum', 'any' => 'moderate_forum')), 'viewwarning' => array('label' => $txt['profile_view_warnings'], 'enabled' => in_array('w', $context['admin_features']) && !empty($modSettings['warning_enable']) && $cur_profile['warning'] && (!empty($modSettings['warning_show']) && ($context['user']['is_owner'] || $modSettings['warning_show'] == 2)), 'file' => 'ProfileInfo.controller.php', 'controller' => 'ProfileInfo_Controller', 'function' => 'action_viewWarning', 'permission' => array('own' => 'profile_view_own', 'any' => 'issue_warning')))), 'edit_profile' => array('title' => $txt['profileEdit'], 'areas' => array('account' => array('label' => $txt['account'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_account', 'enabled' => $context['user']['is_admin'] || $cur_profile['id_group'] != 1 && !in_array(1, explode(',', $cur_profile['additional_groups'])), 'sc' => 'post', 'token' => 'profile-ac%u', 'password' => true, 'permission' => array('own' => array('profile_identity_any', 'profile_identity_own', 'manage_membergroups'), 'any' => array('profile_identity_any', 'manage_membergroups'))), 'forumprofile' => array('label' => $txt['forumprofile'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_forumProfile', 'sc' => 'post', 'token' => 'profile-fp%u', 'permission' => array('own' => array('profile_extra_any', 'profile_extra_own', 'profile_title_own', 'profile_title_any'), 'any' => array('profile_extra_any', 'profile_title_any'))), 'theme' => array('label' => $txt['theme'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_themepick', 'sc' => 'post', 'token' => 'profile-th%u', 'permission' => array('own' => array('profile_extra_any', 'profile_extra_own'), 'any' => array('profile_extra_any'))), 'authentication' => array('label' => $txt['authentication'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_authentication', 'enabled' => !empty($modSettings['enableOpenID']) || !empty($cur_profile['openid_uri']), 'sc' => 'post', 'token' => 'profile-au%u', 'hidden' => empty($modSettings['enableOpenID']) && empty($cur_profile['openid_uri']), 'password' => true, 'permission' => array('own' => array('profile_identity_any', 'profile_identity_own'), 'any' => array('profile_identity_any'))), 'notification' => array('label' => $txt['notifications'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_notification', 'sc' => 'post', 'token' => 'profile-nt%u', 'permission' => array('own' => array('profile_extra_any', 'profile_extra_own'), 'any' => array('profile_extra_any'))), 'contactprefs' => array('label' => $txt['contactprefs'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_pmprefs', 'enabled' => allowedTo(array('profile_extra_own', 'profile_extra_any')), 'sc' => 'post', 'token' => 'profile-pm%u', 'permission' => array('own' => array('pm_read'), 'any' => array('profile_extra_any'))), 'ignoreboards' => array('label' => $txt['ignoreboards'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_ignoreboards', 'enabled' => !empty($modSettings['allow_ignore_boards']), 'sc' => 'post', 'token' => 'profile-ib%u', 'permission' => array('own' => array('profile_extra_any', 'profile_extra_own'), 'any' => array('profile_extra_any'))), 'lists' => array('label' => $txt['editBuddyIgnoreLists'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_editBuddyIgnoreLists', 'enabled' => !empty($modSettings['enable_buddylist']) && $context['user']['is_owner'], 'sc' => 'post', 'token' => 'profile-bl%u', 'subsections' => array('buddies' => array($txt['editBuddies']), 'ignore' => array($txt['editIgnoreList'])), 'permission' => array('own' => array('profile_extra_any', 'profile_extra_own'), 'any' => array())), 'groupmembership' => array('label' => $txt['groupmembership'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_groupMembership', 'enabled' => !empty($modSettings['show_group_membership']) && $context['user']['is_owner'], 'sc' => 'request', 'token' => 'profile-gm%u', 'token_type' => 'request', 'permission' => array('own' => array('profile_view_own'), 'any' => array('manage_membergroups'))))), 'profile_action' => array('title' => $txt['profileAction'], 'areas' => array('sendpm' => array('label' => $txt['profileSendIm'], 'custom_url' => $scripturl . '?action=pm;sa=send', 'permission' => array('own' => array(), 'any' => array('pm_send'))), 'issuewarning' => array('label' => $txt['profile_issue_warning'], 'enabled' => in_array('w', $context['admin_features']) && !empty($modSettings['warning_enable']) && (!$context['user']['is_owner'] || $context['user']['is_admin']), 'file' => 'ProfileAccount.controller.php', 'controller' => 'ProfileAccount_Controller', 'function' => 'action_issuewarning', 'token' => 'profile-iw%u', 'permission' => array('own' => array(), 'any' => array('issue_warning'))), 'banuser' => array('label' => $txt['profileBanUser'], 'custom_url' => $scripturl . '?action=admin;area=ban;sa=add', 'enabled' => $cur_profile['id_group'] != 1 && !in_array(1, explode(',', $cur_profile['additional_groups'])), 'permission' => array('own' => array(), 'any' => array('manage_bans'))), 'subscriptions' => array('label' => $txt['subscriptions'], 'file' => 'ProfileSubscriptions.controller.php', 'controller' => 'ProfileSubscriptions_Controller', 'function' => 'action_subscriptions', 'enabled' => !empty($modSettings['paid_enabled']), 'permission' => array('own' => array('profile_view_own'), 'any' => array('moderate_forum'))), 'deleteaccount' => array('label' => $txt['deleteAccount'], 'file' => 'ProfileAccount.controller.php', 'controller' => 'ProfileAccount_Controller', 'function' => 'action_deleteaccount', 'sc' => 'post', 'token' => 'profile-da%u', 'password' => true, 'permission' => array('own' => array('profile_remove_any', 'profile_remove_own'), 'any' => array('profile_remove_any'))), 'activateaccount' => array('file' => 'ProfileAccount.controller.php', 'controller' => 'ProfileAccount_Controller', 'function' => 'action_activateaccount', 'sc' => 'get', 'token' => 'profile-aa%u', 'permission' => array('own' => array(), 'any' => array('moderate_forum')))))); // Is there an updated message to show? if (isset($_GET['updated'])) { $context['profile_updated'] = $txt['profile_updated_own']; } // Set a few options for the menu. $menuOptions = array('disable_url_session_check' => true, 'hook' => 'profile', 'extra_url_parameters' => array('u' => $context['id_member']), 'default_include_dir' => CONTROLLERDIR); // Actually create the menu! $profile_include_data = createMenu($profile_areas, $menuOptions); unset($profile_areas); // If it said no permissions that meant it wasn't valid! if ($profile_include_data && empty($profile_include_data['permission'])) { $profile_include_data['enabled'] = false; } // No menu and guest? A warm welcome to register if (!$profile_include_data && $user_info['is_guest']) { is_not_guest(); } // No menu means no access. if (!$profile_include_data || isset($profile_include_data['enabled']) && $profile_include_data['enabled'] === false) { fatal_lang_error('no_access', false); } // Make a note of the Unique ID for this menu. $context['profile_menu_id'] = $context['max_menu_id']; $context['profile_menu_name'] = 'menu_data_' . $context['profile_menu_id']; // Set the selected item - now it's been validated. $current_area = $profile_include_data['current_area']; $context['menu_item_selected'] = $current_area; // Before we go any further, let's work on the area we've said is valid. // Note this is done here just in case we ever compromise the menu function in error! $this->_completed_save = false; $context['do_preview'] = isset($_REQUEST['preview_signature']); // Are we saving data in a valid area? if (isset($profile_include_data['sc']) && (isset($_REQUEST['save']) || $context['do_preview'])) { checkSession($profile_include_data['sc']); $this->_completed_save = true; } // Does this require session validating? if (!empty($area['validate']) || isset($_REQUEST['save']) && !$context['user']['is_owner']) { validateSession(); } // Do we need to perform a token check? if (!empty($profile_include_data['token'])) { if ($profile_include_data['token'] !== true) { $token_name = str_replace('%u', $context['id_member'], $profile_include_data['token']); } else { $token_name = 'profile-u' . $context['id_member']; } if (isset($profile_include_data['token_type']) && in_array($profile_include_data['token_type'], array('request', 'post', 'get'))) { $token_type = $profile_include_data['token_type']; } else { $token_type = 'post'; } if (isset($_REQUEST['save'])) { validateToken($token_name, $token_type); } } // Permissions for good measure. if (!empty($profile_include_data['permission'])) { isAllowedTo($profile_include_data['permission'][$context['user']['is_owner'] ? 'own' : 'any']); } // Create a token if needed. if (!empty($profile_include_data['token'])) { createToken($token_name, $token_type); $context['token_check'] = $token_name; } // Build the link tree. $context['linktree'][] = array('url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : ''), 'name' => sprintf($txt['profile_of_username'], $context['member']['name'])); if (!empty($profile_include_data['label'])) { $context['linktree'][] = array('url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : '') . ';area=' . $profile_include_data['current_area'], 'name' => $profile_include_data['label']); } if (!empty($profile_include_data['current_subsection']) && $profile_include_data['subsections'][$profile_include_data['current_subsection']][0] != $profile_include_data['label']) { $context['linktree'][] = array('url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : '') . ';area=' . $profile_include_data['current_area'] . ';sa=' . $profile_include_data['current_subsection'], 'name' => $profile_include_data['subsections'][$profile_include_data['current_subsection']][0]); } // Set the template for this area... if you still can :P // and add the profile layer. $context['sub_template'] = $profile_include_data['function']; Template_Layers::getInstance()->add('profile'); loadJavascriptFile('profile.js'); // All the subactions that require a user password in order to validate. $check_password = $context['user']['is_owner'] && !empty($profile_include_data['password']); $context['require_password'] = $check_password && empty($user_settings['openid_uri']); // These will get populated soon! $post_errors = array(); $profile_vars = array(); // Right - are we saving - if so let's save the old data first. if ($this->_completed_save) { // Clean up the POST variables. $_POST = htmltrim__recursive($_POST); $_POST = htmlspecialchars__recursive($_POST); if ($check_password) { // If we're using OpenID try to revalidate. if (!empty($user_settings['openid_uri'])) { require_once SUBSDIR . '/OpenID.subs.php'; $openID = new OpenID(); $openID->revalidate(); } else { // You didn't even enter a password! if (trim($_POST['oldpasswrd']) == '') { $post_errors[] = 'no_password'; } // Since the password got modified due to all the $_POST cleaning, lets undo it so we can get the correct password $_POST['oldpasswrd'] = un_htmlspecialchars($_POST['oldpasswrd']); // Does the integration want to check passwords? $good_password = in_array(true, call_integration_hook('integrate_verify_password', array($cur_profile['member_name'], $_POST['oldpasswrd'], false)), true); // Start up the password checker, we have work to do require_once SUBSDIR . '/Auth.subs.php'; // Bad password!!! if (!$good_password && !validateLoginPassword($_POST['oldpasswrd'], $user_info['passwd'], $user_profile[$memID]['member_name'])) { $post_errors[] = 'bad_password'; } // Warn other elements not to jump the gun and do custom changes! if (in_array('bad_password', $post_errors)) { $context['password_auth_failed'] = true; } } } // Change the IP address in the database. if ($context['user']['is_owner']) { $profile_vars['member_ip'] = $user_info['ip']; } // Now call the sub-action function... if ($current_area == 'activateaccount') { if (empty($post_errors)) { require_once CONTROLLERDIR . '/ProfileAccount.controller.php'; $controller = new ProfileAccount_Controller(); $controller->action_activateaccount(); } } elseif ($current_area == 'deleteaccount') { if (empty($post_errors)) { require_once CONTROLLERDIR . '/ProfileAccount.controller.php'; $controller = new ProfileAccount_Controller(); $controller->action_deleteaccount2(); redirectexit(); } } elseif ($current_area == 'groupmembership' && empty($post_errors)) { require_once CONTROLLERDIR . '/ProfileOptions.controller.php'; $controller = new Profileoptions_Controller(); $msg = $controller->action_groupMembership2(); // Whatever we've done, we have nothing else to do here... redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=groupmembership' . (!empty($msg) ? ';msg=' . $msg : '')); } elseif ($current_area == 'authentication') { require_once CONTROLLERDIR . '/ProfileOptions.controller.php'; $controller = new ProfileOptions_Controller(); $controller->action_authentication(true); } elseif (in_array($current_area, array('account', 'forumprofile', 'theme', 'contactprefs'))) { saveProfileFields(); } else { $force_redirect = true; saveProfileChanges($profile_vars, $memID); } call_integration_hook('integrate_profile_save', array(&$profile_vars, &$post_errors, $memID)); // There was a problem, let them try to re-enter. if (!empty($post_errors)) { // Load the language file so we can give a nice explanation of the errors. loadLanguage('Errors'); $context['post_errors'] = $post_errors; } elseif (!empty($profile_vars)) { // If we've changed the password, notify any integration that may be listening in. if (isset($profile_vars['passwd'])) { call_integration_hook('integrate_reset_pass', array($cur_profile['member_name'], $cur_profile['member_name'], $_POST['passwrd2'])); } updateMemberData($memID, $profile_vars); // What if this is the newest member? if ($modSettings['latestMember'] == $memID) { updateStats('member'); } elseif (isset($profile_vars['real_name'])) { updateSettings(array('memberlist_updated' => time())); } // If the member changed his/her birthdate, update calendar statistics. if (isset($profile_vars['birthdate']) || isset($profile_vars['real_name'])) { updateSettings(array('calendar_updated' => time())); } // Anything worth logging? if (!empty($context['log_changes']) && !empty($modSettings['modlog_enabled'])) { $log_changes = array(); foreach ($context['log_changes'] as $k => $v) { $log_changes[] = array('action' => $k, 'log_type' => 'user', 'extra' => array_merge($v, array('applicator' => $user_info['id'], 'member_affected' => $memID))); } logActions($log_changes); } // Have we got any post save functions to execute? if (!empty($context['profile_execute_on_save'])) { foreach ($context['profile_execute_on_save'] as $saveFunc) { $saveFunc(); } } // Let them know it worked! $context['profile_updated'] = $context['user']['is_owner'] ? $txt['profile_updated_own'] : sprintf($txt['profile_updated_else'], $cur_profile['member_name']); // Invalidate any cached data. cache_put_data('member_data-profile-' . $memID, null, 0); } } // Have some errors for some reason? if (!empty($post_errors)) { // Set all the errors so the template knows what went wrong. foreach ($post_errors as $error_type) { $context['modify_error'][$error_type] = true; } } elseif (!empty($profile_vars) && $context['user']['is_owner'] && !$context['do_preview']) { redirectexit('action=profile;area=' . $current_area . ';updated'); } elseif (!empty($force_redirect)) { redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=' . $current_area); } // Let go to the right place if (isset($profile_include_data['file'])) { require_once $profile_include_data['file']; } callMenu($profile_include_data); // Set the page title if it's not already set... if (!isset($context['page_title'])) { $context['page_title'] = $txt['profile'] . (isset($txt[$current_area]) ? ' - ' . $txt[$current_area] : ''); } }
/** * Registers a member to the forum. * * What it does: * - Allows two types of interface: 'guest' and 'admin'. The first * - includes hammering protection, the latter can perform the registration silently. * - The strings used in the options array are assumed to be escaped. * - Allows to perform several checks on the input, e.g. reserved names. * - The function will adjust member statistics. * - If an error is detected will fatal error on all errors unless return_errors is true. * * @package Members * @uses Auth.subs.php * @uses Mail.subs.php * @param mixed[] $regOptions * @param string $error_context * @return integer the ID of the newly created member */ function registerMember(&$regOptions, $error_context = 'register') { global $scripturl, $txt, $modSettings, $user_info; $db = database(); loadLanguage('Login'); // We'll need some external functions. require_once SUBSDIR . '/Auth.subs.php'; require_once SUBSDIR . '/Mail.subs.php'; // Put any errors in here. $reg_errors = Error_Context::context($error_context, 0); // Registration from the admin center, let them sweat a little more. if ($regOptions['interface'] == 'admin') { is_not_guest(); isAllowedTo('moderate_forum'); } elseif ($regOptions['interface'] == 'guest') { // You cannot register twice... if (empty($user_info['is_guest'])) { redirectexit(); } // Make sure they didn't just register with this session. if (!empty($_SESSION['just_registered']) && empty($modSettings['disableRegisterCheck'])) { fatal_lang_error('register_only_once', false); } } // What method of authorization are we going to use? if (empty($regOptions['auth_method']) || !in_array($regOptions['auth_method'], array('password', 'openid'))) { if (!empty($regOptions['openid'])) { $regOptions['auth_method'] = 'openid'; } else { $regOptions['auth_method'] = 'password'; } } // Spaces and other odd characters are evil... $regOptions['username'] = trim(preg_replace('~[\\t\\n\\r \\x0B\\0\\x{A0}\\x{AD}\\x{2000}-\\x{200F}\\x{201F}\\x{202F}\\x{3000}\\x{FEFF}]+~u', ' ', $regOptions['username'])); // Valid emails only require_once SUBSDIR . '/DataValidator.class.php'; if (!Data_Validator::is_valid($regOptions, array('email' => 'valid_email|required|max_length[255]'), array('email' => 'trim'))) { $reg_errors->addError('bad_email'); } validateUsername(0, $regOptions['username'], $error_context, !empty($regOptions['check_reserved_name'])); // Generate a validation code if it's supposed to be emailed. $validation_code = ''; if ($regOptions['require'] == 'activation') { $validation_code = generateValidationCode(); } // If you haven't put in a password generate one. if ($regOptions['interface'] == 'admin' && $regOptions['password'] == '' && $regOptions['auth_method'] == 'password') { mt_srand(time() + 1277); $regOptions['password'] = generateValidationCode(); $regOptions['password_check'] = $regOptions['password']; } elseif ($regOptions['password'] != $regOptions['password_check'] && $regOptions['auth_method'] == 'password') { $reg_errors->addError('passwords_dont_match'); } // That's kind of easy to guess... if ($regOptions['password'] == '') { if ($regOptions['auth_method'] == 'password') { $reg_errors->addError('no_password'); } else { $regOptions['password'] = sha1(mt_rand()); } } // Now perform hard password validation as required. if (!empty($regOptions['check_password_strength']) && $regOptions['password'] != '') { $passwordError = validatePassword($regOptions['password'], $regOptions['username'], array($regOptions['email'])); // Password isn't legal? if ($passwordError != null) { $reg_errors->addError('profile_error_password_' . $passwordError); } } // You may not be allowed to register this email. if (!empty($regOptions['check_email_ban'])) { isBannedEmail($regOptions['email'], 'cannot_register', $txt['ban_register_prohibited']); } // Check if the email address is in use. $request = $db->query('', ' SELECT id_member FROM {db_prefix}members WHERE email_address = {string:email_address} OR email_address = {string:username} LIMIT 1', array('email_address' => $regOptions['email'], 'username' => $regOptions['username'])); if ($db->num_rows($request) != 0) { $reg_errors->addError(array('email_in_use', array(htmlspecialchars($regOptions['email'], ENT_COMPAT, 'UTF-8')))); } $db->free_result($request); // Perhaps someone else wants to check this user call_integration_hook('integrate_register_check', array(&$regOptions, &$reg_errors)); // If there's any errors left return them at once! if ($reg_errors->hasErrors()) { return false; } $reservedVars = array('actual_theme_url', 'actual_images_url', 'base_theme_dir', 'base_theme_url', 'default_images_url', 'default_theme_dir', 'default_theme_url', 'default_template', 'images_url', 'number_recent_posts', 'smiley_sets_default', 'theme_dir', 'theme_id', 'theme_layers', 'theme_templates', 'theme_url'); // Can't change reserved vars. if (isset($regOptions['theme_vars']) && count(array_intersect(array_keys($regOptions['theme_vars']), $reservedVars)) != 0) { fatal_lang_error('no_theme'); } // New password hash require_once SUBSDIR . '/Auth.subs.php'; // Some of these might be overwritten. (the lower ones that are in the arrays below.) $regOptions['register_vars'] = array('member_name' => $regOptions['username'], 'email_address' => $regOptions['email'], 'passwd' => validateLoginPassword($regOptions['password'], '', $regOptions['username'], true), 'password_salt' => substr(md5(mt_rand()), 0, 4), 'posts' => 0, 'date_registered' => !empty($regOptions['time']) ? $regOptions['time'] : time(), 'member_ip' => $regOptions['interface'] == 'admin' ? '127.0.0.1' : $regOptions['ip'], 'member_ip2' => $regOptions['interface'] == 'admin' ? '127.0.0.1' : $regOptions['ip2'], 'validation_code' => $validation_code, 'real_name' => $regOptions['username'], 'personal_text' => $modSettings['default_personal_text'], 'pm_email_notify' => 1, 'id_theme' => 0, 'id_post_group' => 4, 'lngfile' => '', 'buddy_list' => '', 'pm_ignore_list' => '', 'message_labels' => '', 'website_title' => '', 'website_url' => '', 'location' => '', 'time_format' => '', 'signature' => '', 'avatar' => '', 'usertitle' => '', 'secret_question' => '', 'secret_answer' => '', 'additional_groups' => '', 'ignore_boards' => '', 'smiley_set' => '', 'openid_uri' => !empty($regOptions['openid']) ? $regOptions['openid'] : ''); // Setup the activation status on this new account so it is correct - firstly is it an under age account? if ($regOptions['require'] == 'coppa') { $regOptions['register_vars']['is_activated'] = 5; // @todo This should be changed. To what should be it be changed?? $regOptions['register_vars']['validation_code'] = ''; } elseif ($regOptions['require'] == 'nothing') { $regOptions['register_vars']['is_activated'] = 1; } elseif ($regOptions['require'] == 'activation') { $regOptions['register_vars']['is_activated'] = 0; } else { $regOptions['register_vars']['is_activated'] = 3; } if (isset($regOptions['memberGroup'])) { // Make sure the id_group will be valid, if this is an administator. $regOptions['register_vars']['id_group'] = $regOptions['memberGroup'] == 1 && !allowedTo('admin_forum') ? 0 : $regOptions['memberGroup']; // Check if this group is assignable. $unassignableGroups = array(-1, 3); $request = $db->query('', ' SELECT id_group FROM {db_prefix}membergroups WHERE min_posts != {int:min_posts}' . (allowedTo('admin_forum') ? '' : ' OR group_type = {int:is_protected}'), array('min_posts' => -1, 'is_protected' => 1)); while ($row = $db->fetch_assoc($request)) { $unassignableGroups[] = $row['id_group']; } $db->free_result($request); if (in_array($regOptions['register_vars']['id_group'], $unassignableGroups)) { $regOptions['register_vars']['id_group'] = 0; } } // Integrate optional member settings to be set. if (!empty($regOptions['extra_register_vars'])) { foreach ($regOptions['extra_register_vars'] as $var => $value) { $regOptions['register_vars'][$var] = $value; } } // Integrate optional user theme options to be set. $theme_vars = array(); if (!empty($regOptions['theme_vars'])) { foreach ($regOptions['theme_vars'] as $var => $value) { $theme_vars[$var] = $value; } } // Right, now let's prepare for insertion. $knownInts = array('date_registered', 'posts', 'id_group', 'last_login', 'personal_messages', 'unread_messages', 'notifications', 'new_pm', 'pm_prefs', 'gender', 'hide_email', 'show_online', 'pm_email_notify', 'karma_good', 'karma_bad', 'notify_announcements', 'notify_send_body', 'notify_regularity', 'notify_types', 'id_theme', 'is_activated', 'id_msg_last_visit', 'id_post_group', 'total_time_logged_in', 'warning'); $knownFloats = array('time_offset'); // Call an optional function to validate the users' input. call_integration_hook('integrate_register', array(&$regOptions, &$theme_vars, &$knownInts, &$knownFloats)); $column_names = array(); $values = array(); foreach ($regOptions['register_vars'] as $var => $val) { $type = 'string'; if (in_array($var, $knownInts)) { $type = 'int'; } elseif (in_array($var, $knownFloats)) { $type = 'float'; } elseif ($var == 'birthdate') { $type = 'date'; } $column_names[$var] = $type; $values[$var] = $val; } // Register them into the database. $db->insert('', '{db_prefix}members', $column_names, $values, array('id_member')); $memberID = $db->insert_id('{db_prefix}members', 'id_member'); // Update the number of members and latest member's info - and pass the name, but remove the 's. if ($regOptions['register_vars']['is_activated'] == 1) { updateMemberStats($memberID, $regOptions['register_vars']['real_name']); } else { updateMemberStats(); } // Theme variables too? if (!empty($theme_vars)) { $inserts = array(); foreach ($theme_vars as $var => $val) { $inserts[] = array($memberID, $var, $val); } $db->insert('insert', '{db_prefix}themes', array('id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), $inserts, array('id_member', 'variable')); } // If it's enabled, increase the registrations for today. trackStats(array('registers' => '+')); // Administrative registrations are a bit different... if ($regOptions['interface'] == 'admin') { if ($regOptions['require'] == 'activation') { $email_message = 'admin_register_activate'; } elseif (!empty($regOptions['send_welcome_email'])) { $email_message = 'admin_register_immediate'; } if (isset($email_message)) { $replacements = array('REALNAME' => $regOptions['register_vars']['real_name'], 'USERNAME' => $regOptions['username'], 'PASSWORD' => $regOptions['password'], 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $memberID . ';code=' . $validation_code, 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $memberID, 'ACTIVATIONCODE' => $validation_code); $emaildata = loadEmailTemplate($email_message, $replacements); sendmail($regOptions['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); } } else { // Can post straight away - welcome them to your fantastic community... if ($regOptions['require'] == 'nothing') { if (!empty($regOptions['send_welcome_email'])) { $replacements = array('REALNAME' => $regOptions['register_vars']['real_name'], 'USERNAME' => $regOptions['username'], 'PASSWORD' => $regOptions['password'], 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', 'OPENID' => !empty($regOptions['openid']) ? $regOptions['openid'] : ''); $emaildata = loadEmailTemplate('register_' . ($regOptions['auth_method'] == 'openid' ? 'openid_' : '') . 'immediate', $replacements); sendmail($regOptions['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); } // Send admin their notification. require_once SUBSDIR . '/Notification.subs.php'; sendAdminNotifications('standard', $memberID, $regOptions['username']); } elseif ($regOptions['require'] == 'activation' || $regOptions['require'] == 'coppa') { $replacements = array('REALNAME' => $regOptions['register_vars']['real_name'], 'USERNAME' => $regOptions['username'], 'PASSWORD' => $regOptions['password'], 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', 'OPENID' => !empty($regOptions['openid']) ? $regOptions['openid'] : ''); if ($regOptions['require'] == 'activation') { $replacements += array('ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $memberID . ';code=' . $validation_code, 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $memberID, 'ACTIVATIONCODE' => $validation_code); } else { $replacements += array('COPPALINK' => $scripturl . '?action=coppa;u=' . $memberID); } $emaildata = loadEmailTemplate('register_' . ($regOptions['auth_method'] == 'openid' ? 'openid_' : '') . ($regOptions['require'] == 'activation' ? 'activate' : 'coppa'), $replacements); sendmail($regOptions['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); } else { $replacements = array('REALNAME' => $regOptions['register_vars']['real_name'], 'USERNAME' => $regOptions['username'], 'PASSWORD' => $regOptions['password'], 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder', 'OPENID' => !empty($regOptions['openid']) ? $regOptions['openid'] : ''); $emaildata = loadEmailTemplate('register_' . ($regOptions['auth_method'] == 'openid' ? 'openid_' : '') . 'pending', $replacements); sendmail($regOptions['email'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); // Admin gets informed here... require_once SUBSDIR . '/Notification.subs.php'; sendAdminNotifications('approval', $memberID, $regOptions['username']); } // Okay, they're for sure registered... make sure the session is aware of this for security. (Just married :P!) $_SESSION['just_registered'] = 1; } // If they are for sure registered, let other people to know about it call_integration_hook('integrate_register_after', array($regOptions, $memberID)); return $memberID; }
/** * Check if the user is who he/she says he is. * * What it does: * - This function makes sure the user is who they claim to be by requiring a * password to be typed in every hour. * - This check can be turned on and off by the securityDisable setting. * - Uses the adminLogin() function of subs/Auth.subs.php if they need to login, * which saves all request (POST and GET) data. * * @param string $type = admin */ function validateSession($type = 'admin') { global $modSettings, $user_info, $user_settings; // Guests are not welcome here. is_not_guest(); // Validate what type of session check this is. $types = array(); call_integration_hook('integrate_validateSession', array(&$types)); $type = in_array($type, $types) || $type == 'moderate' ? $type : 'admin'; // Set the lifetime for our admin session. Default is ten minutes. $refreshTime = 600; if (isset($modSettings['admin_session_lifetime'])) { // Maybe someone is paranoid or mistakenly misconfigured the param? Give them at least 5 minutes. if ($modSettings['admin_session_lifetime'] < 5) { $refreshTime = 300; } elseif ($modSettings['admin_session_lifetime'] > 14400) { $refreshTime = 86400; } else { $refreshTime = $modSettings['admin_session_lifetime'] * 60; } } // If we're using XML give an additional ten minutes grace as an admin can't log on in XML mode. if (isset($_GET['xml'])) { $refreshTime += 600; } // Is the security option off? if (!empty($modSettings['securityDisable' . ($type != 'admin' ? '_' . $type : '')])) { return; } // If their admin or moderator session hasn't expired yet, let it pass, let the admin session trump a moderation one as well if (!empty($_SESSION[$type . '_time']) && $_SESSION[$type . '_time'] + $refreshTime >= time() || !empty($_SESSION['admin_time']) && $_SESSION['admin_time'] + $refreshTime >= time()) { return; } require_once SUBSDIR . '/Auth.subs.php'; // Comming from the login screen if (isset($_POST[$type . '_pass']) || isset($_POST[$type . '_hash_pass'])) { checkSession(); validateToken('admin-login'); // Hashed password, ahoy! if (isset($_POST[$type . '_hash_pass']) && strlen($_POST[$type . '_hash_pass']) === 64) { // Allow integration to verify the password $good_password = in_array(true, call_integration_hook('integrate_verify_password', array($user_info['username'], $_POST[$type . '_hash_pass'], true)), true); $password = $_POST[$type . '_hash_pass']; if ($good_password || validateLoginPassword($password, $user_info['passwd'])) { $_SESSION[$type . '_time'] = time(); unset($_SESSION['request_referer']); return; } } // Posting the password... check it. if (isset($_POST[$type . '_pass']) && str_replace('*', '', $_POST[$type . '_pass']) !== '') { // Give integrated systems a chance to verify this password $good_password = in_array(true, call_integration_hook('integrate_verify_password', array($user_info['username'], $_POST[$type . '_pass'], false)), true); // Password correct? $password = $_POST[$type . '_pass']; if ($good_password || validateLoginPassword($password, $user_info['passwd'], $user_info['username'])) { $_SESSION[$type . '_time'] = time(); unset($_SESSION['request_referer']); return; } } } // OpenID? if (!empty($user_settings['openid_uri'])) { require_once SUBSDIR . '/OpenID.subs.php'; $openID = new OpenID(); $openID->revalidate(); $_SESSION[$type . '_time'] = time(); unset($_SESSION['request_referer']); return; } // Better be sure to remember the real referer if (empty($_SESSION['request_referer'])) { $_SESSION['request_referer'] = isset($_SERVER['HTTP_REFERER']) ? @parse_url($_SERVER['HTTP_REFERER']) : array(); } elseif (empty($_POST)) { unset($_SESSION['request_referer']); } // Need to type in a password for that, man. if (!isset($_GET['xml'])) { adminLogin($type); } else { return 'session_verify_fail'; } }
/** * Changing authentication method? * Only appropriate for people using OpenID. * * @param bool $saving = false */ public function action_authentication($saving = false) { global $context, $cur_profile, $post_errors, $modSettings; $memID = currentMemberID(); loadLanguage('Login'); loadTemplate('ProfileOptions'); // We are saving? if ($saving) { // Moving to password passed authentication? if ($_POST['authenticate'] == 'passwd') { // Didn't enter anything? if ($_POST['passwrd1'] == '') { $post_errors[] = 'no_password'; } elseif (!isset($_POST['passwrd2']) || $_POST['passwrd1'] != $_POST['passwrd2']) { $post_errors[] = 'bad_new_password'; } else { require_once SUBSDIR . '/Auth.subs.php'; $passwordErrors = validatePassword($_POST['passwrd1'], $cur_profile['member_name'], array($cur_profile['real_name'], $cur_profile['email_address'])); // Were there errors? if ($passwordErrors != null) { $post_errors[] = 'password_' . $passwordErrors; } } if (empty($post_errors)) { // Integration? call_integration_hook('integrate_reset_pass', array($cur_profile['member_name'], $cur_profile['member_name'], $_POST['passwrd1'])); // Go then. require_once SUBSDIR . '/Auth.subs.php'; $new_pass = $_POST['passwrd1']; $passwd = validateLoginPassword($new_pass, '', $cur_profile['member_name'], true); // Do the important bits. updateMemberData($memID, array('openid_uri' => '', 'passwd' => $passwd)); if ($context['user']['is_owner']) { setLoginCookie(60 * $modSettings['cookieTime'], $memID, hash('sha256', $new_pass . $cur_profile['password_salt'])); redirectexit('action=profile;area=authentication;updated'); } else { redirectexit('action=profile;u=' . $memID); } } return true; } elseif ($_POST['authenticate'] == 'openid' && !empty($_POST['openid_identifier'])) { require_once SUBSDIR . '/OpenID.subs.php'; require_once SUBSDIR . '/Members.subs.php'; $openID = new OpenID(); $_POST['openid_identifier'] = $openID->canonize($_POST['openid_identifier']); if (memberExists($_POST['openid_identifier'])) { $post_errors[] = 'openid_in_use'; } elseif (empty($post_errors)) { // Authenticate using the new OpenID URI first to make sure they didn't make a mistake. if ($context['user']['is_owner']) { $_SESSION['new_openid_uri'] = $_POST['openid_identifier']; $openID->validate($_POST['openid_identifier'], false, null, 'change_uri'); } else { updateMemberData($memID, array('openid_uri' => $_POST['openid_identifier'])); } } } } // Some stuff. $context['member']['openid_uri'] = $cur_profile['openid_uri']; $context['auth_method'] = empty($cur_profile['openid_uri']) ? 'password' : 'openid'; $context['sub_template'] = 'authentication_method'; loadJavascriptFile('register.js'); }