X-Git-Url: http://git.polytechnique.org/?a=blobdiff_plain;f=include%2Femails.inc.php;h=b83a5eafa140c10326752ed1a4428aac2d274780;hb=d3a4f32c262cefe7741fb03e5201e3b49fba95d1;hp=e1750a7d750f318ab5a429aede136aa87f8f6a42;hpb=7852229b8a8d821ecefc11dfb6dd061c2ef3dda5;p=platal.git diff --git a/include/emails.inc.php b/include/emails.inc.php index e1750a7..b83a5ea 100644 --- a/include/emails.inc.php +++ b/include/emails.inc.php @@ -24,9 +24,9 @@ define('ERROR_INACTIVE_REDIRECTION', 2); define('ERROR_INVALID_EMAIL', 3); define('ERROR_LOOP_EMAIL', 4); -function add_to_list_alias(User $user, $local_part, $domain, $type = 'user') +function add_to_list_alias(User $user, $local_part, $domain, $type = 'alias') { - Platal::assert($user !== null); + Platal::assert($user !== null, 'User should not be null.'); XDB::execute('INSERT IGNORE INTO email_virtual (email, domain, redirect, type) SELECT {?}, id, {?}, {?} @@ -35,9 +35,9 @@ function add_to_list_alias(User $user, $local_part, $domain, $type = 'user') $local_part, $user->forlifeEmail(), $type, $domain); } -function delete_from_list_alias(User $user, $local_part, $domain, $type = 'user') +function delete_from_list_alias(User $user, $local_part, $domain, $type = 'alias') { - Platal::assert($user !== null); + Platal::assert($user !== null, 'User should not be null.'); XDB::execute('DELETE v FROM email_virtual AS v @@ -47,9 +47,9 @@ function delete_from_list_alias(User $user, $local_part, $domain, $type = 'user' $local_part, $domain, $user->forlifeEmail(), $type); } -function update_list_alias(User $user, $former_email, $local_part, $domain, $type = 'user') +function update_list_alias(User $user, $former_email, $local_part, $domain, $type = 'alias') { - Platal::assert($user !== null); + Platal::assert($user !== null, 'User should not be null.'); XDB::execute('UPDATE email_virtual AS v INNER JOIN email_virtual_domains AS d ON (v.domain = d.id) @@ -64,15 +64,23 @@ function list_alias_members($local_part, $domain) FROM email_virtual AS v INNER JOIN email_virtual_domains AS m ON (v.domain = m.id) INNER JOIN email_virtual_domains AS d ON (d.aliasing = m.id) - WHERE v.email = {?} AND d.name = {?} AND type = \'user\'', + WHERE v.email = {?} AND d.name = {?} AND type = \'alias\'', $local_part, $domain); - $members = array(); + $users = array(); + $nonusers = array(); foreach ($emails as $email) { - $members[] = User::getSilent($email); + if ($user = User::getSilent($email)) { + $users[] = $user; + } else { + $nonusers[] = $email; + } } - return $members; + return array( + 'users' => $users, + 'nonusers' => $nonusers + ); } function delete_list_alias($local_part, $domain) @@ -81,7 +89,7 @@ function delete_list_alias($local_part, $domain) FROM email_virtual AS v INNER JOIN email_virtual_domains AS m ON (v.domain = m.id) INNER JOIN email_virtual_domains AS d ON (d.aliasing = m.id) - WHERE v.email = {?} AND d.name = {?} AND type = \'user\'', + WHERE v.email = {?} AND d.name = {?} AND type = \'alias\'', $local_part, $domain); } @@ -90,7 +98,7 @@ function iterate_list_alias($domain) return XDB::fetchColumn('SELECT CONCAT(v.email, \'@\', m.name) FROM email_virtual AS v INNER JOIN email_virtual_domains AS m ON (v.domain = m.id) - WHERE m.name = {?} AND v.type = \'user\' + WHERE m.name = {?} AND v.type = \'alias\' GROUP BY v.email', $domain); } @@ -140,7 +148,8 @@ function mark_broken_email($email, $admin = false) return; } - $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 + $user = XDB::fetchOneAssoc('SELECT r1.uid, r1.broken_level != 0 AS broken, COUNT(r2.uid) AS nb_mails, + s.email AS alias, DATE_ADD(r1.last, INTERVAL 14 DAY) < CURDATE() as notify FROM email_redirect_account AS r1 INNER JOIN accounts AS a ON (a.uid = r1.uid) INNER JOIN email_source_account AS s ON (a.uid = s.uid AND s.flags = \'bestalias\') @@ -158,7 +167,7 @@ function mark_broken_email($email, $admin = false) WHERE redirect = {?}', $email); } elseif ($admin) { XDB::execute('UPDATE email_redirect_account - SET last = CURDATE(), broken_level = IF(broken_level > 1, 3, 2) + SET last = CURDATE(), broken_level = broken_level + 1 WHERE redirect = {?} AND DATE_ADD(last, INTERVAL 14 DAY) < CURDATE()', $email); } else { @@ -176,14 +185,13 @@ function mark_broken_email($email, $admin = false) // eventually selects a new bestalias when required. function fix_bestalias(User $user) { - $count = XDB::fetchOneCell('SELECT COUNT(*) - FROM email_source_account - WHERE uid = {?} AND FIND_IN_SET(\'bestalias\', flags)', - $user->id()); + // First check if the bestalias is properly set. + $alias_count = XDB::fetchOneCell('SELECT COUNT(*) + FROM email_source_account + WHERE uid = {?} AND FIND_IN_SET(\'bestalias\', flags) AND expire IS NULL', + $user->id()); - if ($count == 1) { - return; - } elseif ($count > 1) { + if ($alias_count > 1) { // If too many bestaliases, delete the bestalias flag from all this // user's emails (this should never happen). XDB::execute("UPDATE email_source_account @@ -191,15 +199,37 @@ function fix_bestalias(User $user) WHERE uid = {?}", $user->id()); } + if ($alias_count != 1) { + // If no bestalias is selected, we choose the shortest email which is not + // related to a usage name and contains a '.'. + XDB::execute("UPDATE email_source_account + SET flags = CONCAT_WS(',', IF(flags = '', NULL, flags), 'bestalias') + WHERE uid = {?} AND expire IS NULL + ORDER BY NOT FIND_IN_SET('usage', flags), email LIKE '%.%', LENGTH(email) + LIMIT 1", + $user->id()); + } + + // First check if best_domain is properly set. + $domain_count = XDB::fetchOneCell('SELECT COUNT(*) + FROM accounts AS a + INNER JOIN email_source_account AS s ON (s.uid = a.uid AND FIND_IN_SET(\'bestalias\', s.flags)) + INNER JOIN email_virtual_domains AS d ON (d.id = a.best_domain) + INNER JOIN email_virtual_domains AS m ON (d.aliasing = m.id) + INNER JOIN email_virtual_domains AS v ON (v.aliasing = m.id AND v.id = s.domain) + WHERE a.uid = {?} AND (m.name = {?} OR m.name = {?})', + $user->id(), $user->mainEmailDomain(), Platal::globals()->mail->alias_dom); + + if ($domain_count == 0) { + XDB::execute('UPDATE accounts AS a + INNER JOIN email_source_account AS s ON (s.uid = a.uid AND FIND_IN_SET(\'bestalias\', s.flags)) + INNER JOIN email_virtual_domains AS d ON (d.aliasing = s.domain AND (d.name = {?} OR d.name = {?})) + SET a.best_domain = d.id + WHERE a.uid = {?}', + $user->mainEmailDomain(), Platal::globals()->mail->alias_dom, $user->id()); + } + - // If no bestalias is selected, we choose the shortest email which is not - // related to a usage name and contains a '.'. - XDB::execute("UPDATE email_source_account - SET flags = CONCAT_WS(',', IF(flags = '', NULL, flags), 'bestalias') - WHERE uid = {?} - ORDER BY NOT FIND_IN_SET('usage', flags), email LIKE '%.%', LENGTH(email) - LIMIT 1", - $user->id()); } // function valide_email() {{{1 @@ -217,7 +247,7 @@ function valide_email($str) return; } list($ident, $dom) = explode('@', $em); - if ($dom == $globals->mail->domain || $dom == $globals->mail->domain2) { + if (User::isMainMailDomain($dom)) { list($ident1) = explode('_', $ident); list($ident) = explode('+', $ident1); } @@ -231,8 +261,7 @@ function valide_email($str) */ function isvalid_email_redirection($email) { - return isvalid_email($email) && - !preg_match("/@(polytechnique\.(org|edu)|melix\.(org|net)|m4x\.org)$/", $email); + return isvalid_email($email) && !preg_match("/@polytechnique\.edu$/", $email) && User::isForeignEmailAddress($email); } // function ids_from_mails() {{{1 @@ -240,37 +269,43 @@ function isvalid_email_redirection($email) // given email when we found a matching user. function ids_from_mails(array $emails) { - global $globals; - // Removes duplicates, if any. $emails = array_unique($emails); // Formats and splits by domain type (locally managed or external) emails. - $domain_emails = array(); - $other_emails = array(); + $main_domain_emails = array(); + $aux_domain_emails = array(); + $other_emails = array(); foreach ($emails as $email) { if (strpos($email, '@') === false) { - $user = $email; - $domain = $globals->mail->domain2; + $main_domain_emails[] = $email; } else { - list($user, $domain) = explode('@', $email); - } - if ($domain == $globals->mail->alias_dom || $domain == $globals->mail->alias_dom2 - || $domain == $globals->mail->domain || $domain == $globals->mail->domain2) { - list($user) = explode('+', $user); - list($user) = explode('_', $user); - $domain_emails[$email] = strtolower($user . '@' . $domain); - } else { - $other_emails[$email] = strtolower($user . '@' . $domain); + if (User::isForeignEmailAddress($email)) { + $other_emails[$email] = strtolower($user . '@' . $domain); + } else { + list($local_part, $domain) = explode('@', $email); + list($local_part) = explode('+', $local_part); + list($local_part) = explode('_', $local_part); + if (User::isMainMailDomain($domain)) { + $main_domain_emails[$email] = strtolower($local_part); + } elseif (User::isAliasMailDomain($domain)) { + $aux_domain_emails[$email] = strtolower($local_part); + } + } } } // Retrieves emails from our domains. - $domain_uids = XDB::fetchAllAssoc('email', - 'SELECT email, uid - FROM email_source_account - WHERE email IN {?}', - array_unique($domain_emails)); + $main_domain_uids = XDB::fetchAllAssoc('email', + 'SELECT email, uid + FROM email_source_account + WHERE email IN {?} AND type != \'alias_aux\'', + array_unique($main_domain_emails)); + $aux_domain_uids = XDB::fetchAllAssoc('email', + 'SELECT email, uid + FROM email_source_account + WHERE email IN {?} AND type = \'alias_aux\'', + array_unique($aux_domain_emails)); // Retrieves emails from redirections. $other_uids = XDB::fetchAllAssoc('redirect', @@ -281,25 +316,36 @@ function ids_from_mails(array $emails) // Associates given emails with the corresponding uid. $uids = array(); - foreach (array_merge($domain_emails, $other_emails) as $email => $canonical_email) { - if (array_key_exists($canonical_email, $domain_uids)) { - $uids[$email] = $domain_uids[$canonical_email]; - } elseif (array_key_exists($canonical_email, $other_uids)) { - $uids[$email] = $other_uids[$canonical_email]; - } + foreach ($main_domain_emails as $email => $key) { + $uids[$email] = $main_domain_uids[$key]; + } + foreach ($aux_domain_emails as $email => $key) { + $uids[$email] = $aux_domain_uids[$key]; + } + foreach ($other_emails as $email => $key) { + $uids[$email] = $other_uids[$key]; } - return $uids; + return array_unique($uids); } // class Bogo {{{1 // The Bogo class represents a spam filtering level in plat/al architecture. class Bogo { - private static $states = array('let_spams', 'tag_spams', 'tag_and_drop_spams', 'drop_spams'); + public static $states = array( + 0 => 'default', + 1 => 'let_spams', + 2 => 'tag_spams', + 3 => 'tag_and_drop_spams', + 4 => 'drop_spams' + ); private $user; - private $state; + public $state; + public $single_state; + public $redirections; + public $single_redirection; public function __construct(User $user) { @@ -308,28 +354,52 @@ class Bogo } $this->user = &$user; - $res = XDB::query('SELECT action - FROM email_redirect_account - WHERE uid = {?} AND (type = \'smtp\' OR type = \'googleapps\')', - $user->id()); - if ($res->numRows() == 0) { + $res = XDB::fetchOneAssoc('SELECT COUNT(DISTINCT(action)) AS action_count, COUNT(redirect) AS redirect_count, action + FROM email_redirect_account + WHERE uid = {?} AND (type = \'smtp\' OR type = \'googleapps\') AND flags = \'active\' + GROUP BY uid', + $user->id()); + if ($res['redirect_count'] == 0) { return; } - $this->state = $res->fetchOneCell(); + + $this->single_redirection = ($res['redirect_count'] == 1); + $this->redirections = XDB::fetchAllAssoc('SELECT IF(type = \'googleapps\', type, redirect) AS redirect, type, action + FROM email_redirect_account + WHERE uid = {?} AND (type = \'smtp\' OR type = \'googleapps\') + ORDER BY type, redirect', + $user->id()); + + foreach ($this->redirections AS &$redirection) { + $redirection['filter'] = array_search($redirection['action'], self::$states); + } + if ($res['action_count'] == 1) { + $this->state = array_search($res['action'], self::$states); + $this->single_state = true; + } else { + $this->single_state = $this->state = false; + } } - public function change($state) + public function changeAll($state) { - $this->state = is_int($state) ? self::$states[$state] : $state; + Platal::assert($state >= 0 && $state < count(self::$states), 'Unknown antispam level.'); + + $this->state = $state; XDB::execute('UPDATE email_redirect_account SET action = {?} WHERE uid = {?} AND (type = \'smtp\' OR type = \'googleapps\')', - $this->state, $this->user->id()); + self::$states[$this->state], $this->user->id()); } - public function level() + public function change($redirection, $state) { - return array_search($this->state, self::$states); + Platal::assert($state >= 0 && $state < count(self::$states), 'Unknown antispam level.'); + + XDB::execute('UPDATE email_redirect_account + SET action = {?} + WHERE uid = {?} AND (type = {?} OR redirect = {?})', + self::$states[$state], $this->user->id(), $redirection, $redirection); } } @@ -361,6 +431,7 @@ class Email public $display_email; public $domain; public $action; + public $filter_level; // Redirection status properties. public $active; @@ -394,6 +465,7 @@ class Email $this->$status = ($status == $row['flags']); } $this->sufficient = ($this->type == 'smtp' || $this->type == 'googleapps'); + $this->filter_level = ($this->type == 'imap') ? null : array_search($this->action, Bogo::$states); $this->user = &$user; } @@ -527,7 +599,7 @@ class Email static public function activate_storage(User $user, $storage) { - Platal::assert(in_array($storage, self::get_allowed_storages($user))); + Platal::assert(in_array($storage, self::get_allowed_storages($user)), 'Unknown storage.'); if (!self::is_active_storage($user, $storage)) { global $globals; @@ -555,7 +627,7 @@ class Email } $res = XDB::fetchOneCell('SELECT COUNT(*) FROM email_redirect_account - WHERE uid = {?} AND type = {?} AND flags = \'active\')', + WHERE uid = {?} AND type = {?} AND flags = \'active\'', $user->id(), $storage); return !is_null($res) && $res > 0; } @@ -569,12 +641,10 @@ class Redirect private $user; public $emails; - public $bogo; public function __construct(User $user) { $this->user = &$user; - $this->bogo = new Bogo($user); // Adds third-party email redirections. $res = XDB::iterator('SELECT redirect, rewrite, type, action, broken_date, broken_level, last, flags, hash, allow_rewrite @@ -612,6 +682,7 @@ class Redirect } } check_redirect($this); + $this->update_imap(); return SUCCESS; } @@ -624,10 +695,15 @@ class Redirect if (!isvalid_email_redirection($email_stripped)) { return ERROR_LOOP_EMAIL; } + // We first need to retrieve the value for the antispam filter: it is + // either the user's redirections common value, or if they differ, our + // default value. + $bogo = new Bogo($this->user); + $filter = ($bogo->single_state ? Bogo::$states[$bogo->state] : Bogo::$states[0]); // If the email was already present for this user, we reset it to the default values, we thus use REPLACE INTO. - XDB::execute('REPLACE INTO email_redirect_account (uid, redirect, flags) - VALUES ({?}, {?}, \'active\')', - $this->user->id(), $email); + XDB::execute('REPLACE INTO email_redirect_account (uid, redirect, flags, action) + VALUES ({?}, {?}, \'active\', {?})', + $this->user->id(), $email, $filter); if ($logger = S::v('log', null)) { // may be absent --> step4.php S::logger()->log('email_add', $email . ($this->user->id() != S::v('uid') ? " (admin on {$this->user->login()})" : "")); } @@ -640,7 +716,7 @@ class Redirect 'redirect' => $email, 'rewrite' => '', 'type' => 'smtp', - 'action' => 'default', + 'action' => $filter, 'broken_date' => '0000-00-00', 'broken_level' => 0, 'last' => '0000-00-00', @@ -652,6 +728,7 @@ class Redirect // security stuff check_email($email, "Ajout d'une adresse surveillée aux redirections de " . $this->user->login()); check_redirect($this); + $this->update_imap(); return SUCCESS; } @@ -666,6 +743,7 @@ class Redirect $email->set_rewrite($emails_rewrite[$email->email]); } check_redirect($this); + $this->update_imap(); return SUCCESS; } @@ -688,6 +766,7 @@ class Redirect $this->emails[$thisone]->deactivate(); } check_redirect($this); + $this->update_imap(); if ($allinactive && !$activate) { return ERROR_INACTIVE_REDIRECTION; } @@ -700,6 +779,7 @@ class Redirect if ($mail->email == $email) { $mail->set_rewrite($redirect); check_redirect($this); + $this->update_imap(); return; } } @@ -710,6 +790,7 @@ class Redirect foreach ($this->emails as &$mail) { if ($mail->email == $email) { check_redirect($this); + $this->update_imap(); return $mail->clean_errors(); } } @@ -728,6 +809,7 @@ class Redirect } } check_redirect($this); + $this->update_imap(); } public function enable() @@ -742,6 +824,7 @@ class Redirect } check_redirect($this); } + $this->update_imap(); } public function get_broken_mx() @@ -794,6 +877,22 @@ class Redirect { return $this->user->id(); } + + private function update_imap() + { + // Imaps must bounce if and only if the user has no active redirection. + if (!$this->other_active('')) { + XDB::execute('UPDATE email_redirect_account + SET action = \'imap_and_bounce\' + WHERE type = \'imap\' AND uid = {?}', + $this->user->id()); + } else { + XDB::execute('UPDATE email_redirect_account + SET action = \'let_spams\' + WHERE type = \'imap\' AND uid = {?}', + $this->user->id()); + } + } } // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: