Reminder::action_picktype()   C
last analyzed

Complexity

Conditions 15
Paths 39

Size

Total Lines 100
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 240

Importance

Changes 0
Metric Value
cc 15
eloc 46
nc 39
nop 0
dl 0
loc 100
ccs 0
cts 44
cp 0
crap 240
rs 5.9166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Handles sending out of password reminders, as well as the answer / question
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 dev
14
 *
15
 */
16
17
namespace ElkArte\Controller;
18
19
use ElkArte\AbstractController;
20
use ElkArte\Errors\Errors;
0 ignored issues
show
Bug introduced by
The type ElkArte\Errors\Errors was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use ElkArte\Exceptions\Exception;
22
use ElkArte\Helper\Util;
23
use ElkArte\Languages\Txt;
24
use ElkArte\Profile\Profile;
25
26
/**
27
 * Handles sending out reminders, and checking the secret answer and question.
28
 */
29
class Reminder extends AbstractController
30
{
31
	/**
32
	 * This is the pre-dispatch function
33
	 *
34
	 * @uses Profile language files and Reminder template
35
	 */
36
	public function pre_dispatch()
37
	{
38
		global $txt, $context;
39
40
		Txt::load('Profile');
41
		theme()->getTemplates()->load('Reminder');
42
43
		$context['page_title'] = $txt['authentication_reminder'];
44
		$context['robot_no_index'] = true;
45
	}
46
47
	/**
48
	 * Default action for reminder.
49
	 *
50
	 * @uses template_reminder() sub template
51
	 */
52
	public function action_index()
53
	{
54
		global $context;
55
56
		$context['sub_template'] = 'reminder';
57
58
		// Nothing to do, the template will ask for an action to pick
59
		createToken('remind');
60
	}
61
62
	/**
63
	 * Pick a reminder type.
64
	 *
65
	 * Accessed by sa=picktype
66
	 */
67
	public function action_picktype()
68
	{
69
		global $context, $txt, $webmaster_email, $language, $modSettings;
70
71
		// Security
72
		checkSession();
73
		validateToken('remind');
74
		require_once(SUBSDIR . '/Auth.subs.php');
75
76
		// No where params just yet
77
		$where_params = array();
78
		$where = '';
79
80
		// Coming with a known ID?
81
		if (!empty($this->_req->post->uid))
82
		{
83
			$where = 'id_member = {int:id_member}';
84
			$where_params['id_member'] = (int) $this->_req->post->uid;
85
		}
86
		elseif ($this->_req->getPost('user') !== '')
87
		{
88
			$where = 'member_name = {string:member_name}';
89
			$where_params['member_name'] = $this->_req->getPost('user', 'trim|\\ElkArte\\Helper\\Util::htmlspecialchars[ENT_QUOTES]');
90
			$where_params['email_address'] = $this->_req->getPost('user', 'trim|\\ElkArte\\Helper\\Util::htmlspecialchars[ENT_QUOTES]');
91
		}
92
93
		// You must enter a username/email address.
94
		if (empty($where))
95
		{
96
			throw new Exception('username_no_exist', false);
97
		}
98
99
		// Make sure we are not being slammed
100
		// Don't call this if you're coming from the "Choose a reminder type" page - otherwise you'll likely get an error
101
		if (!isset($this->_req->post->reminder_type) || !in_array($this->_req->post->reminder_type, array('email', 'secret')))
102
		{
103
			spamProtection('remind');
104
		}
105
106
		// Find this member
107
		$member = findUser($where, $where_params);
108
109
		$context['account_type'] = 'password';
110
111
		// If the user isn't activated/approved, give them some feedback on what to do next.
112
		if ($member['is_activated'] != 1)
113
		{
114
			// Awaiting approval...
115
			if (trim($member['validation_code']) === '')
116
			{
117
				throw new Exception($txt['registration_not_approved'] . ' <a class="linkbutton" href="' . getUrl('action', ['action' => 'register', 'sa' => 'activate', 'user' => $this->_req->post->user]) . '">' . $txt['here'] . '</a>.', false);
118
			}
119
120
			throw new Exception($txt['registration_not_activated'] . ' <a class="linkbutton" href="' . getUrl('action', ['action' => 'register', 'sa' => 'activate', 'user' => $this->_req->post->user]) . '">' . $txt['here'] . '</a>.', false);
121
		}
122
123
		// You can't get emailed if you have no email address.
124
		$member['email_address'] = trim($member['email_address']);
125
		if ($member['email_address'] === '')
126
		{
127
			throw new Exception($txt['no_reminder_email'] . '<br />' . $txt['send_email'] . ' <a href="mailto:' . $webmaster_email . '">webmaster</a> ' . $txt['to_ask_password'] . '.');
128
		}
129
130
		// If they have no secret question then they can only get emailed the item, or they are requesting the email, send them an email.
131
		if (empty($member['secret_question']) || ($this->_req->getPost('reminder_type') === 'email'))
132
		{
133
			// Randomly generate a new password, with only alpha numeric characters that is a max length of 14 chars.
134
			$password = generateValidationCode(14);
135
			require_once(SUBSDIR . '/Mail.subs.php');
136
			$replacements = array(
137
				'REALNAME' => $member['real_name'],
138
				'REMINDLINK' => getUrl('action', ['action' => 'reminder', 'sa' => 'setpassword', 'u' => $member['id_member'], 'code' => $password]),
139
				'IP' => $this->user->ip,
140
				'MEMBERNAME' => $member['member_name'],
141
			);
142
143
			// Email them their new password
144
			$emaildata = loadEmailTemplate('forgot_' . $context['account_type'], $replacements, empty($member['lngfile']) || empty($modSettings['userLanguage']) ? $language : $member['lngfile']);
145
			sendmail($member['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 1);
146
147
			// Set up the template.
148
			$context['description'] = $txt['reminder_sent'];
149
			$context['sub_template'] = 'sent';
150
151
			// Don't really.
152
			return null;
153
		}
154
155
		// Otherwise are ready to answer the question?
156
		if (isset($this->_req->post->reminder_type) && $this->_req->post->reminder_type === 'secret')
157
		{
158
			return secretAnswerInput();
159
		}
160
161
		// No we're here setup the context for template number 2!
162
		createToken('remind');
163
		$context['sub_template'] = 'reminder_pick';
164
		$context['current_member'] = array(
165
			'id' => $member['id_member'],
166
			'name' => $member['member_name'],
167
		);
168
	}
169
170
	/**
171
	 * Set your new password after you asked for a reset link
172
	 *
173
	 * sa=setpassword
174
	 */
175
	public function action_setpassword()
176
	{
177
		global $txt, $context;
178
179
		Txt::load('Login');
180
181
		// You need a code!
182
		if (!isset($this->_req->query->code))
183
		{
184
			throw new Exception('no_access', false);
185
		}
186
187
		// Fill the context array.
188
		$context += array(
189
			'page_title' => $txt['reminder_set_password'],
190
			'sub_template' => 'set_password',
191
			'code' => Util::htmlspecialchars($this->_req->query->code),
192
			'memID' => (int) $this->_req->query->u
193
		);
194
195
		// Some extra js is needed
196
		loadJavascriptFile('register.js');
197
198
		// Tokens!
199
		createToken('remind-sp');
200
	}
201
202
	/**
203
	 * Handle the password change.
204
	 *
205
	 * sa=setpassword2
206
	 */
207
	public function action_setpassword2()
208
	{
209
		global $context, $txt;
210
211
		checkSession();
212
		validateToken('remind-sp');
213
214
		if (empty($this->_req->post->u) || !isset($this->_req->post->passwrd1, $this->_req->post->passwrd2))
215
		{
216
			throw new Exception('no_access', false);
217
		}
218
219
		if ($this->_req->post->passwrd1 !== $this->_req->post->passwrd2)
220
		{
221
			throw new Exception('passwords_dont_match', false);
222
		}
223
224
		if ($this->_req->post->passwrd1 === '')
225
		{
226
			throw new Exception('no_password', false);
227
		}
228
229
		$member_id = $this->_req->getPost('u', 'intval', -1);
230
		$code = $this->_req->getPost('code', 'trim', '');
231
232
		Txt::load('Login');
233
234
		// Get the code as it should be from the database.
235
		require_once(SUBSDIR . '/Members.subs.php');
236
		$member = getBasicMemberData($member_id, array('authentication' => true));
237
238
		// Does this user exist at all? Is he activated? Does he have a validation code?
239
		if (empty($member) || $member['is_activated'] != 1 || $member['validation_code'] === '')
240
		{
241
			throw new Exception('invalid_userid', false);
242
		}
243
244
		// Is the password actually valid to the forums rules?
245
		require_once(SUBSDIR . '/Auth.subs.php');
246
		$passwordError = validatePassword($this->_req->post->passwrd1, $member['member_name'], array($member['email_address']));
247
248
		// What - it's not?
249
		if ($passwordError !== null)
250
		{
251
			throw new Exception('profile_error_password_' . $passwordError, false);
252
		}
253
254
		// Quit if this code is not right.
255
		if (empty($code) || $member['validation_code'] !== substr(hash('sha256', $code), 0, 10))
256
		{
257
			// Stop brute force attacks like this.
258
			validatePasswordFlood($member_id, $member['passwd_flood'], false);
259
260
			throw new Exception($txt['invalid_activation_code'], false);
261
		}
262
263
		// Just in case, flood control.
264
		validatePasswordFlood($member_id, $member['passwd_flood'], true);
265
266
		// User validated.  Update the database!
267
		require_once(SUBSDIR . '/Auth.subs.php');
268
		$sha_passwd = $this->_req->post->passwrd1;
269
		require_once(SUBSDIR . '/Members.subs.php');
270
		if (isset($this->_req->post->otp))
271
		{
272
			updateMemberData($member_id, array('validation_code' => '', 'passwd' => validateLoginPassword($sha_passwd, '', $member['member_name'], true), 'enable_otp' => 0));
273
		}
274
		else
275
		{
276
			updateMemberData($member_id, array('validation_code' => '', 'passwd' => validateLoginPassword($sha_passwd, '', $member['member_name'], true)));
277
		}
278
279
		call_integration_hook('integrate_reset_pass', array($member['member_name'], $member['member_name'], $this->_req->post->passwrd1));
280
281
		theme()->getTemplates()->load('Login');
282
		$context += array(
283
			'page_title' => $txt['reminder_password_set'],
284
			'sub_template' => 'login',
285
			'default_username' => $member['member_name'],
286
			'default_password' => $this->_req->post->passwrd1,
287
			'never_expire' => false,
288
			'description' => $txt['reminder_password_set']
289
		);
290
		createToken('login');
291
	}
292
293
	/**
294
	 * Verify the answer to the secret question.
295
	 * Accessed with sa=secret2
296
	 */
297
	public function action_secret2()
298
	{
299
		global $txt, $context;
300
301
		checkSession();
302
		validateToken('remind-sai');
303
304
		// Hacker?  How did you get this far without an email or username?
305
		if (empty($this->_req->post->uid))
306
		{
307
			throw new Exception('username_no_exist', false);
308
		}
309
310
		Txt::load('Login');
311
312
		// Get the information from the database.
313
		require_once(SUBSDIR . '/Members.subs.php');
314
		$member = getBasicMemberData((int) $this->_req->post->uid, array('authentication' => true));
315
		if (empty($member))
316
		{
317
			throw new Exception('username_no_exist', false);
318
		}
319
320
		// Check if the secret answer is correct.
321
		if ($member['secret_question'] === '' || $member['secret_answer'] === '' || md5($this->_req->post->secret_answer) !== $member['secret_answer'])
322
		{
323
			Errors::instance()->log_error(sprintf($txt['reminder_error'], $member['member_name']), 'user');
324
			throw new Exception('incorrect_answer', false);
325
		}
326
327
		// You can't use a blank one!
328
		if (trim($this->_req->post->passwrd1) === '')
329
		{
330
			throw new Exception('no_password', false);
331
		}
332
333
		// They have to be the same too.
334
		if ($this->_req->post->passwrd1 !== $this->_req->post->passwrd2)
335
		{
336
			throw new Exception('passwords_dont_match', false);
337
		}
338
339
		// Make sure they have a strong enough password.
340
		require_once(SUBSDIR . '/Auth.subs.php');
341
		$passwordError = validatePassword($this->_req->post->passwrd1, $member['member_name'], array($member['email_address']));
342
343
		// Invalid?
344
		if ($passwordError !== null)
345
		{
346
			throw new Exception('profile_error_password_' . $passwordError, false);
347
		}
348
349
		// Alright, so long as 'yer sure.
350
		require_once(SUBSDIR . '/Auth.subs.php');
351
		$sha_passwd = $this->_req->post->passwrd1;
352
		require_once(SUBSDIR . '/Members.subs.php');
353
		updateMemberData($member['id_member'], array('passwd' => validateLoginPassword($sha_passwd, '', $member['member_name'], true)));
354
355
		call_integration_hook('integrate_reset_pass', array($member['member_name'], $member['member_name'], $this->_req->post->passwrd1));
356
357
		// Tell them it went fine.
358
		theme()->getTemplates()->load('Login');
359
		$context += array(
360
			'page_title' => $txt['reminder_password_set'],
361
			'sub_template' => 'login',
362
			'default_username' => $member['member_name'],
363
			'default_password' => $this->_req->post->passwrd1,
364
			'never_expire' => false,
365
			'description' => $txt['reminder_password_set']
366
		);
367
368
		createToken('login');
369
	}
370
}
371
372
/**
373
 * Get the secret answer.
374
 */
375
function secretAnswerInput()
376
{
377
	global $context;
378
379
	checkSession();
380
381
	// Strings for the register auto javascript clever stuffy wuffy.
382
	Txt::load('Login');
383
384
	// Check they entered something...
385
	if (empty($_POST['uid']))
386
	{
387
		throw new Exception('username_no_exist', false);
388
	}
389
390
	// Get the stuff....
391
	require_once(SUBSDIR . '/Members.subs.php');
392
	$member = getBasicMemberData((int) $_POST['uid'], array('authentication' => true));
393
	if (empty($member))
394
	{
395
		throw new Exception('username_no_exist', false);
396
	}
397
398
	$context['account_type'] = 'password';
399
400
	// If there is NO secret question - then throw an error.
401
	if (trim($member['secret_question']) === '')
402
	{
403
		throw new Exception('registration_no_secret_question', false);
404
	}
405
406
	// Ask for the answer...
407
	$context['remind_user'] = $member['id_member'];
408
	$context['remind_type'] = '';
409
	$context['secret_question'] = $member['secret_question'];
410
	$context['sub_template'] = 'ask';
411
412
	loadJavascriptFile('register.js');
413
414
	createToken('remind-sai');
415
416
	return true;
417
}
418