Failed Conditions
Branch release-2.1 (4e22cf)
by Rick
06:39
created

Sources/Reminder.php (4 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Handle sending out reminders, and checking the secret answer and question.  It uses just a few functions to do this, which are:
5
 * Simple Machines Forum (SMF)
6
 *
7
 * @package SMF
8
 * @author Simple Machines http://www.simplemachines.org
9
 * @copyright 2017 Simple Machines and individual contributors
10
 * @license http://www.simplemachines.org/about/smf/license.php BSD
11
 *
12
 * @version 2.1 Beta 4
13
 */
14
15
if (!defined('SMF'))
16
	die('No direct access...');
17
18
/**
19
 * This is the controlling delegator
20
 * @uses Profile language files and Reminder template
21
 */
22
function RemindMe()
23
{
24
	global $txt, $context;
25
26
	loadLanguage('Profile');
27
	loadTemplate('Reminder');
28
29
	$context['page_title'] = $txt['authentication_reminder'];
30
	$context['robot_no_index'] = true;
31
32
	// Delegation can be useful sometimes.
33
	$subActions = array(
34
		'picktype' => 'RemindPick',
35
		'secret2' => 'SecretAnswer2',
36
		'setpassword' =>'setPassword',
37
		'setpassword2' =>'setPassword2'
38
	);
39
40
	// Any subaction?  If none, fall through to the main template, which will ask for one.
41 View Code Duplication
	if (isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]))
42
		call_helper($subActions[$_REQUEST['sa']]);
43
44
	// Creating a one time token.
45
	else
46
		createToken('remind');
47
}
48
49
/**
50
 * Allows the user to pick how they wish to be reminded
51
 */
52
function RemindPick()
53
{
54
	global $context, $txt, $scripturl, $sourcedir, $user_info, $webmaster_email, $smcFunc, $language, $modSettings;
55
56
	checkSession();
57
	validateToken('remind');
58
	createToken('remind');
59
60
	// Coming with a known ID?
61
	if (!empty($_REQUEST['uid']))
62
	{
63
		$where = 'id_member = {int:id_member}';
64
		$where_params['id_member'] = (int) $_REQUEST['uid'];
65
	}
66
	elseif (isset($_POST['user']) && $_POST['user'] != '')
67
	{
68
		$where = 'member_name = {string:member_name}';
69
		$where_params['member_name'] = $_POST['user'];
70
		$where_params['email_address'] = $_POST['user'];
71
	}
72
73
	// You must enter a username/email address.
74
	if (empty($where))
75
		fatal_lang_error('username_no_exist', false);
76
77
	// Make sure we are not being slammed
78
	// Don't call this if you're coming from the "Choose a reminder type" page - otherwise you'll likely get an error
79
	if (!isset($_POST['reminder_type']) || !in_array($_POST['reminder_type'], array('email', 'secret')))
80
	{
81
		spamProtection('remind');
82
	}
83
84
	// Find the user!
85
	$request = $smcFunc['db_query']('', '
86
		SELECT id_member, real_name, member_name, email_address, is_activated, validation_code, lngfile, secret_question
87
		FROM {db_prefix}members
88
		WHERE ' . $where . '
0 ignored issues
show
The variable $where does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
89
		LIMIT 1',
90
		$where_params
0 ignored issues
show
The variable $where_params does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
91
	);
92
	// Maybe email?
93
	if ($smcFunc['db_num_rows']($request) == 0 && empty($_REQUEST['uid']))
94
	{
95
		$smcFunc['db_free_result']($request);
96
97
		$request = $smcFunc['db_query']('', '
98
			SELECT id_member, real_name, member_name, email_address, is_activated, validation_code, lngfile, secret_question
99
			FROM {db_prefix}members
100
			WHERE email_address = {string:email_address}
101
			LIMIT 1',
102
			$where_params
103
		);
104
		if ($smcFunc['db_num_rows']($request) == 0)
105
			fatal_lang_error('no_user_with_email', false);
106
	}
107
108
	$row = $smcFunc['db_fetch_assoc']($request);
109
	$smcFunc['db_free_result']($request);
110
111
	// If the user isn't activated/approved, give them some feedback on what to do next.
112
	if ($row['is_activated'] != 1)
113
	{
114
		// Awaiting approval...
115
		if (trim($row['validation_code']) == '')
116
			fatal_error(sprintf($txt['registration_not_approved'], $scripturl . '?action=activate;user=' . $_POST['user']), false);
117
		else
118
			fatal_error(sprintf($txt['registration_not_activated'], $scripturl . '?action=activate;user=' . $_POST['user']), false);
119
	}
120
121
	// You can't get emailed if you have no email address.
122
	$row['email_address'] = trim($row['email_address']);
123
	if ($row['email_address'] == '')
124
		fatal_error($txt['no_reminder_email'] . '<br>' . $txt['send_email'] . ' <a href="mailto:' . $webmaster_email . '">webmaster</a> ' . $txt['to_ask_password'] . '.');
125
126
	// If they have no secret question then they can only get emailed the item, or they are requesting the email, send them an email.
127
	if (empty($row['secret_question']) || (isset($_POST['reminder_type']) && $_POST['reminder_type'] == 'email'))
128
	{
129
		// Randomly generate a new password, with only alpha numeric characters that is a max length of 10 chars.
130
		require_once($sourcedir . '/Subs-Members.php');
131
		$password = generateValidationCode();
132
133
		require_once($sourcedir . '/Subs-Post.php');
134
		$replacements = array(
135
			'REALNAME' => $row['real_name'],
136
			'REMINDLINK' => $scripturl . '?action=reminder;sa=setpassword;u=' . $row['id_member'] . ';code=' . $password,
137
			'IP' => $user_info['ip'],
138
			'MEMBERNAME' => $row['member_name'],
139
		);
140
141
		$emaildata = loadEmailTemplate('forgot_password', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']);
142
		$context['description'] = $txt['reminder_sent'];
143
144
		sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, 'reminder', $emaildata['is_html'], 1);
145
146
		// Set the password in the database.
147
		updateMemberData($row['id_member'], array('validation_code' => substr(md5($password), 0, 10)));
148
149
		// Set up the template.
150
		$context['sub_template'] = 'sent';
151
152
		// Don't really.
153
		return;
154
	}
155
	// Otherwise are ready to answer the question?
156
	elseif (isset($_POST['reminder_type']) && $_POST['reminder_type'] == 'secret')
157
	{
158
		return SecretAnswerInput();
159
	}
160
161
	// No we're here setup the context for template number 2!
162
	$context['sub_template'] = 'reminder_pick';
163
	$context['current_member'] = array(
164
		'id' => $row['id_member'],
165
		'name' => $row['member_name'],
166
	);
167
}
168
169
/**
170
 * Allows the user to set their new password
171
 */
172
function setPassword()
173
{
174
	global $txt, $context;
175
176
	loadLanguage('Login');
177
178
	// You need a code!
179
	if (!isset($_REQUEST['code']))
180
		fatal_lang_error('no_access', false);
181
182
	// Fill the context array.
183
	$context += array(
184
		'page_title' => $txt['reminder_set_password'],
185
		'sub_template' => 'set_password',
186
		'code' => $_REQUEST['code'],
187
		'memID' => (int) $_REQUEST['u']
188
	);
189
190
	loadJavaScriptFile('register.js', array('defer' => false), 'smf_register');
191
192
	// Tokens!
193
	createToken('remind-sp');
194
}
195
196
/**
197
 * Actually sets the new password
198
 */
199
function setPassword2()
200
{
201
	global $context, $txt, $smcFunc, $sourcedir;
202
203
	checkSession();
204
	validateToken('remind-sp');
205
206
	if (empty($_POST['u']) || !isset($_POST['passwrd1']) || !isset($_POST['passwrd2']))
207
		fatal_lang_error('no_access', false);
208
209
	$_POST['u'] = (int) $_POST['u'];
210
211
	if ($_POST['passwrd1'] != $_POST['passwrd2'])
212
		fatal_lang_error('passwords_dont_match', false);
213
214
	if ($_POST['passwrd1'] == '')
215
		fatal_lang_error('no_password', false);
216
217
	loadLanguage('Login');
218
219
	// Get the code as it should be from the database.
220
	$request = $smcFunc['db_query']('', '
221
		SELECT validation_code, member_name, email_address, passwd_flood
222
		FROM {db_prefix}members
223
		WHERE id_member = {int:id_member}
224
			AND is_activated = {int:is_activated}
225
			AND validation_code != {string:blank_string}
226
		LIMIT 1',
227
		array(
228
			'id_member' => $_POST['u'],
229
			'is_activated' => 1,
230
			'blank_string' => '',
231
		)
232
	);
233
234
	// Does this user exist at all?
235
	if ($smcFunc['db_num_rows']($request) == 0)
236
		fatal_lang_error('invalid_userid', false);
237
238
	list ($realCode, $username, $email, $flood_value) = $smcFunc['db_fetch_row']($request);
239
	$smcFunc['db_free_result']($request);
240
241
	// Is the password actually valid?
242
	require_once($sourcedir . '/Subs-Auth.php');
243
	$passwordError = validatePassword($_POST['passwrd1'], $username, array($email));
244
245
	// What - it's not?
246
	if ($passwordError != null)
0 ignored issues
show
It seems like you are loosely comparing $passwordError of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
247
		fatal_lang_error('profile_error_password_' . $passwordError, false);
248
249
	require_once($sourcedir . '/LogInOut.php');
250
251
	// Quit if this code is not right.
252
	if (empty($_POST['code']) || substr($realCode, 0, 10) !== substr(md5($_POST['code']), 0, 10))
253
	{
254
		// Stop brute force attacks like this.
255
		validatePasswordFlood($_POST['u'], $flood_value, false);
256
257
		fatal_error($txt['invalid_activation_code'], false);
258
	}
259
260
	// Just in case, flood control.
261
	validatePasswordFlood($_POST['u'], $flood_value, true);
262
263
	// User validated.  Update the database!
264
	updateMemberData($_POST['u'], array('validation_code' => '', 'passwd' => hash_password($username, $_POST['passwrd1'])));
265
266
	call_integration_hook('integrate_reset_pass', array($username, $username, $_POST['passwrd1']));
267
268
	loadTemplate('Login');
269
	$context += array(
270
		'page_title' => $txt['reminder_password_set'],
271
		'sub_template' => 'login',
272
		'default_username' => $username,
273
		'default_password' => $_POST['passwrd1'],
274
		'never_expire' => false,
275
		'description' => $txt['reminder_password_set']
276
	);
277
278
	createToken('login');
279
}
280
281
/**
282
 * Allows the user to enter their secret answer
283
 */
284
function SecretAnswerInput()
285
{
286
	global $context, $smcFunc;
287
288
	checkSession();
289
290
	// Strings for the register auto javascript clever stuffy wuffy.
291
	loadLanguage('Login');
292
293
	// Check they entered something...
294
	if (empty($_REQUEST['uid']))
295
		fatal_lang_error('username_no_exist', false);
296
297
	// Get the stuff....
298
	$request = $smcFunc['db_query']('', '
299
		SELECT id_member, real_name, member_name, secret_question
300
		FROM {db_prefix}members
301
		WHERE id_member = {int:id_member}
302
		LIMIT 1',
303
		array(
304
			'id_member' => (int) $_REQUEST['uid'],
305
		)
306
	);
307
	if ($smcFunc['db_num_rows']($request) == 0)
308
		fatal_lang_error('username_no_exist', false);
309
310
	$row = $smcFunc['db_fetch_assoc']($request);
311
	$smcFunc['db_free_result']($request);
312
313
	// If there is NO secret question - then throw an error.
314
	if (trim($row['secret_question']) == '')
315
		fatal_lang_error('registration_no_secret_question', false);
316
317
	// Ask for the answer...
318
	$context['remind_user'] = $row['id_member'];
319
	$context['remind_type'] = '';
320
	$context['secret_question'] = $row['secret_question'];
321
322
	$context['sub_template'] = 'ask';
323
	createToken('remind-sai');
324
	loadJavaScriptFile('register.js', array('defer' => false), 'smf_register');
325
}
326
327
/**
328
 * Validates the secret answer input by the user
329
 */
330
function SecretAnswer2()
331
{
332
	global $txt, $context, $smcFunc, $sourcedir;
333
334
	checkSession();
335
	validateToken('remind-sai');
336
337
	// Hacker?  How did you get this far without an email or username?
338
	if (empty($_REQUEST['uid']))
339
		fatal_lang_error('username_no_exist', false);
340
341
	loadLanguage('Login');
342
343
	// Get the information from the database.
344
	$request = $smcFunc['db_query']('', '
345
		SELECT id_member, real_name, member_name, secret_answer, secret_question, email_address
346
		FROM {db_prefix}members
347
		WHERE id_member = {int:id_member}
348
		LIMIT 1',
349
		array(
350
			'id_member' => $_REQUEST['uid'],
351
		)
352
	);
353
	if ($smcFunc['db_num_rows']($request) == 0)
354
		fatal_lang_error('username_no_exist', false);
355
356
	$row = $smcFunc['db_fetch_assoc']($request);
357
	$smcFunc['db_free_result']($request);
358
359
	// Check if the secret answer is correct.
360
	if ($row['secret_question'] == '' || $row['secret_answer'] == '' || md5($_POST['secret_answer']) != $row['secret_answer'])
361
	{
362
		log_error(sprintf($txt['reminder_error'], $row['member_name']), 'user');
363
		fatal_lang_error('incorrect_answer', false);
364
	}
365
366
	// You can't use a blank one!
367
	if (strlen(trim($_POST['passwrd1'])) === 0)
368
		fatal_lang_error('no_password', false);
369
370
	// They have to be the same too.
371
	if ($_POST['passwrd1'] != $_POST['passwrd2'])
372
		fatal_lang_error('passwords_dont_match', false);
373
374
	// Make sure they have a strong enough password.
375
	require_once($sourcedir . '/Subs-Auth.php');
376
	$passwordError = validatePassword($_POST['passwrd1'], $row['member_name'], array($row['email_address']));
377
378
	// Invalid?
379
	if ($passwordError != null)
0 ignored issues
show
It seems like you are loosely comparing $passwordError of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
380
		fatal_lang_error('profile_error_password_' . $passwordError, false);
381
382
	// Alright, so long as 'yer sure.
383
	updateMemberData($row['id_member'], array('passwd' => hash_password($row['member_name'], $_POST['passwrd1'])));
384
385
	call_integration_hook('integrate_reset_pass', array($row['member_name'], $row['member_name'], $_POST['passwrd1']));
386
387
	// Tell them it went fine.
388
	loadTemplate('Login');
389
	$context += array(
390
		'page_title' => $txt['reminder_password_set'],
391
		'sub_template' => 'login',
392
		'default_username' => $row['member_name'],
393
		'default_password' => $_POST['passwrd1'],
394
		'never_expire' => false,
395
		'description' => $txt['reminder_password_set']
396
	);
397
398
	createToken('login');
399
}
400
401
?>