Moving to GitHub.
[platal.git] / modules / platal.php
index 197728e..1772921 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /***************************************************************************
- *  Copyright (C) 2003-2010 Polytechnique.org                              *
+ *  Copyright (C) 2003-2014 Polytechnique.org                              *
  *  http://opensource.polytechnique.org/                                   *
  *                                                                         *
  *  This program is free software; you can redistribute it and/or modify   *
@@ -21,7 +21,7 @@
 
 function bugize($list)
 {
-    $list = split(',', $list);
+    $list = preg_split('/,/', $list, -1, PREG_SPLIT_NO_EMPTY);
     $ans  = array();
 
     foreach ($list as $bug) {
@@ -38,28 +38,33 @@ class PlatalModule extends PLModule
     function handlers()
     {
         return array(
-            'index'             => $this->make_hook('index',     AUTH_PUBLIC),
-            'cacert.pem'        => $this->make_hook('cacert',    AUTH_PUBLIC),
-            'changelog'         => $this->make_hook('changelog', AUTH_PUBLIC),
+            'index'             => $this->make_hook('index',        AUTH_PUBLIC),
+            'cacert.pem'        => $this->make_hook('cacert',       AUTH_PUBLIC),
+            'changelog'         => $this->make_hook('changelog',    AUTH_PUBLIC),
 
             // Preferences thingies
-            'prefs'             => $this->make_hook('prefs',     AUTH_COOKIE),
-            'prefs/rss'         => $this->make_hook('prefs_rss', AUTH_COOKIE),
-            'prefs/webredirect' => $this->make_hook('webredir',  AUTH_MDP),
-            'prefs/skin'        => $this->make_hook('skin',      AUTH_COOKIE),
+            'prefs'             => $this->make_hook('prefs',        AUTH_COOKIE, 'user,groups'),
+            'prefs/rss'         => $this->make_hook('prefs_rss',    AUTH_COOKIE, 'user'),
+            'prefs/webredirect' => $this->make_hook('webredir',     AUTH_PASSWD, 'mail'),
+            'prefs/skin'        => $this->make_hook('skin',         AUTH_COOKIE, 'user'),
+            'prefs/email'       => $this->make_hook('prefs_email',  AUTH_COOKIE, 'mail'),
 
             // password related thingies
-            'password'          => $this->make_hook('password',  AUTH_MDP),
-            'tmpPWD'            => $this->make_hook('tmpPWD',    AUTH_PUBLIC),
-            'password/smtp'     => $this->make_hook('smtppass',  AUTH_MDP),
-            'recovery'          => $this->make_hook('recovery',  AUTH_PUBLIC),
-            'exit'              => $this->make_hook('exit',      AUTH_PUBLIC),
-            'review'            => $this->make_hook('review',    AUTH_PUBLIC),
-            'deconnexion.php'   => $this->make_hook('exit',      AUTH_PUBLIC),
+            'password'          => $this->make_hook('password',     AUTH_PASSWD, 'user,groups'),
+            'password/smtp'     => $this->make_hook('smtppass',     AUTH_PASSWD, 'mail'),
+            'tmpPWD'            => $this->make_hook('tmpPWD',       AUTH_PUBLIC),
+            'recovery'          => $this->make_hook('recovery',     AUTH_PUBLIC),
+            'recovery/ext'      => $this->make_hook('recovery_ext', AUTH_PUBLIC),
+            'register/ext'      => $this->make_hook('register_ext', AUTH_PUBLIC),
+            'exit'              => $this->make_hook('exit',         AUTH_PUBLIC),
+            'review'            => $this->make_hook('review',       AUTH_PUBLIC),
+            'deconnexion.php'   => $this->make_hook('exit',         AUTH_PUBLIC),
+
+            'error'             => $this->make_hook('test_error',   AUTH_COOKIE),
         );
     }
 
-    function handler_index(&$page)
+    function handler_index($page)
     {
         // Include X-XRDS-Location response-header for Yadis discovery
         global $globals;
@@ -73,14 +78,14 @@ class PlatalModule extends PLModule
         }
     }
 
-    function handler_cacert(&$page)
+    function handler_cacert($page)
     {
         pl_cached_content_headers("application/x-x509-ca-cert");
         readfile("/etc/ssl/xorgCA/cacert.pem");
         exit;
     }
 
-    function handler_changelog(&$page, $core = null)
+    function handler_changelog($page, $core = null)
     {
         $page->changeTpl('platal/changeLog.tpl');
 
@@ -113,46 +118,40 @@ class PlatalModule extends PLModule
     function __set_rss_state($state)
     {
         if ($state) {
-            S::set('token', rand_url_id(16));
-            XDB::execute('UPDATE  accounts
-                             SET  token = {?}
-                           WHERE  uid = {?}', S::s('token'), S::i('uid'));
+            if (!S::user()->token) {
+                S::user()->token = rand_url_id(16);
+                S::set('token', S::user()->token);
+                XDB::execute('UPDATE  accounts
+                                 SET  token = {?}
+                               WHERE  uid = {?}', S::user()->token, S::i('uid'));
+            }
         } else {
             S::kill('token');
+            S::user()->token = null;
             XDB::execute('UPDATE  accounts
                              SET  token = NULL
                            WHERE  uid = {?}', S::i('uid'));
         }
     }
 
-    function handler_prefs(&$page)
+    function handler_prefs($page)
     {
         $page->changeTpl('platal/preferences.tpl');
         $page->setTitle('Mes préférences');
 
         if (Post::has('email_format')) {
+            S::assert_xsrf_token();
             $fmt = Post::s('email_format');
-            XDB::execute("UPDATE accounts
-                             SET email_format = {?}
-                           WHERE uid = {?}",
-                         $fmt, S::v('uid'));
-            S::set('email_format', $fmt);
+            S::user()->setEmailFormat($fmt);
         }
 
         if (Post::has('rss')) {
-            $this->__set_rss_state(Post::b('rss'));
+            S::assert_xsrf_token();
+            $this->__set_rss_state(Post::s('rss') == 'on');
         }
-
-        # FIXME: this code is not multi-domain compatible. We should decide how
-        # carva will extend to users not in the main domain.
-        $res = XDB::query("SELECT  alias
-                             FROM  aliases
-                            WHERE  uid = {?} AND FIND_IN_SET('bestalias', flags)",
-                          S::user()->id());
-        $page->assign('bestalias', $res->fetchOneCell());
     }
 
-    function handler_webredir(&$page)
+    function handler_webredir($page)
     {
         $page->changeTpl('platal/webredirect.tpl');
         $page->setTitle('Redirection de page WEB');
@@ -162,8 +161,9 @@ class PlatalModule extends PLModule
                 $page->trigError('URL invalide');
             } else {
                 $url = Env::t('url');
-                XDB::execute('REPLACE INTO  carvas (uid, url)
-                                    VALUES  ({?}, {?})',
+                XDB::execute('INSERT INTO  carvas (uid, url)
+                                   VALUES  ({?}, {?})
+                  ON DUPLICATE KEY UPDATE  url = VALUES(url)',
                              S::i('uid'), $url);
                 S::logger()->log('carva_add', 'http://' . $url);
                 $page->trigSuccess("Redirection activée vers <a href='http://$url'>$url</a>");
@@ -183,14 +183,14 @@ class PlatalModule extends PLModule
 
         # FIXME: this code is not multi-domain compatible. We should decide how
         # carva will extend to users not in the main domain.
-        $res = XDB::query("SELECT  alias
-                             FROM  aliases
-                            WHERE  uid = {?} AND FIND_IN_SET('bestalias', flags)",
-                          S::user()->id());
-        $page->assign('bestalias', $res->fetchOneCell());
+        $best = XDB::fetchOneCell('SELECT  email
+                                     FROM  email_source_account
+                                    WHERE  uid = {?} AND FIND_IN_SET(\'bestalias\', flags)',
+                                  S::user()->id());
+        $page->assign('bestalias', $best);
     }
 
-    function handler_prefs_rss(&$page)
+    function handler_prefs_rss($page)
     {
         $page->changeTpl('platal/filrss.tpl');
 
@@ -202,14 +202,55 @@ class PlatalModule extends PLModule
         }
     }
 
-    function handler_password(&$page)
+    function handler_prefs_email($page)
+    {
+        $page->changeTpl('platal/email_preferences.tpl');
+
+        if (Post::has('submit')) {
+            S::assert_xsrf_token();
+
+            $from_email = Post::t('from_email');
+            $from_format = Post::v('from_format');
+
+            // Checks email.
+            $email_regex = '/^[a-z0-9.\-+_\$]+@([\-.+_]?[a-z0-9])+$/i';
+            if (!preg_match($email_regex, $from_email)) {
+                $full_regex = '/^[^<]*<[a-z0-9.\-+_\$]+@([\-.+_]?[a-z0-9])+>$/i';
+                if (!preg_match($full_regex, $from_email)) {
+                    $page->trigError("L'adresse email est erronée.");
+                    $error = true;
+                    $page->assign('from_email', $from_email);
+                    $page->assign('from_format', $from_format);
+                    $page->assign('error', true);
+                    return;
+                }
+            }
+
+            // Saves data.
+            XDB::execute('UPDATE  accounts
+                             SET  from_email = {?}, from_format = {?}
+                           WHERE  uid = {?}',
+                         $from_email, ($from_format == 'html' ? 'html' : 'text'), S::user()->id());
+            $page->trigSuccess('Données enregistrées.');
+        }
+
+        $data = XDB::fetchOneAssoc('SELECT  from_email, from_format
+                                      FROM  accounts
+                                     WHERE  uid = {?}',
+                                   S::user()->id());
+        $page->assign('from_email', $data['from_email']);
+        $page->assign('from_format', $data['from_format']);
+        $page->assign('error', false);
+    }
+
+    function handler_password($page)
     {
         global $globals;
 
-        if (Post::has('response2'))  {
+        if (Post::has('pwhash') && Post::t('pwhash'))  {
             S::assert_xsrf_token();
 
-            S::set('password', $password = Post::v('response2'));
+            S::set('password', $password = Post::t('pwhash'));
             XDB::execute('UPDATE  accounts
                              SET  password = {?}
                            WHERE  uid={?}', $password,
@@ -228,16 +269,16 @@ class PlatalModule extends PLModule
             S::logger()->log('passwd');
             Platal::session()->setAccessCookie(true);
 
-            $page->changeTpl('platal/motdepasse.success.tpl');
+            $page->changeTpl('platal/password.success.tpl');
             $page->run();
         }
 
-        $page->changeTpl('platal/motdepasse.tpl');
-        $page->addJsLink('motdepasse.js');
+        $page->changeTpl('platal/password.tpl');
         $page->setTitle('Mon mot de passe');
+        $page->assign('do_auth', 0);
     }
 
-    function handler_smtppass(&$page)
+    function handler_smtppass($page)
     {
         $page->changeTpl('platal/acces_smtp.tpl');
         $page->setTitle('Acces SMTP/NNTP');
@@ -271,7 +312,7 @@ class PlatalModule extends PLModule
         $page->assign('actif', $res->fetchOneCell());
     }
 
-    function handler_recovery(&$page)
+    function handler_recovery($page)
     {
         global $globals;
 
@@ -281,7 +322,7 @@ class PlatalModule extends PLModule
             return;
         }
 
-        if (!ereg('[0-3][0-9][0-1][0-9][1][9]([0-9]{2})', Env::v('birth'))) {
+        if (!preg_match('/^[0-3][0-9][0-1][0-9][1][9]([0-9]{2})$/', Env::v('birth'))) {
             $page->trigError('Date de naissance incorrecte ou incohérente');
             return;
         }
@@ -306,35 +347,40 @@ class PlatalModule extends PLModule
             return;
         }
 
-        $res = XDB::query("SELECT  COUNT(*)
-                             FROM  emails
-                            WHERE  uid = {?} AND flags != 'panne' AND flags != 'filter'", $user->id());
-        $count = intval($res->fetchOneCell());
-        if ($count == 0) {
+        if ($user->lost) {
             $page->assign('no_addr', true);
             return;
         }
 
         $page->assign('ok', true);
 
-        $url   = rand_url_id();
+        $url = rand_url_id();
         XDB::execute('INSERT INTO  account_lost_passwords (certificat,uid,created)
                            VALUES  ({?},{?},NOW())', $url, $user->id());
-        $res   = XDB::query('SELECT  email
-                               FROM  emails
-                              WHERE  uid = {?} AND email = {?}',
-                            $user->id(), Post::v('email'));
-        if ($res->numRows()) {
-            $mails = $res->fetchOneCell();
-        } else {
-            $res   = XDB::query("SELECT  email
-                                   FROM  emails
-                                  WHERE  uid = {?} AND NOT FIND_IN_SET('filter', flags)", $user->id());
-            $mails = implode(', ', $res->fetchColumn());
+        $to = XDB::fetchOneCell('SELECT  redirect
+                                   FROM  email_redirect_account
+                                  WHERE  uid = {?} AND redirect = {?}',
+                                $user->id(), Post::t('email'));
+        if (is_null($to)) {
+            $emails = XDB::fetchColumn('SELECT  redirect
+                                          FROM  email_redirect_account
+                                         WHERE  uid = {?} AND flags = \'inactive\' AND type = \'smtp\'',
+                                       $user->id());
+            $inactives_to = implode(', ', $emails);
         }
         $mymail = new PlMailer();
         $mymail->setFrom('"Gestion des mots de passe" <support+password@' . $globals->mail->domain . '>');
-        $mymail->addTo($mails);
+        if (is_null($to)) {
+            $mymail->addTo($user);
+            $log_to = $user->bestEmail();
+            if (!is_null($inactives_to)) {
+                $log_to = $inactives_to . ', ' . $log_to;
+                $mymail->addTo($inactives_to);
+            }
+        } else {
+            $mymail->addTo($to);
+            $log_to = $to;
+        }
         $mymail->setSubject("Ton certificat d'authentification");
         $mymail->setTxtBody("Visite la page suivante qui expire dans six heures :
 {$globals->baseurl}/tmpPWD/$url
@@ -345,38 +391,65 @@ Si en cliquant dessus tu n'y arrives pas, copie intégralement l'adresse dans la
 Polytechnique.org
 \"Le portail des élèves & anciens élèves de l'École polytechnique\"
 
-Email envoyé à ".Env::v('login') . (Post::has('email') ? "
-Adresse de secours : " . Post::v('email') : ""));
+Email envoyé à ".Env::v('login') . (is_null($to) ? '' : '
+Adresse de secours : ' . $to));
         $mymail->send();
 
-        // on cree un objet logger et on log l'evenement
-        S::logger($user->id())->log('recovery', $mails);
+        S::logger($user->id())->log('recovery', $log_to);
     }
 
-    function handler_tmpPWD(&$page, $certif = null)
+    function handler_recovery_ext($page)
+    {
+        $page->changeTpl('xnet/recovery.tpl');
+
+        if (!Post::has('login')) {
+            return;
+        }
+
+        $user = User::getSilent(Post::t('login'));
+        if (is_null($user)) {
+            $page->trigError('Le compte n\'existe pas.');
+            return;
+        }
+        if ($user->state != 'active') {
+            $page->trigError('Ton compte n\'est pas activé.');
+            return;
+        }
+
+        $page->assign('ok', true);
+
+        $hash = rand_url_id();
+        XDB::execute('INSERT INTO  account_lost_passwords (uid, created, certificat)
+                           VALUES  ({?}, NOW(), {?})',
+                     $user->id(), $hash);
+
+        $mymail = new PlMailer('platal/password_recovery_xnet.mail.tpl');
+        $mymail->setTo($user);
+        $mymail->assign('hash', $hash);
+        $mymail->assign('email', Post::t('login'));
+        $mymail->send();
+
+        S::logger($user->id())->log('recovery', $user->bestEmail());
+    }
+
+    function handler_tmpPWD($page, $certif = null)
     {
         global $globals;
-        // XXX: recovery requires data from the profile
         XDB::execute('DELETE FROM  account_lost_passwords
                             WHERE  DATE_SUB(NOW(), INTERVAL 380 MINUTE) > created');
 
-        $res = XDB::query('SELECT  uid
-                             FROM  account_lost_passwords WHERE certificat={?}', $certif);
-        $ligne = $res->fetchOneAssoc();
-        if (!$ligne) {
-            $page->changeTpl('platal/index.tpl');
-            $page->kill("Cette adresse n'existe pas ou n'existe plus sur le serveur.");
-        }
-
-        $uid = $ligne["uid"];
-        if (Post::has('response2')) {
-            $password = Post::v('response2');
+        if (Post::has('pwhash') && Post::t('pwhash')) {
+            $uid = XDB::fetchOneCell('SELECT  uid
+                                        FROM  accounts
+                                       WHERE  hruid = {?}',
+                                     Post::t('username'));
+            $password = Post::t('pwhash');
             XDB::query('UPDATE  accounts
-                           SET  password={?}
+                           SET  password = {?}
                          WHERE  uid = {?} AND state = \'active\'',
                        $password, $uid);
             XDB::query('DELETE FROM  account_lost_passwords
-                              WHERE  certificat={?}', $certif);
+                              WHERE  certificat = {?}', $certif);
 
             // If GoogleApps is enabled, and the user did choose to use synchronized passwords,
             // updates the Google Apps password as well.
@@ -389,14 +462,74 @@ Adresse de secours : " . Post::v('email') : ""));
             }
 
             S::logger($uid)->log("passwd", "");
+
+            // Try to start a session (so the user don't have to log in); we will use
+            // the password available in Post:: to authenticate the user.
+            Platal::session()->start(AUTH_PASSWD);
+
             $page->changeTpl('platal/tmpPWD.success.tpl');
         } else {
-            $page->changeTpl('platal/motdepasse.tpl');
-            $page->addJsLink('motdepasse.js');
+            $res = XDB::query('SELECT  uid
+                                 FROM  account_lost_passwords
+                                WHERE  certificat = {?}', $certif);
+            $ligne = $res->fetchOneAssoc();
+            if (!$ligne) {
+                $page->changeTpl('platal/index.tpl');
+                $page->kill("Cette adresse n'existe pas ou n'existe plus sur le serveur.");
+            }
+
+            $hruid = XDB::fetchOneCell('SELECT  hruid
+                                          FROM  accounts
+                                         WHERE  uid = {?}',
+                                       $ligne['uid']);
+            $page->changeTpl('platal/password.tpl');
+            $page->assign('hruid', $hruid);
+            $page->assign('do_auth', 1);
         }
     }
 
-    function handler_skin(&$page)
+    function handler_register_ext($page, $hash = null)
+    {
+        XDB::execute('DELETE FROM  register_pending_xnet
+                            WHERE  DATE_SUB(NOW(), INTERVAL 1 MONTH) > date');
+        $res = XDB::fetchOneAssoc('SELECT  uid, hruid, email
+                                     FROM  register_pending_xnet
+                                    WHERE  hash = {?}',
+                                  $hash);
+
+        if (is_null($hash) || is_null($res)) {
+            $page->trigErrorRedirect('Cette adresse n\'existe pas ou n\'existe plus sur le serveur.', '');
+        }
+
+        if (Post::has('pwhash') && Post::t('pwhash')) {
+            XDB::startTransaction();
+            XDB::query('UPDATE  accounts
+                           SET  password = {?}, state = \'active\', registration_date = NOW()
+                         WHERE  uid = {?} AND state = \'pending\' AND type = \'xnet\'',
+                       Post::t('pwhash'), $res['uid']);
+            XDB::query('DELETE FROM  register_pending_xnet
+                              WHERE  uid = {?}',
+                              $res['uid']);
+            XDB::commit();
+
+            S::logger($res['uid'])->log('passwd', '');
+
+            // Try to start a session (so the user don't have to log in); we will use
+            // the password available in Post:: to authenticate the user.
+            Post::kill('wait');
+            Platal::session()->startAvailableAuth();
+
+            $page->changeTpl('xnet/register.success.tpl');
+            $page->assign('email', $res['email']);
+        } else {
+            $page->changeTpl('platal/password.tpl');
+            $page->assign('xnet', true);
+            $page->assign('hruid', $res['hruid']);
+            $page->assign('do_auth', 1);
+        }
+    }
+
+    function handler_skin($page)
     {
         global $globals;
 
@@ -425,12 +558,18 @@ Adresse de secours : " . Post::v('email') : ""));
         $page->assign('skins', XDB::iterator($sql));
     }
 
-    function handler_exit(&$page, $level = null)
+    function handler_exit($page, $level = null)
     {
         if (S::suid()) {
-            S::logger()->log('suid_stop', S::user()->login() . " by " . S::suid('hruid'));
+            $old = S::user()->login();
+            S::logger()->log('suid_stop', $old . " by " . S::suid('hruid'));
             Platal::session()->stopSUID();
-            pl_redirect('admin/user/' . S::user()->login());
+            $target = S::s('suid_startpage');
+            S::kill('suid_startpage');
+            if (!empty($target)) {
+                http_redirect($target);
+            }
+            pl_redirect('admin/user/' . $old);
         }
 
         if ($level == 'forget' || $level == 'forgetall') {
@@ -453,7 +592,7 @@ Adresse de secours : " . Post::v('email') : ""));
         }
     }
 
-    function handler_review(&$page, $action = null, $mode = null) 
+    function handler_review($page, $action = null, $mode = null)
     {
         // Include X-XRDS-Location response-header for Yadis discovery
         global $globals;
@@ -473,7 +612,16 @@ Adresse de secours : " . Post::v('email') : ""));
         }
         $wiz->apply($page, 'review', $action, $mode);
     }
+
+    function handler_test_error($page, $mode = null)
+    {
+        if ($mode == 'js') {
+            $page->changeTpl('platal/error.tpl');
+        } else {
+            throw new Exception("Blih");
+        }
+    }
 }
 
-// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
+// vim:set et sw=4 sts=4 sws=4 foldmethod=marker fenc=utf-8:
 ?>