Completed
Pull Request — master (#9921)
by Robin
42:04 queued 09:20
created
core/Controller/LostController.php 1 patch
Indentation   +318 added lines, -318 removed lines patch added patch discarded remove patch
@@ -57,322 +57,322 @@
 block discarded – undo
57 57
  */
58 58
 class LostController extends Controller {
59 59
 
60
-	/** @var IURLGenerator */
61
-	protected $urlGenerator;
62
-	/** @var IUserManager */
63
-	protected $userManager;
64
-	/** @var Defaults */
65
-	protected $defaults;
66
-	/** @var IL10N */
67
-	protected $l10n;
68
-	/** @var string */
69
-	protected $from;
70
-	/** @var IManager */
71
-	protected $encryptionManager;
72
-	/** @var IConfig */
73
-	protected $config;
74
-	/** @var ISecureRandom */
75
-	protected $secureRandom;
76
-	/** @var IMailer */
77
-	protected $mailer;
78
-	/** @var ITimeFactory */
79
-	protected $timeFactory;
80
-	/** @var ICrypto */
81
-	protected $crypto;
82
-
83
-	/**
84
-	 * @param string $appName
85
-	 * @param IRequest $request
86
-	 * @param IURLGenerator $urlGenerator
87
-	 * @param IUserManager $userManager
88
-	 * @param Defaults $defaults
89
-	 * @param IL10N $l10n
90
-	 * @param IConfig $config
91
-	 * @param ISecureRandom $secureRandom
92
-	 * @param string $defaultMailAddress
93
-	 * @param IManager $encryptionManager
94
-	 * @param IMailer $mailer
95
-	 * @param ITimeFactory $timeFactory
96
-	 * @param ICrypto $crypto
97
-	 */
98
-	public function __construct($appName,
99
-								IRequest $request,
100
-								IURLGenerator $urlGenerator,
101
-								IUserManager $userManager,
102
-								Defaults $defaults,
103
-								IL10N $l10n,
104
-								IConfig $config,
105
-								ISecureRandom $secureRandom,
106
-								$defaultMailAddress,
107
-								IManager $encryptionManager,
108
-								IMailer $mailer,
109
-								ITimeFactory $timeFactory,
110
-								ICrypto $crypto) {
111
-		parent::__construct($appName, $request);
112
-		$this->urlGenerator = $urlGenerator;
113
-		$this->userManager = $userManager;
114
-		$this->defaults = $defaults;
115
-		$this->l10n = $l10n;
116
-		$this->secureRandom = $secureRandom;
117
-		$this->from = $defaultMailAddress;
118
-		$this->encryptionManager = $encryptionManager;
119
-		$this->config = $config;
120
-		$this->mailer = $mailer;
121
-		$this->timeFactory = $timeFactory;
122
-		$this->crypto = $crypto;
123
-	}
124
-
125
-	/**
126
-	 * Someone wants to reset their password:
127
-	 *
128
-	 * @PublicPage
129
-	 * @NoCSRFRequired
130
-	 *
131
-	 * @param string $token
132
-	 * @param string $userId
133
-	 * @return TemplateResponse
134
-	 */
135
-	public function resetform($token, $userId) {
136
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
137
-			return new TemplateResponse('core', 'error', [
138
-					'errors' => [['error' => $this->l10n->t('Password reset is disabled')]]
139
-				],
140
-				'guest'
141
-			);
142
-		}
143
-
144
-		try {
145
-			$this->checkPasswordResetToken($token, $userId);
146
-		} catch (\Exception $e) {
147
-			return new TemplateResponse(
148
-				'core', 'error', [
149
-					"errors" => array(array("error" => $e->getMessage()))
150
-				],
151
-				'guest'
152
-			);
153
-		}
154
-
155
-		return new TemplateResponse(
156
-			'core',
157
-			'lostpassword/resetpassword',
158
-			array(
159
-				'link' => $this->urlGenerator->linkToRouteAbsolute('core.lost.setPassword', array('userId' => $userId, 'token' => $token)),
160
-			),
161
-			'guest'
162
-		);
163
-	}
164
-
165
-	/**
166
-	 * @param string $token
167
-	 * @param string $userId
168
-	 * @throws \Exception
169
-	 */
170
-	protected function checkPasswordResetToken($token, $userId) {
171
-		$user = $this->userManager->get($userId);
172
-		if($user === null || !$user->isEnabled()) {
173
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
174
-		}
175
-
176
-		try {
177
-			$encryptedToken = $this->config->getUserValue($userId, 'core', 'lostpassword', null);
178
-			$mailAddress = !is_null($user->getEMailAddress()) ? $user->getEMailAddress() : '';
179
-			$decryptedToken = $this->crypto->decrypt($encryptedToken, $mailAddress.$this->config->getSystemValue('secret'));
180
-		} catch (\Exception $e) {
181
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
182
-		}
183
-
184
-		$splittedToken = explode(':', $decryptedToken);
185
-		if(count($splittedToken) !== 2) {
186
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
187
-		}
188
-
189
-		if ($splittedToken[0] < ($this->timeFactory->getTime() - 60*60*12) ||
190
-			$user->getLastLogin() > $splittedToken[0]) {
191
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is expired'));
192
-		}
193
-
194
-		if (!hash_equals($splittedToken[1], $token)) {
195
-			throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
196
-		}
197
-	}
198
-
199
-	/**
200
-	 * @param $message
201
-	 * @param array $additional
202
-	 * @return array
203
-	 */
204
-	private function error($message, array $additional=array()) {
205
-		return array_merge(array('status' => 'error', 'msg' => $message), $additional);
206
-	}
207
-
208
-	/**
209
-	 * @param array $data
210
-	 * @return array
211
-	 */
212
-	private function success($data = []) {
213
-		return array_merge($data, ['status'=>'success']);
214
-	}
215
-
216
-	/**
217
-	 * @PublicPage
218
-	 * @BruteForceProtection(action=passwordResetEmail)
219
-	 * @AnonRateThrottle(limit=10, period=300)
220
-	 *
221
-	 * @param string $user
222
-	 * @return JSONResponse
223
-	 */
224
-	public function email($user){
225
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
226
-			return new JSONResponse($this->error($this->l10n->t('Password reset is disabled')));
227
-		}
228
-
229
-		\OCP\Util::emitHook(
230
-			'\OCA\Files_Sharing\API\Server2Server',
231
-			'preLoginNameUsedAsUserName',
232
-			['uid' => &$user]
233
-		);
234
-
235
-		// FIXME: use HTTP error codes
236
-		try {
237
-			$this->sendEmail($user);
238
-		} catch (\Exception $e){
239
-			$response = new JSONResponse($this->error($e->getMessage()));
240
-			$response->throttle();
241
-			return $response;
242
-		}
243
-
244
-		$response = new JSONResponse($this->success());
245
-		$response->throttle();
246
-		return $response;
247
-	}
248
-
249
-	/**
250
-	 * @PublicPage
251
-	 * @param string $token
252
-	 * @param string $userId
253
-	 * @param string $password
254
-	 * @param boolean $proceed
255
-	 * @return array
256
-	 */
257
-	public function setPassword($token, $userId, $password, $proceed) {
258
-		if ($this->config->getSystemValue('lost_password_link', '') !== '') {
259
-			return $this->error($this->l10n->t('Password reset is disabled'));
260
-		}
261
-
262
-		if ($this->encryptionManager->isEnabled() && !$proceed) {
263
-			return $this->error('', array('encryption' => true));
264
-		}
265
-
266
-		try {
267
-			$this->checkPasswordResetToken($token, $userId);
268
-			$user = $this->userManager->get($userId);
269
-
270
-			\OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'pre_passwordReset', array('uid' => $userId, 'password' => $password));
271
-
272
-			if (!$user->setPassword($password)) {
273
-				throw new \Exception();
274
-			}
275
-
276
-			\OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'post_passwordReset', array('uid' => $userId, 'password' => $password));
277
-
278
-			$this->config->deleteUserValue($userId, 'core', 'lostpassword');
279
-			@\OC::$server->getUserSession()->unsetMagicInCookie();
280
-		} catch (HintException $e){
281
-			return $this->error($e->getHint());
282
-		} catch (\Exception $e){
283
-			return $this->error($e->getMessage());
284
-		}
285
-
286
-		return $this->success(['user' => $userId]);
287
-	}
288
-
289
-	/**
290
-	 * @param string $input
291
-	 * @throws \Exception
292
-	 */
293
-	protected function sendEmail($input) {
294
-		$user = $this->findUserByIdOrMail($input);
295
-		$email = $user->getEMailAddress();
296
-
297
-		if (empty($email)) {
298
-			throw new \Exception(
299
-				$this->l10n->t('Could not send reset email because there is no email address for this username. Please contact your administrator.')
300
-			);
301
-		}
302
-
303
-		// Generate the token. It is stored encrypted in the database with the
304
-		// secret being the users' email address appended with the system secret.
305
-		// This makes the token automatically invalidate once the user changes
306
-		// their email address.
307
-		$token = $this->secureRandom->generate(
308
-			21,
309
-			ISecureRandom::CHAR_DIGITS.
310
-			ISecureRandom::CHAR_LOWER.
311
-			ISecureRandom::CHAR_UPPER
312
-		);
313
-		$tokenValue = $this->timeFactory->getTime() .':'. $token;
314
-		$encryptedValue = $this->crypto->encrypt($tokenValue, $email . $this->config->getSystemValue('secret'));
315
-		$this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
316
-
317
-		$link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', array('userId' => $user->getUID(), 'token' => $token));
318
-
319
-		$emailTemplate = $this->mailer->createEMailTemplate('core.ResetPassword', [
320
-			'link' => $link,
321
-		]);
322
-
323
-		$emailTemplate->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
324
-		$emailTemplate->addHeader();
325
-		$emailTemplate->addHeading($this->l10n->t('Password reset'));
326
-
327
-		$emailTemplate->addBodyText(
328
-			htmlspecialchars($this->l10n->t('Click the following button to reset your password. If you have not requested the password reset, then ignore this email.')),
329
-			$this->l10n->t('Click the following link to reset your password. If you have not requested the password reset, then ignore this email.')
330
-		);
331
-
332
-		$emailTemplate->addBodyButton(
333
-			htmlspecialchars($this->l10n->t('Reset your password')),
334
-			$link,
335
-			false
336
-		);
337
-		$emailTemplate->addFooter();
338
-
339
-		try {
340
-			$message = $this->mailer->createMessage();
341
-			$message->setTo([$email => $user->getUID()]);
342
-			$message->setFrom([$this->from => $this->defaults->getName()]);
343
-			$message->useTemplate($emailTemplate);
344
-			$this->mailer->send($message);
345
-		} catch (\Exception $e) {
346
-			throw new \Exception($this->l10n->t(
347
-				'Couldn\'t send reset email. Please contact your administrator.'
348
-			));
349
-		}
350
-	}
351
-
352
-	/**
353
-	 * @param string $input
354
-	 * @return IUser
355
-	 * @throws \InvalidArgumentException
356
-	 */
357
-	protected function findUserByIdOrMail($input) {
358
-		$user = $this->userManager->get($input);
359
-		if ($user instanceof IUser) {
360
-			if (!$user->isEnabled()) {
361
-				throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
362
-			}
363
-
364
-			return $user;
365
-		}
366
-		$users = $this->userManager->getByEmail($input);
367
-		if (count($users) === 1) {
368
-			$user = $users[0];
369
-			if (!$user->isEnabled()) {
370
-				throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
371
-			}
372
-
373
-			return $user;
374
-		}
375
-
376
-		throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
377
-	}
60
+    /** @var IURLGenerator */
61
+    protected $urlGenerator;
62
+    /** @var IUserManager */
63
+    protected $userManager;
64
+    /** @var Defaults */
65
+    protected $defaults;
66
+    /** @var IL10N */
67
+    protected $l10n;
68
+    /** @var string */
69
+    protected $from;
70
+    /** @var IManager */
71
+    protected $encryptionManager;
72
+    /** @var IConfig */
73
+    protected $config;
74
+    /** @var ISecureRandom */
75
+    protected $secureRandom;
76
+    /** @var IMailer */
77
+    protected $mailer;
78
+    /** @var ITimeFactory */
79
+    protected $timeFactory;
80
+    /** @var ICrypto */
81
+    protected $crypto;
82
+
83
+    /**
84
+     * @param string $appName
85
+     * @param IRequest $request
86
+     * @param IURLGenerator $urlGenerator
87
+     * @param IUserManager $userManager
88
+     * @param Defaults $defaults
89
+     * @param IL10N $l10n
90
+     * @param IConfig $config
91
+     * @param ISecureRandom $secureRandom
92
+     * @param string $defaultMailAddress
93
+     * @param IManager $encryptionManager
94
+     * @param IMailer $mailer
95
+     * @param ITimeFactory $timeFactory
96
+     * @param ICrypto $crypto
97
+     */
98
+    public function __construct($appName,
99
+                                IRequest $request,
100
+                                IURLGenerator $urlGenerator,
101
+                                IUserManager $userManager,
102
+                                Defaults $defaults,
103
+                                IL10N $l10n,
104
+                                IConfig $config,
105
+                                ISecureRandom $secureRandom,
106
+                                $defaultMailAddress,
107
+                                IManager $encryptionManager,
108
+                                IMailer $mailer,
109
+                                ITimeFactory $timeFactory,
110
+                                ICrypto $crypto) {
111
+        parent::__construct($appName, $request);
112
+        $this->urlGenerator = $urlGenerator;
113
+        $this->userManager = $userManager;
114
+        $this->defaults = $defaults;
115
+        $this->l10n = $l10n;
116
+        $this->secureRandom = $secureRandom;
117
+        $this->from = $defaultMailAddress;
118
+        $this->encryptionManager = $encryptionManager;
119
+        $this->config = $config;
120
+        $this->mailer = $mailer;
121
+        $this->timeFactory = $timeFactory;
122
+        $this->crypto = $crypto;
123
+    }
124
+
125
+    /**
126
+     * Someone wants to reset their password:
127
+     *
128
+     * @PublicPage
129
+     * @NoCSRFRequired
130
+     *
131
+     * @param string $token
132
+     * @param string $userId
133
+     * @return TemplateResponse
134
+     */
135
+    public function resetform($token, $userId) {
136
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
137
+            return new TemplateResponse('core', 'error', [
138
+                    'errors' => [['error' => $this->l10n->t('Password reset is disabled')]]
139
+                ],
140
+                'guest'
141
+            );
142
+        }
143
+
144
+        try {
145
+            $this->checkPasswordResetToken($token, $userId);
146
+        } catch (\Exception $e) {
147
+            return new TemplateResponse(
148
+                'core', 'error', [
149
+                    "errors" => array(array("error" => $e->getMessage()))
150
+                ],
151
+                'guest'
152
+            );
153
+        }
154
+
155
+        return new TemplateResponse(
156
+            'core',
157
+            'lostpassword/resetpassword',
158
+            array(
159
+                'link' => $this->urlGenerator->linkToRouteAbsolute('core.lost.setPassword', array('userId' => $userId, 'token' => $token)),
160
+            ),
161
+            'guest'
162
+        );
163
+    }
164
+
165
+    /**
166
+     * @param string $token
167
+     * @param string $userId
168
+     * @throws \Exception
169
+     */
170
+    protected function checkPasswordResetToken($token, $userId) {
171
+        $user = $this->userManager->get($userId);
172
+        if($user === null || !$user->isEnabled()) {
173
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
174
+        }
175
+
176
+        try {
177
+            $encryptedToken = $this->config->getUserValue($userId, 'core', 'lostpassword', null);
178
+            $mailAddress = !is_null($user->getEMailAddress()) ? $user->getEMailAddress() : '';
179
+            $decryptedToken = $this->crypto->decrypt($encryptedToken, $mailAddress.$this->config->getSystemValue('secret'));
180
+        } catch (\Exception $e) {
181
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
182
+        }
183
+
184
+        $splittedToken = explode(':', $decryptedToken);
185
+        if(count($splittedToken) !== 2) {
186
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
187
+        }
188
+
189
+        if ($splittedToken[0] < ($this->timeFactory->getTime() - 60*60*12) ||
190
+            $user->getLastLogin() > $splittedToken[0]) {
191
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is expired'));
192
+        }
193
+
194
+        if (!hash_equals($splittedToken[1], $token)) {
195
+            throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid'));
196
+        }
197
+    }
198
+
199
+    /**
200
+     * @param $message
201
+     * @param array $additional
202
+     * @return array
203
+     */
204
+    private function error($message, array $additional=array()) {
205
+        return array_merge(array('status' => 'error', 'msg' => $message), $additional);
206
+    }
207
+
208
+    /**
209
+     * @param array $data
210
+     * @return array
211
+     */
212
+    private function success($data = []) {
213
+        return array_merge($data, ['status'=>'success']);
214
+    }
215
+
216
+    /**
217
+     * @PublicPage
218
+     * @BruteForceProtection(action=passwordResetEmail)
219
+     * @AnonRateThrottle(limit=10, period=300)
220
+     *
221
+     * @param string $user
222
+     * @return JSONResponse
223
+     */
224
+    public function email($user){
225
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
226
+            return new JSONResponse($this->error($this->l10n->t('Password reset is disabled')));
227
+        }
228
+
229
+        \OCP\Util::emitHook(
230
+            '\OCA\Files_Sharing\API\Server2Server',
231
+            'preLoginNameUsedAsUserName',
232
+            ['uid' => &$user]
233
+        );
234
+
235
+        // FIXME: use HTTP error codes
236
+        try {
237
+            $this->sendEmail($user);
238
+        } catch (\Exception $e){
239
+            $response = new JSONResponse($this->error($e->getMessage()));
240
+            $response->throttle();
241
+            return $response;
242
+        }
243
+
244
+        $response = new JSONResponse($this->success());
245
+        $response->throttle();
246
+        return $response;
247
+    }
248
+
249
+    /**
250
+     * @PublicPage
251
+     * @param string $token
252
+     * @param string $userId
253
+     * @param string $password
254
+     * @param boolean $proceed
255
+     * @return array
256
+     */
257
+    public function setPassword($token, $userId, $password, $proceed) {
258
+        if ($this->config->getSystemValue('lost_password_link', '') !== '') {
259
+            return $this->error($this->l10n->t('Password reset is disabled'));
260
+        }
261
+
262
+        if ($this->encryptionManager->isEnabled() && !$proceed) {
263
+            return $this->error('', array('encryption' => true));
264
+        }
265
+
266
+        try {
267
+            $this->checkPasswordResetToken($token, $userId);
268
+            $user = $this->userManager->get($userId);
269
+
270
+            \OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'pre_passwordReset', array('uid' => $userId, 'password' => $password));
271
+
272
+            if (!$user->setPassword($password)) {
273
+                throw new \Exception();
274
+            }
275
+
276
+            \OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'post_passwordReset', array('uid' => $userId, 'password' => $password));
277
+
278
+            $this->config->deleteUserValue($userId, 'core', 'lostpassword');
279
+            @\OC::$server->getUserSession()->unsetMagicInCookie();
280
+        } catch (HintException $e){
281
+            return $this->error($e->getHint());
282
+        } catch (\Exception $e){
283
+            return $this->error($e->getMessage());
284
+        }
285
+
286
+        return $this->success(['user' => $userId]);
287
+    }
288
+
289
+    /**
290
+     * @param string $input
291
+     * @throws \Exception
292
+     */
293
+    protected function sendEmail($input) {
294
+        $user = $this->findUserByIdOrMail($input);
295
+        $email = $user->getEMailAddress();
296
+
297
+        if (empty($email)) {
298
+            throw new \Exception(
299
+                $this->l10n->t('Could not send reset email because there is no email address for this username. Please contact your administrator.')
300
+            );
301
+        }
302
+
303
+        // Generate the token. It is stored encrypted in the database with the
304
+        // secret being the users' email address appended with the system secret.
305
+        // This makes the token automatically invalidate once the user changes
306
+        // their email address.
307
+        $token = $this->secureRandom->generate(
308
+            21,
309
+            ISecureRandom::CHAR_DIGITS.
310
+            ISecureRandom::CHAR_LOWER.
311
+            ISecureRandom::CHAR_UPPER
312
+        );
313
+        $tokenValue = $this->timeFactory->getTime() .':'. $token;
314
+        $encryptedValue = $this->crypto->encrypt($tokenValue, $email . $this->config->getSystemValue('secret'));
315
+        $this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
316
+
317
+        $link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', array('userId' => $user->getUID(), 'token' => $token));
318
+
319
+        $emailTemplate = $this->mailer->createEMailTemplate('core.ResetPassword', [
320
+            'link' => $link,
321
+        ]);
322
+
323
+        $emailTemplate->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
324
+        $emailTemplate->addHeader();
325
+        $emailTemplate->addHeading($this->l10n->t('Password reset'));
326
+
327
+        $emailTemplate->addBodyText(
328
+            htmlspecialchars($this->l10n->t('Click the following button to reset your password. If you have not requested the password reset, then ignore this email.')),
329
+            $this->l10n->t('Click the following link to reset your password. If you have not requested the password reset, then ignore this email.')
330
+        );
331
+
332
+        $emailTemplate->addBodyButton(
333
+            htmlspecialchars($this->l10n->t('Reset your password')),
334
+            $link,
335
+            false
336
+        );
337
+        $emailTemplate->addFooter();
338
+
339
+        try {
340
+            $message = $this->mailer->createMessage();
341
+            $message->setTo([$email => $user->getUID()]);
342
+            $message->setFrom([$this->from => $this->defaults->getName()]);
343
+            $message->useTemplate($emailTemplate);
344
+            $this->mailer->send($message);
345
+        } catch (\Exception $e) {
346
+            throw new \Exception($this->l10n->t(
347
+                'Couldn\'t send reset email. Please contact your administrator.'
348
+            ));
349
+        }
350
+    }
351
+
352
+    /**
353
+     * @param string $input
354
+     * @return IUser
355
+     * @throws \InvalidArgumentException
356
+     */
357
+    protected function findUserByIdOrMail($input) {
358
+        $user = $this->userManager->get($input);
359
+        if ($user instanceof IUser) {
360
+            if (!$user->isEnabled()) {
361
+                throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
362
+            }
363
+
364
+            return $user;
365
+        }
366
+        $users = $this->userManager->getByEmail($input);
367
+        if (count($users) === 1) {
368
+            $user = $users[0];
369
+            if (!$user->isEnabled()) {
370
+                throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
371
+            }
372
+
373
+            return $user;
374
+        }
375
+
376
+        throw new \InvalidArgumentException($this->l10n->t('Couldn\'t send reset email. Please make sure your username is correct.'));
377
+    }
378 378
 }
Please login to merge, or discard this patch.