]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - actions/recoverpassword.php
Fix broken user activitystreams feed due to deleted notices
[quix0rs-gnu-social.git] / actions / recoverpassword.php
1 <?php
2 /*
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2008, 2009, StatusNet, Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
21
22 // You have 24 hours to claim your password
23
24 define('MAX_RECOVERY_TIME', 24 * 60 * 60);
25
26 class RecoverpasswordAction extends Action
27 {
28     var $mode = null;
29     var $msg = null;
30     var $success = null;
31
32     function handle()
33     {
34         parent::handle();
35         if (common_logged_in()) {
36             // TRANS: Client error displayed trying to recover password while already logged in.
37             $this->clientError(_('You are already logged in!'));
38         } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
39             if ($this->arg('recover')) {
40                 $this->recoverPassword();
41             } else if ($this->arg('reset')) {
42                 $this->resetPassword();
43             } else {
44                 // TRANS: Client error displayed when unexpected data is posted in the password recovery form.
45                 $this->clientError(_('Unexpected form submission.'));
46             }
47         } else {
48             if ($this->trimmed('code')) {
49                 $this->checkCode();
50             } else {
51                 $this->showForm();
52             }
53         }
54     }
55
56     function checkCode()
57     {
58         $code = $this->trimmed('code');
59         $confirm = Confirm_address::getKV('code', $code);
60
61         if (!$confirm) {
62             // TRANS: Client error displayed when password recovery code is not correct.
63             $this->clientError(_('No such recovery code.'));
64         }
65         if ($confirm->address_type != 'recover') {
66             // TRANS: Client error displayed when no proper password recovery code was submitted.
67             $this->clientError(_('Not a recovery code.'));
68         }
69
70         $user = User::getKV($confirm->user_id);
71
72         if (!$user) {
73             // TRANS: Server error displayed trying to recover password without providing a user.
74             $this->serverError(_('Recovery code for unknown user.'));
75         }
76
77         $touched = strtotime($confirm->modified);
78         $email = $confirm->address;
79
80         // Burn this code
81
82         $confirm->delete();
83
84         // These should be reaped, but for now we just check mod time
85         // Note: it's still deleted; let's avoid a second attempt!
86
87         if ((time() - $touched) > MAX_RECOVERY_TIME) {
88             common_log(LOG_WARNING,
89                        'Attempted redemption on recovery code ' .
90                        'that is ' . $touched . ' seconds old. ');
91             // TRANS: Client error displayed trying to recover password with too old a recovery code.
92             $this->clientError(_('This confirmation code is too old. ' .
93                                    'Please start again.'));
94         }
95
96         // If we used an outstanding confirmation to send the email,
97         // it's been confirmed at this point.
98
99         if (!$user->email) {
100             $orig = clone($user);
101             $user->email = $email;
102             // Throws exception on failure.
103             $user->updateWithKeys($orig);
104         }
105
106         // Success!
107
108         $this->setTempUser($user);
109         $this->showPasswordForm();
110     }
111
112     function setTempUser(&$user)
113     {
114         common_ensure_session();
115         $_SESSION['tempuser'] = $user->id;
116     }
117
118     function getTempUser()
119     {
120         common_ensure_session();
121         $user_id = $_SESSION['tempuser'];
122         if ($user_id) {
123             $user = User::getKV($user_id);
124         }
125         return $user;
126     }
127
128     function clearTempUser()
129     {
130         common_ensure_session();
131         unset($_SESSION['tempuser']);
132     }
133
134     function showPageNotice()
135     {
136         if ($this->msg) {
137             $this->element('div', ($this->success) ? 'success' : 'error', $this->msg);
138         } else {
139             $this->elementStart('div', 'instructions');
140             if ($this->mode == 'recover') {
141                 $this->element('p', null,
142                                // TRANS: Page notice for password recovery page.
143                                _('If you have forgotten or lost your' .
144                                  ' password, you can get a new one sent to' .
145                                  ' the email address you have stored' .
146                                  ' in your account.'));
147             } else if ($this->mode == 'reset') {
148                 $this->element('p', null,
149                                // TRANS: Page notice for password change page.
150                                _('You have been identified. Enter a' .
151                                  ' new password below.'));
152             }
153             $this->elementEnd('div');
154         }
155     }
156
157     function showForm($msg=null)
158     {
159         $this->msg = $msg;
160         $this->mode = 'recover';
161         $this->showPage();
162     }
163
164     function showContent()
165     {
166         if ($this->mode == 'recover') {
167             $this->showRecoverForm();
168         } else if ($this->mode == 'reset') {
169             $this->showResetForm();
170         }
171     }
172
173     function showRecoverForm()
174     {
175         $this->elementStart('form', array('method' => 'post',
176                                            'id' => 'form_password_recover',
177                                            'class' => 'form_settings',
178                                            'action' => common_local_url('recoverpassword')));
179         $this->elementStart('fieldset');
180         // TRANS: Fieldset legend for password recovery page.
181         $this->element('legend', null, _('Password recovery'));
182         $this->elementStart('ul', 'form_data');
183         $this->elementStart('li');
184         // TRANS: Field label on password recovery page.
185         $this->input('nicknameoremail', _('Nickname or email address'),
186                      $this->trimmed('nicknameoremail'),
187                      // TRANS: Title for field label on password recovery page.
188                      _('Your nickname on this server, ' .
189                         'or your registered email address.'));
190         $this->elementEnd('li');
191         $this->elementEnd('ul');
192         $this->element('input', array('name' => 'recover',
193                                       'type' => 'hidden',
194                                       // TRANS: Field label on password recovery page.
195                                       'value' => _('Recover')));
196         // TRANS: Button text on password recovery page.
197         $this->submit('recover', _m('BUTTON','Recover'));
198         $this->elementEnd('fieldset');
199         $this->elementEnd('form');
200     }
201
202     function title()
203     {
204         switch ($this->mode) {
205          // TRANS: Title for password recovery page in password reset mode.
206          case 'reset': return _('Reset password');
207          // TRANS: Title for password recovery page in password recover mode.
208          case 'recover': return _('Recover password');
209          // TRANS: Title for password recovery page in email sent mode.
210          case 'sent': return _('Password recovery requested');
211          // TRANS: Title for password recovery page in password saved mode.
212          case 'saved': return _('Password saved');
213          default:
214             // TRANS: Title for password recovery page when an unknown action has been specified.
215             return _('Unknown action');
216         }
217     }
218
219     function showPasswordForm($msg=null)
220     {
221         $this->msg = $msg;
222         $this->mode = 'reset';
223         $this->showPage();
224     }
225
226     function showResetForm()
227     {
228         $this->elementStart('form', array('method' => 'post',
229                                            'id' => 'form_password_change',
230                                            'class' => 'form_settings',
231                                            'action' => common_local_url('recoverpassword')));
232         $this->elementStart('fieldset');
233          // TRANS: Fieldset legend for password reset form.
234         $this->element('legend', null, _('Password change'));
235         $this->hidden('token', common_session_token());
236         $this->elementStart('ul', 'form_data');
237         $this->elementStart('li');
238          // TRANS: Field label for password reset form.
239         $this->password('newpassword', _('New password'),
240                         // TRANS: Title for field label for password reset form.
241                         _('6 or more characters, and do not forget it!'));
242         $this->elementEnd('li');
243         $this->elementStart('li');
244          // TRANS: Field label for password reset form where the password has to be typed again.
245         $this->password('confirm', _('Confirm'),
246                         // TRANS: Title for field label for password reset form where the password has to be typed again.
247                         _('Same as password above.'));
248         $this->elementEnd('li');
249         $this->elementEnd('ul');
250          // TRANS: Button text for password reset form.
251         $this->submit('reset', _m('BUTTON','Reset'));
252         $this->elementEnd('fieldset');
253         $this->elementEnd('form');
254     }
255
256     function recoverPassword()
257     {
258         $nore = $this->trimmed('nicknameoremail');
259
260         if (!$nore) {
261             // TRANS: Form instructions for password recovery form.
262             $this->showForm(_('Enter a nickname or email address.'));
263             return;
264         }
265
266         try {
267             User::recoverPassword($nore);
268             $this->mode = 'sent';
269             if (common_is_email($nore) && common_config('site', 'fakeaddressrecovery')) {
270                 // TRANS: User notification when recovering password by giving email address,
271                 //        regardless if the mail was sent or not (to hide registered email status).
272                 $this->msg = _('If the email address you provided was found in the database, a recovery mail with instructions has been sent there.');
273             } else {
274                 // TRANS: User notification after an e-mail with instructions was sent from the password recovery form.
275                 $this->msg = _('Instructions for recovering your password ' .
276                                'have been sent to the email address registered to your ' .
277                                'account.');
278             }
279             $this->success = true;
280         } catch (Exception $e) {
281             $this->success = false;
282             $this->msg = $e->getMessage();
283         }
284         $this->showPage();
285     }
286
287     function resetPassword()
288     {
289         // CSRF protection
290         $token = $this->trimmed('token');
291         if (!$token || $token != common_session_token()) {
292             // TRANS: Form validation error message.
293             $this->showForm(_('There was a problem with your session token. Try again, please.'));
294             return;
295         }
296
297         $user = $this->getTempUser();
298
299         if (!$user) {
300             // TRANS: Client error displayed when trying to reset as password without providing a user.
301             $this->clientError(_('Unexpected password reset.'));
302         }
303
304         $newpassword = $this->trimmed('newpassword');
305         $confirm = $this->trimmed('confirm');
306
307         if (!$newpassword || strlen($newpassword) < 6) {
308             // TRANS: Reset password form validation error message.
309             $this->showPasswordForm(_('Password must be 6 characters or more.'));
310             return;
311         }
312         if ($newpassword != $confirm) {
313             // TRANS: Reset password form validation error message.
314             $this->showPasswordForm(_('Password and confirmation do not match.'));
315             return;
316         }
317
318         // OK, we're ready to go
319         $user->setPassword($newpassword);
320
321         $this->clearTempUser();
322
323         if (!common_set_user($user->nickname)) {
324             // TRANS: Server error displayed when something does wrong with the user object during password reset.
325             $this->serverError(_('Error setting user.'));
326         }
327
328         common_real_login(true);
329
330         $this->mode = 'saved';
331         // TRANS: Success message for user after password reset.
332         $this->msg = _('New password successfully saved. ' .
333                        'You are now logged in.');
334         $this->success = true;
335         $this->showPage();
336     }
337
338     /**
339      * A local menu
340      *
341      * Shows different login/register actions.
342      *
343      * @return void
344      */
345
346     function showLocalNav()
347     {
348         $nav = new LoginGroupNav($this);
349         $nav->show();
350     }
351 }