add a human check to registrations
[authserver.git] / app / 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 $errors = $utils->checkForSecureConnection();
10 $utils->sendSecurityHeaders();
11
12 // Initialize the HTML document with our basic elements.
13 extract($utils->initHTMLDocument(sprintf(_('%s Authentication Server'), $utils->settings['operator_name']))); // sets $document, $html, $head, $title, $body
14
15 if (!count($errors)) {
16   $session = $utils->initSession(); // Read session or create new session and set cookie.
17   $user = array('id' => 0, 'email' => '');
18   $pagetype = 'default';
19   if (is_null($session)) {
20     $errors[] = _('The session system is not working.').' '
21                 .sprintf(_('Please <a href="%s">contact %s</a> and tell the team about this.'), $utils->settings['operator_contact_url'], $utils->settings['operator_name']);
22   }
23   elseif (array_key_exists('logout', $_GET)) {
24     $result = $db->prepare('UPDATE `auth_sessions` SET `logged_in` = FALSE WHERE `id` = :sessid;');
25     if (!$result->execute(array(':sessid' => $session['id']))) {
26       $utils->log('logout_failure', 'session: '.$session['id']);
27       $errors[] = _('Unexpected error while logging out.');
28     }
29     $session['logged_in'] = 0;
30   }
31   elseif (array_key_exists('email', $_POST)) {
32     if (!preg_match('/^[^@]+@([^@]+\.[^@]+|localhost)$/', $_POST['email'])) {
33       $errors[] = _('The email address is invalid.');
34     }
35     elseif ($utils->verifyTimeCode($_POST['tcode'] ?? '', $session)) {
36       $result = $db->prepare('SELECT `id`, `pwdhash`, `email`, `status`, `verify_hash`, `group_id`, `hcheck_question`, `hcheck_solution` FROM `auth_users` WHERE `email` = :email;');
37       $result->execute(array(':email' => $_POST['email']));
38       $user_data = $result->fetch(PDO::FETCH_ASSOC);
39       if ($user_data) {
40         $user = $user_data;
41       }
42       // If we need to add the email to a group, note here which user's group we should be added to - otherwise, set to 0.
43       $addgroup = (array_key_exists('grouptoexisting', $_POST) && intval($session['user']) && ($session['user'] != @$user['id'])) ? $session['user'] : 0;
44       if ($user['id'] && $user['status'] != 'unchecked' && array_key_exists('pwd', $_POST)) {
45         // existing user, check password
46         if (($user['status'] == 'ok') && $utils->pwdVerify(@$_POST['pwd'], $user)) {
47           // Check if a newer hashing algorithm is available
48           // or the cost has changed
49           if ($utils->pwdNeedsRehash($user)) {
50             // If so, create a new hash, and replace the old one
51             $newHash = $utils->pwdHash($_POST['pwd']);
52             $result = $db->prepare('UPDATE `auth_users` SET `pwdhash` = :pwdhash WHERE `id` = :userid;');
53             if (!$result->execute(array(':pwdhash' => $newHash, ':userid' => $user['id']))) {
54               $utils->log('user_hash_save_failure', 'user: '.$user['id']);
55             }
56             else {
57               $utils->log('pwd_rehash_success', 'user: '.$user['id']);
58             }
59           }
60
61           // Log user in - update session key for that, see https://wiki.mozilla.org/WebAppSec/Secure_Coding_Guidelines#Login
62           $utils->log('login', 'user: '.$user['id']);
63           $prev_session = $session;
64           $session = $utils->getLoginSession($user['id'], $session);
65           // 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.
66           if (strlen(@$user['verify_hash'])) {
67             $result = $db->prepare('UPDATE `auth_users` SET `verify_hash` = \'\' WHERE `id` = :userid;');
68             if (!$result->execute(array(':userid' => $user['id']))) {
69               $utils->log('empty_vhash_failure', 'user: '.$user['id']);
70             }
71             else {
72               $user['verify_hash'] = '';
73             }
74           }
75           $utils->doRedirectIfSet($prev_session);
76         }
77         else {
78           $errors[] = _('This password is invalid or your email is not verified yet. Did you type them correctly?');
79         }
80       }
81       else {
82         // new user: check password, create user and send verification; existing users: re-send verification or send password change instructions
83         if (array_key_exists('pwd', $_POST)) {
84           $errors += $utils->checkPasswordConstraints(strval($_POST['pwd']), $_POST['email']);
85         }
86         if (!count($errors)) {
87           // Put user into the DB
88           if (!$user['id']) {
89             $newHash = $utils->pwdHash($_POST['pwd']);
90             $vcode = $utils->createVerificationCode();
91             $result = $db->prepare('INSERT INTO `auth_users` (`email`, `pwdhash`, `status`, `verify_hash`) VALUES (:email, :pwdhash, :status, :vcode);');
92             if (!$result->execute(array(':email' => $_POST['email'], ':pwdhash' => $newHash, ':status' => 'unchecked', ':vcode' => $vcode))) {
93               $utils->log('user_insert_failure', 'email: '.$_POST['email'].' - '.$result->errorInfo()[2]);
94               $errors[] = _('Could not add user.').' '
95                           .sprintf(_('Please <a href="%s">contact %s</a> and tell the team about this.'), $utils->settings['operator_contact_url'], $utils->settings['operator_name']);
96             }
97             $user = [
98               'id' => $db->lastInsertId(),
99               'email' => $_POST['email'],
100               'pwdhash' => $newHash,
101               'status' => 'unchecked',
102               'verify_hash' => $vcode,
103               'hcheck_question' => null,
104               'hcheck_solution' => null,
105             ];
106             $utils->log('new_user', 'user: '.$user['id'].', email: '.$user['email']);
107           }
108           $utils->log('user_log_check', 'user: '.$user['id'].', email: '.$user['email'].', status: '.$user['status']);
109           if ($user['status'] == 'unchecked' && !is_null($user['hcheck_question']) && array_key_exists('hcheck_solution', $_POST)) {
110             if ($_POST['hcheck_solution'] == $user['hcheck_solution']) {
111               $result = $db->prepare('UPDATE `auth_users` SET `status` = :status, `hcheck_question` = :hcquestion, `hcheck_solution` = :hcsolution WHERE `id` = :userid;');
112               if (!$result->execute(array(':status' => 'unverified', ':hcquestion' => null, ':hcsolution' => null, ':userid' => $user['id']))) {
113                 $errors[] = _('Could not update user status.').' '
114                             .sprintf(_('Please <a href="%s">contact %s</a> and tell the team about this.'), $utils->settings['operator_contact_url'], $utils->settings['operator_name']);
115               }
116               $user['status'] = 'unverified';
117               $utils->log('user_checked', 'user: '.$user['id'].', email: '.$user['email']);
118             }
119             else {
120               $errors[] = _('Solution was not correct. Please start over.');
121               $utils->log('user_check_failed', 'user: '.$user['id'].', email: '.$user['email']);
122             }
123           }
124           if ($user['status'] == 'unchecked') {
125             // Display a humanity check.
126             $pagetype = 'human_check';
127             // simple numbers, we stay within the 0 to 100 range
128             $num1 = mt_rand(0, 10);
129             $num2 = mt_rand($num1, 100);
130             $operation = mt_rand(0, 1); // 0 is addition, 1 is subtraction
131             if ($operation == 0) {
132               $user['hcheck_question'] = sprintf(_('%s plus %s equals'), ($num2 - $num1), $num1);
133               $user['hcheck_solution'] = $num2;
134             }
135             else {
136               $user['hcheck_question'] = sprintf(_('%s minus %s equals'), $num2, $num1);
137               $user['hcheck_solution'] = $num2 - $num1;
138             }
139             $result = $db->prepare('UPDATE `auth_users` SET `hcheck_question` = :hcquestion, `hcheck_solution` = :hcsolution WHERE `id` = :userid;');
140             if (!$result->execute(array(':hcquestion' => $user['hcheck_question'], ':hcsolution' => $user['hcheck_solution'], ':userid' => $user['id']))) {
141               $errors[] = _('Could not generate check for being a human.').' '
142                           .sprintf(_('Please <a href="%s">contact %s</a> and tell the team about this.'), $utils->settings['operator_contact_url'], $utils->settings['operator_name']);
143             }
144           }
145           elseif ($user['status'] == 'unverified') {
146             // Send email for verification and show message to point to it.
147             $mail = new email();
148             $mail->setCharset('utf-8');
149             $mail->addHeader('X-KAIRO-AUTH', 'email_verification');
150             $mail->addRecipient($user['email']);
151             $mail->setSender($utils->settings['info_from_email'], sprintf(_('%s Authentication Service'), $utils->settings['operator_name']));
152             $mail->setSubject(sprintf(_('Email Verification for %s Authentication'), $utils->settings['operator_name']));
153             $mail->addMailText(_('Welcome!')."\n\n");
154             $mail->addMailText(sprintf(_('This email address, %s, has been used for registration on "%s".'),
155                                        $user['email'], sprintf(_('%s Authentication Service'), $utils->settings['operator_name']))."\n\n");
156             $mail->addMailText(_('Please confirm that registration by clicking the following link (or calling it up in your browser):')."\n");
157             $mail->addMailText($utils->getDomainBaseURL().strstr($_SERVER['REQUEST_URI'], '?', true)
158                               .'?email='.rawurlencode($user['email']).'&verification_code='.rawurlencode($user['verify_hash'])."\n\n");
159             $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");
160             $mail->addMailText(_('Those websites will get to know your email address but not your password, which we store securely.')."\n");
161             $mail->addMailText(_('If you do not call this confirmation link within 72 hours, your data will be deleted from our database.')."\n\n");
162             $mail->addMailText(sprintf(_('The %s team'), $utils->settings['operator_name']));
163             //$mail->setDebugAddress("robert@localhost");
164             $mailsent = $mail->send();
165             if ($mailsent) {
166               $pagetype = 'verification_sent';
167             }
168             else {
169               $utils->log('verify_mail_failure', 'user: '.$user['id'].', email: '.$user['email']);
170               $errors[] = _('The confirmation email could not be sent to you.').' '
171                           .sprintf(_('Please <a href="%s">contact %s</a> and tell the team about this.'), $utils->settings['operator_contact_url'], $utils->settings['operator_name']);
172             }
173           }
174           else {
175             // Password reset requested with "Password forgotten?" function.
176             $vcode = $utils->createVerificationCode();
177             $result = $db->prepare('UPDATE `auth_users` SET `verify_hash` = :vcode WHERE `id` = :userid;');
178             if (!$result->execute(array(':vcode' => $vcode, ':userid' => $user['id']))) {
179               $utils->log('vhash_set_failure', 'user: '.$user['id']);
180               $errors[] = _('Could not initiate reset request.').' '
181                           .sprintf(_('Please <a href="%s">contact %s</a> and tell the team about this.'), $utils->settings['operator_contact_url'], $utils->settings['operator_name']);
182             }
183             else {
184               $utils->log('pwd_reset_request', 'user: '.$user['id'].', email: '.$user['email']);
185               $resetcode = $vcode.dechex($user['id'] + $session['id']).'_'.$utils->createTimeCode($session, null, 60);
186               // Send email with instructions for resetting the password.
187               $mail = new email();
188               $mail->setCharset('utf-8');
189               $mail->addHeader('X-KAIRO-AUTH', 'password_reset');
190               $mail->addRecipient($user['email']);
191               $mail->setSender($utils->settings['info_from_email'], sprintf(_('%s Authentication Service'), $utils->settings['operator_name']));
192               $mail->setSubject(sprintf(_('How to reset your password for %s Authentication'), $utils->settings['operator_name']));
193               $mail->addMailText(_('Hi,')."\n\n");
194               $mail->addMailText(sprintf(_('A request for setting a new password for this email address, %s, has been submitted on "%s".'),
195                                          $user['email'], sprintf(_('%s Authentication Service'), $utils->settings['operator_name']))."\n\n");
196               $mail->addMailText(_('You can set a new password by clicking the following link (or calling it up in your browser):')."\n");
197               $mail->addMailText($utils->getDomainBaseURL().strstr($_SERVER['REQUEST_URI'], '?', true)
198                                 .'?email='.rawurlencode($user['email']).'&reset_code='.rawurlencode($resetcode)."\n\n");
199               $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");
200               $mail->addMailText(sprintf(_('The %s team'), $utils->settings['operator_name']));
201               //$mail->setDebugAddress("robert@localhost");
202               $mailsent = $mail->send();
203               if ($mailsent) {
204                 $pagetype = 'resetmail_sent';
205               }
206               else {
207                 $utils->log('pwd_reset_mail_failure', 'user: '.$user['id'].', email: '.$user['email']);
208                 $errors[] = _('The email with password reset instructions could not be sent to you.').' '
209                             .sprintf(_('Please <a href="%s">contact %s</a> and tell the team about this.'), $utils->settings['operator_contact_url'], $utils->settings['operator_name']);
210               }
211             }
212           }
213         }
214       }
215       if (!count($errors) && ($addgroup > 0)) {
216         // We should add the login email to the group of that existing user.
217         $result = $db->prepare('SELECT `group_id` FROM `auth_users` WHERE `id` = :userid;');
218         $result->execute(array(':userid' => $addgroup));
219         $grpuser = $result->fetch(PDO::FETCH_ASSOC);
220         if (!intval($grpuser['group_id'])) {
221           // If that user doesn't have a group, put him into a group with his own user ID.
222           $result = $db->prepare('UPDATE `auth_users` SET `group_id` = :groupid WHERE `id` = :userid;');
223           if (!$result->execute(array(':groupid' => $addgroup, ':userid' => $addgroup))) {
224             $utils->log('group_save_failure', 'user: '.$addgroup);
225           }
226           else {
227             $utils->log('new grouping', 'user: '.$addgroup.', group: '.$addgroup);
228           }
229         }
230         // Save grouping for the new or logged-in user.
231         $result = $db->prepare('UPDATE `auth_users` SET `group_id` = :groupid WHERE `id` = :userid;');
232         if (!$result->execute(array(':groupid' => $addgroup, ':userid' => $user['id']))) {
233           $utils->log('group_save_failure', 'user: '.$user['id']);
234         }
235         else {
236           $utils->log('new grouping', 'user: '.$user['id'].', group: '.$addgroup);
237           $user['group_id'] = $addgroup;
238         }
239       }
240     }
241     else {
242       $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.');
243     }
244   }
245   elseif (array_key_exists('reset', $_GET)) {
246     if ($session['logged_in']) {
247       $result = $db->prepare('SELECT `id`,`email`,`group_id` FROM `auth_users` WHERE `id` = :userid;');
248       $result->execute(array(':userid' => $session['user']));
249       $user = $result->fetch(PDO::FETCH_ASSOC);
250       if (!$user['id']) {
251         $utils->log('reset_user_read_failure', 'user: '.$session['user']);
252       }
253       $pagetype = 'resetpwd';
254     }
255     else {
256       // Display form for entering email.
257       $pagetype = 'resetstart';
258     }
259   }
260   elseif (array_key_exists('verification_code', $_GET)) {
261     $result = $db->prepare('SELECT `id`,`email`,`group_id` FROM `auth_users` WHERE `email` = :email AND `status` = \'unverified\' AND `verify_hash` = :vcode;');
262     $result->execute(array(':email' => @$_GET['email'], ':vcode' => $_GET['verification_code']));
263     $user = $result->fetch(PDO::FETCH_ASSOC);
264     if ($user['id']) {
265       $result = $db->prepare('UPDATE `auth_users` SET `verify_hash` = \'\', `status` = \'ok\' WHERE `id` = :userid;');
266       if (!$result->execute(array(':userid' => $user['id']))) {
267         $utils->log('verification_save_failure', 'user: '.$user['id']);
268         $errors[] = _('Could not save confirmation.').' '
269                     .sprintf(_('Please <a href="%s">contact %s</a> and tell the team about this.'), $utils->settings['operator_contact_url'], $utils->settings['operator_name']);
270       }
271       $pagetype = 'verification_done';
272     }
273     else {
274       $errors[] = _('The confirmation link you called is not valid. Possibly it has expired and you need to try registering again.');
275     }
276   }
277   elseif (array_key_exists('reset_code', $_GET)) {
278     $reset_fail = true;
279     $result = $db->prepare('SELECT `id`,`email`,`verify_hash`,`group_id` FROM `auth_users` WHERE `email` = :email');
280     $result->execute(array(':email' => @$_GET['email']));
281     $user = $result->fetch(PDO::FETCH_ASSOC);
282     if ($user['id']) {
283       // Deconstruct reset code and verify it.
284       if (preg_match('/^([0-9a-f]{'.strlen($user['verify_hash']).'})([0-9a-f]+)_(\d+\.\d+)$/', $_GET['reset_code'], $regs)) {
285         $tcode_sessid = hexdec($regs[2]) - $user['id'];
286         $result = $db->prepare('SELECT `id`,`sesskey` FROM `auth_sessions` WHERE `id` = :sessid;');
287         $result->execute(array(':sessid' => $tcode_sessid));
288         $row = $result->fetch(PDO::FETCH_ASSOC);
289         if ($row) {
290           $tcode_session = $row;
291           if (($regs[1] == $user['verify_hash']) &&
292               $utils->verifyTimeCode($regs[3], $session, 60)) {
293             // Set a new verify_hash for the actual password reset.
294             $user['verify_hash'] = $utils->createVerificationCode();
295             $result = $db->prepare('UPDATE `auth_users` SET `verify_hash` = :vcode WHERE `id` = :userid;');
296             if (!$result->execute(array(':vcode' => $user['verify_hash'], ':userid' => $user['id']))) {
297               $utils->log('vhash_reset_failure', 'user: '.$user['id']);
298             }
299             $result = $db->prepare('UPDATE `auth_sessions` SET `user` = :userid WHERE `id` = :sessid;');
300             if (!$result->execute(array(':userid' => $user['id'], ':sessid' => $session['id']))) {
301               $utils->log('reset_session_set_user_failure', 'session: '.$session['id']);
302             }
303             $pagetype = 'resetpwd';
304             $reset_fail = false;
305           }
306         }
307       }
308     }
309     if ($reset_fail) {
310       $errors[] = _('The password reset link you called is not valid. Possibly it has expired and you need to call the "Password forgotten?" function again.');
311     }
312   }
313   elseif (array_key_exists('clients', $_GET)) {
314     $result = $db->prepare('SELECT `id`,`email`,`group_id` FROM `auth_users` WHERE `id` = :userid;');
315     $result->execute(array(':userid' => $session['user']));
316     $user = $result->fetch(PDO::FETCH_ASSOC);
317     if ($session['logged_in'] && $user['id']) {
318       if (array_key_exists('client_id', $_POST) && (strlen($_POST['client_id']) >= 5)) {
319         $clientid = $_POST['client_id'];
320         $clientsecret = $utils->createClientSecret();
321         $rediruri = strval(@$_POST['redirect_uri']);
322         $scope = strval(@$_POST['scope']);
323         $result = $db->prepare('INSERT INTO `oauth_clients` (`client_id`, `client_secret`, `redirect_uri`, `scope`, `user_id`) VALUES (:clientid, :secret, :rediruri, :scope, :userid);');
324         if (!$result->execute(array(':clientid' => $clientid,
325                                     ':secret' => $clientsecret,
326                                     ':rediruri' => $rediruri,
327                                     ':scope' => $scope,
328                                     ':userid' => $user['id']))) {
329           $utils->log('client_save_failure', 'client: '.$clientid);
330           $errors[] = _('Unexpectedly failed to save new client information.').' '
331                       .sprintf(_('Please <a href="%s">contact %s</a> and tell the team about this.'), $utils->settings['operator_contact_url'], $utils->settings['operator_name']);
332         }
333       }
334       if (!count($errors)) {
335         // List clients
336         $result = $db->prepare('SELECT `client_id`,`client_secret`,`redirect_uri`,`scope` FROM `oauth_clients` WHERE `user_id` = :userid;');
337         $result->execute(array(':userid' => $user['id']));
338         $clients = $result->fetchAll(PDO::FETCH_ASSOC);
339         if (!$clients) { $clients = array(); }
340         $pagetype = 'clientlist';
341       }
342     }
343     else {
344       $errors[] = _('This function is only available if you are logged in.');
345     }
346   }
347   elseif (intval($session['user'])) {
348     $result = $db->prepare('SELECT `id`,`email`,`verify_hash`,`group_id` FROM `auth_users` WHERE `id` = :userid;');
349     $result->execute(array(':userid' => $session['user']));
350     $user = $result->fetch(PDO::FETCH_ASSOC);
351     if (!$user['id']) {
352       $utils->log('user_read_failure', 'user: '.$session['user']);
353     }
354     // Password reset requested.
355     if (array_key_exists('pwd', $_POST) && array_key_exists('reset', $_POST) && array_key_exists('tcode', $_POST)) {
356       // If not logged in, a password reset needs to have the proper vcode set.
357       if (!$session['logged_in'] && (!strlen(@$_POST['vcode']) || ($_POST['vcode'] != $user['verify_hash']))) {
358         $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.');
359       }
360       // If not logged in, a password reset also needs to have the proper email set.
361       if (!$session['logged_in'] && !count($errors) && (@$_POST['email_hidden'] != $user['email'])) {
362         $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.');
363       }
364       // Check validity of time code.
365       if (!count($errors) && !$utils->verifyTimeCode($_POST['tcode'], $session)) {
366         $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.');
367       }
368       $errors += $utils->checkPasswordConstraints(strval($_POST['pwd']), $user['email']);
369       if (!count($errors)) {
370         $newHash = $utils->pwdHash($_POST['pwd']);
371         $result = $db->prepare('UPDATE `auth_users` SET `pwdhash` = :pwdhash, `verify_hash` = \'\' WHERE `id` = :userid;');
372         if (!$result->execute(array(':pwdhash' => $newHash, ':userid' => $session['user']))) {
373           $utils->log('pwd_reset_failure', 'user: '.$session['user']);
374           $errors[] = _('Password reset failed.').' '
375                       .sprintf(_('Please <a href="%s">contact %s</a> and tell the team about this.'), $utils->settings['operator_contact_url'], $utils->settings['operator_name']);
376         }
377         else {
378           $pagetype = 'reset_done';
379         }
380       }
381     }
382     else {
383       $utils->doRedirectIfSet($session);
384     }
385   }
386 }
387
388 if (!count($errors)) {
389   if ($pagetype == 'human_check') {
390     $para = $body->appendElement('p', _('This is a new registration, please verify that you are a human by solving the calculation below.'));
391     $para->setAttribute('class', 'humancheckinfo');
392     $form = $body->appendForm('./', 'POST', 'humancheckform');
393     $form->setAttribute('id', 'humancheckform');
394     $ulist = $form->appendElement('ul');
395     $ulist->setAttribute('class', 'flat humancheck');
396     $litem = $ulist->appendElement('li');
397     $litem->setAttribute('class', 'donotshow');
398     $inptxt = $litem->appendInputEmail('email', 30, 20, 'login_email', $user['email']);
399     $inptxt->setAttribute('autocomplete', 'email');
400     $inptxt->setAttribute('placeholder', _('Email'));
401     $litem = $ulist->appendElement('li');
402     $litem->appendText($user['hcheck_question'].' ');
403     $inptxt = $litem->appendInputText('hcheck_solution', 20, 10, 'hcheck_solution');
404     $litem = $ulist->appendElement('li');
405     $litem->appendInputHidden('tcode', $utils->createTimeCode($session));
406     $submit = $litem->appendInputSubmit(_('Continue Registration'));
407     $para = $form->appendElement('p');
408     $para->setAttribute('class', 'toplink small');
409     $link = $para->appendLink('./', _('Cancel'));
410   }
411   elseif ($pagetype == 'verification_sent') {
412     $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']));
413     $para->setAttribute('class', 'verifyinfo pending');
414     $para = $body->appendElement('p', _('Reload this page after you confirm to continue.'));
415     $para->setAttribute('class', 'verifyinfo pending');
416     $para = $body->appendElement('p');
417     $para->setAttribute('class', 'verifyinfo pending');
418     $link = $para->appendLink('./', _('Reload'));
419   }
420   elseif ($pagetype == 'resetmail_sent') {
421     $para = $body->appendElement('p',
422         _('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.'));
423     $para->setAttribute('class', 'resetinfo pending');
424     $para = $body->appendElement('p');
425     $para->setAttribute('class', 'resetinfo pending small');
426     $link = $para->appendLink('./', _('Back to top'));
427   }
428   elseif ($pagetype == 'resetstart') {
429     $para = $body->appendElement('p', _('If you forgot your password or didn\'t receive the registration confirmation, please enter your email here.'));
430     $para->setAttribute('class', '');
431     $form = $body->appendForm('./?reset', 'POST', 'resetform');
432     $form->setAttribute('id', 'loginform');
433     $form->setAttribute('class', 'loginarea hidden');
434     $ulist = $form->appendElement('ul');
435     $ulist->setAttribute('class', 'flat login');
436     $litem = $ulist->appendElement('li');
437     $inptxt = $litem->appendInputEmail('email', 30, 20, 'login_email');
438     $inptxt->setAttribute('autocomplete', 'email');
439     $inptxt->setAttribute('required', '');
440     $inptxt->setAttribute('placeholder', _('Email'));
441     $litem = $ulist->appendElement('li');
442     $litem->appendInputHidden('tcode', $utils->createTimeCode($session));
443     $submit = $litem->appendInputSubmit(_('Send instructions to email'));
444     $para = $form->appendElement('p');
445     $para->setAttribute('class', 'toplink small');
446     $link = $para->appendLink('./', _('Cancel'));
447   }
448   elseif ($pagetype == 'resetpwd') {
449     $para = $body->appendElement('p', sprintf(_('You can set a new password for %s here.'), $user['email']));
450     $para->setAttribute('class', 'newpwdinfo');
451     $form = $body->appendForm('./', 'POST', 'newpwdform');
452     $form->setAttribute('id', 'loginform');
453     $form->setAttribute('class', 'loginarea hidden');
454     $ulist = $form->appendElement('ul');
455     $ulist->setAttribute('class', 'flat login');
456     $litem = $ulist->appendElement('li');
457     $litem->setAttribute('class', 'donotshow');
458     $inptxt = $litem->appendInputEmail('email_hidden', 30, 20, 'login_email', $user['email']);
459     $inptxt->setAttribute('autocomplete', 'email');
460     $inptxt->setAttribute('placeholder', _('Email'));
461     $litem = $ulist->appendElement('li');
462     $inptxt = $litem->appendInputPassword('pwd', 20, 20, 'login_pwd', '');
463     $inptxt->setAttribute('required', '');
464     $inptxt->setAttribute('placeholder', _('Password'));
465     $inptxt->setAttribute('class', 'login');
466     $litem = $ulist->appendElement('li');
467     $litem->appendInputHidden('reset', '');
468     $litem->appendInputHidden('tcode', $utils->createTimeCode($session));
469     if (!$session['logged_in'] && strlen(@$user['verify_hash'])) {
470       $litem->appendInputHidden('vcode', $user['verify_hash']);
471     }
472     $submit = $litem->appendInputSubmit(_('Save password'));
473     $para = $form->appendElement('p');
474     $para->setAttribute('class', 'toplink small');
475     $link = $para->appendLink('./', _('Cancel'));
476   }
477   elseif ($pagetype == 'clientlist') {
478     $scopes = array('clientreg', 'email');
479     $form = $body->appendForm('?clients', 'POST', 'newclientform');
480     $form->setAttribute('id', 'clientform');
481     $tbl = $form->appendElement('table');
482     $tbl->setAttribute('class', 'clientlist border');
483     $thead = $tbl->appendElement('thead');
484     $trow = $thead->appendElement('tr');
485     $trow->appendElement('th', _('Client ID'));
486     $trow->appendElement('th', _('Client Secrect'));
487     $trow->appendElement('th', _('Redirect URI'));
488     $trow->appendElement('th', _('Scope'));
489     $trow->appendElement('th');
490     $tbody = $tbl->appendElement('tbody');
491     foreach ($clients as $client) {
492       $trow = $tbody->appendElement('tr');
493       $trow->appendElement('td', $client['client_id']);
494       $trow->appendElement('td', $client['client_secret']);
495       $trow->appendElement('td', $client['redirect_uri']);
496       $trow->appendElement('td', $client['scope']);
497       $trow->appendElement('td'); // Future: Delete link?
498     }
499     // Form fields for adding a new one.
500     $tfoot = $tbl->appendElement('tfoot');
501     $trow = $tfoot->appendElement('tr');
502     $cell = $trow->appendElement('td');
503     $inptxt = $cell->appendInputText('client_id', 80, 25, 'client_id');
504     $cell = $trow->appendElement('td'); // Empty, as secret will be generated.
505     $cell = $trow->appendElement('td');
506     $inptxt = $cell->appendInputText('redirect_uri', 500, 50, 'redirect_uri');
507     $cell = $trow->appendElement('td');
508     $select = $cell->appendElementSelect('scope');
509     foreach ($scopes as $scope) {
510       $select->appendElementOption($scope, $scope);
511     }
512     //$inptxt = $cell->appendInputText('scope', 100, 20, 'scope');
513     $cell = $trow->appendElement('td');
514     $submit = $cell->appendInputSubmit(_('Create'));
515     $para = $form->appendElement('p');
516     $para->setAttribute('class', 'toplink');
517     $link = $para->appendLink('./', _('Back to top'));
518   }
519   elseif ($session['logged_in'] && (!array_key_exists('addemail', $_GET))) {
520     if ($pagetype == 'reset_done') {
521       $para = $body->appendElement('p', _('Your password has successfully been reset.'));
522       $para->setAttribute('class', 'resetinfo done');
523     }
524     $div = $body->appendElement('div', $user['email']);
525     $div->setAttribute('class', 'loginheader');
526     $groupmails = $utils->getGroupedEmails($user['group_id'], $user['email']);
527     if (count($groupmails)) {
528       $para = $div->appendElement('p', _('Grouped with: ').implode(', ', $groupmails));
529       $para->setAttribute('class', 'small groupmails');
530     }
531     $div = $body->appendElement('div');
532     $div->setAttribute('class', 'loginlinks');
533     $ulist = $div->appendElement('ul');
534     $ulist->setAttribute('class', 'flat');
535     $litem = $ulist->appendElement('li');
536     $link = $litem->appendLink('./?logout', _('Log out'));
537     $litem = $ulist->appendElement('li');
538     $link = $litem->appendLink('./?addemail', _('Add another email address'));
539     if (($utils->client_reg_email_whitelist === false) || (in_array($user['email'], $utils->client_reg_email_whitelist))) {
540       $litem = $ulist->appendElement('li');
541       $link = $litem->appendLink('./?clients', _('Manage OAuth2 clients'));
542     }
543     $litem = $ulist->appendElement('li');
544     $litem->appendLink('./?reset', _('Set new password'));
545   }
546   else { // not logged in
547     $addfields = array();
548     if ($pagetype == 'verification_done') {
549       $para = $body->appendElement('p', _('Hooray! Your email was successfully confirmed! You can log in now.'));
550       $para->setAttribute('class', 'verifyinfo done');
551     }
552     elseif ($pagetype == 'reset_done') {
553       $para = $body->appendElement('p', _('Your password has successfully been reset. You can log in now with the new password.'));
554       $para->setAttribute('class', 'resetinfo done');
555     }
556     elseif (array_key_exists('addemail', $_GET)) {
557       $para = $body->appendElement('p', sprintf(_('Add another email grouped with %s by either logging in with it or specifying the email and a new password to use.'), $user['email']));
558       $para->setAttribute('class', 'addemailinfo');
559       $addfields['grouptoexisting'] = '1';
560     }
561     $utils->appendLoginForm($body, $session, $user, $addfields);
562   }
563 }
564
565 if (count($errors)) {
566   $body->appendElement('p', ((count($errors) <= 1)
567                             ?_('The following error was detected')
568                             :_('The following errors were detected')).':');
569   $list = $body->appendElement('ul');
570   $list->setAttribute('class', 'flat warn');
571   foreach ($errors as $msg) {
572     $item = $list->appendElement('li');
573     $item->appendHTMLMarkup($msg);
574   }
575   $body->appendButton(_('Back'), 'history.back();');
576 }
577
578 // Send HTML to client.
579 print($document->saveHTML());
580 ?>