PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/sources/controllers/Register.controller.php

https://github.com/Arantor/Elkarte
PHP | 953 lines | 649 code | 143 blank | 161 comment | 194 complexity | 848759a9bb5d18e3a420666d3bb85958 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0
  1. <?php
  2. /**
  3. * @name ElkArte Forum
  4. * @copyright ElkArte Forum contributors
  5. * @license BSD http://opensource.org/licenses/BSD-3-Clause
  6. *
  7. * This software is a derived product, based on:
  8. *
  9. * Simple Machines Forum (SMF)
  10. * copyright: 2011 Simple Machines (http://www.simplemachines.org)
  11. * license: BSD, See included LICENSE.TXT for terms and conditions.
  12. *
  13. * @version 1.0 Alpha
  14. *
  15. * This file has two main jobs, but they really are one. It registers new
  16. * members, and it helps the administrator moderate member registrations.
  17. * Similarly, it handles account activation as well.
  18. *
  19. */
  20. if (!defined('ELKARTE'))
  21. die('No access...');
  22. /**
  23. * Begin the registration process.
  24. * Accessed by ?action=register
  25. *
  26. * @param array $reg_errors = array()
  27. */
  28. function action_register($reg_errors = array())
  29. {
  30. global $txt, $context, $settings, $modSettings, $user_info;
  31. global $language, $scripturl, $smcFunc, $smcFunc, $cur_profile;
  32. // Is this an incoming AJAX check?
  33. if (isset($_GET['sa']) && $_GET['sa'] == 'usernamecheck')
  34. return RegisterCheckUsername();
  35. // Check if the administrator has it disabled.
  36. if (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == '3')
  37. fatal_lang_error('registration_disabled', false);
  38. // If this user is an admin - redirect them to the admin registration page.
  39. if (allowedTo('moderate_forum') && !$user_info['is_guest'])
  40. redirectexit('action=admin;area=regcenter;sa=register');
  41. // You are not a guest, so you are a member - and members don't get to register twice!
  42. elseif (empty($user_info['is_guest']))
  43. redirectexit();
  44. if (isset($_POST['show_contact']))
  45. redirectexit('action=contact');
  46. loadLanguage('Login');
  47. loadTemplate('Register');
  48. // Do we need them to agree to the registration agreement, first?
  49. $context['require_agreement'] = !empty($modSettings['requireAgreement']);
  50. $context['registration_passed_agreement'] = !empty($_SESSION['registration_agreed']);
  51. $context['show_coppa'] = !empty($modSettings['coppaAge']);
  52. $context['show_contact_button'] = !empty($modSettings['enable_contactform']) && $modSettings['enable_contactform'] == 'registration';
  53. // Under age restrictions?
  54. if ($context['show_coppa'])
  55. {
  56. $context['skip_coppa'] = false;
  57. $context['coppa_agree_above'] = sprintf($txt[($context['require_agreement'] ? 'agreement_' : '') . 'agree_coppa_above'], $modSettings['coppaAge']);
  58. $context['coppa_agree_below'] = sprintf($txt[($context['require_agreement'] ? 'agreement_' : '') . 'agree_coppa_below'], $modSettings['coppaAge']);
  59. }
  60. // What step are we at?
  61. $current_step = isset($_REQUEST['step']) ? (int) $_REQUEST['step'] : ($context['require_agreement'] ? 1 : 2);
  62. // Does this user agree to the registation agreement?
  63. if ($current_step == 1 && (isset($_POST['accept_agreement']) || isset($_POST['accept_agreement_coppa'])))
  64. {
  65. $context['registration_passed_agreement'] = $_SESSION['registration_agreed'] = true;
  66. $current_step = 2;
  67. // Skip the coppa procedure if the user says he's old enough.
  68. if ($context['show_coppa'])
  69. {
  70. $_SESSION['skip_coppa'] = !empty($_POST['accept_agreement']);
  71. // Are they saying they're under age, while under age registration is disabled?
  72. if (empty($modSettings['coppaType']) && empty($_SESSION['skip_coppa']))
  73. {
  74. loadLanguage('Login');
  75. fatal_lang_error('under_age_registration_prohibited', false, array($modSettings['coppaAge']));
  76. }
  77. }
  78. }
  79. // Make sure they don't squeeze through without agreeing.
  80. elseif ($current_step > 1 && $context['require_agreement'] && !$context['registration_passed_agreement'])
  81. $current_step = 1;
  82. // Show the user the right form.
  83. $context['sub_template'] = $current_step == 1 ? 'registration_agreement' : 'registration_form';
  84. $context['page_title'] = $current_step == 1 ? $txt['registration_agreement'] : $txt['registration_form'];
  85. // Add the register chain to the link tree.
  86. $context['linktree'][] = array(
  87. 'url' => $scripturl . '?action=register',
  88. 'name' => $txt['register'],
  89. );
  90. // If you have to agree to the agreement, it needs to be fetched from the file.
  91. if ($context['require_agreement'])
  92. {
  93. // Have we got a localized one?
  94. if (file_exists(BOARDDIR . '/agreement.' . $user_info['language'] . '.txt'))
  95. $context['agreement'] = parse_bbc(file_get_contents(BOARDDIR . '/agreement.' . $user_info['language'] . '.txt'), true, 'agreement_' . $user_info['language']);
  96. elseif (file_exists(BOARDDIR . '/agreement.txt'))
  97. $context['agreement'] = parse_bbc(file_get_contents(BOARDDIR . '/agreement.txt'), true, 'agreement');
  98. else
  99. $context['agreement'] = '';
  100. // Nothing to show, lets disable registration and inform the admin of this error
  101. if (empty($context['agreement']))
  102. {
  103. // No file found or a blank file, log the error so the admin knows there is a problem!
  104. log_error($txt['registration_agreement_missing'], 'critical');
  105. fatal_lang_error('registration_disabled', false);
  106. }
  107. }
  108. if (!empty($modSettings['userLanguage']))
  109. {
  110. $selectedLanguage = empty($_SESSION['language']) ? $language : $_SESSION['language'];
  111. // Do we have any languages?
  112. if (empty($context['languages']))
  113. getLanguages();
  114. // Try to find our selected language.
  115. foreach ($context['languages'] as $key => $lang)
  116. {
  117. $context['languages'][$key]['name'] = $lang['name'];
  118. // Found it!
  119. if ($selectedLanguage == $lang['filename'])
  120. $context['languages'][$key]['selected'] = true;
  121. }
  122. }
  123. // Any custom fields we want filled in?
  124. require_once(SUBSDIR . '/Profile.subs.php');
  125. loadCustomFields(0, 'register');
  126. // Or any standard ones?
  127. if (!empty($modSettings['registration_fields']))
  128. {
  129. // Setup some important context.
  130. loadLanguage('Profile');
  131. loadTemplate('Profile');
  132. $context['user']['is_owner'] = true;
  133. // Here, and here only, emulate the permissions the user would have to do this.
  134. $user_info['permissions'] = array_merge($user_info['permissions'], array('profile_account_own', 'profile_extra_own'));
  135. $reg_fields = explode(',', $modSettings['registration_fields']);
  136. // We might have had some submissions on this front - go check.
  137. foreach ($reg_fields as $field)
  138. if (isset($_POST[$field]))
  139. $cur_profile[$field] = $smcFunc['htmlspecialchars']($_POST[$field]);
  140. // Load all the fields in question.
  141. setupProfileContext($reg_fields);
  142. }
  143. // Generate a visual verification code to make sure the user is no bot.
  144. if (!empty($modSettings['reg_verification']))
  145. {
  146. require_once(SUBSDIR . '/Editor.subs.php');
  147. $verificationOptions = array(
  148. 'id' => 'register',
  149. );
  150. $context['visual_verification'] = create_control_verification($verificationOptions);
  151. $context['visual_verification_id'] = $verificationOptions['id'];
  152. }
  153. // Otherwise we have nothing to show.
  154. else
  155. $context['visual_verification'] = false;
  156. // Are they coming from an OpenID login attempt?
  157. if (!empty($_SESSION['openid']['verified']) && !empty($_SESSION['openid']['openid_uri']))
  158. {
  159. $context['openid'] = $_SESSION['openid']['openid_uri'];
  160. $context['username'] = $smcFunc['htmlspecialchars'](!empty($_POST['user']) ? $_POST['user'] : $_SESSION['openid']['nickname']);
  161. $context['email'] = $smcFunc['htmlspecialchars'](!empty($_POST['email']) ? $_POST['email'] : $_SESSION['openid']['email']);
  162. }
  163. // See whether we have some prefiled values.
  164. else
  165. {
  166. $context += array(
  167. 'openid' => isset($_POST['openid_identifier']) ? $_POST['openid_identifier'] : '',
  168. 'username' => isset($_POST['user']) ? $smcFunc['htmlspecialchars']($_POST['user']) : '',
  169. 'email' => isset($_POST['email']) ? $smcFunc['htmlspecialchars']($_POST['email']) : '',
  170. );
  171. }
  172. // @todo Why isn't this a simple set operation?
  173. // Were there any errors?
  174. $context['registration_errors'] = array();
  175. if (!empty($reg_errors))
  176. foreach ($reg_errors as $error)
  177. $context['registration_errors'][] = $error;
  178. createToken('register');
  179. }
  180. /**
  181. * Actually register the member.
  182. * @todo split this function in two functions:
  183. * - a function that handles action=register2, which needs no parameter;
  184. * - a function that processes the case of OpenID verification.
  185. *
  186. * @param bool $verifiedOpenID = false
  187. */
  188. function action_register2($verifiedOpenID = false)
  189. {
  190. global $scripturl, $txt, $modSettings, $context;
  191. global $user_info, $options, $settings, $smcFunc;
  192. checkSession();
  193. validateToken('register');
  194. // Start collecting together any errors.
  195. $reg_errors = array();
  196. // Did we save some open ID fields?
  197. if ($verifiedOpenID && !empty($context['openid_save_fields']))
  198. {
  199. foreach ($context['openid_save_fields'] as $id => $value)
  200. $_POST[$id] = $value;
  201. }
  202. // You can't register if it's disabled.
  203. if (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 3)
  204. fatal_lang_error('registration_disabled', false);
  205. // Things we don't do for people who have already confirmed their OpenID allegances via register.
  206. if (!$verifiedOpenID)
  207. {
  208. // Well, if you don't agree, you can't register.
  209. if (!empty($modSettings['requireAgreement']) && empty($_SESSION['registration_agreed']))
  210. redirectexit();
  211. // Make sure they came from *somewhere*, have a session.
  212. if (!isset($_SESSION['old_url']))
  213. redirectexit('action=register');
  214. // If we don't require an agreement, we need a extra check for coppa.
  215. if (empty($modSettings['requireAgreement']) && !empty($modSettings['coppaAge']))
  216. $_SESSION['skip_coppa'] = !empty($_POST['accept_agreement']);
  217. // Are they under age, and under age users are banned?
  218. if (!empty($modSettings['coppaAge']) && empty($modSettings['coppaType']) && empty($_SESSION['skip_coppa']))
  219. {
  220. // @todo This should be put in Errors, imho.
  221. loadLanguage('Login');
  222. fatal_lang_error('under_age_registration_prohibited', false, array($modSettings['coppaAge']));
  223. }
  224. // Check whether the visual verification code was entered correctly.
  225. if (!empty($modSettings['reg_verification']))
  226. {
  227. require_once(SUBSDIR . '/Editor.subs.php');
  228. $verificationOptions = array(
  229. 'id' => 'register',
  230. );
  231. $context['visual_verification'] = create_control_verification($verificationOptions, true);
  232. if (is_array($context['visual_verification']))
  233. {
  234. loadLanguage('Errors');
  235. foreach ($context['visual_verification'] as $error)
  236. $reg_errors[] = $txt['error_' . $error];
  237. }
  238. }
  239. }
  240. foreach ($_POST as $key => $value)
  241. {
  242. if (!is_array($_POST[$key]))
  243. $_POST[$key] = htmltrim__recursive(str_replace(array("\n", "\r"), '', $_POST[$key]));
  244. }
  245. // Collect all extra registration fields someone might have filled in.
  246. $possible_strings = array(
  247. 'website_url', 'website_title',
  248. 'location', 'birthdate',
  249. 'time_format',
  250. 'buddy_list',
  251. 'pm_ignore_list',
  252. 'smiley_set',
  253. 'signature', 'personal_text', 'avatar',
  254. 'lngfile',
  255. 'secret_question', 'secret_answer',
  256. );
  257. $possible_ints = array(
  258. 'pm_email_notify',
  259. 'notify_types',
  260. 'gender',
  261. 'id_theme',
  262. );
  263. $possible_floats = array(
  264. 'time_offset',
  265. );
  266. $possible_bools = array(
  267. 'notify_announcements', 'notify_regularity', 'notify_send_body',
  268. 'hide_email', 'show_online',
  269. );
  270. if (isset($_POST['secret_answer']) && $_POST['secret_answer'] != '')
  271. $_POST['secret_answer'] = md5($_POST['secret_answer']);
  272. // Needed for isReservedName() and registerMember().
  273. require_once(SUBSDIR . '/Members.subs.php');
  274. // Validation... even if we're not a mall.
  275. if (isset($_POST['real_name']) && (!empty($modSettings['allow_editDisplayName']) || allowedTo('moderate_forum')))
  276. {
  277. $_POST['real_name'] = trim(preg_replace('~[\s]~u', ' ', $_POST['real_name']));
  278. if (trim($_POST['real_name']) != '' && !isReservedName($_POST['real_name']) && $smcFunc['strlen']($_POST['real_name']) < 60)
  279. $possible_strings[] = 'real_name';
  280. }
  281. // Handle a string as a birthdate...
  282. if (isset($_POST['birthdate']) && $_POST['birthdate'] != '')
  283. $_POST['birthdate'] = strftime('%Y-%m-%d', strtotime($_POST['birthdate']));
  284. // Or birthdate parts...
  285. elseif (!empty($_POST['bday1']) && !empty($_POST['bday2']))
  286. $_POST['birthdate'] = sprintf('%04d-%02d-%02d', empty($_POST['bday3']) ? 0 : (int) $_POST['bday3'], (int) $_POST['bday1'], (int) $_POST['bday2']);
  287. // By default assume email is hidden, only show it if we tell it to.
  288. $_POST['hide_email'] = !empty($_POST['allow_email']) ? 0 : 1;
  289. // Validate the passed language file.
  290. if (isset($_POST['lngfile']) && !empty($modSettings['userLanguage']))
  291. {
  292. // Do we have any languages?
  293. if (empty($context['languages']))
  294. getLanguages();
  295. // Did we find it?
  296. if (isset($context['languages'][$_POST['lngfile']]))
  297. $_SESSION['language'] = $_POST['lngfile'];
  298. else
  299. unset($_POST['lngfile']);
  300. }
  301. else
  302. unset($_POST['lngfile']);
  303. // Set the options needed for registration.
  304. $regOptions = array(
  305. 'interface' => 'guest',
  306. 'username' => !empty($_POST['user']) ? $_POST['user'] : '',
  307. 'email' => !empty($_POST['email']) ? $_POST['email'] : '',
  308. 'password' => !empty($_POST['passwrd1']) ? $_POST['passwrd1'] : '',
  309. 'password_check' => !empty($_POST['passwrd2']) ? $_POST['passwrd2'] : '',
  310. 'openid' => !empty($_POST['openid_identifier']) ? $_POST['openid_identifier'] : '',
  311. 'auth_method' => !empty($_POST['authenticate']) ? $_POST['authenticate'] : '',
  312. 'check_reserved_name' => true,
  313. 'check_password_strength' => true,
  314. 'check_email_ban' => true,
  315. 'send_welcome_email' => !empty($modSettings['send_welcomeEmail']),
  316. 'require' => !empty($modSettings['coppaAge']) && !$verifiedOpenID && empty($_SESSION['skip_coppa']) ? 'coppa' : (empty($modSettings['registration_method']) ? 'nothing' : ($modSettings['registration_method'] == 1 ? 'activation' : 'approval')),
  317. 'extra_register_vars' => array(),
  318. 'theme_vars' => array(),
  319. );
  320. // Include the additional options that might have been filled in.
  321. foreach ($possible_strings as $var)
  322. if (isset($_POST[$var]))
  323. $regOptions['extra_register_vars'][$var] = $smcFunc['htmlspecialchars']($_POST[$var], ENT_QUOTES);
  324. foreach ($possible_ints as $var)
  325. if (isset($_POST[$var]))
  326. $regOptions['extra_register_vars'][$var] = (int) $_POST[$var];
  327. foreach ($possible_floats as $var)
  328. if (isset($_POST[$var]))
  329. $regOptions['extra_register_vars'][$var] = (float) $_POST[$var];
  330. foreach ($possible_bools as $var)
  331. if (isset($_POST[$var]))
  332. $regOptions['extra_register_vars'][$var] = empty($_POST[$var]) ? 0 : 1;
  333. // Registration options are always default options...
  334. if (isset($_POST['default_options']))
  335. $_POST['options'] = isset($_POST['options']) ? $_POST['options'] + $_POST['default_options'] : $_POST['default_options'];
  336. $regOptions['theme_vars'] = isset($_POST['options']) && is_array($_POST['options']) ? $_POST['options'] : array();
  337. // Make sure they are clean, dammit!
  338. $regOptions['theme_vars'] = htmlspecialchars__recursive($regOptions['theme_vars']);
  339. // Check whether we have fields that simply MUST be displayed?
  340. $request = $smcFunc['db_query']('', '
  341. SELECT col_name, field_name, field_type, field_length, mask, show_reg
  342. FROM {db_prefix}custom_fields
  343. WHERE active = {int:is_active}',
  344. array(
  345. 'is_active' => 1,
  346. )
  347. );
  348. $custom_field_errors = array();
  349. while ($row = $smcFunc['db_fetch_assoc']($request))
  350. {
  351. // Don't allow overriding of the theme variables.
  352. if (isset($regOptions['theme_vars'][$row['col_name']]))
  353. unset($regOptions['theme_vars'][$row['col_name']]);
  354. // Not actually showing it then?
  355. if (!$row['show_reg'])
  356. continue;
  357. // Prepare the value!
  358. $value = isset($_POST['customfield'][$row['col_name']]) ? trim($_POST['customfield'][$row['col_name']]) : '';
  359. // We only care for text fields as the others are valid to be empty.
  360. if (!in_array($row['field_type'], array('check', 'select', 'radio')))
  361. {
  362. // Is it too long?
  363. if ($row['field_length'] && $row['field_length'] < $smcFunc['strlen']($value))
  364. $custom_field_errors[] = array('custom_field_too_long', array($row['field_name'], $row['field_length']));
  365. // Any masks to apply?
  366. if ($row['field_type'] == 'text' && !empty($row['mask']) && $row['mask'] != 'none')
  367. {
  368. // @todo We never error on this - just ignore it at the moment...
  369. if ($row['mask'] == 'email' && (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $value) === 0 || strlen($value) > 255))
  370. $custom_field_errors[] = array('custom_field_invalid_email', array($row['field_name']));
  371. elseif ($row['mask'] == 'number' && preg_match('~[^\d]~', $value))
  372. $custom_field_errors[] = array('custom_field_not_number', array($row['field_name']));
  373. elseif (substr($row['mask'], 0, 5) == 'regex' && preg_match(substr($row['mask'], 5), $value) === 0)
  374. $custom_field_errors[] = array('custom_field_inproper_format', array($row['field_name']));
  375. }
  376. }
  377. // Is this required but not there?
  378. if (trim($value) == '' && $row['show_reg'] > 1)
  379. $custom_field_errors[] = array('custom_field_empty', array($row['field_name']));
  380. }
  381. $smcFunc['db_free_result']($request);
  382. // Process any errors.
  383. if (!empty($custom_field_errors))
  384. {
  385. loadLanguage('Errors');
  386. foreach ($custom_field_errors as $error)
  387. $reg_errors[] = vsprintf($txt['error_' . $error[0]], $error[1]);
  388. }
  389. // Lets check for other errors before trying to register the member.
  390. if (!empty($reg_errors))
  391. {
  392. $_REQUEST['step'] = 2;
  393. return action_register($reg_errors);
  394. }
  395. // If they're wanting to use OpenID we need to validate them first.
  396. if (empty($_SESSION['openid']['verified']) && !empty($_POST['authenticate']) && $_POST['authenticate'] == 'openid')
  397. {
  398. // What do we need to save?
  399. $save_variables = array();
  400. foreach ($_POST as $k => $v)
  401. if (!in_array($k, array('sc', 'sesc', $context['session_var'], 'passwrd1', 'passwrd2', 'regSubmit')))
  402. $save_variables[$k] = $v;
  403. require_once(SUBSDIR . '/OpenID.subs.php');
  404. openID_validate($_POST['openid_identifier'], false, $save_variables);
  405. }
  406. // If we've come from OpenID set up some default stuff.
  407. elseif ($verifiedOpenID || (!empty($_POST['openid_identifier']) && $_POST['authenticate'] == 'openid'))
  408. {
  409. $regOptions['username'] = !empty($_POST['user']) && trim($_POST['user']) != '' ? $_POST['user'] : $_SESSION['openid']['nickname'];
  410. $regOptions['email'] = !empty($_POST['email']) && trim($_POST['email']) != '' ? $_POST['email'] : $_SESSION['openid']['email'];
  411. $regOptions['auth_method'] = 'openid';
  412. $regOptions['openid'] = !empty($_POST['openid_identifier']) ? $_POST['openid_identifier'] : $_SESSION['openid']['openid_uri'];
  413. }
  414. $memberID = registerMember($regOptions, true);
  415. // Was there actually an error of some kind dear boy?
  416. if (is_array($memberID))
  417. {
  418. $reg_errors = array_merge($reg_errors, $memberID);
  419. $_REQUEST['step'] = 2;
  420. return action_register($reg_errors);
  421. }
  422. // Do our spam protection now.
  423. spamProtection('register');
  424. // We'll do custom fields after as then we get to use the helper function!
  425. if (!empty($_POST['customfield']))
  426. {
  427. require_once(SUBSDIR . '/Profile.subs.php');
  428. makeCustomFieldChanges($memberID, 'register');
  429. }
  430. // If COPPA has been selected then things get complicated, setup the template.
  431. if (!empty($modSettings['coppaAge']) && empty($_SESSION['skip_coppa']))
  432. redirectexit('action=coppa;member=' . $memberID);
  433. // Basic template variable setup.
  434. elseif (!empty($modSettings['registration_method']))
  435. {
  436. loadTemplate('Register');
  437. $context += array(
  438. 'page_title' => $txt['register'],
  439. 'title' => $txt['registration_successful'],
  440. 'sub_template' => 'after',
  441. 'description' => $modSettings['registration_method'] == 2 ? $txt['approval_after_registration'] : $txt['activate_after_registration']
  442. );
  443. }
  444. else
  445. {
  446. call_integration_hook('integrate_activate', array($regOptions['username']));
  447. setLoginCookie(60 * $modSettings['cookieTime'], $memberID, sha1(sha1(strtolower($regOptions['username']) . $regOptions['password']) . $regOptions['register_vars']['password_salt']));
  448. redirectexit('action=login2;sa=check;member=' . $memberID, $context['server']['needs_login_fix']);
  449. }
  450. }
  451. /**
  452. * Verify the activation code, and activate the user if correct.
  453. * Accessed by ?action=activate
  454. */
  455. function action_activate()
  456. {
  457. global $context, $txt, $modSettings, $scripturl, $smcFunc, $language, $user_info;
  458. // Logged in users should not bother to activate their accounts
  459. if (!empty($user_info['id']))
  460. redirectexit();
  461. loadLanguage('Login');
  462. loadTemplate('Login');
  463. if (empty($_REQUEST['u']) && empty($_POST['user']))
  464. {
  465. if (empty($modSettings['registration_method']) || $modSettings['registration_method'] == '3')
  466. fatal_lang_error('no_access', false);
  467. $context['member_id'] = 0;
  468. $context['sub_template'] = 'resend';
  469. $context['page_title'] = $txt['invalid_activation_resend'];
  470. $context['can_activate'] = empty($modSettings['registration_method']) || $modSettings['registration_method'] == '1';
  471. $context['default_username'] = isset($_GET['user']) ? $_GET['user'] : '';
  472. return;
  473. }
  474. // Get the code from the database...
  475. $request = $smcFunc['db_query']('', '
  476. SELECT id_member, validation_code, member_name, real_name, email_address, is_activated, passwd, lngfile
  477. FROM {db_prefix}members' . (empty($_REQUEST['u']) ? '
  478. WHERE member_name = {string:email_address} OR email_address = {string:email_address}' : '
  479. WHERE id_member = {int:id_member}') . '
  480. LIMIT 1',
  481. array(
  482. 'id_member' => isset($_REQUEST['u']) ? (int) $_REQUEST['u'] : 0,
  483. 'email_address' => isset($_POST['user']) ? $_POST['user'] : '',
  484. )
  485. );
  486. // Does this user exist at all?
  487. if ($smcFunc['db_num_rows']($request) == 0)
  488. {
  489. $context['sub_template'] = 'retry_activate';
  490. $context['page_title'] = $txt['invalid_userid'];
  491. $context['member_id'] = 0;
  492. return;
  493. }
  494. $row = $smcFunc['db_fetch_assoc']($request);
  495. $smcFunc['db_free_result']($request);
  496. // Change their email address? (they probably tried a fake one first :P.)
  497. if (isset($_POST['new_email'], $_REQUEST['passwd']) && sha1(strtolower($row['member_name']) . $_REQUEST['passwd']) == $row['passwd'] && ($row['is_activated'] == 0 || $row['is_activated'] == 2))
  498. {
  499. if (empty($modSettings['registration_method']) || $modSettings['registration_method'] == 3)
  500. fatal_lang_error('no_access', false);
  501. // @todo Separate the sprintf?
  502. if (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $_POST['new_email']) == 0)
  503. fatal_error(sprintf($txt['valid_email_needed'], htmlspecialchars($_POST['new_email'])), false);
  504. // Make sure their email isn't banned.
  505. isBannedEmail($_POST['new_email'], 'cannot_register', $txt['ban_register_prohibited']);
  506. // Ummm... don't even dare try to take someone else's email!!
  507. $request = $smcFunc['db_query']('', '
  508. SELECT id_member
  509. FROM {db_prefix}members
  510. WHERE email_address = {string:email_address}
  511. LIMIT 1',
  512. array(
  513. 'email_address' => $_POST['new_email'],
  514. )
  515. );
  516. // @todo Separate the sprintf?
  517. if ($smcFunc['db_num_rows']($request) != 0)
  518. fatal_lang_error('email_in_use', false, array(htmlspecialchars($_POST['new_email'])));
  519. $smcFunc['db_free_result']($request);
  520. updateMemberData($row['id_member'], array('email_address' => $_POST['new_email']));
  521. $row['email_address'] = $_POST['new_email'];
  522. $email_change = true;
  523. }
  524. // Resend the password, but only if the account wasn't activated yet.
  525. if (!empty($_REQUEST['sa']) && $_REQUEST['sa'] == 'resend' && ($row['is_activated'] == 0 || $row['is_activated'] == 2) && (!isset($_REQUEST['code']) || $_REQUEST['code'] == ''))
  526. {
  527. require_once(SUBSDIR . '/Mail.subs.php');
  528. $replacements = array(
  529. 'REALNAME' => $row['real_name'],
  530. 'USERNAME' => $row['member_name'],
  531. 'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $row['id_member'] . ';code=' . $row['validation_code'],
  532. 'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $row['id_member'],
  533. 'ACTIVATIONCODE' => $row['validation_code'],
  534. 'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder',
  535. );
  536. $emaildata = loadEmailTemplate('resend_activate_message', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']);
  537. sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 0);
  538. $context['page_title'] = $txt['invalid_activation_resend'];
  539. // This will ensure we don't actually get an error message if it works!
  540. $context['error_title'] = '';
  541. fatal_lang_error(!empty($email_change) ? 'change_email_success' : 'resend_email_success', false);
  542. }
  543. // Quit if this code is not right.
  544. if (empty($_REQUEST['code']) || $row['validation_code'] != $_REQUEST['code'])
  545. {
  546. if (!empty($row['is_activated']))
  547. fatal_lang_error('already_activated', false);
  548. elseif ($row['validation_code'] == '')
  549. {
  550. loadLanguage('Profile');
  551. fatal_error($txt['registration_not_approved'] . ' <a href="' . $scripturl . '?action=activate;user=' . $row['member_name'] . '">' . $txt['here'] . '</a>.', false);
  552. }
  553. $context['sub_template'] = 'retry_activate';
  554. $context['page_title'] = $txt['invalid_activation_code'];
  555. $context['member_id'] = $row['id_member'];
  556. return;
  557. }
  558. // Let the integration know that they've been activated!
  559. call_integration_hook('integrate_activate', array($row['member_name']));
  560. // Validation complete - update the database!
  561. updateMemberData($row['id_member'], array('is_activated' => 1, 'validation_code' => ''));
  562. // Also do a proper member stat re-evaluation.
  563. updateStats('member', false);
  564. if (!isset($_POST['new_email']))
  565. {
  566. require_once(SUBSDIR . '/Post.subs.php');
  567. adminNotify('activation', $row['id_member'], $row['member_name']);
  568. }
  569. $context += array(
  570. 'page_title' => $txt['registration_successful'],
  571. 'sub_template' => 'login',
  572. 'default_username' => $row['member_name'],
  573. 'default_password' => '',
  574. 'never_expire' => false,
  575. 'description' => $txt['activate_success']
  576. );
  577. }
  578. /**
  579. * This function will display the contact information for the forum, as well a form to fill in.
  580. * Accessed by action=coppa
  581. */
  582. function action_coppa()
  583. {
  584. global $context, $modSettings, $txt, $smcFunc;
  585. loadLanguage('Login');
  586. loadTemplate('Register');
  587. // No User ID??
  588. if (!isset($_GET['member']))
  589. fatal_lang_error('no_access', false);
  590. // Get the user details...
  591. $request = $smcFunc['db_query']('', '
  592. SELECT member_name
  593. FROM {db_prefix}members
  594. WHERE id_member = {int:id_member}
  595. AND is_activated = {int:is_coppa}',
  596. array(
  597. 'id_member' => (int) $_GET['member'],
  598. 'is_coppa' => 5,
  599. )
  600. );
  601. if ($smcFunc['db_num_rows']($request) == 0)
  602. fatal_lang_error('no_access', false);
  603. list ($username) = $smcFunc['db_fetch_row']($request);
  604. $smcFunc['db_free_result']($request);
  605. if (isset($_GET['form']))
  606. {
  607. // Some simple contact stuff for the forum.
  608. $context['forum_contacts'] = (!empty($modSettings['coppaPost']) ? $modSettings['coppaPost'] . '<br /><br />' : '') . (!empty($modSettings['coppaFax']) ? $modSettings['coppaFax'] . '<br />' : '');
  609. $context['forum_contacts'] = !empty($context['forum_contacts']) ? $context['forum_name_html_safe'] . '<br />' . $context['forum_contacts'] : '';
  610. // Showing template?
  611. if (!isset($_GET['dl']))
  612. {
  613. // Shortcut for producing underlines.
  614. $context['ul'] = '<u>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</u>';
  615. $context['template_layers'] = array();
  616. $context['sub_template'] = 'coppa_form';
  617. $context['page_title'] = $txt['coppa_form_title'];
  618. $context['coppa_body'] = str_replace(array('{PARENT_NAME}', '{CHILD_NAME}', '{USER_NAME}'), array($context['ul'], $context['ul'], $username), $txt['coppa_form_body']);
  619. }
  620. // Downloading.
  621. else
  622. {
  623. // The data.
  624. $ul = ' ';
  625. $crlf = "\r\n";
  626. $data = $context['forum_contacts'] . $crlf . $txt['coppa_form_address'] . ':' . $crlf . $txt['coppa_form_date'] . ':' . $crlf . $crlf . $crlf . $txt['coppa_form_body'];
  627. $data = str_replace(array('{PARENT_NAME}', '{CHILD_NAME}', '{USER_NAME}', '<br>', '<br />'), array($ul, $ul, $username, $crlf, $crlf), $data);
  628. // Send the headers.
  629. header('Connection: close');
  630. header('Content-Disposition: attachment; filename="approval.txt"');
  631. header('Content-Type: ' . (isBrowser('ie') || isBrowser('opera') ? 'application/octetstream' : 'application/octet-stream'));
  632. header('Content-Length: ' . count($data));
  633. echo $data;
  634. obExit(false);
  635. }
  636. }
  637. else
  638. {
  639. $context += array(
  640. 'page_title' => $txt['coppa_title'],
  641. 'sub_template' => 'coppa',
  642. );
  643. $context['coppa'] = array(
  644. 'body' => str_replace('{MINIMUM_AGE}', $modSettings['coppaAge'], $txt['coppa_after_registration']),
  645. 'many_options' => !empty($modSettings['coppaPost']) && !empty($modSettings['coppaFax']),
  646. 'post' => empty($modSettings['coppaPost']) ? '' : $modSettings['coppaPost'],
  647. 'fax' => empty($modSettings['coppaFax']) ? '' : $modSettings['coppaFax'],
  648. 'phone' => empty($modSettings['coppaPhone']) ? '' : str_replace('{PHONE_NUMBER}', $modSettings['coppaPhone'], $txt['coppa_send_by_phone']),
  649. 'id' => $_GET['member'],
  650. );
  651. }
  652. }
  653. /**
  654. * Show the verification code or let it hear.
  655. * Accessed by ?action=verificationcode
  656. */
  657. function action_verificationcode()
  658. {
  659. global $modSettings, $context, $scripturl;
  660. $verification_id = isset($_GET['vid']) ? $_GET['vid'] : '';
  661. $code = $verification_id && isset($_SESSION[$verification_id . '_vv']) ? $_SESSION[$verification_id . '_vv']['code'] : (isset($_SESSION['visual_verification_code']) ? $_SESSION['visual_verification_code'] : '');
  662. // Somehow no code was generated or the session was lost.
  663. if (empty($code))
  664. {
  665. header('Content-Type: image/gif');
  666. die("\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B");
  667. }
  668. // Show a window that will play the verification code.
  669. elseif (isset($_REQUEST['sound']))
  670. {
  671. loadLanguage('Login');
  672. loadTemplate('Register');
  673. $context['verification_sound_href'] = $scripturl . '?action=verificationcode;rand=' . md5(mt_rand()) . ($verification_id ? ';vid=' . $verification_id : '') . ';format=.wav';
  674. $context['sub_template'] = 'verification_sound';
  675. $context['template_layers'] = array();
  676. obExit();
  677. }
  678. // If we have GD, try the nice code.
  679. elseif (empty($_REQUEST['format']))
  680. {
  681. require_once(SUBSDIR . '/Graphics.subs.php');
  682. if (in_array('gd', get_loaded_extensions()) && !showCodeImage($code))
  683. header('HTTP/1.1 400 Bad Request');
  684. // Otherwise just show a pre-defined letter.
  685. elseif (isset($_REQUEST['letter']))
  686. {
  687. $_REQUEST['letter'] = (int) $_REQUEST['letter'];
  688. if ($_REQUEST['letter'] > 0 && $_REQUEST['letter'] <= strlen($code) && !showLetterImage(strtolower($code{$_REQUEST['letter'] - 1})))
  689. {
  690. header('Content-Type: image/gif');
  691. die("\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B");
  692. }
  693. }
  694. // You must be up to no good.
  695. else
  696. {
  697. header('Content-Type: image/gif');
  698. die("\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B");
  699. }
  700. }
  701. elseif ($_REQUEST['format'] === '.wav')
  702. {
  703. require_once(SUBSDIR . '/Sound.subs.php');
  704. if (!createWaveFile($code))
  705. header('HTTP/1.1 400 Bad Request');
  706. }
  707. // We all die one day...
  708. die();
  709. }
  710. /**
  711. * See if a username already exists.
  712. */
  713. function RegisterCheckUsername()
  714. {
  715. global $smcFunc, $context, $txt;
  716. // This is XML!
  717. loadTemplate('Xml');
  718. $context['sub_template'] = 'check_username';
  719. $context['checked_username'] = isset($_GET['username']) ? un_htmlspecialchars($_GET['username']) : '';
  720. $context['valid_username'] = true;
  721. // Clean it up like mother would.
  722. $context['checked_username'] = preg_replace('~[\t\n\r\x0B\0\x{A0}]+~u', ' ', $context['checked_username']);
  723. require_once(SUBSDIR . '/Auth.subs.php');
  724. $errors = validateUsername(0, $context['checked_username'], true);
  725. $context['valid_username'] = empty($errors);
  726. }
  727. /**
  728. * Shows the contact form for the user to fill out
  729. * Needs to be enabled to be used
  730. */
  731. function action_contact()
  732. {
  733. global $context, $txt, $smcFunc, $user_info, $modSettings;
  734. // Already inside, no need to use this, just send a PM
  735. // Disabled, you cannot enter.
  736. if (!$user_info['is_guest'] || empty($modSettings['enable_contactform']) || $modSettings['enable_contactform'] == 'disabled')
  737. redirectexit();
  738. loadLanguage('Login');
  739. loadTemplate('Register');
  740. spamProtection('contact');
  741. if (isset($_REQUEST['send']))
  742. {
  743. checkSession('post');
  744. validateToken('contact');
  745. // No errors, yet.
  746. $context['errors'] = array();
  747. // Could they get the right send topic verification code?
  748. require_once(SUBSDIR . '/Editor.subs.php');
  749. $verificationOptions = array(
  750. 'id' => 'contactform',
  751. );
  752. $context['require_verification'] = create_control_verification($verificationOptions, true);
  753. if (is_array($context['require_verification']))
  754. {
  755. loadLanguage('Errors');
  756. foreach ($context['require_verification'] as $error)
  757. $context['errors'][] = $txt['error_' . $error];
  758. }
  759. // Check the email for validity.
  760. $email = !empty($_POST['emailaddres']) ? trim($_POST['emailaddres']) : '';
  761. if (empty($email))
  762. $context['errors'][] = $txt['error_no_email'];
  763. if (preg_match('~^[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', $email) == 0)
  764. $context['errors'][] = $txt['error_bad_email'];
  765. $message = !empty($_POST['contactmessage']) ? trim($smcFunc['htmlspecialchars']($_POST['contactmessage'])) : '';
  766. if (empty($message))
  767. $context['errors'][] = $txt['error_no_message'];
  768. if (empty($context['errors']))
  769. {
  770. $request = $smcFunc['db_query']('', '
  771. SELECT id_member
  772. FROM {db_prefix}members
  773. WHERE id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0
  774. ORDER BY real_name',
  775. array(
  776. 'admin_group' => 1,
  777. )
  778. );
  779. $admins = array();
  780. while ($row = $smcFunc['db_fetch_assoc']($request))
  781. $admins[] = $row['id_member'];
  782. $smcFunc['db_free_result']($request);
  783. if (!empty($admins))
  784. {
  785. require_once(SUBSDIR . '/Post.subs.php');
  786. sendpm(array('to' => $admins, 'bcc' => array()), $txt['contact_subject'], $_REQUEST['contactmessage'], false, array('id' => 0, 'name' => $email, 'username' => $email));
  787. }
  788. // Send the PM
  789. redirectexit('action=contact;sa=done');
  790. }
  791. else
  792. {
  793. $context['emailaddres'] = $email;
  794. $context['contactmessage'] = $message;
  795. }
  796. }
  797. if (isset($_GET['sa']) && $_GET['sa'] == 'done')
  798. $context['sub_template'] = 'contact_form_done';
  799. else
  800. {
  801. $context['sub_template'] = 'contact_form';
  802. require_once(SUBSDIR . '/Editor.subs.php');
  803. $verificationOptions = array(
  804. 'id' => 'contactform',
  805. );
  806. $context['require_verification'] = create_control_verification($verificationOptions);
  807. $context['visual_verification_id'] = $verificationOptions['id'];
  808. }
  809. createToken('contact');
  810. }