1<?php
2
3namespace dokuwiki\Action;
4
5use dokuwiki\Ui\UserResendPwd;
6use dokuwiki\Action\Exception\ActionAbort;
7use dokuwiki\Action\Exception\ActionDisabledException;
8use dokuwiki\Extension\AuthPlugin;
9use dokuwiki\Ui;
10
11/**
12 * Class Resendpwd
13 *
14 * Handle password recovery
15 *
16 * @package dokuwiki\Action
17 */
18class Resendpwd extends AbstractAclAction
19{
20    /** @inheritdoc */
21    public function minimumPermission()
22    {
23        return AUTH_NONE;
24    }
25
26    /** @inheritdoc */
27    public function checkPreconditions()
28    {
29        parent::checkPreconditions();
30
31        /** @var AuthPlugin $auth */
32        global $auth;
33        global $conf;
34        if (isset($conf['resendpasswd']) && !$conf['resendpasswd'])
35            throw new ActionDisabledException(); //legacy option
36        if (!$auth->canDo('modPass')) throw new ActionDisabledException();
37    }
38
39    /** @inheritdoc */
40    public function preProcess()
41    {
42        if ($this->resendpwd()) {
43            throw new ActionAbort('login');
44        }
45    }
46
47    /** @inheritdoc */
48    public function tplContent()
49    {
50        (new UserResendPwd())->show();
51    }
52
53    /**
54     * Send a  new password
55     *
56     * This function handles both phases of the password reset:
57     *
58     *   - handling the first request of password reset
59     *   - validating the password reset auth token
60     *
61     * @author Benoit Chesneau <benoit@bchesneau.info>
62     * @author Chris Smith <chris@jalakai.co.uk>
63     * @author Andreas Gohr <andi@splitbrain.org>
64     * @fixme this should be split up into multiple methods
65     * @return bool true on success, false on any error
66     */
67    protected function resendpwd()
68    {
69        global $lang;
70        global $conf;
71        /* @var AuthPlugin $auth */
72        global $auth;
73        global $INPUT;
74
75        if (!actionOK('resendpwd')) {
76            msg($lang['resendna'], -1);
77            return false;
78        }
79
80        $token = preg_replace('/[^a-f0-9]+/', '', $INPUT->str('pwauth'));
81
82        if ($token) {
83            // we're in token phase - get user info from token
84
85            $tfile = $conf['cachedir'] . '/' . $token[0] . '/' . $token . '.pwauth';
86            if (!file_exists($tfile)) {
87                msg($lang['resendpwdbadauth'], -1);
88                $INPUT->remove('pwauth');
89                return false;
90            }
91            // token is only valid for 3 days
92            if ((time() - filemtime($tfile)) > (3 * 60 * 60 * 24)) {
93                msg($lang['resendpwdbadauth'], -1);
94                $INPUT->remove('pwauth');
95                @unlink($tfile);
96                return false;
97            }
98
99            $user = io_readfile($tfile);
100            $userinfo = $auth->getUserData($user, $requireGroups = false);
101            if (empty($userinfo['mail'])) {
102                msg($lang['resendpwdnouser'], -1);
103                return false;
104            }
105
106            if (!$conf['autopasswd']) { // we let the user choose a password
107                $pass = $INPUT->str('pass');
108
109                // password given correctly?
110                if (!$pass) return false;
111                if ($pass != $INPUT->str('passchk')) {
112                    msg($lang['regbadpass'], -1);
113                    return false;
114                }
115
116                // change it
117                if (!$auth->triggerUserMod('modify', [$user, ['pass' => $pass]])) {
118                    msg($lang['proffail'], -1);
119                    return false;
120                }
121            } else { // autogenerate the password and send by mail
122                $pass = auth_pwgen($user);
123                if (!$auth->triggerUserMod('modify', [$user, ['pass' => $pass]])) {
124                    msg($lang['proffail'], -1);
125                    return false;
126                }
127
128                if (auth_sendPassword($user, $pass)) {
129                    msg($lang['resendpwdsuccess'], 1);
130                } else {
131                    msg($lang['regmailfail'], -1);
132                }
133            }
134
135            @unlink($tfile);
136            return true;
137        } else {
138            // we're in request phase
139
140            if (!$INPUT->post->bool('save')) return false;
141
142            if (!$INPUT->post->str('login')) {
143                msg($lang['resendpwdmissing'], -1);
144                return false;
145            } else {
146                $user = trim($auth->cleanUser($INPUT->post->str('login')));
147            }
148
149            $userinfo = $auth->getUserData($user, $requireGroups = false);
150            if (empty($userinfo['mail'])) {
151                msg($lang['resendpwdnouser'], -1);
152                return false;
153            }
154
155            // generate auth token
156            $token = md5(auth_randombytes(16)); // random secret
157            $tfile = $conf['cachedir'] . '/' . $token[0] . '/' . $token . '.pwauth';
158            $url = wl('', ['do' => 'resendpwd', 'pwauth' => $token], true, '&');
159
160            io_saveFile($tfile, $user);
161
162            $text = rawLocale('pwconfirm');
163            $trep = [
164                'FULLNAME' => $userinfo['name'],
165                'LOGIN' => $user,
166                'CONFIRM' => $url
167            ];
168
169            $mail = new \Mailer();
170            $mail->to($userinfo['name'] . ' <' . $userinfo['mail'] . '>');
171            $mail->subject($lang['regpwmail']);
172            $mail->setBody($text, $trep);
173            if ($mail->send()) {
174                msg($lang['resendpwdconfirm'], 1);
175            } else {
176                msg($lang['regmailfail'], -1);
177            }
178            return true;
179        }
180        // never reached
181    }
182}
183