KaiRo bug 392 - Create an interstitial page to confirm the user to log in
[authserver.git] / index.php
1 <?php
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 // Include the common auth system files (including the OAuth2 Server object).
7 require_once(__DIR__.'/authsystem.inc.php');
8
9 // Start HTML document as a DOM object.
10 extract(ExtendedDocument::initHTML5()); // sets $document, $html, $head, $title, $body
11 $document->formatOutput = true; // we want a nice output
12
13 $style = $head->appendElement('link');
14 $style->setAttribute('rel', 'stylesheet');
15 $style->setAttribute('href', 'authsystem.css');
16 $head->appendJSFile('authsystem.js');
17 $title->appendText('KaiRo.at Authentication Server');
18 $h1 = $body->appendElement('h1', 'KaiRo.at Authentication Server');
19
20 // Make the document not be scaled on mobile devices.
21 $vpmeta = $head->appendElement('meta');
22 $vpmeta->setAttribute('name', 'viewport');
23 $vpmeta->setAttribute('content', 'width=device-width, height=device-height');
24
25 $errors = $utils->checkForSecureConnection();
26 $utils->sendSecurityHeaders();
27
28 $para = $body->appendElement('p', _('This login system does not work without JavaScript. Please activate JavaScript for this site to log in.'));
29 $para->setAttribute('id', 'jswarning');
30 $para->setAttribute('class', 'warn');
31
32 if (!count($errors)) {
33   $session = $utils->initSession(); // Read session or create new session and set cookie.
34   $user = array('id' => 0, 'email' => '');
35   $pagetype = 'default';
36   if (is_null($session)) {
37     $errors[] = _('The session system is not working. Please <a href="https://www.kairo.at/contact">contact KaiRo.at</a> and tell the team about this.');
38   }
39   elseif (array_key_exists('logout', $_GET)) {
40     $result = $db->prepare('UPDATE `auth_sessions` SET `logged_in` = FALSE WHERE `id` = :sessid;');
41     if (!$result->execute(array(':sessid' => $session['id']))) {
42       $utils->log('logout_failure', 'session: '.$session['id']);
43       $errors[] = _('Unexpected error while logging out.');
44     }
45     $session['logged_in'] = 0;
46   }
47   elseif (array_key_exists('email', $_POST)) {
48     if (!preg_match('/^[^@]+@[^@]+\.[^@]+$/', $_POST['email'])) {
49       $errors[] = _('The email address is invalid.');
50     }
51     elseif ($utils->verifyTimeCode(@$_POST['tcode'], $session)) {
52       $result = $db->prepare('SELECT `id`, `pwdhash`, `email`, `status`, `verify_hash` FROM `auth_users` WHERE `email` = :email;');
53       $result->execute(array(':email' => $_POST['email']));
54       $user = $result->fetch(PDO::FETCH_ASSOC);
55       if ($user['id'] && array_key_exists('pwd', $_POST)) {
56         // existing user, check password
57         if (($user['status'] == 'ok') && $utils->pwdVerify(@$_POST['pwd'], $user)) {
58           // Check if a newer hashing algorithm is available
59           // or the cost has changed
60           if ($utils->pwdNeedsRehash($user)) {
61             // If so, create a new hash, and replace the old one
62             $newHash = $utils->pwdHash($_POST['pwd']);
63             $result = $db->prepare('UPDATE `auth_users` SET `pwdhash` = :pwdhash WHERE `id` = :userid;');
64             if (!$result->execute(array(':pwdhash' => $newHash, ':userid' => $user['id']))) {
65               $utils->log('user_hash_save_failure', 'user: '.$user['id']);
66             }
67             else {
68               $utils->log('pwd_rehash_success', 'user: '.$user['id']);
69             }
70           }
71
72           // Log user in - update session key for that, see https://wiki.mozilla.org/WebAppSec/Secure_Coding_Guidelines#Login
73           $utils->log('login', 'user: '.$user['id']);
74           $sesskey = $utils->createSessionKey();
75           setcookie('sessionkey', $sesskey, 0, "", "", !$utils->running_on_localhost, true); // Last two params are secure and httponly, secure is not set on localhost.
76           // If the session has a redirect set, make sure it's performed.
77           if (strlen(@$session['saved_redirect'])) {
78             header('Location: '.$utils->getDomainBaseURL().$session['saved_redirect']);
79             // Remove redirect.
80             $result = $db->prepare('UPDATE `auth_sessions` SET `saved_redirect` = :redir WHERE `id` = :sessid;');
81             if (!$result->execute(array(':redir' => '', ':sessid' => $session['id']))) {
82               $utils->log('redir_save_failure', 'session: '.$session['id'].', redirect: (empty)');
83             }
84           }
85           // If the session has a user set, create a new one - otherwise take existing session entry.
86           if (intval($session['user'])) {
87             $result = $db->prepare('INSERT INTO `auth_sessions` (`sesskey`, `time_expire`, `user`, `logged_in`) VALUES (:sesskey, :expire, :userid, TRUE);');
88             $result->execute(array(':sesskey' => $sesskey, ':userid' => $user['id'], ':expire' => gmdate('Y-m-d H:i:s', strtotime('+1 day'))));
89             // After insert, actually fetch the session row from the DB so we have all values.
90             $result = $db->prepare('SELECT * FROM auth_sessions WHERE `sesskey` = :sesskey AND `time_expire` > :expire;');
91             $result->execute(array(':sesskey' => $sesskey, ':expire' => gmdate('Y-m-d H:i:s')));
92             $row = $result->fetch(PDO::FETCH_ASSOC);
93             if ($row) {
94               $session = $row;
95             }
96             else {
97               $utils->log('create_session_failure', 'at login, prev session: '.$session['id'].', new user: '.$user['id']);
98               $errors[] = _('The session system is not working. Please <a href="https://www.kairo.at/contact">contact KaiRo.at</a> and tell the team about this.');
99             }
100           }
101           else {
102             $result = $db->prepare('UPDATE `auth_sessions` SET `sesskey` = :sesskey, `user` = :userid, `logged_in` = TRUE, `time_expire` = :expire WHERE `id` = :sessid;');
103             if (!$result->execute(array(':sesskey' => $sesskey, ':userid' => $user['id'], ':expire' => gmdate('Y-m-d H:i:s', strtotime('+1 day')), ':sessid' => $session['id']))) {
104               $utils->log('login_failure', 'session: '.$session['id'].', user: '.$user['id']);
105               $errors[] = _('Login failed unexpectedly. Please <a href="https://www.kairo.at/contact">contact KaiRo.at</a> and tell the team about this.');
106             }
107             else {
108               // After update, actually fetch the session row from the DB so we have all values.
109               $result = $db->prepare('SELECT * FROM auth_sessions WHERE `sesskey` = :sesskey AND `time_expire` > :expire;');
110               $result->execute(array(':sesskey' => $sesskey, ':expire' => gmdate('Y-m-d H:i:s')));
111               $row = $result->fetch(PDO::FETCH_ASSOC);
112               if ($row) {
113                 $session = $row;
114               }
115             }
116           }
117           // If a verify_hash if set on a verified user, a password reset had been requested. As a login works right now, cancel that reset request by deleting the hash.
118           if (strlen(@$user['verify_hash'])) {
119             $result = $db->prepare('UPDATE `auth_users` SET `verify_hash` = \'\' WHERE `id` = :userid;');
120             if (!$result->execute(array(':userid' => $user['id']))) {
121               $utils->log('empty_vhash_failure', 'user: '.$user['id']);
122             }
123             else {
124               $user['verify_hash'] = '';
125             }
126           }
127         }
128         else {
129           $errors[] = _('This password is invalid or your email is not verified yet. Did you type them correctly?');
130         }
131       }
132       else {
133         // new user: check password, create user and send verification; existing users: re-send verification or send password change instructions
134         if (array_key_exists('pwd', $_POST)) {
135           $errors += $utils->checkPasswordConstraints(strval($_POST['pwd']), $_POST['email']);
136         }
137         if (!count($errors)) {
138           // Put user into the DB
139           if (!$user['id']) {
140             $newHash = $utils->pwdHash($_POST['pwd']);
141             $vcode = $utils->createVerificationCode();
142             $result = $db->prepare('INSERT INTO `auth_users` (`email`, `pwdhash`, `status`, `verify_hash`) VALUES (:email, :pwdhash, \'unverified\', :vcode);');
143             if (!$result->execute(array(':email' => $_POST['email'], ':pwdhash' => $newHash, ':vcode' => $vcode))) {
144               $utils->log('user_insert_failure', 'email: '.$_POST['email']);
145               $errors[] = _('Could not add user. Please <a href="https://www.kairo.at/contact">contact KaiRo.at</a> and tell the team about this.');
146             }
147             $user = array('id' => $db->lastInsertId(),
148                           'email' => $_POST['email'],
149                           'pwdhash' => $newHash,
150                           'status' => 'unverified',
151                           'verify_hash' => $vcode);
152             $utils->log('new_user', 'user: '.$user['id'].', email: '.$user['email']);
153           }
154           if ($user['status'] == 'unverified') {
155             // Send email for verification and show message to point to it.
156             $mail = new email();
157             $mail->setCharset('utf-8');
158             $mail->addHeader('X-KAIRO-AUTH', 'email_verification');
159             $mail->addRecipient($user['email']);
160             $mail->setSender('noreply@auth.kairo.at', _('KaiRo.at Authentication Service'));
161             $mail->setSubject('Email Verification for KaiRo.at Authentication');
162             $mail->addMailText(_('Welcome!')."\n\n");
163             $mail->addMailText(sprintf(_('This email address, %s, has been used for registration on "%s".'),
164                                       $user['email'], _('KaiRo.at Authentication Service'))."\n\n");
165             $mail->addMailText(_('Please confirm that registration by clicking the following link (or calling it up in your browser):')."\n");
166             $mail->addMailText($utils->getDomainBaseURL().strstr($_SERVER['REQUEST_URI'], '?', true)
167                               .'?email='.rawurlencode($user['email']).'&verification_code='.rawurlencode($user['verify_hash'])."\n\n");
168             $mail->addMailText(_('With this confirmation, you accept that we handle your data for the purpose of logging you into other websites when you request that.')."\n");
169             $mail->addMailText(_('Those websites will get to know your email address but not your password, which we store securely.')."\n");
170             $mail->addMailText(_('If you do not call this confirmation link within 72 hours, your data will be deleted from our database.')."\n\n");
171             $mail->addMailText(sprintf(_('The %s team'), 'KaiRo.at'));
172             //$mail->setDebugAddress("robert@localhost");
173             $mailsent = $mail->send();
174             if ($mailsent) {
175               $pagetype = 'verification_sent';
176             }
177             else {
178               $utils->log('verify_mail_failure', 'user: '.$user['id'].', email: '.$user['email']);
179               $errors[] = _('The confirmation email could not be sent to you. Please <a href="https://www.kairo.at/contact">contact KaiRo.at</a> and tell the team about this.');
180             }
181           }
182           else {
183             // Password reset requested with "Password forgotten?" function.
184             $vcode = $utils->createVerificationCode();
185             $result = $db->prepare('UPDATE `auth_users` SET `verify_hash` = :vcode WHERE `id` = :userid;');
186             if (!$result->execute(array(':vcode' => $vcode, ':userid' => $user['id']))) {
187               $utils->log('vhash_set_failure', 'user: '.$user['id']);
188               $errors[] = _('Could not initiate reset request. Please <a href="https://www.kairo.at/contact">contact KaiRo.at</a> and tell the team about this.');
189             }
190             else {
191               $utils->log('pwd_reset_request', 'user: '.$user['id'].', email: '.$user['email']);
192               $resetcode = $vcode.dechex($user['id'] + $session['id']).'_'.$utils->createTimeCode($session, null, 60);
193               // Send email with instructions for resetting the password.
194               $mail = new email();
195               $mail->setCharset('utf-8');
196               $mail->addHeader('X-KAIRO-AUTH', 'password_reset');
197               $mail->addRecipient($user['email']);
198               $mail->setSender('noreply@auth.kairo.at', _('KaiRo.at Authentication Service'));
199               $mail->setSubject('How to reset your password for KaiRo.at Authentication');
200               $mail->addMailText(_('Hi,')."\n\n");
201               $mail->addMailText(sprintf(_('A request for setting a new password for this email address, %s, has been submitted on "%s".'),
202                                         $user['email'], _('KaiRo.at Authentication Service'))."\n\n");
203               $mail->addMailText(_('You can set a new password by clicking the following link (or calling it up in your browser):')."\n");
204               $mail->addMailText($utils->getDomainBaseURL().strstr($_SERVER['REQUEST_URI'], '?', true)
205                                 .'?email='.rawurlencode($user['email']).'&reset_code='.rawurlencode($resetcode)."\n\n");
206               $mail->addMailText(_('If you do not call this confirmation link within 1 hour, this link expires and the existing password is being kept in place.')."\n\n");
207               $mail->addMailText(sprintf(_('The %s team'), 'KaiRo.at'));
208               //$mail->setDebugAddress("robert@localhost");
209               $mailsent = $mail->send();
210               if ($mailsent) {
211                 $pagetype = 'resetmail_sent';
212               }
213               else {
214                 $utils->log('pwd_reset_mail_failure', 'user: '.$user['id'].', email: '.$user['email']);
215                 $errors[] = _('The email with password reset instructions could not be sent to you. Please <a href="https://www.kairo.at/contact">contact KaiRo.at</a> and tell the team about this.');
216               }
217             }
218           }
219         }
220       }
221     }
222     else {
223       $errors[] = _('The form you used was not valid. Possibly it has expired and you need to initiate the action again, or you have disabled cookies for this site.');
224     }
225   }
226   elseif (array_key_exists('reset', $_GET)) {
227     if ($session['logged_in']) {
228       $result = $db->prepare('SELECT `id`,`email` FROM `auth_users` WHERE `id` = :userid;');
229       $result->execute(array(':userid' => $session['user']));
230       $user = $result->fetch(PDO::FETCH_ASSOC);
231       if (!$user['id']) {
232         $utils->log('reset_user_read_failure', 'user: '.$session['user']);
233       }
234       $pagetype = 'resetpwd';
235     }
236     else {
237       // Display form for entering email.
238       $pagetype = 'resetstart';
239     }
240   }
241   elseif (array_key_exists('verification_code', $_GET)) {
242     $result = $db->prepare('SELECT `id`,`email` FROM `auth_users` WHERE `email` = :email AND `status` = \'unverified\' AND `verify_hash` = :vcode;');
243     $result->execute(array(':email' => @$_GET['email'], ':vcode' => $_GET['verification_code']));
244     $user = $result->fetch(PDO::FETCH_ASSOC);
245     if ($user['id']) {
246       $result = $db->prepare('UPDATE `auth_users` SET `verify_hash` = \'\', `status` = \'ok\' WHERE `id` = :userid;');
247       if (!$result->execute(array(':userid' => $user['id']))) {
248         $utils->log('verification_save_failure', 'user: '.$user['id']);
249         $errors[] = _('Could not save confirmation. Please <a href="https://www.kairo.at/contact">contact KaiRo.at</a> and tell the team about this.');
250       }
251       $pagetype = 'verification_done';
252     }
253     else {
254       $errors[] = _('The confirmation link you called is not valid. Possibly it has expired and you need to try registering again.');
255     }
256   }
257   elseif (array_key_exists('reset_code', $_GET)) {
258     $reset_fail = true;
259     $result = $db->prepare('SELECT `id`,`email`,`verify_hash` FROM `auth_users` WHERE `email` = :email');
260     $result->execute(array(':email' => @$_GET['email']));
261     $user = $result->fetch(PDO::FETCH_ASSOC);
262     if ($user['id']) {
263       // Deconstruct reset code and verify it.
264       if (preg_match('/^([0-9a-f]{'.strlen($user['verify_hash']).'})([0-9a-f]+)_(\d+\.\d+)$/', $_GET['reset_code'], $regs)) {
265         $tcode_sessid = hexdec($regs[2]) - $user['id'];
266         $result = $db->prepare('SELECT `id`,`sesskey` FROM `auth_sessions` WHERE `id` = :sessid;');
267         $result->execute(array(':sessid' => $tcode_sessid));
268         $row = $result->fetch(PDO::FETCH_ASSOC);
269         if ($row) {
270           $tcode_session = $row;
271           if (($regs[1] == $user['verify_hash']) &&
272               $utils->verifyTimeCode($regs[3], $session, 60)) {
273             // Set a new verify_hash for the actual password reset.
274             $user['verify_hash'] = $utils->createVerificationCode();
275             $result = $db->prepare('UPDATE `auth_users` SET `verify_hash` = :vcode WHERE `id` = :userid;');
276             if (!$result->execute(array(':vcode' => $user['verify_hash'], ':userid' => $user['id']))) {
277               $utils->log('vhash_reset_failure', 'user: '.$user['id']);
278             }
279             $result = $db->prepare('UPDATE `auth_sessions` SET `user` = :userid WHERE `id` = :sessid;');
280             if (!$result->execute(array(':userid' => $user['id'], ':sessid' => $session['id']))) {
281               $utils->log('reset_session_set_user_failure', 'session: '.$session['id']);
282             }
283             $pagetype = 'resetpwd';
284             $reset_fail = false;
285           }
286         }
287       }
288     }
289     if ($reset_fail) {
290       $errors[] = _('The password reset link you called is not valid. Possibly it has expired and you need to call the "Password forgotten?" function again.');
291     }
292   }
293   elseif (array_key_exists('clients', $_GET)) {
294     $result = $db->prepare('SELECT `id`,`email` FROM `auth_users` WHERE `id` = :userid;');
295     $result->execute(array(':userid' => $session['user']));
296     $user = $result->fetch(PDO::FETCH_ASSOC);
297     if ($session['logged_in'] && $user['id']) {
298       if (array_key_exists('client_id', $_POST) && (strlen($_POST['client_id']) >= 5)) {
299         $clientid = $_POST['client_id'];
300         $clientsecret = $utils->createClientSecret();
301         $rediruri = strval(@$_POST['redirect_uri']);
302         $scope = strval(@$_POST['scope']);
303         $result = $db->prepare('INSERT INTO `oauth_clients` (`client_id`, `client_secret`, `redirect_uri`, `scope`, `user_id`) VALUES (:clientid, :secret, :rediruri, :scope, :userid);');
304         if (!$result->execute(array(':clientid' => $clientid,
305                                     ':secret' => $clientsecret,
306                                     ':rediruri' => $rediruri,
307                                     ':scope' => $scope,
308                                     ':userid' => $user['id']))) {
309           $utils->log('client_save_failure', 'client: '.$clientid);
310           $errors[] = 'Unexpectedly failed to save new client information. Please <a href="https://www.kairo.at/contact">contact KaiRo.at</a> and tell the team about this.';
311         }
312       }
313       if (!count($errors)) {
314         // List clients
315         $result = $db->prepare('SELECT `client_id`,`client_secret`,`redirect_uri`,`scope` FROM `oauth_clients` WHERE `user_id` = :userid;');
316         $result->execute(array(':userid' => $user['id']));
317         $clients = $result->fetchAll(PDO::FETCH_ASSOC);
318         if (!$clients) { $clients = array(); }
319         $pagetype = 'clientlist';
320       }
321     }
322     else {
323       $errors[] = _('This function is only available if you are logged in.');
324     }
325   }
326   elseif (intval($session['user'])) {
327     $result = $db->prepare('SELECT `id`,`email`,`verify_hash` FROM `auth_users` WHERE `id` = :userid;');
328     $result->execute(array(':userid' => $session['user']));
329     $user = $result->fetch(PDO::FETCH_ASSOC);
330     if (!$user['id']) {
331       $utils->log('user_read_failure', 'user: '.$session['user']);
332     }
333     // Password reset requested.
334     if (array_key_exists('pwd', $_POST) && array_key_exists('reset', $_POST) && array_key_exists('tcode', $_POST)) {
335       // If not logged in, a password reset needs to have the proper vcode set.
336       if (!$session['logged_in'] && (!strlen(@$_POST['vcode']) || ($_POST['vcode'] != $user['verify_hash']))) {
337         $errors[] = _('Password reset failed. The reset form you used was not valid. Possibly it has expired and you need to initiate the password reset again.');
338       }
339       // If not logged in, a password reset also needs to have the proper email set.
340       if (!$session['logged_in'] && !count($errors) && (@$_POST['email_hidden'] != $user['email'])) {
341         $errors[] = _('Password reset failed. The reset form you used was not valid. Possibly it has expired and you need to initiate the password reset again.');
342       }
343       // Check validity of time code.
344       if (!count($errors) && !$utils->verifyTimeCode($_POST['tcode'], $session)) {
345         $errors[] = _('Password reset failed. The reset form you used was not valid. Possibly it has expired and you need to initiate the password reset again.');
346       }
347       $errors += $utils->checkPasswordConstraints(strval($_POST['pwd']), $user['email']);
348       if (!count($errors)) {
349         $newHash = $utils->pwdHash($_POST['pwd']);
350         $result = $db->prepare('UPDATE `auth_users` SET `pwdhash` = :pwdhash, `verify_hash` = \'\' WHERE `id` = :userid;');
351         if (!$result->execute(array(':pwdhash' => $newHash, ':userid' => $session['user']))) {
352           $utils->log('pwd_reset_failure', 'user: '.$session['user']);
353           $errors[] = _('Password reset failed. Please <a href="https://www.kairo.at/contact">contact KaiRo.at</a> and tell the team about this.');
354         }
355         else {
356           $pagetype = 'reset_done';
357         }
358       }
359     }
360   }
361 }
362
363 if (!count($errors)) {
364   if ($pagetype == 'verification_sent') {
365     $para = $body->appendElement('p', sprintf(_('An email for confirmation has been sent to %s. Please follow the link provided there to complete the process.'), $user['email']));
366     $para->setAttribute('class', 'verifyinfo pending');
367     $para = $body->appendElement('p', _('Reload this page after you confirm to continue.'));
368     $para->setAttribute('class', 'verifyinfo pending');
369     $para = $body->appendElement('p');
370     $para->setAttribute('class', 'verifyinfo pending');
371     $link = $para->appendLink('./', _('Reload'));
372   }
373   elseif ($pagetype == 'resetmail_sent') {
374     $para = $body->appendElement('p',
375         _('An email has been sent to the requested account with further information. If you do not receive an email then please confirm you have entered the same email address used during account registration.'));
376     $para->setAttribute('class', 'resetinfo pending');
377     $para = $body->appendElement('p');
378     $para->setAttribute('class', 'resetinfo pending small');
379     $link = $para->appendLink('./', _('Back to top'));
380   }
381   elseif ($pagetype == 'resetstart') {
382     $para = $body->appendElement('p', _('If you forgot your password or didn\'t receive the registration confirmation, please enter your email here.'));
383     $para->setAttribute('class', '');
384     $form = $body->appendForm('./?reset', 'POST', 'resetform');
385     $form->setAttribute('id', 'loginform');
386     $form->setAttribute('class', 'loginarea hidden');
387     $ulist = $form->appendElement('ul');
388     $ulist->setAttribute('class', 'flat login');
389     $litem = $ulist->appendElement('li');
390     $inptxt = $litem->appendInputEmail('email', 30, 20, 'login_email');
391     $inptxt->setAttribute('autocomplete', 'email');
392     $inptxt->setAttribute('required', '');
393     $inptxt->setAttribute('placeholder', _('Email'));
394     $litem = $ulist->appendElement('li');
395     $litem->appendInputHidden('tcode', $utils->createTimeCode($session));
396     $submit = $litem->appendInputSubmit(_('Send instructions to email'));
397     $para = $form->appendElement('p');
398     $para->setAttribute('class', 'toplink small');
399     $link = $para->appendLink('./', _('Cancel'));
400   }
401   elseif ($pagetype == 'resetpwd') {
402     $para = $body->appendElement('p', sprintf(_('You can set a new password for %s here.'), $user['email']));
403     $para->setAttribute('class', 'newpwdinfo');
404     $form = $body->appendForm('./', 'POST', 'newpwdform');
405     $form->setAttribute('id', 'loginform');
406     $form->setAttribute('class', 'loginarea hidden');
407     $ulist = $form->appendElement('ul');
408     $ulist->setAttribute('class', 'flat login');
409     $litem = $ulist->appendElement('li');
410     $litem->setAttribute('class', 'donotshow');
411     $inptxt = $litem->appendInputEmail('email_hidden', 30, 20, 'login_email', $user['email']);
412     $inptxt->setAttribute('autocomplete', 'email');
413     $inptxt->setAttribute('placeholder', _('Email'));
414     $litem = $ulist->appendElement('li');
415     $inptxt = $litem->appendInputPassword('pwd', 20, 20, 'login_pwd', '');
416     $inptxt->setAttribute('required', '');
417     $inptxt->setAttribute('placeholder', _('Password'));
418     $inptxt->setAttribute('class', 'login');
419     $litem = $ulist->appendElement('li');
420     $litem->appendInputHidden('reset', '');
421     $litem->appendInputHidden('tcode', $utils->createTimeCode($session));
422     if (!$session['logged_in'] && strlen(@$user['verify_hash'])) {
423       $litem->appendInputHidden('vcode', $user['verify_hash']);
424     }
425     $submit = $litem->appendInputSubmit(_('Save password'));
426     $para = $form->appendElement('p');
427     $para->setAttribute('class', 'toplink small');
428     $link = $para->appendLink('./', _('Cancel'));
429   }
430   elseif ($pagetype == 'clientlist') {
431     $scopes = array('clientreg', 'email');
432     $form = $body->appendForm('?clients', 'POST', 'newclientform');
433     $form->setAttribute('id', 'clientform');
434     $tbl = $form->appendElement('table');
435     $tbl->setAttribute('class', 'clientlist border');
436     $thead = $tbl->appendElement('thead');
437     $trow = $thead->appendElement('tr');
438     $trow->appendElement('th', _('Client ID'));
439     $trow->appendElement('th', _('Client Secrect'));
440     $trow->appendElement('th', _('Redirect URI'));
441     $trow->appendElement('th', _('Scope'));
442     $trow->appendElement('th');
443     $tbody = $tbl->appendElement('tbody');
444     foreach ($clients as $client) {
445       $trow = $tbody->appendElement('tr');
446       $trow->appendElement('td', $client['client_id']);
447       $trow->appendElement('td', $client['client_secret']);
448       $trow->appendElement('td', $client['redirect_uri']);
449       $trow->appendElement('td', $client['scope']);
450       $trow->appendElement('td'); // Future: Delete link?
451     }
452     // Form fields for adding a new one.
453     $tfoot = $tbl->appendElement('tfoot');
454     $trow = $tfoot->appendElement('tr');
455     $cell = $trow->appendElement('td');
456     $inptxt = $cell->appendInputText('client_id', 80, 25, 'client_id');
457     $cell = $trow->appendElement('td'); // Empty, as secret will be generated.
458     $cell = $trow->appendElement('td');
459     $inptxt = $cell->appendInputText('redirect_uri', 500, 50, 'redirect_uri');
460     $cell = $trow->appendElement('td');
461     $select = $cell->appendElementSelect('scope');
462     foreach ($scopes as $scope) {
463       $select->appendElementOption($scope, $scope);
464     }
465     //$inptxt = $cell->appendInputText('scope', 100, 20, 'scope');
466     $cell = $trow->appendElement('td');
467     $submit = $cell->appendInputSubmit(_('Create'));
468     $para = $form->appendElement('p');
469     $para->setAttribute('class', 'toplink');
470     $link = $para->appendLink('./', _('Back to top'));
471   }
472   elseif ($session['logged_in']) {
473     if ($pagetype == 'reset_done') {
474       $para = $body->appendElement('p', _('Your password has successfully been reset.'));
475       $para->setAttribute('class', 'resetinfo done');
476     }
477     $div = $body->appendElement('div', $user['email']);
478     $div->setAttribute('class', 'loginheader');
479     $div = $body->appendElement('div');
480     $div->setAttribute('class', 'loginlinks');
481     $ulist = $div->appendElement('ul');
482     $ulist->setAttribute('class', 'flat');
483     $litem = $ulist->appendElement('li');
484     $link = $litem->appendLink('./?logout', _('Log out'));
485     if (in_array($user['email'], $utils->client_reg_email_whitelist)) {
486       $litem = $ulist->appendElement('li');
487       $link = $litem->appendLink('./?clients', _('Manage OAuth2 clients'));
488     }
489     $litem = $ulist->appendElement('li');
490     $litem->appendLink('./?reset', _('Set new password'));
491   }
492   else { // not logged in
493     if ($pagetype == 'verification_done') {
494       $para = $body->appendElement('p', _('Hooray! Your email was successfully confirmed! You can log in now.'));
495       $para->setAttribute('class', 'verifyinfo done');
496     }
497     elseif ($pagetype == 'reset_done') {
498       $para = $body->appendElement('p', _('Your password has successfully been reset. You can log in now with the new password.'));
499       $para->setAttribute('class', 'resetinfo done');
500     }
501     $utils->appendLoginForm($body, $session, $user);
502   }
503 }
504
505 if (count($errors)) {
506   $body->appendElement('p', ((count($errors) <= 1)
507                             ?_('The following error was detected')
508                             :_('The following errors were detected')).':');
509   $list = $body->appendElement('ul');
510   $list->setAttribute('class', 'flat warn');
511   foreach ($errors as $msg) {
512     $item = $list->appendElement('li', $msg);
513   }
514   $body->appendButton(_('Back'), 'history.back();');
515 }
516
517 // Send HTML to client.
518 print($document->saveHTML());
519 ?>