Commit | Line | Data |
---|---|---|
0337d704 | 1 | <?php |
2 | /*************************************************************************** | |
12262f13 | 3 | * Copyright (C) 2003-2011 Polytechnique.org * |
0337d704 | 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 | ||
b4503762 SJ |
22 | define('SUCCESS', 1); |
23 | define('ERROR_INACTIVE_REDIRECTION', 2); | |
24 | define('ERROR_INVALID_EMAIL', 3); | |
25 | define('ERROR_LOOP_EMAIL', 4); | |
0337d704 | 26 | |
51c2c63a | 27 | function add_to_list_alias(User $user, $local_part, $domain, $type = 'alias') |
85ddf64f SJ |
28 | { |
29 | Platal::assert($user !== null); | |
30 | ||
31 | XDB::execute('INSERT IGNORE INTO email_virtual (email, domain, redirect, type) | |
32 | SELECT {?}, id, {?}, {?} | |
33 | FROM email_virtual_domains | |
34 | WHERE name = {?}', | |
35 | $local_part, $user->forlifeEmail(), $type, $domain); | |
36 | } | |
37 | ||
51c2c63a | 38 | function delete_from_list_alias(User $user, $local_part, $domain, $type = 'alias') |
85ddf64f SJ |
39 | { |
40 | Platal::assert($user !== null); | |
41 | ||
42 | XDB::execute('DELETE v | |
43 | FROM email_virtual AS v | |
44 | INNER JOIN email_virtual_domains AS m ON (v.domain = m.id) | |
45 | INNER JOIN email_virtual_domains AS d ON (d.aliasing = m.id) | |
46 | WHERE v.email = {?} AND d.name = {?} AND v.redirect = {?} AND type = {?}', | |
47 | $local_part, $domain, $user->forlifeEmail(), $type); | |
48 | } | |
49 | ||
51c2c63a | 50 | function update_list_alias(User $user, $former_email, $local_part, $domain, $type = 'alias') |
7852229b SJ |
51 | { |
52 | Platal::assert($user !== null); | |
53 | ||
54 | XDB::execute('UPDATE email_virtual AS v | |
55 | INNER JOIN email_virtual_domains AS d ON (v.domain = d.id) | |
56 | SET v.redirect = {?} | |
57 | WHERE v.redirect = {?} AND d.name = {?} AND v.email = {?} AND v.type = {?}', | |
58 | $user->forlifeEmail(), $former_email, $domain, $local_part, $type); | |
59 | } | |
60 | ||
85ddf64f SJ |
61 | function list_alias_members($local_part, $domain) |
62 | { | |
63 | $emails = XDB::fetchColumn('SELECT DISTINCT(redirect) | |
64 | FROM email_virtual AS v | |
65 | INNER JOIN email_virtual_domains AS m ON (v.domain = m.id) | |
66 | INNER JOIN email_virtual_domains AS d ON (d.aliasing = m.id) | |
51c2c63a | 67 | WHERE v.email = {?} AND d.name = {?} AND type = \'alias\'', |
85ddf64f SJ |
68 | $local_part, $domain); |
69 | ||
70 | $members = array(); | |
71 | foreach ($emails as $email) { | |
72 | $members[] = User::getSilent($email); | |
73 | } | |
74 | ||
75 | return $members; | |
76 | } | |
77 | ||
78 | function delete_list_alias($local_part, $domain) | |
79 | { | |
80 | XDB::execute('DELETE v | |
81 | FROM email_virtual AS v | |
82 | INNER JOIN email_virtual_domains AS m ON (v.domain = m.id) | |
83 | INNER JOIN email_virtual_domains AS d ON (d.aliasing = m.id) | |
51c2c63a | 84 | WHERE v.email = {?} AND d.name = {?} AND type = \'alias\'', |
85ddf64f SJ |
85 | $local_part, $domain); |
86 | } | |
87 | ||
88 | function iterate_list_alias($domain) | |
89 | { | |
90 | return XDB::fetchColumn('SELECT CONCAT(v.email, \'@\', m.name) | |
91 | FROM email_virtual AS v | |
92 | INNER JOIN email_virtual_domains AS m ON (v.domain = m.id) | |
51c2c63a | 93 | WHERE m.name = {?} AND v.type = \'alias\' |
85ddf64f SJ |
94 | GROUP BY v.email', |
95 | $domain); | |
96 | } | |
97 | ||
98 | function create_list($local_part, $domain) | |
99 | { | |
100 | global $globals; | |
101 | ||
102 | $redirect = $domain . '_' . $local_part . '+'; | |
103 | foreach(array('post', 'owner', 'admin', 'bounces', 'unsubscribe') as $suffix) { | |
104 | XDB::execute('INSERT IGNORE INTO email_virtual (email, domain, redirect, type) | |
105 | SELECT {?}, id, {?}, \'list\' | |
106 | FROM email_virtual_domains | |
107 | WHERE name = {?}', | |
108 | ($suffix == 'post') ? $local_part : $local_part . '-' . $suffix, | |
109 | $redirect . $suffix . '@' . $globals->lists->redirect_domain, $domain); | |
110 | } | |
111 | } | |
112 | ||
113 | function delete_list($local_part, $domain) | |
114 | { | |
115 | global $globals; | |
116 | ||
117 | $redirect = $domain . '_' . $local_part . '+'; | |
118 | foreach(array('post', 'owner', 'admin', 'bounces', 'unsubscribe') as $suffix) { | |
119 | XDB::execute('DELETE email_virtual | |
120 | WHERE redirect = {?} AND type = \'list\'', | |
121 | $redirect . $suffix . '@' . $globals->lists->redirect_domain); | |
122 | } | |
123 | } | |
124 | ||
125 | function list_exist($local_part, $domain) | |
126 | { | |
127 | return XDB::fetchOneCell('SELECT COUNT(*) | |
128 | FROM email_virtual AS v | |
129 | INNER JOIN email_virtual_domains AS m ON (v.domain = m.id) | |
130 | INNER JOIN email_virtual_domains AS d ON (m.id = d.aliasing) | |
131 | WHERE v.email = {?} AND d.name = {?}', | |
132 | $local_part, $domain); | |
133 | } | |
134 | ||
a9fd3272 SJ |
135 | // function mark_broken_email() {{{1 |
136 | function mark_broken_email($email, $admin = false) | |
137 | { | |
138 | $email = valide_email($email); | |
139 | if (empty($email) || $email == '@') { | |
140 | return; | |
141 | } | |
142 | ||
143 | $user = XDB::fetchOneAssoc('SELECT r1.uid, r1.broken_level != 0 AS broken, a.hruid, COUNT(r2.uid) AS nb_mails, a.full_name, s.email AS alias | |
144 | FROM email_redirect_account AS r1 | |
145 | INNER JOIN accounts AS a ON (a.uid = r1.uid) | |
146 | INNER JOIN email_source_account AS s ON (a.uid = s.uid AND s.flags = \'bestalias\') | |
147 | LEFT JOIN email_redirect_account AS r2 ON (a.uid = r2.uid AND r1.redirect != r2.redirect AND | |
148 | r2.broken_level = 0 AND r2.flags = \'active\' AND | |
149 | (r2.type = \'smtp\' OR r2.type = \'googleapps\')) | |
150 | WHERE r1.redirect = {?} | |
151 | GROUP BY r1.uid', $email); | |
152 | ||
153 | if ($user) { | |
154 | // Mark address as broken. | |
155 | if (!$user['broken']) { | |
156 | XDB::execute('UPDATE email_redirect_account | |
157 | SET broken_date = NOW(), last = NOW(), broken_level = 1 | |
158 | WHERE redirect = {?}', $email); | |
159 | } elseif ($admin) { | |
160 | XDB::execute('UPDATE email_redirect_account | |
337a6acf | 161 | SET last = CURDATE(), broken_level = broken_level + 1 |
a9fd3272 SJ |
162 | WHERE redirect = {?} AND DATE_ADD(last, INTERVAL 14 DAY) < CURDATE()', |
163 | $email); | |
164 | } else { | |
165 | XDB::execute('UPDATE email_redirect_account | |
166 | SET broken_level = 1 | |
167 | WHERE redirect = {?} AND broken_level = 0', $email); | |
168 | } | |
169 | } | |
170 | ||
171 | return $user; | |
172 | } | |
173 | ||
441c2451 | 174 | // function fix_bestalias() {{{1 |
11ed26e5 VZ |
175 | // Checks for an existing 'bestalias' among the the current user's aliases, and |
176 | // eventually selects a new bestalias when required. | |
26ba053e | 177 | function fix_bestalias(User $user) |
0337d704 | 178 | { |
b4503762 SJ |
179 | $count = XDB::fetchOneCell('SELECT COUNT(*) |
180 | FROM email_source_account | |
aca54585 | 181 | WHERE uid = {?} AND FIND_IN_SET(\'bestalias\', flags) AND expire IS NULL', |
b4503762 | 182 | $user->id()); |
11ed26e5 | 183 | |
b4503762 SJ |
184 | if ($count == 1) { |
185 | return; | |
186 | } elseif ($count > 1) { | |
187 | // If too many bestaliases, delete the bestalias flag from all this | |
188 | // user's emails (this should never happen). | |
189 | XDB::execute("UPDATE email_source_account | |
190 | SET flags = TRIM(BOTH ',' FROM REPLACE(CONCAT(',', flags, ','), ',bestalias,', ',')) | |
191 | WHERE uid = {?}", | |
192 | $user->id()); | |
193 | } | |
194 | ||
195 | // If no bestalias is selected, we choose the shortest email which is not | |
196 | // related to a usage name and contains a '.'. | |
197 | XDB::execute("UPDATE email_source_account | |
198 | SET flags = CONCAT_WS(',', IF(flags = '', NULL, flags), 'bestalias') | |
aca54585 | 199 | WHERE uid = {?} AND expire IS NULL |
b4503762 SJ |
200 | ORDER BY NOT FIND_IN_SET('usage', flags), email LIKE '%.%', LENGTH(email) |
201 | LIMIT 1", | |
202 | $user->id()); | |
0337d704 | 203 | } |
204 | ||
441c2451 | 205 | // function valide_email() {{{1 |
11ed26e5 VZ |
206 | // Returns a cleaned-up version of the @p email string. It removes garbage |
207 | // characters, and determines the canonical form (without _ and +) for | |
208 | // Polytechnique.org email addresses. | |
0337d704 | 209 | function valide_email($str) |
210 | { | |
a3a049fc | 211 | global $globals; |
212 | ||
213 | $em = trim(rtrim($str)); | |
214 | $em = str_replace('<', '', $em); | |
215 | $em = str_replace('>', '', $em); | |
97664536 SJ |
216 | if (strpos($em, '@') === false) { |
217 | return; | |
218 | } | |
a3a049fc | 219 | list($ident, $dom) = explode('@', $em); |
97664536 | 220 | if ($dom == $globals->mail->domain || $dom == $globals->mail->domain2) { |
a3a049fc | 221 | list($ident1) = explode('_', $ident); |
222 | list($ident) = explode('+', $ident1); | |
223 | } | |
224 | return $ident . '@' . $dom; | |
0337d704 | 225 | } |
226 | ||
c6310567 | 227 | // function isvalid_email_redirection() {{{1 |
b4503762 SJ |
228 | /** Checks if an email is a suitable redirection. |
229 | * @param $email the email to check | |
c6310567 FB |
230 | * @return BOOL |
231 | */ | |
232 | function isvalid_email_redirection($email) | |
233 | { | |
234 | return isvalid_email($email) && | |
235 | !preg_match("/@(polytechnique\.(org|edu)|melix\.(org|net)|m4x\.org)$/", $email); | |
236 | } | |
237 | ||
180627f3 | 238 | // function ids_from_mails() {{{1 |
b4503762 SJ |
239 | // Converts an array of emails to an array of email => uid, where email is the |
240 | // given email when we found a matching user. | |
180627f3 | 241 | function ids_from_mails(array $emails) |
1605f171 RB |
242 | { |
243 | global $globals; | |
180627f3 | 244 | |
b4503762 SJ |
245 | // Removes duplicates, if any. |
246 | $emails = array_unique($emails); | |
247 | ||
248 | // Formats and splits by domain type (locally managed or external) emails. | |
249 | $domain_emails = array(); | |
250 | $other_emails = array(); | |
1605f171 RB |
251 | foreach ($emails as $email) { |
252 | if (strpos($email, '@') === false) { | |
253 | $user = $email; | |
254 | $domain = $globals->mail->domain2; | |
255 | } else { | |
256 | list($user, $domain) = explode('@', $email); | |
257 | } | |
b4503762 SJ |
258 | if ($domain == $globals->mail->alias_dom || $domain == $globals->mail->alias_dom2 |
259 | || $domain == $globals->mail->domain || $domain == $globals->mail->domain2) { | |
1605f171 RB |
260 | list($user) = explode('+', $user); |
261 | list($user) = explode('_', $user); | |
b4503762 | 262 | $domain_emails[$email] = strtolower($user . '@' . $domain); |
1605f171 | 263 | } else { |
b4503762 | 264 | $other_emails[$email] = strtolower($user . '@' . $domain); |
1605f171 RB |
265 | } |
266 | } | |
180627f3 | 267 | |
b4503762 SJ |
268 | // Retrieves emails from our domains. |
269 | $domain_uids = XDB::fetchAllAssoc('email', | |
270 | 'SELECT email, uid | |
271 | FROM email_source_account | |
272 | WHERE email IN {?}', | |
273 | array_unique($domain_emails)); | |
1605f171 | 274 | |
b4503762 SJ |
275 | // Retrieves emails from redirections. |
276 | $other_uids = XDB::fetchAllAssoc('redirect', | |
277 | 'SELECT redirect, uid | |
278 | FROM email_redirect_account | |
279 | WHERE redirect IN {?}', | |
280 | array_unique($other_emails)); | |
1605f171 | 281 | |
b4503762 SJ |
282 | // Associates given emails with the corresponding uid. |
283 | $uids = array(); | |
284 | foreach (array_merge($domain_emails, $other_emails) as $email => $canonical_email) { | |
285 | if (array_key_exists($canonical_email, $domain_uids)) { | |
286 | $uids[$email] = $domain_uids[$canonical_email]; | |
287 | } elseif (array_key_exists($canonical_email, $other_uids)) { | |
288 | $uids[$email] = $other_uids[$canonical_email]; | |
1605f171 RB |
289 | } |
290 | } | |
291 | ||
292 | return $uids; | |
293 | } | |
294 | ||
441c2451 | 295 | // class Bogo {{{1 |
11ed26e5 | 296 | // The Bogo class represents a spam filtering level in plat/al architecture. |
0337d704 | 297 | class Bogo |
298 | { | |
3b346b99 SJ |
299 | private static $states = array( |
300 | 0 => 'default', | |
301 | 1 => 'let_spams', | |
302 | 2 => 'tag_spams', | |
303 | 3 => 'tag_and_drop_spams', | |
304 | 4 => 'drop_spams' | |
305 | ); | |
a3a049fc | 306 | |
12a587df | 307 | private $user; |
3b346b99 SJ |
308 | public $state; |
309 | public $single_state; | |
310 | public $redirections; | |
311 | public $single_redirection; | |
a3a049fc | 312 | |
26ba053e | 313 | public function __construct(User $user) |
0337d704 | 314 | { |
12a587df | 315 | if (!$user) { |
3c1e6a1e | 316 | return; |
317 | } | |
11ed26e5 | 318 | |
12a587df | 319 | $this->user = &$user; |
3b346b99 SJ |
320 | $res = XDB::fetchOneAssoc('SELECT COUNT(DISTINCT(action)) AS action_count, COUNT(redirect) AS redirect_count, action |
321 | FROM email_redirect_account | |
322 | WHERE uid = {?} AND (type = \'smtp\' OR type = \'googleapps\') AND flags = \'active\'', | |
323 | $user->id()); | |
324 | if ($res['redirect_count'] == 0) { | |
b4503762 | 325 | return; |
3c1e6a1e | 326 | } |
3b346b99 SJ |
327 | |
328 | $this->single_redirection = ($res['redirect_count'] == 1); | |
329 | $this->redirections = XDB::fetchAllAssoc('SELECT IF(type = \'googleapps\', type, redirect) AS redirect, type, action | |
330 | FROM email_redirect_account | |
331 | WHERE uid = {?} AND (type = \'smtp\' OR type = \'googleapps\') | |
332 | ORDER BY type, redirect', | |
333 | $user->id()); | |
334 | ||
335 | foreach ($this->redirections AS &$redirection) { | |
336 | $redirection['filter'] = array_search($redirection['action'], self::$states); | |
337 | } | |
338 | if ($res['action_count'] == 1) { | |
339 | $this->state = array_search($res['action'], self::$states); | |
340 | $this->single_state = true; | |
341 | } else { | |
342 | $this->single_state = $this->state = false; | |
343 | } | |
0337d704 | 344 | } |
345 | ||
3b346b99 | 346 | public function changeAll($state) |
0337d704 | 347 | { |
3b346b99 SJ |
348 | Platal::assert($state >= 0 && $state < count(self::$states), 'Unknown antispam level.'); |
349 | ||
350 | $this->state = $state; | |
b4503762 SJ |
351 | XDB::execute('UPDATE email_redirect_account |
352 | SET action = {?} | |
353 | WHERE uid = {?} AND (type = \'smtp\' OR type = \'googleapps\')', | |
3b346b99 | 354 | self::$states[$this->state], $this->user->id()); |
0337d704 | 355 | } |
356 | ||
3b346b99 | 357 | public function change($redirection, $state) |
612a2d8a | 358 | { |
3b346b99 SJ |
359 | Platal::assert($state >= 0 && $state < count(self::$states), 'Unknown antispam level.'); |
360 | ||
361 | XDB::execute('UPDATE email_redirect_account | |
362 | SET action = {?} | |
363 | WHERE uid = {?} AND (type = {?} OR redirect = {?})', | |
364 | self::$states[$state], $this->user->id(), $redirection, $redirection); | |
612a2d8a | 365 | } |
0337d704 | 366 | } |
367 | ||
441c2451 | 368 | // class Email {{{1 |
11ed26e5 | 369 | // Represents an "email address" used as final recipient for plat/al-managed |
b4503762 SJ |
370 | // addresses. |
371 | class Email | |
0337d704 | 372 | { |
b4503762 SJ |
373 | // Lists fields to load automatically. |
374 | static private $field_names = array('rewrite', 'type', 'action', 'broken_date', 'broken_level', 'last', 'hash', 'allow_rewrite'); | |
375 | ||
376 | // Shortname to realname mapping for known mail storage backends. | |
377 | static private $display_names = array( | |
378 | 'imap' => 'Accès de secours aux emails (IMAP)', | |
379 | 'googleapps' => 'Compte Google Apps', | |
380 | ); | |
381 | static private $storage_domains = array( | |
382 | 'imap' => 'imap', | |
383 | 'googleapps' => 'g' | |
384 | ); | |
385 | ||
386 | private $user; | |
612a2d8a | 387 | |
11ed26e5 | 388 | // Basic email properties; $sufficient indicates if the email can be used as |
b4503762 | 389 | // an unique redirection; $redirect contains the delivery email address. |
11ed26e5 VZ |
390 | public $type; |
391 | public $sufficient; | |
612a2d8a | 392 | public $email; |
11ed26e5 | 393 | public $display_email; |
b4503762 SJ |
394 | public $domain; |
395 | public $action; | |
11ed26e5 VZ |
396 | |
397 | // Redirection status properties. | |
612a2d8a | 398 | public $active; |
b4503762 | 399 | public $inactive; |
612a2d8a | 400 | public $broken; |
441c2451 | 401 | public $disabled; |
612a2d8a | 402 | public $rewrite; |
3d17e48f FB |
403 | public $allow_rewrite; |
404 | public $hash; | |
11ed26e5 VZ |
405 | |
406 | // Redirection bounces stats. | |
612a2d8a | 407 | public $last; |
b4503762 SJ |
408 | public $broken_level; |
409 | public $broken_date; | |
0337d704 | 410 | |
b4503762 | 411 | public function __construct(User $user, array $row) |
0337d704 | 412 | { |
b4503762 SJ |
413 | foreach (self::$field_names as $field) { |
414 | if (array_key_exists($field, $row)) { | |
415 | $this->$field = $row[$field]; | |
416 | } | |
417 | } | |
418 | $this->email = $row['redirect']; | |
11ed26e5 | 419 | |
b4503762 SJ |
420 | if (array_key_exists($this->type, Email::$display_names)) { |
421 | $this->display_email = self::$display_names[$this->type]; | |
422 | } else { | |
423 | $this->display_email = $this->email; | |
424 | } | |
425 | foreach (array('active', 'inactive', 'broken', 'disabled') as $status) { | |
426 | $this->$status = ($status == $row['flags']); | |
427 | } | |
428 | $this->sufficient = ($this->type == 'smtp' || $this->type == 'googleapps'); | |
429 | $this->user = &$user; | |
0337d704 | 430 | } |
431 | ||
b4503762 | 432 | // Activates the email address as a redirection. |
11ed26e5 | 433 | public function activate() |
0337d704 | 434 | { |
b4503762 SJ |
435 | if ($this->inactive) { |
436 | XDB::execute('UPDATE email_redirect_account | |
437 | SET broken_level = IF(flags = \'broken\', broken_level - 1, broken_level), flags = \'active\' | |
438 | WHERE uid = {?} AND redirect = {?}', | |
439 | $this->user->id(), $this->email); | |
440 | S::logger()->log('email_on', $this->email . ($this->user->id() != S::v('uid') ? "(admin on {$this->user->login()})" : '')); | |
441 | $this->inactive = false; | |
442 | $this->active = true; | |
0337d704 | 443 | } |
444 | } | |
445 | ||
b4503762 | 446 | // Deactivates the email address as a redirection. |
11ed26e5 | 447 | public function deactivate() |
0337d704 | 448 | { |
0337d704 | 449 | if ($this->active) { |
b4503762 SJ |
450 | XDB::execute('UPDATE email_redirect_account |
451 | SET flags = \'inactive\' | |
452 | WHERE uid = {?} AND redirect = {?}', | |
453 | $this->user->id(), $this->email); | |
454 | S::logger()->log('email_off', $this->email . ($this->user->id() != S::v('uid') ? "(admin on {$this->user->login()})" : "") ); | |
455 | $this->inactive = true; | |
456 | $this->active = false; | |
0337d704 | 457 | } |
458 | } | |
612a2d8a | 459 | |
0337d704 | 460 | |
b4503762 | 461 | // Sets the rewrite rule for the given address. |
11ed26e5 | 462 | public function set_rewrite($rewrite) |
0337d704 | 463 | { |
b4503762 | 464 | if ($this->type != 'smtp' || $this->rewrite == $rewrite) { |
0337d704 | 465 | return; |
466 | } | |
11ed26e5 VZ |
467 | if (!$rewrite || !isvalid_email($rewrite)) { |
468 | $rewrite = ''; | |
12acff5d | 469 | } |
b4503762 SJ |
470 | XDB::execute('UPDATE email_redirect_account |
471 | SET rewrite = {?} | |
472 | WHERE uid = {?} AND redirect = {?} AND type = \'smtp\'', | |
473 | $rewrite, $this->user->id(), $this->email); | |
11ed26e5 | 474 | $this->rewrite = $rewrite; |
3d17e48f FB |
475 | if (!$this->allow_rewrite) { |
476 | global $globals; | |
477 | if (empty($this->hash)) { | |
478 | $this->hash = rand_url_id(); | |
b4503762 SJ |
479 | XDB::execute('UPDATE email_redirect_account |
480 | SET hash = {?} | |
481 | WHERE uid = {?} AND redirect = {?} AND type = \'smtp\'', | |
482 | $this->hash, $this->user->id(), $this->email); | |
3d17e48f | 483 | } |
3d17e48f FB |
484 | $mail = new PlMailer('emails/rewrite-in.mail.tpl'); |
485 | $mail->assign('mail', $this); | |
21c7c593 | 486 | $mail->assign('user', $this->user); |
3d17e48f | 487 | $mail->assign('baseurl', $globals->baseurl); |
c66de9a0 FB |
488 | $mail->assign('sitename', $globals->core->sitename); |
489 | $mail->assign('to', $this->email); | |
21c7c593 | 490 | $mail->send($this->user->isEmailFormatHtml()); |
3d17e48f | 491 | } |
0337d704 | 492 | } |
493 | ||
441c2451 | 494 | |
b4503762 | 495 | // Resets the error counts associated with the redirection. |
11ed26e5 | 496 | public function clean_errors() |
441c2451 | 497 | { |
b4503762 SJ |
498 | if ($this->type != 'smtp') { |
499 | return; | |
500 | } | |
dd70cd28 | 501 | if (!S::admin()) { |
441c2451 | 502 | return false; |
503 | } | |
b4503762 SJ |
504 | $this->broken = 0; |
505 | $this->broken_level = 0; | |
506 | $this->last = 0; | |
507 | return XDB::execute('UPDATE email_redirect_account | |
508 | SET broken_level = 0, broken_date = 0, last = 0 | |
509 | WHERE uid = {?} AND redirect = {?} AND type = \'smtp\'', | |
12a587df | 510 | $this->user->id(), $this->email); |
11ed26e5 VZ |
511 | } |
512 | ||
11ed26e5 | 513 | |
b4503762 SJ |
514 | // Email backend capabilities ('rewrite' refers to From: rewrite for mails |
515 | // forwarded by Polytechnique.org's MXs; 'removable' indicates if the email | |
516 | // can be definitively removed; 'disable' indicates if the email has a third | |
517 | // status 'disabled' in addition to 'active' and 'inactive'). | |
11ed26e5 VZ |
518 | public function has_rewrite() |
519 | { | |
b4503762 | 520 | return ($this->type == 'smtp'); |
11ed26e5 VZ |
521 | } |
522 | ||
11ed26e5 VZ |
523 | public function is_removable() |
524 | { | |
b4503762 | 525 | return ($this->type == 'smtp'); |
11ed26e5 VZ |
526 | } |
527 | ||
11ed26e5 VZ |
528 | public function has_disable() |
529 | { | |
530 | return true; | |
441c2451 | 531 | } |
afde0a3a | 532 | |
b4503762 | 533 | public function is_redirection() |
afde0a3a | 534 | { |
b4503762 | 535 | return ($this->type == 'smtp'); |
afde0a3a VZ |
536 | } |
537 | ||
538 | // Returns the list of allowed storages for the @p user. | |
b4503762 | 539 | static private function get_allowed_storages(User $user) |
afde0a3a VZ |
540 | { |
541 | global $globals; | |
542 | $storages = array(); | |
543 | ||
544 | // Google Apps storage is available for users with valid Google Apps account. | |
545 | require_once 'googleapps.inc.php'; | |
546 | if ($globals->mailstorage->googleapps_domain && | |
12a587df | 547 | GoogleAppsAccount::account_status($user->id()) == 'active') { |
afde0a3a VZ |
548 | $storages[] = 'googleapps'; |
549 | } | |
550 | ||
551 | // IMAP storage is always visible to administrators, and is allowed for | |
552 | // everyone when the service is marked as 'active'. | |
dd70cd28 | 553 | if ($globals->mailstorage->imap_active || S::admin()) { |
afde0a3a VZ |
554 | $storages[] = 'imap'; |
555 | } | |
556 | ||
557 | return $storages; | |
558 | } | |
559 | ||
b4503762 | 560 | static public function activate_storage(User $user, $storage) |
afde0a3a | 561 | { |
b4503762 | 562 | Platal::assert(in_array($storage, self::get_allowed_storages($user))); |
afde0a3a | 563 | |
b4503762 SJ |
564 | if (!self::is_active_storage($user, $storage)) { |
565 | global $globals; | |
566 | ||
567 | XDB::execute('INSERT INTO email_redirect_account (uid, type, redirect, flags) | |
568 | VALUES ({?}, {?}, {?}, \'active\')', | |
569 | $user->id(), $storage, | |
570 | $user->hruid . '@' . self::$storage_domains[$storage] . '.' . $globals->mail->domain); | |
571 | } | |
afde0a3a VZ |
572 | } |
573 | ||
b4503762 | 574 | static public function deactivate_storage(User $user, $storage) |
afde0a3a | 575 | { |
b4503762 SJ |
576 | if (in_array($storage, self::$storage_domains)) { |
577 | XDB::execute('DELETE FROM email_redirect_account | |
578 | WHERE uid = {?} AND type = {?}', | |
579 | $user->id(), $storage); | |
580 | } | |
afde0a3a VZ |
581 | } |
582 | ||
b4503762 | 583 | static public function is_active_storage(User $user, $storage) |
afde0a3a | 584 | { |
b4503762 SJ |
585 | if (!in_array($storage, self::$storage_domains)) { |
586 | return false; | |
afde0a3a | 587 | } |
b4503762 SJ |
588 | $res = XDB::fetchOneCell('SELECT COUNT(*) |
589 | FROM email_redirect_account | |
590 | WHERE uid = {?} AND type = {?} AND flags = \'active\')', | |
591 | $user->id(), $storage); | |
592 | return !is_null($res) && $res > 0; | |
afde0a3a | 593 | } |
afde0a3a | 594 | } |
441c2451 | 595 | // class Redirect {{{1 |
11ed26e5 VZ |
596 | // Redirect is a placeholder class for an user's active redirections (third-party |
597 | // redirection email, or Polytechnique.org mail storages). | |
0337d704 | 598 | class Redirect |
599 | { | |
b4503762 | 600 | private $flags = 'active'; |
12a587df | 601 | private $user; |
612a2d8a | 602 | |
603 | public $emails; | |
604 | public $bogo; | |
0337d704 | 605 | |
26ba053e | 606 | public function __construct(User $user) |
0337d704 | 607 | { |
12a587df VZ |
608 | $this->user = &$user; |
609 | $this->bogo = new Bogo($user); | |
11ed26e5 | 610 | |
afde0a3a | 611 | // Adds third-party email redirections. |
b4503762 SJ |
612 | $res = XDB::iterator('SELECT redirect, rewrite, type, action, broken_date, broken_level, last, flags, hash, allow_rewrite |
613 | FROM email_redirect_account | |
614 | WHERE uid = {?} AND type != \'homonym\'', | |
615 | $user->id()); | |
616 | $this->emails = array(); | |
0337d704 | 617 | while ($row = $res->next()) { |
b4503762 | 618 | $this->emails[] = new Email($user, $row); |
afde0a3a | 619 | } |
0337d704 | 620 | } |
621 | ||
612a2d8a | 622 | public function other_active($email) |
0337d704 | 623 | { |
624 | foreach ($this->emails as $mail) { | |
11ed26e5 | 625 | if ($mail->email != $email && $mail->active && $mail->sufficient) { |
0337d704 | 626 | return true; |
627 | } | |
628 | } | |
629 | return false; | |
630 | } | |
631 | ||
612a2d8a | 632 | public function delete_email($email) |
0337d704 | 633 | { |
0337d704 | 634 | if (!$this->other_active($email)) { |
635 | return ERROR_INACTIVE_REDIRECTION; | |
636 | } | |
b4503762 SJ |
637 | XDB::execute('DELETE FROM email_redirect_account |
638 | WHERE uid = {?} AND redirect = {?} AND type != \'homonym\'', | |
639 | $this->user->id(), $email); | |
12a587df | 640 | S::logger()->log('email_del', $email . ($this->user->id() != S::v('uid') ? " (admin on {$this->user->login()})" : "")); |
11ed26e5 VZ |
641 | foreach ($this->emails as $i => $mail) { |
642 | if ($email == $mail->email) { | |
0337d704 | 643 | unset($this->emails[$i]); |
644 | } | |
3c1e6a1e | 645 | } |
ccdbc270 | 646 | check_redirect($this); |
0337d704 | 647 | return SUCCESS; |
648 | } | |
649 | ||
612a2d8a | 650 | public function add_email($email) |
0337d704 | 651 | { |
0337d704 | 652 | $email_stripped = strtolower(trim($email)); |
653 | if (!isvalid_email($email_stripped)) { | |
654 | return ERROR_INVALID_EMAIL; | |
655 | } | |
656 | if (!isvalid_email_redirection($email_stripped)) { | |
657 | return ERROR_LOOP_EMAIL; | |
658 | } | |
00ba8a74 | 659 | // If the email was already present for this user, we reset it to the default values, we thus use REPLACE INTO. |
b4503762 | 660 | XDB::execute('REPLACE INTO email_redirect_account (uid, redirect, flags) |
00ba8a74 SJ |
661 | VALUES ({?}, {?}, \'active\')', |
662 | $this->user->id(), $email); | |
3c1e6a1e | 663 | if ($logger = S::v('log', null)) { // may be absent --> step4.php |
12a587df | 664 | S::logger()->log('email_add', $email . ($this->user->id() != S::v('uid') ? " (admin on {$this->user->login()})" : "")); |
0337d704 | 665 | } |
3c1e6a1e | 666 | foreach ($this->emails as $mail) { |
667 | if ($mail->email == $email_stripped) { | |
0337d704 | 668 | return SUCCESS; |
669 | } | |
3c1e6a1e | 670 | } |
b4503762 SJ |
671 | $this->emails[] = new Email($this->user, array( |
672 | 'redirect' => $email, | |
673 | 'rewrite' => '', | |
674 | 'type' => 'smtp', | |
675 | 'action' => 'default', | |
676 | 'broken_date' => '0000-00-00', | |
677 | 'broken_level' => 0, | |
678 | 'last' => '0000-00-00', | |
679 | 'flags' => 'active', | |
680 | 'hash' => null, | |
681 | 'allow_rewrite' => 0 | |
682 | )); | |
ca6d07f4 | 683 | |
684 | // security stuff | |
12a587df | 685 | check_email($email, "Ajout d'une adresse surveillée aux redirections de " . $this->user->login()); |
ccdbc270 | 686 | check_redirect($this); |
0337d704 | 687 | return SUCCESS; |
688 | } | |
689 | ||
612a2d8a | 690 | public function modify_email($emails_actifs, $emails_rewrite) |
0337d704 | 691 | { |
b4503762 SJ |
692 | foreach ($this->emails as &$email) { |
693 | if (in_array($email->email, $emails_actifs)) { | |
694 | $email->activate(); | |
3c1e6a1e | 695 | } else { |
b4503762 | 696 | $email->deactivate(); |
3c1e6a1e | 697 | } |
b4503762 | 698 | $email->set_rewrite($emails_rewrite[$email->email]); |
0337d704 | 699 | } |
ccdbc270 | 700 | check_redirect($this); |
b4503762 | 701 | return SUCCESS; |
0337d704 | 702 | } |
703 | ||
eaf30d86 | 704 | public function modify_one_email($email, $activate) |
ccdbc270 | 705 | { |
b7582015 | 706 | $allinactive = true; |
707 | $thisone = false; | |
8ffa657a | 708 | foreach ($this->emails as $i=>$mail) { |
709 | if ($mail->email == $email) { | |
b7582015 | 710 | $thisone = $i; |
8ffa657a | 711 | } |
11ed26e5 | 712 | $allinactive &= !$mail->active || !$mail->sufficient || $mail->email == $email; |
8ffa657a | 713 | } |
b7582015 | 714 | if ($thisone === false) { |
715 | return ERROR_INVALID_EMAIL; | |
716 | } | |
ccdbc270 | 717 | if ($allinactive || $activate) { |
11ed26e5 | 718 | $this->emails[$thisone]->activate(); |
ccdbc270 | 719 | } else { |
11ed26e5 | 720 | $this->emails[$thisone]->deactivate(); |
ccdbc270 | 721 | } |
722 | check_redirect($this); | |
b7582015 | 723 | if ($allinactive && !$activate) { |
724 | return ERROR_INACTIVE_REDIRECTION; | |
eaf30d86 | 725 | } |
b4503762 | 726 | return SUCCESS; |
8ffa657a | 727 | } |
728 | ||
612a2d8a | 729 | public function modify_one_email_redirect($email, $redirect) |
730 | { | |
441c2451 | 731 | foreach ($this->emails as &$mail) { |
612a2d8a | 732 | if ($mail->email == $email) { |
11ed26e5 | 733 | $mail->set_rewrite($redirect); |
ccdbc270 | 734 | check_redirect($this); |
735 | return; | |
612a2d8a | 736 | } |
737 | } | |
738 | } | |
441c2451 | 739 | |
11ed26e5 | 740 | public function clean_errors($email) |
441c2451 | 741 | { |
742 | foreach ($this->emails as &$mail) { | |
743 | if ($mail->email == $email) { | |
e1547442 | 744 | check_redirect($this); |
11ed26e5 | 745 | return $mail->clean_errors(); |
441c2451 | 746 | } |
747 | } | |
748 | return false; | |
749 | } | |
750 | ||
441c2451 | 751 | public function disable() |
752 | { | |
b4503762 | 753 | XDB::execute("UPDATE email_redirect_account |
441c2451 | 754 | SET flags = 'disable' |
20398e42 | 755 | WHERE flags = 'active' AND uid = {?}", $this->user->id()); |
441c2451 | 756 | foreach ($this->emails as &$mail) { |
11ed26e5 | 757 | if ($mail->active && $mail->has_disable()) { |
441c2451 | 758 | $mail->disabled = true; |
759 | $mail->active = false; | |
760 | } | |
761 | } | |
e1547442 | 762 | check_redirect($this); |
441c2451 | 763 | } |
764 | ||
441c2451 | 765 | public function enable() |
766 | { | |
b4503762 | 767 | XDB::execute("UPDATE email_redirect_account |
441c2451 | 768 | SET flags = 'active' |
20398e42 | 769 | WHERE flags = 'disable' AND uid = {?}", $this->user->id()); |
441c2451 | 770 | foreach ($this->emails as &$mail) { |
771 | if ($mail->disabled) { | |
441c2451 | 772 | $mail->disabled = false; |
b4503762 | 773 | $mail->active = true; |
441c2451 | 774 | } |
e1547442 | 775 | check_redirect($this); |
441c2451 | 776 | } |
777 | } | |
778 | ||
612a2d8a | 779 | public function get_broken_mx() |
ccdbc270 | 780 | { |
3083bd93 | 781 | $res = XDB::query("SELECT host, text |
8af1d78f FB |
782 | FROM mx_watch |
783 | WHERE state != 'ok'"); | |
120bd636 | 784 | if (!$res->numRows()) { |
ccdbc270 | 785 | return array(); |
786 | } | |
c754cf5b | 787 | $mxs = $res->fetchAllAssoc(); |
ccdbc270 | 788 | $mails = array(); |
789 | foreach ($this->emails as &$mail) { | |
11ed26e5 | 790 | if ($mail->active && strstr($mail->email, '@') !== false) { |
ccdbc270 | 791 | list(,$domain) = explode('@', $mail->email); |
792 | getmxrr($domain, $lcl_mxs); | |
793 | if (empty($lcl_mxs)) { | |
794 | $lcl_mxs = array($domain); | |
795 | } | |
796 | $broken = false; | |
797 | foreach ($mxs as &$mx) { | |
798 | foreach ($lcl_mxs as $lcl) { | |
c754cf5b | 799 | if (fnmatch($mx['host'], $lcl)) { |
ccdbc270 | 800 | $broken = $mx['text']; |
801 | break; | |
802 | } | |
803 | } | |
804 | if ($broken) { | |
3083bd93 | 805 | $mails[] = array('mail' => $mail->email, 'text' => $broken); |
f653ff3d | 806 | break; |
ccdbc270 | 807 | } |
808 | } | |
809 | } | |
810 | } | |
811 | return $mails; | |
812 | } | |
e97e9b8f | 813 | |
e97e9b8f VZ |
814 | public function active_emails() |
815 | { | |
816 | $emails = array(); | |
817 | foreach ($this->emails as $mail) { | |
818 | if ($mail->active) { | |
819 | $emails[] = $mail; | |
820 | } | |
821 | } | |
822 | return $emails; | |
823 | } | |
45282934 | 824 | |
45282934 VZ |
825 | public function get_uid() |
826 | { | |
12a587df | 827 | return $this->user->id(); |
45282934 | 828 | } |
0337d704 | 829 | } |
830 | ||
a7de4ef7 | 831 | // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: |
0337d704 | 832 | ?> |