Issues (1065)

Sources/Register.php (2 issues)

1
<?php
2
3
/**
4
 * This file has two main jobs, but they really are one.  It registers new
5
 * members, and it helps the administrator moderate member registrations.
6
 * Similarly, it handles account activation as well.
7
 *
8
 * Simple Machines Forum (SMF)
9
 *
10
 * @package SMF
11
 * @author Simple Machines https://www.simplemachines.org
12
 * @copyright 2022 Simple Machines and individual contributors
13
 * @license https://www.simplemachines.org/about/smf/license.php BSD
14
 *
15
 * @version 2.1.3
16
 */
17
18
if (!defined('SMF'))
19
	die('No direct access...');
20
21
/**
22
 * Begin the registration process.
23
 *
24
 * @param array $reg_errors Holds information about any errors that occurred
25
 */
26
function Register($reg_errors = array())
27
{
28
	global $txt, $boarddir, $context, $modSettings, $user_info;
29
	global $language, $scripturl, $smcFunc, $sourcedir, $cur_profile;
30
31
	// Is this an incoming AJAX check?
32
	if (isset($_GET['sa']) && $_GET['sa'] == 'usernamecheck')
33
		return RegisterCheckUsername();
34
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
39
	// If this user is an admin - redirect them to the admin registration page.
40
	if (allowedTo('moderate_forum') && !$user_info['is_guest'])
41
		redirectexit('action=admin;area=regcenter;sa=register');
42
	// You are not a guest, so you are a member - and members don't get to register twice!
43
	elseif (empty($user_info['is_guest']))
44
		redirectexit();
45
46
	loadLanguage('Login');
47
	loadTemplate('Register');
48
49
	// How many steps have we done so far today?
50
	$current_step = isset($_REQUEST['step']) ? (int) $_REQUEST['step'] : (!empty($modSettings['requireAgreement']) || !empty($modSettings['requirePolicyAgreement']) ? 1 : 2);
51
52
	// Do we need them to agree to the registration agreement and/or privacy policy agreement, first?
53
	$context['registration_passed_agreement'] = !empty($_SESSION['registration_agreed']);
54
	$context['show_coppa'] = !empty($modSettings['coppaAge']);
55
56
	$agree_txt_key = '';
57
	if ($current_step == 1)
58
	{
59
		if (!empty($modSettings['requireAgreement']) && !empty($modSettings['requirePolicyAgreement']))
60
			$agree_txt_key = 'agreement_policy_';
61
		elseif (!empty($modSettings['requireAgreement']))
62
			$agree_txt_key = 'agreement_';
63
		elseif (!empty($modSettings['requirePolicyAgreement']))
64
			$agree_txt_key = 'policy_';
65
	}
66
67
	// Under age restrictions?
68
	if ($context['show_coppa'])
69
	{
70
		$context['skip_coppa'] = false;
71
		$context['coppa_agree_above'] = sprintf($txt[$agree_txt_key . 'agree_coppa_above'], $modSettings['coppaAge']);
72
		$context['coppa_agree_below'] = sprintf($txt[$agree_txt_key . 'agree_coppa_below'], $modSettings['coppaAge']);
73
	}
74
	elseif ($agree_txt_key != '')
75
		$context['agree'] = $txt[$agree_txt_key . 'agree'];
76
77
	// Does this user agree to the registation agreement?
78
	if ($current_step == 1 && (isset($_POST['accept_agreement']) || isset($_POST['accept_agreement_coppa'])))
79
	{
80
		$context['registration_passed_agreement'] = $_SESSION['registration_agreed'] = true;
81
		$current_step = 2;
82
83
		// Skip the coppa procedure if the user says he's old enough.
84
		if ($context['show_coppa'])
85
		{
86
			$_SESSION['skip_coppa'] = !empty($_POST['accept_agreement']);
87
88
			// Are they saying they're under age, while under age registration is disabled?
89
			if (empty($modSettings['coppaType']) && empty($_SESSION['skip_coppa']))
90
			{
91
				loadLanguage('Login');
92
				fatal_lang_error('under_age_registration_prohibited', false, array($modSettings['coppaAge']));
93
			}
94
		}
95
	}
96
	// Make sure they don't squeeze through without agreeing.
97
	elseif ($current_step > 1 && (!empty($modSettings['requireAgreement']) || !empty($modSettings['requirePolicyAgreement'])) && !$context['registration_passed_agreement'])
98
		$current_step = 1;
99
100
	// Show the user the right form.
101
	$context['sub_template'] = $current_step == 1 ? 'registration_agreement' : 'registration_form';
102
	$context['page_title'] = $current_step == 1 ? $txt['registration_agreement'] : $txt['registration_form'];
103
104
	// Kinda need this.
105
	if ($context['sub_template'] == 'registration_form')
106
		loadJavaScriptFile('register.js', array('defer' => false, 'minimize' => true), 'smf_register');
107
108
	// Add the register chain to the link tree.
109
	$context['linktree'][] = array(
110
		'url' => $scripturl . '?action=signup',
111
		'name' => $txt['register'],
112
	);
113
114
	// Prepare the time gate! Do it like so, in case later steps want to reset the limit for any reason, but make sure the time is the current one.
115
	if (!isset($_SESSION['register']))
116
		$_SESSION['register'] = array(
117
			'timenow' => time(),
118
			'limit' => 10, // minimum number of seconds required on this page for registration
119
		);
120
	else
121
		$_SESSION['register']['timenow'] = time();
122
123
	// If you have to agree to the agreement, it needs to be fetched from the file.
124
	if (!empty($modSettings['requireAgreement']))
125
	{
126
		// Have we got a localized one?
127
		if (file_exists($boarddir . '/agreement.' . $user_info['language'] . '.txt'))
128
			$context['agreement'] = parse_bbc(file_get_contents($boarddir . '/agreement.' . $user_info['language'] . '.txt'), true, 'agreement_' . $user_info['language']);
129
		elseif (file_exists($boarddir . '/agreement.txt'))
130
			$context['agreement'] = parse_bbc(file_get_contents($boarddir . '/agreement.txt'), true, 'agreement');
131
		else
132
			$context['agreement'] = '';
133
134
		// Nothing to show, lets disable registration and inform the admin of this error
135
		if (empty($context['agreement']))
136
		{
137
			// No file found or a blank file, log the error so the admin knows there is a problem!
138
			log_error($txt['registration_agreement_missing'], 'critical');
139
			fatal_lang_error('registration_disabled', false);
140
		}
141
	}
142
143
	require_once($sourcedir . '/Subs-Notify.php');
144
	$prefs = getNotifyPrefs(0, 'announcements');
145
	$context['notify_announcements'] = !empty($prefs[0]['announcements']);
146
147
	if (!empty($modSettings['userLanguage']))
148
	{
149
		$selectedLanguage = empty($_SESSION['language']) ? $language : $_SESSION['language'];
150
151
		// Do we have any languages?
152
		if (empty($context['languages']))
153
			getLanguages();
154
155
		// Try to find our selected language.
156
		foreach ($context['languages'] as $key => $lang)
157
		{
158
			$context['languages'][$key]['name'] = strtr($lang['name'], array('-utf8' => ''));
159
160
			// Found it!
161
			if ($selectedLanguage == $lang['filename'])
162
				$context['languages'][$key]['selected'] = true;
163
		}
164
	}
165
166
	// If you have to agree to the privacy policy, it needs to be loaded from the database.
167
	if (!empty($modSettings['requirePolicyAgreement']))
168
	{
169
		// Have we got a localized one?
170
		if (!empty($modSettings['policy_' . $user_info['language']]))
171
			$context['privacy_policy'] = parse_bbc($modSettings['policy_' . $user_info['language']]);
172
		elseif (!empty($modSettings['policy_' . $language]))
173
			$context['privacy_policy'] = parse_bbc($modSettings['policy_' . $language]);
174
		else
175
		{
176
			// None was found; log the error so the admin knows there is a problem!
177
			log_error($txt['registration_policy_missing'], 'critical');
178
			fatal_lang_error('registration_disabled', false);
179
		}
180
	}
181
182
	// Any custom fields we want filled in?
183
	require_once($sourcedir . '/Profile.php');
184
	loadCustomFields(0, 'register');
185
186
	// Or any standard ones?
187
	if (!empty($modSettings['registration_fields']))
188
	{
189
		require_once($sourcedir . '/Profile-Modify.php');
190
191
		// Setup some important context.
192
		loadLanguage('Profile');
193
		loadTemplate('Profile');
194
195
		$context['user']['is_owner'] = true;
196
197
		// Here, and here only, emulate the permissions the user would have to do this.
198
		$user_info['permissions'] = array_merge($user_info['permissions'], array('profile_account_own', 'profile_extra_own', 'profile_other_own', 'profile_password_own', 'profile_website_own', 'profile_blurb'));
199
		$reg_fields = explode(',', $modSettings['registration_fields']);
200
201
		// Website is a little different
202
		if (in_array('website', $reg_fields))
203
		{
204
			unset($reg_fields['website']);
205
			if (isset($_POST['website_title']))
206
				$cur_profile['website_title'] = $smcFunc['htmlspecialchars']($_POST['website_title']);
207
			if (isset($_POST['website_url']))
208
				$cur_profile['website_url'] = $smcFunc['htmlspecialchars']($_POST['website_url']);
209
		}
210
211
		// We might have had some submissions on this front - go check.
212
		foreach ($reg_fields as $field)
213
			if (isset($_POST[$field]))
214
				$cur_profile[$field] = $smcFunc['htmlspecialchars']($_POST[$field]);
215
216
		// Load all the fields in question.
217
		setupProfileContext($reg_fields);
218
	}
219
220
	// Generate a visual verification code to make sure the user is no bot.
221
	if (!empty($modSettings['reg_verification']))
222
	{
223
		require_once($sourcedir . '/Subs-Editor.php');
224
		$verificationOptions = array(
225
			'id' => 'register',
226
		);
227
		$context['visual_verification'] = create_control_verification($verificationOptions);
228
		$context['visual_verification_id'] = $verificationOptions['id'];
229
	}
230
	// Otherwise we have nothing to show.
231
	else
232
		$context['visual_verification'] = false;
233
234
	$context += array(
235
		'username' => isset($_POST['user']) ? $smcFunc['htmlspecialchars']($_POST['user']) : '',
236
		'email' => isset($_POST['email']) ? $smcFunc['htmlspecialchars']($_POST['email']) : '',
237
		'notify_announcements' => !empty($_POST['notify_announcements']) ? 1 : 0,
238
	);
239
240
	// Were there any errors?
241
	$context['registration_errors'] = array();
242
	if (!empty($reg_errors))
243
		$context['registration_errors'] = $reg_errors;
244
245
	createToken('register');
246
}
247
248
/**
249
 * Actually register the member.
250
 */
251
function Register2()
252
{
253
	global $txt, $modSettings, $context, $sourcedir;
254
	global $smcFunc, $maintenance;
255
256
	checkSession();
257
	validateToken('register');
258
259
	// Check to ensure we're forcing SSL for authentication
260
	if (!empty($modSettings['force_ssl']) && empty($maintenance) && !httpsOn())
261
		fatal_lang_error('register_ssl_required');
262
263
	// Start collecting together any errors.
264
	$reg_errors = array();
265
266
	// You can't register if it's disabled.
267
	if (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 3)
268
		fatal_lang_error('registration_disabled', false);
269
270
	// Well, if you don't agree, you can't register.
271
	if ((!empty($modSettings['requireAgreement']) || !empty($modSettings['requirePolicyAgreement'])) && empty($_SESSION['registration_agreed']))
272
		redirectexit();
273
274
	// Make sure they came from *somewhere*, have a session.
275
	if (!isset($_SESSION['old_url']))
276
		redirectexit('action=signup');
277
278
	// If we require neither an agreement nor a privacy policy, we need a extra check for coppa.
279
	if (empty($modSettings['requireAgreement']) && empty($modSettings['requirePolicyAgreement']) && !empty($modSettings['coppaAge']))
280
		$_SESSION['skip_coppa'] = !empty($_POST['accept_agreement']);
281
282
	// Are they under age, and under age users are banned?
283
	if (!empty($modSettings['coppaAge']) && empty($modSettings['coppaType']) && empty($_SESSION['skip_coppa']))
284
	{
285
		loadLanguage('Errors');
286
		fatal_lang_error('under_age_registration_prohibited', false, array($modSettings['coppaAge']));
287
	}
288
289
	// Check the time gate for miscreants. First make sure they came from somewhere that actually set it up.
290
	if (empty($_SESSION['register']['timenow']) || empty($_SESSION['register']['limit']))
291
		redirectexit('action=signup');
292
	// Failing that, check the time on it.
293
	if (time() - $_SESSION['register']['timenow'] < $_SESSION['register']['limit'])
294
	{
295
		loadLanguage('Errors');
296
		$reg_errors[] = $txt['error_too_quickly'];
297
	}
298
299
	// Check whether the visual verification code was entered correctly.
300
	if (!empty($modSettings['reg_verification']))
301
	{
302
		require_once($sourcedir . '/Subs-Editor.php');
303
		$verificationOptions = array(
304
			'id' => 'register',
305
		);
306
		$context['visual_verification'] = create_control_verification($verificationOptions, true);
307
308
		if (is_array($context['visual_verification']))
309
		{
310
			loadLanguage('Errors');
311
			foreach ($context['visual_verification'] as $error)
312
				$reg_errors[] = $txt['error_' . $error];
313
		}
314
	}
315
316
	array_walk_recursive(
317
		$_POST,
318
		function (&$value, $key) use ($context, $smcFunc)
0 ignored issues
show
The import $context is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
319
		{
320
			// Normalize Unicode characters. (Does nothing if not in UTF-8 mode.)
321
			$value = $smcFunc['normalize']($value);
322
323
			// Replace any kind of space or illegal character with a normal space, and then trim.
324
			$value = $smcFunc['htmltrim'](normalize_spaces(sanitize_chars($value, 1, ' '), true, true, array('no_breaks' => true, 'replace_tabs' => true, 'collapse_hspace' => true)));
325
		}
326
	);
327
328
	// Collect all extra registration fields someone might have filled in.
329
	$possible_strings = array(
330
		'birthdate',
331
		'timezone',
332
		'buddy_list',
333
		'pm_ignore_list',
334
		'smiley_set',
335
		'personal_text', 'avatar',
336
		'lngfile',
337
		'secret_question', 'secret_answer',
338
	);
339
	$possible_ints = array(
340
		'id_theme',
341
	);
342
	$possible_floats = array(
343
		'time_offset',
344
	);
345
	$possible_bools = array(
346
		'show_online',
347
	);
348
349
	// We may want to add certain things to these if selected in the admin panel.
350
	if (!empty($modSettings['registration_fields']))
351
	{
352
		$reg_fields = explode(',', $modSettings['registration_fields']);
353
354
		// Website is a little different
355
		if (in_array('website', $reg_fields))
356
		{
357
			$possible_strings = array_merge(array('website_url', 'website_title'), $possible_strings);
358
359
			// Make sure their website URL is squeaky clean
360
			if (isset($_POST['website_url']))
361
				$_POST['website_url'] = (string) validate_iri(normalize_iri($_POST['website_url']));
362
		}
363
	}
364
365
	if (isset($_POST['secret_answer']) && $_POST['secret_answer'] != '')
366
		$_POST['secret_answer'] = md5($_POST['secret_answer']);
367
368
	// Needed for isReservedName() and registerMember().
369
	require_once($sourcedir . '/Subs-Members.php');
370
371
	// Maybe you want set the displayed name during registration
372
	if (isset($_POST['real_name']))
373
	{
374
		// Are you already allowed to edit the displayed name?
375
		if (allowedTo('profile_displayed_name') || allowedTo('moderate_forum'))
376
			$canEditDisplayName = true;
377
378
		// If you are a guest, will you be allowed to once you register?
379
		else
380
		{
381
			$request = $smcFunc['db_query']('', '
382
				SELECT add_deny
383
				FROM {db_prefix}permissions
384
				WHERE id_group = {int:id_group} AND permission = {string:permission}',
385
				array(
386
					'id_group' => 0,
387
					'permission' => 'profile_displayed_name_own',
388
				)
389
			);
390
			list($canEditDisplayName) = $smcFunc['db_fetch_row']($request);
391
			$smcFunc['db_free_result']($request);
392
		}
393
394
		// Only set it if you can and if we are sure it is good
395
		if ($canEditDisplayName && $smcFunc['htmltrim']($_POST['real_name']) != '' && !isReservedName($_POST['real_name']) && $smcFunc['strlen']($_POST['real_name']) < 60)
396
			$possible_strings[] = 'real_name';
397
	}
398
399
	// Handle a string as a birthdate...
400
	if (isset($_POST['birthdate']) && $_POST['birthdate'] != '')
401
		$_POST['birthdate'] = smf_strftime('%Y-%m-%d', strtotime($_POST['birthdate']));
402
	// Or birthdate parts...
403
	elseif (!empty($_POST['bday1']) && !empty($_POST['bday2']))
404
		$_POST['birthdate'] = sprintf('%04d-%02d-%02d', empty($_POST['bday3']) ? 0 : (int) $_POST['bday3'], (int) $_POST['bday1'], (int) $_POST['bday2']);
405
406
	// Validate the passed language file.
407
	if (isset($_POST['lngfile']) && !empty($modSettings['userLanguage']))
408
	{
409
		// Do we have any languages?
410
		if (empty($context['languages']))
411
			getLanguages();
412
413
		// Did we find it?
414
		if (isset($context['languages'][$_POST['lngfile']]))
415
			$_SESSION['language'] = $_POST['lngfile'];
416
		else
417
			unset($_POST['lngfile']);
418
	}
419
	else
420
		unset($_POST['lngfile']);
421
422
	// Set the options needed for registration.
423
	$regOptions = array(
424
		'interface' => 'guest',
425
		'username' => !empty($_POST['user']) ? $_POST['user'] : '',
426
		'email' => !empty($_POST['email']) ? $_POST['email'] : '',
427
		'password' => !empty($_POST['passwrd1']) ? $_POST['passwrd1'] : '',
428
		'password_check' => !empty($_POST['passwrd2']) ? $_POST['passwrd2'] : '',
429
		'check_reserved_name' => true,
430
		'check_password_strength' => true,
431
		'check_email_ban' => true,
432
		'send_welcome_email' => !empty($modSettings['send_welcomeEmail']),
433
		'require' => !empty($modSettings['coppaAge']) && empty($_SESSION['skip_coppa']) ? 'coppa' : (empty($modSettings['registration_method']) ? 'nothing' : ($modSettings['registration_method'] == 1 ? 'activation' : 'approval')),
434
		'extra_register_vars' => array(),
435
		'theme_vars' => array(),
436
	);
437
438
	// Include the additional options that might have been filled in.
439
	foreach ($possible_strings as $var)
440
		if (isset($_POST[$var]))
441
			$regOptions['extra_register_vars'][$var] = $smcFunc['htmlspecialchars']($_POST[$var], ENT_QUOTES);
442
	foreach ($possible_ints as $var)
443
		if (isset($_POST[$var]))
444
			$regOptions['extra_register_vars'][$var] = (int) $_POST[$var];
445
	foreach ($possible_floats as $var)
446
		if (isset($_POST[$var]))
447
			$regOptions['extra_register_vars'][$var] = (float) $_POST[$var];
448
	foreach ($possible_bools as $var)
449
		if (isset($_POST[$var]))
450
			$regOptions['extra_register_vars'][$var] = empty($_POST[$var]) ? 0 : 1;
451
452
	// Registration options are always default options...
453
	if (isset($_POST['default_options']))
454
		$_POST['options'] = isset($_POST['options']) ? $_POST['options'] + $_POST['default_options'] : $_POST['default_options'];
455
	$regOptions['theme_vars'] = isset($_POST['options']) && is_array($_POST['options']) ? $_POST['options'] : array();
456
457
	// Note when they accepted the agreement and privacy policy
458
	if (!empty($modSettings['requireAgreement']))
459
		$regOptions['theme_vars']['agreement_accepted'] = time();
460
	if (!empty($modSettings['requirePolicyAgreement']))
461
		$regOptions['theme_vars']['policy_accepted'] = time();
462
463
	// Make sure they are clean, dammit!
464
	$regOptions['theme_vars'] = htmlspecialchars__recursive($regOptions['theme_vars']);
465
466
	// Check whether we have fields that simply MUST be displayed?
467
	$request = $smcFunc['db_query']('', '
468
		SELECT col_name, field_name, field_type, field_length, mask, show_reg
469
		FROM {db_prefix}custom_fields
470
		WHERE active = {int:is_active}
471
		ORDER BY field_order',
472
		array(
473
			'is_active' => 1,
474
		)
475
	);
476
	$custom_field_errors = array();
477
	while ($row = $smcFunc['db_fetch_assoc']($request))
478
	{
479
		// Don't allow overriding of the theme variables.
480
		if (isset($regOptions['theme_vars'][$row['col_name']]))
481
			unset($regOptions['theme_vars'][$row['col_name']]);
482
483
		// Not actually showing it then?
484
		if (!$row['show_reg'])
485
			continue;
486
487
		// Prepare the value!
488
		$value = isset($_POST['customfield'][$row['col_name']]) ? trim($_POST['customfield'][$row['col_name']]) : '';
489
490
		// We only care for text fields as the others are valid to be empty.
491
		if (!in_array($row['field_type'], array('check', 'select', 'radio')))
492
		{
493
			// Is it too long?
494
			if ($row['field_length'] && $row['field_length'] < $smcFunc['strlen']($value))
495
				$custom_field_errors[] = array('custom_field_too_long', array($row['field_name'], $row['field_length']));
496
497
			// Any masks to apply?
498
			if ($row['field_type'] == 'text' && !empty($row['mask']) && $row['mask'] != 'none')
499
			{
500
				if ($row['mask'] == 'email' && (!filter_var($value, FILTER_VALIDATE_EMAIL) || strlen($value) > 255))
501
					$custom_field_errors[] = array('custom_field_invalid_email', array($row['field_name']));
502
				elseif ($row['mask'] == 'number' && preg_match('~[^\d]~', $value))
503
					$custom_field_errors[] = array('custom_field_not_number', array($row['field_name']));
504
				elseif (substr($row['mask'], 0, 5) == 'regex' && trim($value) != '' && preg_match(substr($row['mask'], 5), $value) === 0)
505
					$custom_field_errors[] = array('custom_field_inproper_format', array($row['field_name']));
506
			}
507
		}
508
509
		// Is this required but not there?
510
		if (trim($value) == '' && $row['show_reg'] > 1)
511
			$custom_field_errors[] = array('custom_field_empty', array($row['field_name']));
512
	}
513
	$smcFunc['db_free_result']($request);
514
515
	// Process any errors.
516
	if (!empty($custom_field_errors))
517
	{
518
		loadLanguage('Errors');
519
		foreach ($custom_field_errors as $error)
520
			$reg_errors[] = vsprintf($txt['error_' . $error[0]], (array) $error[1]);
521
	}
522
523
	// Lets check for other errors before trying to register the member.
524
	if (!empty($reg_errors))
525
	{
526
		$_REQUEST['step'] = 2;
527
		$_SESSION['register']['limit'] = 5; // If they've filled in some details, they won't need the full 10 seconds of the limit.
528
		return Register($reg_errors);
529
	}
530
531
	$memberID = registerMember($regOptions, true);
532
533
	// What there actually an error of some kind dear boy?
534
	if (is_array($memberID))
535
	{
536
		$reg_errors = array_merge($reg_errors, $memberID);
537
		$_REQUEST['step'] = 2;
538
		return Register($reg_errors);
539
	}
540
541
	// Do our spam protection now.
542
	spamProtection('register');
543
544
	// Do they want to receive announcements?
545
	require_once($sourcedir . '/Subs-Notify.php');
546
	$prefs = getNotifyPrefs($memberID, 'announcements', true);
547
	$var = !empty($_POST['notify_announcements']);
548
	$pref = !empty($prefs[$memberID]['announcements']);
549
550
	// Don't update if the default is the same.
551
	if ($var != $pref)
552
		setNotifyPrefs($memberID, array('announcements' => (int) !empty($_POST['notify_announcements'])));
553
554
	// We'll do custom fields after as then we get to use the helper function!
555
	if (!empty($_POST['customfield']))
556
	{
557
		require_once($sourcedir . '/Profile.php');
558
		require_once($sourcedir . '/Profile-Modify.php');
559
		makeCustomFieldChanges($memberID, 'register');
560
	}
561
562
	// If COPPA has been selected then things get complicated, setup the template.
563
	if (!empty($modSettings['coppaAge']) && empty($_SESSION['skip_coppa']))
564
		redirectexit('action=coppa;member=' . $memberID);
565
	// Basic template variable setup.
566
	elseif (!empty($modSettings['registration_method']))
567
	{
568
		loadTemplate('Register');
569
570
		$context += array(
571
			'page_title' => $txt['register'],
572
			'title' => $txt['registration_successful'],
573
			'sub_template' => 'after',
574
			'description' => $modSettings['registration_method'] == 2 ? $txt['approval_after_registration'] : $txt['activate_after_registration']
575
		);
576
	}
577
	else
578
	{
579
		call_integration_hook('integrate_activate', array($regOptions['username']));
580
581
		setLoginCookie(60 * $modSettings['cookieTime'], $memberID, hash_salt($regOptions['register_vars']['passwd'], $regOptions['register_vars']['password_salt']));
582
583
		redirectexit('action=login2;sa=check;member=' . $memberID, $context['server']['needs_login_fix']);
584
	}
585
}
586
587
/**
588
 * Activate an users account.
589
 *
590
 * Checks for mail changes, resends password if needed.
591
 */
592
function Activate()
593
{
594
	global $context, $txt, $modSettings, $scripturl, $sourcedir, $smcFunc, $language, $user_info;
595
596
	// Logged in users should not bother to activate their accounts
597
	if (!empty($user_info['id']))
598
		redirectexit();
599
600
	loadLanguage('Login');
601
	loadTemplate('Login');
602
603
	if (empty($_REQUEST['u']) && empty($_POST['user']))
604
	{
605
		if (empty($modSettings['registration_method']) || $modSettings['registration_method'] == '3')
606
			fatal_lang_error('no_access', false);
607
608
		$context['member_id'] = 0;
609
		$context['sub_template'] = 'resend';
610
		$context['page_title'] = $txt['invalid_activation_resend'];
611
		$context['can_activate'] = empty($modSettings['registration_method']) || $modSettings['registration_method'] == '1';
612
		$context['default_username'] = isset($_GET['user']) ? $_GET['user'] : '';
613
614
		return;
615
	}
616
617
	// Get the code from the database...
618
	$request = $smcFunc['db_query']('', '
619
		SELECT id_member, validation_code, member_name, real_name, email_address, is_activated, passwd, lngfile
620
		FROM {db_prefix}members' . (empty($_REQUEST['u']) ? '
621
		WHERE member_name = {string:email_address} OR email_address = {string:email_address}' : '
622
		WHERE id_member = {int:id_member}') . '
623
		LIMIT 1',
624
		array(
625
			'id_member' => isset($_REQUEST['u']) ? (int) $_REQUEST['u'] : 0,
626
			'email_address' => isset($_POST['user']) ? $_POST['user'] : '',
627
		)
628
	);
629
630
	// Does this user exist at all?
631
	if ($smcFunc['db_num_rows']($request) == 0)
632
	{
633
		$context['sub_template'] = 'retry_activate';
634
		$context['page_title'] = $txt['invalid_userid'];
635
		$context['member_id'] = 0;
636
637
		return;
638
	}
639
640
	$row = $smcFunc['db_fetch_assoc']($request);
641
	$smcFunc['db_free_result']($request);
642
643
	// Change their email address? (they probably tried a fake one first :P.)
644
	if (!empty($_POST['new_email']) && !empty($_REQUEST['passwd']) && hash_verify_password($row['member_name'], $_REQUEST['passwd'], $row['passwd']) && ($row['is_activated'] == 0 || $row['is_activated'] == 2))
645
	{
646
		if (empty($modSettings['registration_method']) || $modSettings['registration_method'] == 3)
647
			fatal_lang_error('no_access', false);
648
649
		if (!filter_var($_POST['new_email'], FILTER_VALIDATE_EMAIL))
650
			fatal_error(sprintf($txt['valid_email_needed'], $smcFunc['htmlspecialchars']($_POST['new_email'])), false);
651
652
		// Make sure their email isn't banned.
653
		isBannedEmail($_POST['new_email'], 'cannot_register', $txt['ban_register_prohibited']);
654
655
		// Ummm... don't even dare try to take someone else's email!!
656
		$request = $smcFunc['db_query']('', '
657
			SELECT id_member
658
			FROM {db_prefix}members
659
			WHERE email_address = {string:email_address}
660
			LIMIT 1',
661
			array(
662
				'email_address' => $_POST['new_email'],
663
			)
664
		);
665
666
		if ($smcFunc['db_num_rows']($request) != 0)
667
			fatal_lang_error('email_in_use', false, array($smcFunc['htmlspecialchars']($_POST['new_email'])));
668
		$smcFunc['db_free_result']($request);
669
670
		updateMemberData($row['id_member'], array('email_address' => $_POST['new_email']));
671
		$row['email_address'] = $_POST['new_email'];
672
673
		$email_change = true;
674
	}
675
676
	// Resend the password, but only if the account wasn't activated yet.
677
	if (!empty($_REQUEST['sa']) && $_REQUEST['sa'] == 'resend' && ($row['is_activated'] == 0 || $row['is_activated'] == 2) && (!isset($_REQUEST['code']) || $_REQUEST['code'] == ''))
678
	{
679
		require_once($sourcedir . '/Subs-Post.php');
680
681
		$replacements = array(
682
			'REALNAME' => $row['real_name'],
683
			'USERNAME' => $row['member_name'],
684
			'ACTIVATIONLINK' => $scripturl . '?action=activate;u=' . $row['id_member'] . ';code=' . $row['validation_code'],
685
			'ACTIVATIONLINKWITHOUTCODE' => $scripturl . '?action=activate;u=' . $row['id_member'],
686
			'ACTIVATIONCODE' => $row['validation_code'],
687
			'FORGOTPASSWORDLINK' => $scripturl . '?action=reminder',
688
		);
689
690
		$emaildata = loadEmailTemplate('resend_activate_message', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']);
691
692
		sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, 'resendact', $emaildata['is_html'], 0);
693
694
		$context['page_title'] = $txt['invalid_activation_resend'];
695
696
		// This will ensure we don't actually get an error message if it works!
697
		$context['error_title'] = $txt['invalid_activation_resend'];
698
699
		fatal_lang_error(!empty($email_change) ? 'change_email_success' : 'resend_email_success', false, array(), false);
700
	}
701
702
	// Quit if this code is not right.
703
	if (empty($_REQUEST['code']) || $row['validation_code'] != $_REQUEST['code'])
704
	{
705
		if (!empty($row['is_activated']))
706
			fatal_lang_error('already_activated', false);
707
		elseif ($row['validation_code'] == '')
708
		{
709
			loadLanguage('Profile');
710
			fatal_error(sprintf($txt['registration_not_approved'], $scripturl . '?action=activate;user=' . $row['member_name']), false);
711
		}
712
713
		$context['sub_template'] = 'retry_activate';
714
		$context['page_title'] = $txt['invalid_activation_code'];
715
		$context['member_id'] = $row['id_member'];
716
717
		return;
718
	}
719
720
	// Let the integration know that they've been activated!
721
	call_integration_hook('integrate_activate', array($row['member_name']));
722
723
	// Validation complete - update the database!
724
	updateMemberData($row['id_member'], array('is_activated' => 1, 'validation_code' => ''));
725
726
	// Also do a proper member stat re-evaluation.
727
	updateStats('member', false);
728
729
	// Notify the admin about new activations, but not re-activations.
730
	if (empty($row['is_activated']))
731
	{
732
		require_once($sourcedir . '/Subs-Post.php');
733
734
		adminNotify('activation', $row['id_member'], $row['member_name']);
735
	}
736
737
	$context += array(
738
		'page_title' => $txt['registration_successful'],
739
		'sub_template' => 'login',
740
		'default_username' => $row['member_name'],
741
		'default_password' => '',
742
		'never_expire' => false,
743
		'description' => $txt['activate_success']
744
	);
745
}
746
747
/**
748
 * This function will display the contact information for the forum, as well a form to fill in.
749
 */
750
function CoppaForm()
751
{
752
	global $context, $modSettings, $txt, $smcFunc;
753
754
	loadLanguage('Login');
755
	loadTemplate('Register');
756
757
	// No User ID??
758
	if (!isset($_GET['member']))
759
		fatal_lang_error('no_access', false);
760
761
	// Get the user details...
762
	$request = $smcFunc['db_query']('', '
763
		SELECT member_name
764
		FROM {db_prefix}members
765
		WHERE id_member = {int:id_member}
766
			AND is_activated = {int:is_coppa}',
767
		array(
768
			'id_member' => (int) $_GET['member'],
769
			'is_coppa' => 5,
770
		)
771
	);
772
	if ($smcFunc['db_num_rows']($request) == 0)
773
		fatal_lang_error('no_access', false);
774
	list ($username) = $smcFunc['db_fetch_row']($request);
775
	$smcFunc['db_free_result']($request);
776
777
	if (isset($_GET['form']))
778
	{
779
		// Some simple contact stuff for the forum.
780
		$context['forum_contacts'] = (!empty($modSettings['coppaPost']) ? $modSettings['coppaPost'] . '<br><br>' : '') . (!empty($modSettings['coppaFax']) ? $modSettings['coppaFax'] . '<br>' : '');
781
		$context['forum_contacts'] = !empty($context['forum_contacts']) ? $context['forum_name_html_safe'] . '<br>' . $context['forum_contacts'] : '';
782
783
		// Showing template?
784
		if (!isset($_GET['dl']))
785
		{
786
			// Shortcut for producing underlines.
787
			$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>';
788
			$context['template_layers'] = array();
789
			$context['sub_template'] = 'coppa_form';
790
			$context['page_title'] = sprintf($txt['coppa_form_title'], $context['forum_name_html_safe']);
791
			$context['coppa_body'] = str_replace(array('{PARENT_NAME}', '{CHILD_NAME}', '{USER_NAME}'), array($context['ul'], $context['ul'], $username), sprintf($txt['coppa_form_body'], $context['forum_name_html_safe']));
792
		}
793
		// Downloading.
794
		else
795
		{
796
			// The data.
797
			$ul = '                ';
798
			$crlf = "\r\n";
799
			$data = $context['forum_contacts'] . $crlf . $txt['coppa_form_address'] . ':' . $crlf . $txt['coppa_form_date'] . ':' . $crlf . $crlf . $crlf . sprintf($txt['coppa_form_body'], $context['forum_name_html_safe']);
800
			$data = str_replace(array('{PARENT_NAME}', '{CHILD_NAME}', '{USER_NAME}', '<br>', '<br>'), array($ul, $ul, $username, $crlf, $crlf), $data);
801
802
			// Send the headers.
803
			header('connection: close');
804
			header('content-disposition: attachment; filename="approval.txt"');
805
			header('content-type: ' . (isBrowser('ie') || isBrowser('opera') ? 'application/octetstream' : 'application/octet-stream'));
806
			header('content-length: ' . count($data));
807
808
			echo $data;
809
			obExit(false);
810
		}
811
	}
812
	else
813
	{
814
		$context += array(
815
			'page_title' => $txt['coppa_title'],
816
			'sub_template' => 'coppa',
817
		);
818
819
		$context['coppa'] = array(
820
			'body' => str_replace('{MINIMUM_AGE}', $modSettings['coppaAge'], sprintf($txt['coppa_after_registration'], $context['forum_name_html_safe'])),
821
			'many_options' => !empty($modSettings['coppaPost']) && !empty($modSettings['coppaFax']),
822
			'post' => empty($modSettings['coppaPost']) ? '' : $modSettings['coppaPost'],
823
			'fax' => empty($modSettings['coppaFax']) ? '' : $modSettings['coppaFax'],
824
			'phone' => empty($modSettings['coppaPhone']) ? '' : str_replace('{PHONE_NUMBER}', $modSettings['coppaPhone'], $txt['coppa_send_by_phone']),
825
			'id' => $_GET['member'],
826
		);
827
	}
828
}
829
830
/**
831
 * Show the verification code or let it be heard.
832
 */
833
function VerificationCode()
834
{
835
	global $sourcedir, $context, $scripturl;
836
837
	$verification_id = isset($_GET['vid']) ? $_GET['vid'] : '';
838
	$code = $verification_id && isset($_SESSION[$verification_id . '_vv'], $_SESSION[$verification_id . '_vv']['code']) ? $_SESSION[$verification_id . '_vv']['code'] : (isset($_SESSION['visual_verification_code']) ? $_SESSION['visual_verification_code'] : '');
839
840
	// Somehow no code was generated or the session was lost.
841
	if (empty($code))
842
	{
843
		header('content-type: image/gif');
844
		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");
845
	}
846
847
	// Show a window that will play the verification code.
848
	elseif (isset($_REQUEST['sound']))
849
	{
850
		loadLanguage('Login');
851
		loadTemplate('Register');
852
853
		$context['verification_sound_href'] = $scripturl . '?action=verificationcode;rand=' . md5(mt_rand()) . ($verification_id ? ';vid=' . $verification_id : '') . ';format=.wav';
854
		$context['sub_template'] = 'verification_sound';
855
		$context['template_layers'] = array();
856
857
		obExit();
858
	}
859
860
	// If we have GD, try the nice code.
861
	elseif (empty($_REQUEST['format']))
862
	{
863
		require_once($sourcedir . '/Subs-Graphics.php');
864
865
		if (in_array('gd', get_loaded_extensions()) && !showCodeImage($code))
866
			send_http_status(400);
867
868
		// Otherwise just show a pre-defined letter.
869
		elseif (isset($_REQUEST['letter']))
870
		{
871
			$_REQUEST['letter'] = (int) $_REQUEST['letter'];
872
			if ($_REQUEST['letter'] > 0 && $_REQUEST['letter'] <= strlen($code) && !showLetterImage(strtolower($code[$_REQUEST['letter'] - 1])))
873
			{
874
				header('content-type: image/gif');
875
				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");
876
			}
877
		}
878
		// You must be up to no good.
879
		else
880
		{
881
			header('content-type: image/gif');
882
			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");
883
		}
884
	}
885
886
	elseif ($_REQUEST['format'] === '.wav')
887
	{
888
		require_once($sourcedir . '/Subs-Sound.php');
889
890
		if (!createWaveFile($code))
891
			send_http_status(400);
892
	}
893
894
	// We all die one day...
895
	die();
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
896
}
897
898
/**
899
 * See if a username already exists.
900
 */
901
function RegisterCheckUsername()
902
{
903
	global $sourcedir, $context;
904
905
	// This is XML!
906
	loadTemplate('Xml');
907
	$context['sub_template'] = 'check_username';
908
	$context['checked_username'] = isset($_GET['username']) ? un_htmlspecialchars($_GET['username']) : '';
909
	$context['valid_username'] = true;
910
911
	// Clean it up like mother would.
912
	$context['checked_username'] = trim(normalize_spaces(sanitize_chars($context['checked_username'], 1, ' '), true, true, array('no_breaks' => true, 'replace_tabs' => true, 'collapse_hspace' => true)));
913
914
	require_once($sourcedir . '/Subs-Auth.php');
915
	$errors = validateUsername(0, $context['checked_username'], true);
916
917
	$context['valid_username'] = empty($errors);
918
}
919
920
/**
921
 * It doesn't actually send anything, this action just shows a message for a guest.
922
 */
923
function SendActivation()
924
{
925
	global $context, $txt;
926
927
	$context['user']['is_logged'] = false;
928
	$context['user']['is_guest'] = true;
929
930
	// Send them to the done-with-registration-login screen.
931
	loadTemplate('Register');
932
933
	$context['page_title'] = $txt['profile'];
934
	$context['sub_template'] = 'after';
935
	$context['title'] = $txt['activate_changed_email_title'];
936
	$context['description'] = $txt['activate_changed_email_desc'];
937
938
	// Aaand we're gone!
939
	obExit();
940
}
941
942
?>