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