aliases.id => aliases.uid
[platal.git] / modules / platal.php
1 <?php
2 /***************************************************************************
3 * Copyright (C) 2003-2010 Polytechnique.org *
4 * http://opensource.polytechnique.org/ *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
21
22 function bugize($list)
23 {
24 $list = split(',', $list);
25 $ans = array();
26
27 foreach ($list as $bug) {
28 $clean = str_replace('#', '', $bug);
29 $ans[] = "<a href='http://trackers.polytechnique.org/task/$clean'>$bug</a>";
30 }
31
32 return join(',', $ans);
33 }
34
35
36 class PlatalModule extends PLModule
37 {
38 function handlers()
39 {
40 return array(
41 'index' => $this->make_hook('index', AUTH_PUBLIC),
42 'cacert.pem' => $this->make_hook('cacert', AUTH_PUBLIC),
43 'changelog' => $this->make_hook('changelog', AUTH_PUBLIC),
44
45 // Preferences thingies
46 'prefs' => $this->make_hook('prefs', AUTH_COOKIE),
47 'prefs/rss' => $this->make_hook('prefs_rss', AUTH_COOKIE),
48 'prefs/webredirect' => $this->make_hook('webredir', AUTH_MDP),
49 'prefs/skin' => $this->make_hook('skin', AUTH_COOKIE),
50
51 // password related thingies
52 'password' => $this->make_hook('password', AUTH_MDP),
53 'tmpPWD' => $this->make_hook('tmpPWD', AUTH_PUBLIC),
54 'password/smtp' => $this->make_hook('smtppass', AUTH_MDP),
55 'recovery' => $this->make_hook('recovery', AUTH_PUBLIC),
56 'exit' => $this->make_hook('exit', AUTH_PUBLIC),
57 'review' => $this->make_hook('review', AUTH_PUBLIC),
58 'deconnexion.php' => $this->make_hook('exit', AUTH_PUBLIC),
59 );
60 }
61
62 function handler_index(&$page)
63 {
64 // Include X-XRDS-Location response-header for Yadis discovery
65 global $globals;
66 header('X-XRDS-Location: ' . $globals->baseurl . '/openid/xrds');
67
68 // Redirect to the suitable page
69 if (S::logged()) {
70 pl_redirect('events');
71 } else if (!@$GLOBALS['IS_XNET_SITE']) {
72 $this->handler_review($page);
73 }
74 }
75
76 function handler_cacert(&$page)
77 {
78 pl_cached_content_headers("application/x-x509-ca-cert");
79 readfile("/etc/ssl/xorgCA/cacert.pem");
80 exit;
81 }
82
83 function handler_changelog(&$page, $core = null)
84 {
85 $page->changeTpl('platal/changeLog.tpl');
86
87 function formatChangeLog($file) {
88 $clog = pl_entities(file_get_contents($file));
89 $clog = preg_replace('/===+\s*/', '</pre><hr /><pre>', $clog);
90 // url catch only (not all wiki syntax)
91 $clog = preg_replace(array(
92 '/((?:https?|ftp):\/\/(?:\.*,*[\w@~%$£µ&i#\-+=_\/\?;])*)/ui',
93 '/(\s|^)www\.((?:\.*,*[\w@~%$£µ&i#\-+=_\/\?;])*)/iu',
94 '/(?:mailto:)?([a-z0-9.\-+_]+@([\-.+_]?[a-z0-9])+)/i'),
95 array(
96 '<a href="\\0">\\0</a>',
97 '\\1<a href="http://www.\\2">www.\\2</a>',
98 '<a href="mailto:\\0">\\0</a>'),
99 $clog);
100 $clog = preg_replace('!(#[0-9]+(,[0-9]+)*)!e', 'bugize("\1")', $clog);
101 $clog = preg_replace('!vim:.*$!', '', $clog);
102 return preg_replace("!(<hr />(\\s|\n)*)?<pre>(\s|\n)*</pre>((\\s|\n)*<hr />)?!m", "", "<pre>$clog</pre>");
103 }
104 if ($core != 'core') {
105 $page->assign('core', false);
106 $page->assign('ChangeLog', formatChangeLog(dirname(__FILE__).'/../ChangeLog'));
107 } else {
108 $page->assign('core', true);
109 $page->assign('ChangeLog', formatChangeLog(dirname(__FILE__).'/../core/ChangeLog'));
110 }
111 }
112
113 function __set_rss_state($state)
114 {
115 if ($state) {
116 S::set('token', rand_url_id(16));
117 XDB::execute('UPDATE accounts
118 SET token = {?}
119 WHERE uid = {?}', S::s('token'), S::i('uid'));
120 } else {
121 S::kill('token');
122 XDB::execute('UPDATE accounts
123 SET token = NULL
124 WHERE uid = {?}', S::i('uid'));
125 }
126 }
127
128 function handler_prefs(&$page)
129 {
130 $page->changeTpl('platal/preferences.tpl');
131 $page->setTitle('Mes préférences');
132
133 if (Post::has('email_format')) {
134 $fmt = Post::s('email_format');
135 XDB::execute("UPDATE accounts
136 SET email_format = {?}
137 WHERE uid = {?}",
138 $fmt, S::v('uid'));
139 S::set('email_format', $fmt);
140 }
141
142 if (Post::has('rss')) {
143 $this->__set_rss_state(Post::b('rss'));
144 }
145
146 # FIXME: this code is not multi-domain compatible. We should decide how
147 # carva will extend to users not in the main domain.
148 $res = XDB::query("SELECT alias
149 FROM aliases
150 WHERE uid = {?} AND FIND_IN_SET('bestalias', flags)",
151 S::user()->id());
152 $page->assign('bestalias', $res->fetchOneCell());
153 }
154
155 function handler_webredir(&$page)
156 {
157 $page->changeTpl('platal/webredirect.tpl');
158 $page->setTitle('Redirection de page WEB');
159
160 if (Env::v('submit') == 'Valider' && !Env::blank('url')) {
161 if (Env::blank('url')) {
162 $page->trigError('URL invalide');
163 } else {
164 $url = Env::t('url');
165 XDB::execute('REPLACE INTO carvas (uid, url)
166 VALUES ({?}, {?})',
167 S::i('uid'), $url);
168 S::logger()->log('carva_add', 'http://' . $url);
169 $page->trigSuccess("Redirection activée vers <a href='http://$url'>$url</a>");
170 }
171 } elseif (Env::v('submit') == 'Supprimer') {
172 XDB::execute('DELETE FROM carvas
173 WHERE uid = {?}', S::i('uid'));
174 Post::kill('url');
175 S::logger()->log('carva_del');
176 $page->trigSuccess('Redirection supprimée');
177 }
178
179 $url = XDB::fetchOneCell('SELECT url
180 FROM carvas
181 WHERE uid = {?}', S::i('uid'));
182 $page->assign('carva', $url);
183
184 # FIXME: this code is not multi-domain compatible. We should decide how
185 # carva will extend to users not in the main domain.
186 $res = XDB::query("SELECT alias
187 FROM aliases
188 WHERE uid = {?} AND FIND_IN_SET('bestalias', flags)",
189 S::user()->id());
190 $page->assign('bestalias', $res->fetchOneCell());
191 }
192
193 function handler_prefs_rss(&$page)
194 {
195 $page->changeTpl('platal/filrss.tpl');
196
197 $page->assign('goback', Env::v('referer', 'login'));
198
199 if (Env::v('act_rss') == 'Activer') {
200 $this->__set_rss_state(true);
201 $page->trigSuccess("Ton Fil RSS est activé.");
202 }
203 }
204
205 function handler_password(&$page)
206 {
207 global $globals;
208
209 if (Post::has('response2')) {
210 S::assert_xsrf_token();
211
212 S::set('password', $password = Post::v('response2'));
213 XDB::execute('UPDATE accounts
214 SET password = {?}
215 WHERE uid={?}', $password,
216 S::i('uid'));
217
218 // If GoogleApps is enabled, and the user did choose to use synchronized passwords,
219 // updates the Google Apps password as well.
220 if ($globals->mailstorage->googleapps_domain) {
221 require_once 'googleapps.inc.php';
222 $account = new GoogleAppsAccount(S::user());
223 if ($account->active() && $account->sync_password) {
224 $account->set_password($password);
225 }
226 }
227
228 S::logger()->log('passwd');
229 Platal::session()->setAccessCookie(true);
230
231 $page->changeTpl('platal/motdepasse.success.tpl');
232 $page->run();
233 }
234
235 $page->changeTpl('platal/motdepasse.tpl');
236 $page->addJsLink('motdepasse.js');
237 $page->setTitle('Mon mot de passe');
238 }
239
240 function handler_smtppass(&$page)
241 {
242 $page->changeTpl('platal/acces_smtp.tpl');
243 $page->setTitle('Acces SMTP/NNTP');
244
245 $wp = new PlWikiPage('Xorg.SMTPSécurisé');
246 $wp->buildCache();
247 $wp = new PlWikiPage('Xorg.NNTPSécurisé');
248 $wp->buildCache();
249
250 $uid = S::i('uid');
251 $pass = Env::v('smtppass1');
252
253 if (Env::v('op') == "Valider" && strlen($pass) >= 6
254 && Env::v('smtppass1') == Env::v('smtppass2')) {
255 XDB::execute('UPDATE accounts
256 SET weak_password = {?}
257 WHERE uid = {?}', $pass, $uid);
258 $page->trigSuccess('Mot de passe enregistré');
259 S::logger()->log("passwd_ssl");
260 } elseif (Env::v('op') == "Supprimer") {
261 XDB::execute('UPDATE accounts
262 SET weak_password = NULL
263 WHERE uid = {?}', $uid);
264 $page->trigSuccess('Compte SMTP et NNTP supprimé');
265 S::logger()->log("passwd_del");
266 }
267
268 $res = XDB::query("SELECT weak_password IS NOT NULL
269 FROM accounts
270 WHERE uid = {?}", $uid);
271 $page->assign('actif', $res->fetchOneCell());
272 }
273
274 function handler_recovery(&$page)
275 {
276 global $globals;
277
278 $page->changeTpl('platal/recovery.tpl');
279
280 if (!Env::has('login') || !Env::has('birth')) {
281 return;
282 }
283
284 if (!ereg('[0-3][0-9][0-1][0-9][1][9]([0-9]{2})', Env::v('birth'))) {
285 $page->trigError('Date de naissance incorrecte ou incohérente');
286 return;
287 }
288
289 $birth = sprintf('%s-%s-%s',
290 substr(Env::v('birth'), 4, 4),
291 substr(Env::v('birth'), 2, 2),
292 substr(Env::v('birth'), 0, 2));
293
294 $mailorg = strtok(Env::v('login'), '@');
295
296 $profile = Profile::get(Env::t('login'));
297 if (is_null($profile) || $profile->birthdate != $birth) {
298 $page->trigError('Les informations que tu as rentrées ne permettent pas de récupérer ton mot de passe.<br />'.
299 'Si tu as un homonyme, utilise prenom.nom.promo comme login');
300 return;
301 }
302
303 $user = $profile->owner();
304 if ($user->state != 'active') {
305 $page->trigError('Ton compte n\'est pas activé.');
306 return;
307 }
308
309 $res = XDB::query("SELECT COUNT(*)
310 FROM emails
311 WHERE uid = {?} AND flags != 'panne' AND flags != 'filter'", $user->id());
312 $count = intval($res->fetchOneCell());
313 if ($count == 0) {
314 $page->assign('no_addr', true);
315 return;
316 }
317
318 $page->assign('ok', true);
319
320 $url = rand_url_id();
321 XDB::execute('INSERT INTO account_lost_passwords (certificat,uid,created)
322 VALUES ({?},{?},NOW())', $url, $user->id());
323 $res = XDB::query('SELECT email
324 FROM emails
325 WHERE uid = {?} AND email = {?}',
326 $user->id(), Post::v('email'));
327 if ($res->numRows()) {
328 $mails = $res->fetchOneCell();
329 } else {
330 $res = XDB::query("SELECT email
331 FROM emails
332 WHERE uid = {?} AND NOT FIND_IN_SET('filter', flags)", $user->id());
333 $mails = implode(', ', $res->fetchColumn());
334 }
335 $mymail = new PlMailer();
336 $mymail->setFrom('"Gestion des mots de passe" <support+password@' . $globals->mail->domain . '>');
337 $mymail->addTo($mails);
338 $mymail->setSubject("Ton certificat d'authentification");
339 $mymail->setTxtBody("Visite la page suivante qui expire dans six heures :
340 {$globals->baseurl}/tmpPWD/$url
341
342 Si en cliquant dessus tu n'y arrives pas, copie intégralement l'adresse dans la barre de ton navigateur. Si tu n'as pas utilisé ce lien dans six heures, tu peux tout simplement recommencer cette procédure.
343
344 --
345 Polytechnique.org
346 \"Le portail des élèves & anciens élèves de l'École polytechnique\"
347
348 Email envoyé à ".Env::v('login') . (Post::has('email') ? "
349 Adresse de secours : " . Post::v('email') : ""));
350 $mymail->send();
351
352 // on cree un objet logger et on log l'evenement
353 S::logger($user->id())->log('recovery', $mails);
354 }
355
356 function handler_tmpPWD(&$page, $certif = null)
357 {
358 global $globals;
359 // XXX: recovery requires data from the profile
360 XDB::execute('DELETE FROM account_lost_passwords
361 WHERE DATE_SUB(NOW(), INTERVAL 380 MINUTE) > created');
362
363 $res = XDB::query('SELECT uid
364 FROM account_lost_passwords WHERE certificat={?}', $certif);
365 $ligne = $res->fetchOneAssoc();
366 if (!$ligne) {
367 $page->changeTpl('platal/index.tpl');
368 $page->kill("Cette adresse n'existe pas ou n'existe plus sur le serveur.");
369 }
370
371 $uid = $ligne["uid"];
372 if (Post::has('response2')) {
373 $password = Post::v('response2');
374 XDB::query('UPDATE accounts
375 SET password={?}
376 WHERE uid = {?} AND state = \'active\'',
377 $password, $uid);
378 XDB::query('DELETE FROM account_lost_passwords
379 WHERE certificat={?}', $certif);
380
381 // If GoogleApps is enabled, and the user did choose to use synchronized passwords,
382 // updates the Google Apps password as well.
383 if ($globals->mailstorage->googleapps_domain) {
384 require_once 'googleapps.inc.php';
385 $account = new GoogleAppsAccount(User::getSilent($uid));
386 if ($account->active() && $account->sync_password) {
387 $account->set_password($password);
388 }
389 }
390
391 S::logger($uid)->log("passwd", "");
392 $page->changeTpl('platal/tmpPWD.success.tpl');
393 } else {
394 $page->changeTpl('platal/motdepasse.tpl');
395 $page->addJsLink('motdepasse.js');
396 }
397 }
398
399 function handler_skin(&$page)
400 {
401 global $globals;
402
403 $page->changeTpl('platal/skins.tpl');
404 $page->setTitle('Skins');
405
406 if (Env::has('newskin')) { // formulaire soumis, traitons les données envoyées
407 XDB::execute('UPDATE accounts
408 SET skin = {?}
409 WHERE uid = {?}',
410 Env::i('newskin'), S::i('uid'));
411 S::kill('skin');
412 Platal::session()->setSkin();
413 }
414
415 $res = XDB::query('SELECT id
416 FROM skins
417 WHERE skin_tpl = {?}', S::v('skin'));
418 $page->assign('skin_id', $res->fetchOneCell());
419
420 $sql = 'SELECT s.*, auteur, COUNT(*) AS nb
421 FROM skins AS s
422 LEFT JOIN accounts AS a ON (a.skin = s.id)
423 WHERE skin_tpl != \'\' AND ext != \'\'
424 GROUP BY id ORDER BY s.date DESC';
425 $page->assign('skins', XDB::iterator($sql));
426 }
427
428 function handler_exit(&$page, $level = null)
429 {
430 if (S::suid()) {
431 S::logger()->log('suid_stop', S::user()->login() . " by " . S::suid('hruid'));
432 Platal::session()->stopSUID();
433 pl_redirect('admin/user/' . S::user()->login());
434 }
435
436 if ($level == 'forget' || $level == 'forgetall') {
437 Platal::session()->killAccessCookie();
438 }
439
440 if ($level == 'forgetuid' || $level == 'forgetall') {
441 Platal::session()->killLoginFormCookies();
442 }
443
444 if (S::logged()) {
445 S::logger()->log('deconnexion', @$_SERVER['HTTP_REFERER']);
446 Platal::session()->destroy();
447 }
448
449 if (Get::has('redirect')) {
450 http_redirect(rawurldecode(Get::v('redirect')));
451 } else {
452 $page->changeTpl('platal/exit.tpl');
453 }
454 }
455
456 function handler_review(&$page, $action = null, $mode = null)
457 {
458 // Include X-XRDS-Location response-header for Yadis discovery
459 global $globals;
460 header('X-XRDS-Location: ' . $globals->baseurl . '/openid/xrds');
461
462 $this->load('review.inc.php');
463 $dom = 'Review';
464 if (@$GLOBALS['IS_XNET_SITE']) {
465 $dom .= 'Xnet';
466 }
467 $wp = new PlWikiPage($dom . '.Admin');
468 $conf = explode('%0a', $wp->getField('text'));
469 $wiz = new PlWizard('Tour d\'horizon', PlPage::getCoreTpl('plwizard.tpl'), true);
470 foreach ($conf as $line) {
471 $list = preg_split('/\s*[*|]\s*/', $line, -1, PREG_SPLIT_NO_EMPTY);
472 $wiz->addPage('ReviewPage', $list[0], $list[1]);
473 }
474 $wiz->apply($page, 'review', $action, $mode);
475 }
476 }
477
478 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
479 ?>