/** * Update some basic statistics. * * 'member' statistic updates the latest member, the total member * count, and the number of unapproved members. * 'member' also only counts approved members when approval is on, but * is much more efficient with it off. * * 'message' changes the total number of messages, and the * highest message id by id_msg - which can be parameters 1 and 2, * respectively. * * 'topic' updates the total number of topics, or if parameter1 is true * simply increments them. * * 'subject' updateds the log_search_subjects in the event of a topic being * moved, removed or split. parameter1 is the topicid, parameter2 is the new subject * * 'postgroups' case updates those members who match condition's * post-based membergroups in the database (restricted by parameter1). * * @param string $type Stat type - can be 'member', 'message', 'topic', 'subject' or 'postgroups' * @param int|string|boolean|mixed[]|null $parameter1 pass through value * @param int|string|boolean|mixed[]|null $parameter2 pass through value */ function updateStats($type, $parameter1 = null, $parameter2 = null) { switch ($type) { case 'member': require_once SUBSDIR . '/Members.subs.php'; updateMemberStats($parameter1, $parameter2); break; case 'message': require_once SUBSDIR . '/Messages.subs.php'; updateMessageStats($parameter1, $parameter2); break; case 'subject': require_once SUBSDIR . '/Messages.subs.php'; updateSubjectStats($parameter1, $parameter2); break; case 'topic': require_once SUBSDIR . '/Topic.subs.php'; updateTopicStats($parameter1); break; case 'postgroups': require_once SUBSDIR . '/Membergroups.subs.php'; updatePostGroupStats($parameter1, $parameter2); break; default: trigger_error('updateStats(): Invalid statistic type \'' . $type . '\'', E_USER_NOTICE); } }
/** * 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; }