Move /parse_url module to /parseurl
[friendica.git/.git] / mod / lostpass.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2020, Friendica
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 use Friendica\App;
23 use Friendica\Core\Renderer;
24 use Friendica\Database\DBA;
25 use Friendica\DI;
26 use Friendica\Model\User;
27 use Friendica\Util\DateTimeFormat;
28 use Friendica\Util\Strings;
29
30 function lostpass_post(App $a)
31 {
32         $loginame = Strings::escapeTags(trim($_POST['login-name']));
33         if (!$loginame) {
34                 DI::baseUrl()->redirect();
35         }
36
37         $condition = ['(`email` = ? OR `nickname` = ?) AND `verified` = 1 AND `blocked` = 0', $loginame, $loginame];
38         $user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'email', 'language'], $condition);
39         if (!DBA::isResult($user)) {
40                 notice(DI::l10n()->t('No valid account found.'));
41                 DI::baseUrl()->redirect();
42         }
43
44         $pwdreset_token = Strings::getRandomHex(32);
45
46         $fields = [
47                 'pwdreset' => hash('sha256', $pwdreset_token),
48                 'pwdreset_time' => DateTimeFormat::utcNow()
49         ];
50         $result = DBA::update('user', $fields, ['uid' => $user['uid']]);
51         if ($result) {
52                 info(DI::l10n()->t('Password reset request issued. Check your email.'));
53         }
54
55         $sitename = DI::config()->get('config', 'sitename');
56         $resetlink = DI::baseUrl() . '/lostpass/' . $pwdreset_token;
57
58         $preamble = Strings::deindent(DI::l10n()->t('
59                 Dear %1$s,
60                         A request was recently received at "%2$s" to reset your account
61                 password. In order to confirm this request, please select the verification link
62                 below or paste it into your web browser address bar.
63
64                 If you did NOT request this change, please DO NOT follow the link
65                 provided and ignore and/or delete this email, the request will expire shortly.
66
67                 Your password will not be changed unless we can verify that you
68                 issued this request.', $user['username'], $sitename));
69         $body = Strings::deindent(DI::l10n()->t('
70                 Follow this link soon to verify your identity:
71
72                 %1$s
73
74                 You will then receive a follow-up message containing the new password.
75                 You may change that password from your account settings page after logging in.
76
77                 The login details are as follows:
78
79                 Site Location:  %2$s
80                 Login Name:     %3$s', $resetlink, DI::baseUrl(), $user['nickname']));
81
82         $email = DI::emailer()
83                 ->newSystemMail()
84                 ->withMessage(DI::l10n()->t('Password reset requested at %s', $sitename), $preamble, $body)
85                 ->forUser($user)
86                 ->withRecipient($user['email'])
87                 ->build();
88
89         DI::emailer()->send($email);
90         DI::baseUrl()->redirect();
91 }
92
93 function lostpass_content(App $a)
94 {
95         if ($a->argc > 1) {
96                 $pwdreset_token = $a->argv[1];
97
98                 $user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'email', 'pwdreset_time', 'language'], ['pwdreset' => hash('sha256', $pwdreset_token)]);
99                 if (!DBA::isResult($user)) {
100                         notice(DI::l10n()->t("Request could not be verified. \x28You may have previously submitted it.\x29 Password reset failed."));
101
102                         return lostpass_form();
103                 }
104
105                 // Password reset requests expire in 60 minutes
106                 if ($user['pwdreset_time'] < DateTimeFormat::utc('now - 1 hour')) {
107                         $fields = [
108                                 'pwdreset' => null,
109                                 'pwdreset_time' => null
110                         ];
111                         DBA::update('user', $fields, ['uid' => $user['uid']]);
112
113                         notice(DI::l10n()->t('Request has expired, please make a new one.'));
114
115                         return lostpass_form();
116                 }
117
118                 return lostpass_generate_password($user);
119         } else {
120                 return lostpass_form();
121         }
122 }
123
124 function lostpass_form()
125 {
126         $tpl = Renderer::getMarkupTemplate('lostpass.tpl');
127         $o = Renderer::replaceMacros($tpl, [
128                 '$title' => DI::l10n()->t('Forgot your Password?'),
129                 '$desc' => DI::l10n()->t('Enter your email address and submit to have your password reset. Then check your email for further instructions.'),
130                 '$name' => DI::l10n()->t('Nickname or Email: '),
131                 '$submit' => DI::l10n()->t('Reset')
132         ]);
133
134         return $o;
135 }
136
137 function lostpass_generate_password($user)
138 {
139         $o = '';
140
141         $new_password = User::generateNewPassword();
142         $result = User::updatePassword($user['uid'], $new_password);
143         if (DBA::isResult($result)) {
144                 $tpl = Renderer::getMarkupTemplate('pwdreset.tpl');
145                 $o .= Renderer::replaceMacros($tpl, [
146                         '$lbl1'    => DI::l10n()->t('Password Reset'),
147                         '$lbl2'    => DI::l10n()->t('Your password has been reset as requested.'),
148                         '$lbl3'    => DI::l10n()->t('Your new password is'),
149                         '$lbl4'    => DI::l10n()->t('Save or copy your new password - and then'),
150                         '$lbl5'    => '<a href="' . DI::baseUrl() . '">' . DI::l10n()->t('click here to login') . '</a>.',
151                         '$lbl6'    => DI::l10n()->t('Your password may be changed from the <em>Settings</em> page after successful login.'),
152                         '$newpass' => $new_password,
153                 ]);
154
155                 info(DI::l10n()->t("Your password has been reset."));
156
157                 $sitename = DI::config()->get('config', 'sitename');
158                 $preamble = Strings::deindent(DI::l10n()->t('
159                         Dear %1$s,
160                                 Your password has been changed as requested. Please retain this
161                         information for your records ' . "\x28" . 'or change your password immediately to
162                         something that you will remember' . "\x29" . '.
163                 ', $user['username']));
164                 $body = Strings::deindent(DI::l10n()->t('
165                         Your login details are as follows:
166
167                         Site Location:  %1$s
168                         Login Name:     %2$s
169                         Password:       %3$s
170
171                         You may change that password from your account settings page after logging in.
172                 ', DI::baseUrl(), $user['nickname'], $new_password));
173
174                 $email = DI::emailer()
175                         ->newSystemMail()
176                         ->withMessage(DI::l10n()->t('Your password has been changed at %s', $sitename), $preamble, $body)
177                         ->forUser($user)
178                         ->withRecipient($user['email'])
179                         ->build();
180                 DI::emailer()->send($email);
181         }
182
183         return $o;
184 }