|
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 . ' |
|
|
|
|
|
|
89
|
|
|
LIMIT 1', |
|
90
|
|
|
$where_params |
|
|
|
|
|
|
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) |
|
|
|
|
|
|
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) |
|
|
|
|
|
|
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
|
|
|
?> |
|
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.