Alternc  latest
Alternc logiel libre pour l'hébergement
m_mail.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  ----------------------------------------------------------------------
5  LICENSE
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License (GPL)
9  as published by the Free Software Foundation; either version 2
10  of the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  To read the license please visit http://www.gnu.org/copyleft/gpl.html
18  ----------------------------------------------------------------------
19 */
20 
21 /**
22  * This class handle emails (pop and/or aliases and even wrapper for internal
23  * classes) of hosted users.
24  *
25  * This class is directly using the following alternc MySQL tables:
26  * address = any used email address will be defined here, mailbox = pop/imap mailboxes, recipient = redirection from an email to another
27  * and indirectly the domain class, to know domain names from their id in the DB.
28  * This class is also defining a few hooks, search ->invoke in the code.
29  *
30  * @copyright AlternC-Team 2000-2017 https://alternc.com/
31  */
32 class m_mail {
33 
34  /**
35  * domain list for this account
36  * @access private
37  */
38  var $domains;
39 
40 
41  /**
42  * If an email has those chars, 'not nice in shell env' ;)
43  * we don't store the email in $mail/u/{user}_domain, but in $mail/_/{address_id}_domain
44  * @access private
45  */
46  var $specialchars = array('"', "'", '\\', '/');
47 
48 
49  /**
50  * If an email has those chars, we will ONLY allow RECIPIENTS, NOT POP/IMAP for DOVECOT !
51  * Since Dovecot doesn't allow those characters
52  * @access private
53  */
54  var $forbiddenchars = array('"', "'", '\\', '/', '?', '!', '*', '$', '|', '#', '+');
55 
56 
57  /**
58  * Number of results for a pager display
59  * @access public
60  */
61  var $total;
62 
63  // Human server name for help
66  var $cache_domain_mail_size = array();
67  var $enum_domains = array();
68 
69 
70  /**
71  * Constructeur
72  */
73  function m_mail() {
74  global $L_FQDN;
75  $this->srv_postfix = variable_get('fqdn_postfix', $L_FQDN, 'FQDN name for humans for smtp services. If you change it, launch reload-certs', array('desc' => 'Name', 'type' => 'string'));
76  $this->srv_dovecot = variable_get('fqdn_dovecot', $L_FQDN, 'FQDN name for humans for pop/imap services. If you change it, launch reload-certs', array('desc' => 'Name', 'type' => 'string'));
77  }
78 
79 
80  /**
81  * Hook called by menu class to add the email menu to the left pane
82  */
83  function hook_menu() {
84  $obj = array(
85  'title' => _("Email Addresses"),
86  'link' => 'toggle',
87  'pos' => 30,
88  'links' => array(),
89  );
90 
91  foreach ($this->enum_domains() as $d) {
92  $obj['links'][] = array(
93  'txt' => htmlentities($d["domaine"]) . '&nbsp;' . htmlentities("(" . $d["nb_mail"] . ")"),
94  'url' => "mail_list.php?domain_id=" . urlencode($d['id']),
95  );
96  }
97 
98  return $obj;
99  }
100 
101 
102  function get_total_size_for_domain($domain) {
103  global $db;
104  if (empty($this->cache_domain_mail_size)) {
105  $db->query("SELECT SUBSTRING_INDEX(user,'@', -1) as domain, SUM(quota_dovecot) AS sum FROM dovecot_quota group by domain ;");
106  while ($db->next_record()) {
107  $dd = $db->f('domain');
108  $this->cache_domain_mail_size[$dd] = $db->f('sum');
109  }
110  }
111  if (isset($this->cache_domain_mail_size[$domain])) {
112  return $this->cache_domain_mail_size[$domain];
113  }
114  return 0;
115  }
116 
117 
118  /**
119  * @param string $domain_id
120  */
121  function catchall_getinfos($domain_id) {
122  global $dom, $db;
123  $rr = array(
124  'mail_id' => '',
125  'domain' => $dom->get_domain_byid($domain_id),
126  'target' => '',
127  'type' => '',
128  );
129 
130  $db->query("select r.recipients as dst, a.id mail_id from address a, recipient r where a.domain_id = ? and r.address_id = a.id and a.address='';", array($domain_id));
131  if ($db->next_record()) {
132  $rr['target'] = $db->f('dst');
133  $rr['mail_id'] = $db->f('mail_id');
134  }
135 
136  // Does it redirect to a specific mail or to a domain
137  if (empty($rr['target'])) {
138  $rr['type'] = 'none';
139  } elseif (substr($rr['target'], 0, 1) == '@') {
140  $rr['type'] = 'domain';
141  } else {
142  $rr['type'] = 'mail';
143  }
144 
145  return $rr;
146  }
147 
148 
149  /**
150  * @param string $domain_id
151  */
152  function catchall_del($domain_id) {
153  $catch = $this->catchall_getinfos($domain_id);
154  if (empty($catch['mail_id'])) {
155  return false;
156  }
157  return $this->delete($catch['mail_id']);
158  }
159 
160 
161  /**
162  * @param string $domain_id
163  * @param string $target
164  */
165  function catchall_set($domain_id, $target) {
166  global $msg;
167  $target = rtrim($target);
168  if (strlen($target) > 0 && substr_count($target, '@') == 0) { // Pas de @
169  $target = '@' . $target;
170  }
171 
172  if (substr($target, 0, 1) == '@') { // the first character is @
173  // FIXME validate domain
174  } else { // it MUST be an email
175  if (!filter_var($target, FILTER_VALIDATE_EMAIL)) {
176  $msg->raise("ERROR", "mail", _("The email you entered is syntaxically incorrect"));
177  return false;
178  }
179  }
180  $this->catchall_del($domain_id);
181  return $this->create_alias($domain_id, '', $target, "catchall", true);
182  }
183 
184 
185  /**
186  * get_quota (hook for quota class), returns the number of used
187  * service for a quota-bound service
188  * @param $name string the named quota we want
189  * @return the number of used service for the specified quota,
190  * or false if I'm not the one for the named quota
191  */
192  function hook_quota_get() {
193  global $db, $msg, $cuid, $quota;
194  $msg->debug("mail", "getquota");
195  $q = Array("name" => "mail", "description" => _("Email addresses"), "used" => 0);
196  $db->query("SELECT COUNT(*) AS cnt FROM address a, domaines d WHERE a.domain_id=d.id AND d.compte= ? AND a.type='';", array($cuid));
197  if ($db->next_record()) {
198  $q['used'] = $db->f("cnt");
199  $q['sizeondisk'] = $quota->get_size_mail_sum_user($cuid)/1024;
200  }
201  return $q;
202  }
203 
204 
205  /**
206  * Password policy kind used in this class (hook for admin class)
207  * @return array an array of policykey => "policy name (for humans)"
208  */
210  return array("pop" => _("Email account password"));
211  }
212 
213 
214  /**
215  * Returns the list of mail-hosting domains for a user
216  * @return array indexed array of hosted domains
217  */
218  function enum_domains($uid = -1) {
219  global $db, $msg, $cuid;
220  $msg->debug("mail", "enum_domains");
221  if ($uid == -1) {
222  $uid = $cuid;
223  }
224  $db->query("
225 SELECT
226  d.id,
227  d.domaine,
228  IFNULL( COUNT(a.id), 0) as nb_mail
229 FROM
230  domaines d LEFT JOIN address a ON (d.id=a.domain_id AND a.type='')
231 WHERE
232  d.compte = ?
233  and d.gesmx = 1
234 GROUP BY
235  d.id
236 ORDER BY
237  d.domaine
238 ;
239 ", array($uid));
240  $this->enum_domains = array();
241  while ($db->next_record()) {
242  $this->enum_domains[] = $db->Record;
243  }
244  return $this->enum_domains;
245  }
246 
247 
248  /**
249  * available: tells if an email address can be installed in the server
250  * check the domain part (is it mine too), the syntax, and the availability.
251  * @param $mail string email to check
252  * @return boolean true if the email can be installed on the server
253  */
254  function available($mail) {
255  global $db, $msg, $dom;
256  $msg->log("mail", "available");
257  list($login, $domain) = explode("@", $mail, 2);
258  // Validate the domain ownership & syntax
259  if (!($dom_id = $dom->get_domain_byname($domain))) {
260  return false;
261  }
262  // Validate the email syntax:
263  if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
264  $msg->raise("ERROR", "mail", _("The email you entered is syntaxically incorrect"));
265  return false;
266  }
267  // Check the availability
268  $db->query("SELECT a.id FROM address a WHERE a.domain_id= ? AND a.address= ?;", array($dom_id, $login));
269  if ($db->next_record()) {
270  return false;
271  } else {
272  return true;
273  }
274  }
275 
276 
277  /**
278  * function used to list every mail address hosted on a domain.
279  * @param $dom_id integer the domain id.
280  * @param $search string search that string in recipients or address.
281  * @param $offset integer skip THAT much emails in the result.
282  * @param $count integer return no more than THAT much emails. -1 for ALL. Offset is ignored then.
283  * @result an array of each mail hosted under the domain.
284  */
285  function enum_domain_mails($dom_id = null, $search = "", $offset = 0, $count = 30, $show_systemmails = false) {
286  global $db, $msg, $hooks;
287  $msg->debug("mail", "enum_domains_mail");
288 
289  $query_args = array($dom_id);
290  $search = trim($search);
291  $where = " a.domain_id = ? ";
292 
293  if ($search) {
294  $where .= " AND (a.address LIKE ? OR r.recipients LIKE ? )";
295  array_push($query_args, "%" . $search . "%", "%" . $search . "%");
296  }
297  if (!$show_systemmails) {
298  $where .= " AND type='' ";
299  }
300  $db->query("SELECT count(a.id) AS total FROM address a LEFT JOIN recipient r ON r.address_id=a.id WHERE " . $where . ";", $query_args);
301  $db->next_record();
302  $this->total = $db->f("total");
303  if ($count != -1) {
304  $offset = intval($offset);
305  $count = intval($count);
306  $limit = " LIMIT $offset, $count ";
307  } else {
308  $limit = "";
309  }
310  $db->query("SELECT a.id, a.address, a.password, a.`enabled`, a.mail_action, d.domaine AS domain, m.quota, m.quota*1024*1024 AS quotabytes, q.quota_dovecot as used, NOT ISNULL(m.id) AS islocal, a.type, r.recipients, m.lastlogin, a.domain_id
311  FROM ((domaines d, address a LEFT JOIN mailbox m ON m.address_id=a.id) LEFT JOIN dovecot_quota q ON CONCAT(a.address,'@',d.domaine) = q.user) LEFT JOIN recipient r ON r.address_id=a.id
312  WHERE " . $where . " AND d.id=a.domain_id ORDER BY a.address ASC " . $limit . " ;", $query_args);
313  if (!$db->next_record()) {
314  $msg->raise("ERROR", "mail", _("No email found for this query"));
315  return array();
316  }
317  $res = array();
318  do {
319  $details = $db->Record;
320  // if necessary, fill the typedata with data from hooks ...
321  if ($details["type"]) {
322  $result = $hooks->invoke("hook_mail_get_details", array($details)); // Will fill typedata if necessary
323  $details["typedata"] = implode("<br />", $result);
324  }
325  $res[] = $details;
326  } while ($db->next_record());
327  return $res;
328  }
329 
330  function hook_mail_get_details($detail) {
331  if ($detail['type'] == 'catchall') {
332  return _(sprintf("Special mail address for catch-all. <a href='mail_manage_catchall.php?domain_id=%s'>Click here to manage it.</a>", $detail['domain_id']));
333  }
334  }
335 
336 
337  /**
338  * Function used to insert a new mail into the db
339  * should be used by the web interface, not by third-party programs.
340  *
341  * This function calls the hook "hooks_mail_cancreate"
342  * which must return FALSE if the user can't create this email, and raise and error accordingly
343  *
344  * @param $dom_id integer A domain_id (owned by the user)
345  * (will be the part at the right of the @ in the email)
346  * @param $mail string the left part of the email to create (something@dom_id)
347  * @return an hashtable containing the database id of the newly created mail,
348  * or false if an error occured ($msg is filled accordingly)
349  */
350  function create($dom_id, $mail, $type = "", $dontcheck = false) {
351  global $msg, $db, $quota, $dom, $hooks;
352  $msg->log("mail", "create", $mail);
353 
354  // Validate the domain id
355  if (!($domain = $dom->get_domain_byid($dom_id))) {
356  return false;
357  }
358 
359  // Validate the email syntax:
360  $m = $mail . "@" . $domain;
361  if (!filter_var($m, FILTER_VALIDATE_EMAIL) && !$dontcheck) {
362  $msg->raise("ERROR", "mail", _("The email you entered is syntaxically incorrect"));
363  return false;
364  }
365 
366  // Call other classes to check we can create it:
367  $cancreate = $hooks->invoke("hook_mail_cancreate", array($dom_id, $mail));
368  if (in_array(false, $cancreate, true)) {
369  return false;
370  }
371 
372  // Check the quota:
373  if (($type=="")&&!$quota->cancreate("mail")) {
374  $msg->raise("ALERT", "mail", _("You cannot create email addresses: your quota is over"));
375  return false;
376  }
377  // Already exists?
378  $db->query("SELECT * FROM address WHERE domain_id= ? AND address= ? ;", array($dom_id, $mail));
379  if ($db->next_record()) {
380  if ($db->f("type") == "mailman")
381  $msg->raise("ERROR", "mail", _("This email address already exists in mailman"));
382  else
383  $msg->raise("ERROR", "mail", _("This email address already exists"));
384 
385  return false;
386  }
387  // Create it now
388  $db->query("INSERT INTO address (domain_id, address,type) VALUES (?, ?, ?);", array($dom_id, $mail, $type));
389  if (!($id = $db->lastid())) {
390  $msg->raise("ERROR", "mail", _("An unexpected error occured when creating the email"));
391  return false;
392  }
393  return $id;
394  }
395 
396 
397  /**
398  * function used to get every information we can on a mail
399  * @param $mail_id integer
400  * @return array a hashtable with all the informations for that email
401  */
402  function get_details($mail_id) {
403  global $db, $msg, $hooks;
404  $msg->debug("mail", "get_details");
405 
406  $mail_id = intval($mail_id);
407  // Validate that this email is owned by me...
408  if (!($mail = $this->is_it_my_mail($mail_id))) {
409  return false;
410  }
411 
412  // We fetch all the informations for that email: these will fill the hastable :
413  $db->query("SELECT a.id, a.address, a.password, a.enabled, d.domaine AS domain, m.path, m.quota, m.quota*1024*1024 AS quotabytes, q.quota_dovecot AS used, NOT ISNULL(m.id) AS islocal, a.type, r.recipients, m.lastlogin, a.mail_action, m.mail_action AS mailbox_action FROM ((domaines d, address a LEFT JOIN mailbox m ON m.address_id=a.id) LEFT JOIN dovecot_quota q ON CONCAT(a.address,'@',d.domaine) = q.user) LEFT JOIN recipient r ON r.address_id=a.id WHERE a.id= ? AND d.id=a.domain_id;", array($mail_id));
414  if (!$db->next_record()) {
415  return false;
416  }
417  $details = $db->Record;
418  // if necessary, fill the typedata with data from hooks ...
419  if ($details["type"]) {
420  $result = $hooks->invoke("hook_mail_get_details", array($mail_id)); // Will fill typedata if necessary
421  $details["typedata"] = implode("<br />", $result);
422  }
423  return $details;
424  }
425 
426  private $isitmy_cache = array();
427 
428 
429  /**
430  * Check if an email is mine ...
431  *
432  * @param $mail_id integer the number of the email to check
433  * @return string the complete email address if that's mine, false if not
434  * ($msg is filled accordingly)
435  */
436  function is_it_my_mail($mail_id) {
437  global $msg, $db, $cuid;
438  $mail_id = intval($mail_id);
439  // cache it (may be called more than one time in the same page).
440  if (isset($this->isitmy_cache[$mail_id])) {
441  return $this->isitmy_cache[$mail_id];
442  }
443  $db->query("SELECT concat(a.address,'@',d.domaine) AS email FROM address a, domaines d WHERE d.id=a.domain_id AND a.id= ? AND d.compte= ?;", array($mail_id, $cuid));
444  if ($db->next_record()) {
445  return $this->isitmy_cache[$mail_id] = $db->f("email");
446  } else {
447  $msg->raise("ERROR", "mail", _("This email is not yours, you can't change anything on it"));
448  return $this->isitmy_cache[$mail_id] = false;
449  }
450  }
451 
452 
453  /**
454  * Hook called when the DOMAIN class will delete a domain.
455  * OR when the DOMAIN class tells us we don't host the emails of this domain anymore.
456  * @param $dom the ID of the domain to delete
457  * @return boolean if the email has been properly deleted
458  * or false if an error occured ($msg is filled accordingly)
459  */
460  function hook_dom_del_mx_domain($dom_id) {
461  global $db;
462  $list = $this->enum_domain_mails($dom_id, "", 0, -1);
463  if (is_array($list)) {
464  foreach ($list as $one) {
465  $this->delete($one["id"]);
466  }
467  }
468  $db->query("SELECT domaine FROM domaines WHERE id= ? ;", array($dom_id));
469  if (!$db->next_record()) {
470  return false;
471  }
472  $domain=$db->Record["domaine"];
473  $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND (type='defmx' OR type='defmx2');", array($domain));
474 
475  $this->del_dns_dmarc($domain);
476  $this->del_dns_spf($domain);
477  $this->del_dns_autoconf($domain);
478  $this->dkim_del($domain);
479 
480  $db->query("UPDATE domaines SET dns_action='UPDATE' WHERE id= ? ;", array($dom_id));
481  return true;
482  }
483 
484 
485  /**
486  * return the alternc account's ID of the mail_id
487  */
488  function get_account_by_mail_id($mail_id) {
489  global $db;
490  $db->query("select compte as uid from domaines d, address a where a.domain_id = d.id and a.id = ? ;", array($mail_id));
491  if (!$db->next_record()) {
492  return false;
493  }
494  return $db->f('uid');
495  }
496 
497 
498  /**
499  * Function used to delete a mail from the db
500  * should be used by the web interface, not by third-party programs.
501  *
502  * @param $mail_id integer the number of the email to delete
503  * @return boolean if the email has been properly deleted
504  * or false if an error occured ($msg is filled accordingly)
505  */
506  function delete($mail_id) {
507  global $msg, $db, $hooks;
508  $msg->log("mail", "delete");
509 
510  $mail_id = intval($mail_id);
511 
512  if (!$mail_id) {
513  $msg->raise("ERROR", "mail", _("The email you entered is syntaxically incorrect"));
514  return false;
515  }
516  // Validate that this email is owned by me...
517  if (!($mail = $this->is_it_my_mail($mail_id))) {
518  return false;
519  }
520 
521  $mailinfos = $this->get_details($mail_id);
522  $hooks->invoke('hook_mail_delete', array($mail_id, $mailinfos['address'] . '@' . $mailinfos['domain']));
523 
524  // Search for that address:
525  $db->query("SELECT a.id, a.type, a.mail_action, m.mail_action AS mailbox_action, NOT ISNULL(m.id) AS islocal FROM address a LEFT JOIN mailbox m ON m.address_id=a.id WHERE a.id= ? ;", array($mail_id));
526  if (!$db->next_record()) {
527  $msg->raise("ERROR", "mail", _("The email %s does not exist, it can't be deleted"), $mail);
528  return false;
529  }
530  if ($db->f("mail_action") != "OK" || ($db->f("islocal") && $db->f("mailbox_action") != "OK")) { // will be deleted soon ...
531  $msg->raise("ERROR", "mail", _("The email %s is already marked for deletion, it can't be deleted"), $mail);
532  return false;
533  }
534  $mail_id = $db->f("id");
535 
536  if ($db->f("islocal")) {
537  // If it's a pop/imap mailbox, mark it for deletion
538  $db->query("UPDATE address SET mail_action='DELETE', enabled=0 WHERE id= ?;", array($mail_id));
539  $db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id= ?;", array($mail_id));
540  } else {
541  // If it's only aliases, delete it NOW.
542  $db->query("DELETE FROM address WHERE id= ? ;", array($mail_id));
543  $db->query("DELETE FROM mailbox WHERE address_id= ? ;", array($mail_id));
544  $db->query("DELETE FROM recipient WHERE address_id= ? ;", array($mail_id));
545  }
546  return true;
547  }
548 
549 
550  /**
551  * Function used to undelete a pending deletion mail from the db
552  * should be used by the web interface, not by third-party programs.
553  *
554  * @param $mail_id integer the email id
555  * @return boolean if the email has been properly undeleted
556  * or false if an error occured ($msg is filled accordingly)
557  */
558  function undelete($mail_id) {
559  global $msg, $db;
560  $msg->log("mail", "undelete");
561 
562  $mail_id = intval($mail_id);
563 
564  if (!$mail_id) {
565  $msg->raise("ERROR", "mail", _("The email you entered does not exist"));
566  return false;
567  }
568  // Validate that this email is owned by me...
569  if (!($mail = $this->is_it_my_mail($mail_id))) {
570  return false;
571  }
572 
573  // Search for that address:
574  $db->query("SELECT a.id, a.type, a.mail_action, m.mail_action AS mailbox_action, NOT ISNULL(m.id) AS islocal FROM address a LEFT JOIN mailbox m ON m.address_id=a.id WHERE a.id= ? ;", array($mail_id));
575  if (!$db->next_record()) {
576  $msg->raise("ERROR", "mail", _("The email %s does not exist, it can't be undeleted"), $mail);
577  return false;
578  }
579  if ($db->f("type") != "") { // Technically special : mailman, sympa ...
580  $msg->raise("ERROR", "mail", _("The email %s is special, it can't be undeleted"), $mail);
581  return false;
582  }
583  if ($db->f("mailbox_action") != "DELETE" || $db->f("mail_action") != "DELETE") { // will be deleted soon ...
584  $msg->raise("ALERT", "mail", _("Sorry, deletion of email %s is already in progress, or not marked for deletion, it can't be undeleted"), $mail);
585  return false;
586  }
587  $mail_id = $db->f("id");
588 
589  if ($db->f("islocal")) {
590  // If it's a pop/imap mailbox, mark it for deletion
591  $db->query("UPDATE address SET mail_action='OK', `enabled`=1 WHERE id= ?;", array($mail_id));
592  $db->query("UPDATE mailbox SET mail_action='OK' WHERE address_id= ? ;", array($mail_id));
593  return true;
594  } else {
595  $msg->raise("ERROR", "mail", _("-- Program Error -- The email %s can't be undeleted"), $mail);
596  return false;
597  }
598  }
599 
600 
601  /**
602  * set the password of an email address.
603  * @param $mail_id integer email ID
604  * @param $pass string the new password.
605  * @return boolean true if the password has been set, false else, raise an error.
606  */
607  function set_passwd($mail_id, $pass, $canbeempty = false) {
608  global $db, $msg, $admin;
609  $msg->log("mail", "setpasswd");
610 
611  if (!($email = $this->is_it_my_mail($mail_id))) {
612  return false;
613  }
614  if (!$admin->checkPolicy("pop", $email, $pass, $canbeempty)) {
615  return false;
616  }
617  if ($canbeempty && empty($pass)) {
618  return $db->query("UPDATE address SET password= ? where id = ? ;",
619  array(null, $mail_id ));
620  } else if (!$db->query("UPDATE address SET password= ? where id = ? ;",
621  array(_dovecot_hash($pass), $mail_id ))) {
622  return false;
623  }
624  return true;
625  }
626 
627 
628  /**
629  * Enables an email address.
630  * @param $mail_id integer Email ID
631  * @return boolean true if the email has been enabled.
632  */
633  function enable($mail_id) {
634  global $db, $msg;
635  $msg->log("mail", "enable");
636  if (!($email = $this->is_it_my_mail($mail_id))) {
637  return false;
638  }
639  if (!$db->query("UPDATE address SET `enabled`=1 where id= ? ;", array($mail_id))) {
640  return false;
641  }
642  return true;
643  }
644 
645 
646  /**
647  * Disables an email address.
648  * @param $mail_id integer Email ID
649  * @return boolean true if the email has been enabled.
650  */
651  function disable($mail_id) {
652  global $db, $msg;
653  $msg->log("mail", "disable");
654  if (!($email = $this->is_it_my_mail($mail_id))) {
655  return false;
656  }
657  if (!$db->query("UPDATE address SET `enabled`=0 where id= ? ;", array($mail_id))) {
658  return false;
659  }
660  return true;
661  }
662 
663 
664  /**
665  * Function used to update an email settings
666  * should be used by the web interface, not by third-party programs.
667  *
668  * @param $mail_id integer the number of the email to delete
669  * @param integer $islocal boolean is it a POP/IMAP mailbox ?
670  * @param integer $quotamb integer if islocal=1, quota in MB
671  * @param string $recipients string recipients, one mail per line.
672  * @return boolean if the email has been properly edited
673  * or false if an error occured ($msg is filled accordingly)
674  */
675  function set_details($mail_id, $islocal, $quotamb, $recipients, $delivery = "dovecot", $dontcheck = false) {
676  global $msg, $db;
677  $msg->log("mail", "set_details");
678  if (!($me = $this->get_details($mail_id))) {
679  return false;
680  }
681  if ($me["islocal"] && !$islocal) {
682  // delete pop
683  $db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id= ? ;", array($mail_id));
684  }
685  if (!$me["islocal"] && $islocal) {
686  // create pop
687  $path = "";
688  if ($delivery == "dovecot") {
689  $path = ALTERNC_MAIL . "/" . substr($me["address"] . "_", 0, 1) . "/" . $me["address"] . "_" . $me["domain"];
690  }
691  foreach ($this->forbiddenchars as $str) {
692  if (strpos($me["address"], $str) !== false) {
693  $msg->raise("ERROR", "mail", _("There is forbidden characters in your email address. You can't make it a POP/IMAP account, you can only use it as redirection to other emails"));
694  return false;
695  }
696  }
697  foreach ($this->specialchars as $str) {
698  if (strpos($me["address"], $str) !== false) {
699  $path = ALTERNC_MAIL . "/_/" . $me["id"] . "_" . $me["domain"];
700  break;
701  }
702  }
703  $db->query("INSERT INTO mailbox SET address_id= ? , delivery= ?, path= ? ;", array($mail_id, $delivery, $path));
704  }
705  if ($me["islocal"] && $islocal && $me["mailbox_action"] == "DELETE") {
706  $db->query("UPDATE mailbox SET mail_action='OK' WHERE mail_action='DELETE' AND address_id= ? ;", array($mail_id));
707  }
708 
709  if ($islocal) {
710  if ($quotamb != 0 && $quotamb < (intval($me["used"] / 1024 / 1024) + 1)) {
711  $quotamb = intval($me["used"] / 1024 / 1024) + 1;
712  $msg->raise("ALERT", "mail", _("You set a quota smaller than the current mailbox size. Since it's not allowed, we set the quota to the current mailbox size"));
713  }
714  $db->query("UPDATE mailbox SET quota= ? WHERE address_id= ? ;", array($quotamb, $mail_id));
715  }
716 
717  $recipients = preg_replace('/[\r\t\s]/', "\n", $recipients); // Handle space AND new line
718  $r = explode("\n", $recipients);
719  $red = "";
720  foreach ($r as $m) {
721  $m = trim($m);
722  if ($m && ( filter_var($m, FILTER_VALIDATE_EMAIL) || $dontcheck) // Recipient Email is valid
723  && $m != ($me["address"] . "@" . $me["domain"])) { // And not myself (no loop allowed easily ;) )
724  $red.=$m . "\n";
725  }
726  }
727  $db->query("DELETE FROM recipient WHERE address_id= ? ;", array($mail_id));
728  if (isset($red) && $red) {
729  $db->query("INSERT INTO recipient SET address_id= ?, recipients= ? ;", array($mail_id, $red));
730  }
731  if (!$islocal && !$red) {
732  $msg->raise("ALERT", "mail", _("Warning: you created an email which is not an alias, and not a POP/IMAP mailbox. This is certainly NOT what you want to do. To fix this, edit the email address and check 'Yes' in POP/IMAP account, or set some recipients in the redirection field."));
733  }
734  return true;
735  }
736 
737 
738  /**
739  * A wrapper used by mailman class to create it's needed addresses
740  * @ param : $dom_id , the domain id associated to a given address
741  * @ param : $m , the left part of the mail address being created
742  * @ param : $delivery , the delivery used to deliver the mail
743  */
744  function add_wrapper($dom_id, $m, $delivery) {
745  global $msg, $mail;
746  $msg->log("mail", "add_wrapper", "creating $delivery $m address");
747 
748  $mail_id = $mail->create($dom_id, $m, $delivery);
749  $this->set_details($mail_id, 1, 0, '', $delivery);
750  // FIXME return error code
751  }
752 
753 
754  /**
755  * A function used to create an alias for a specific address
756  * @ param : $dom_id , the domain sql identifier
757  * @ param : $m , the alias we want to create
758  * @ param : $alias , the already existing aliased address
759  * @ param : $type, the type of the alias created
760  * @param string $m
761  * @param string $alias
762  * @param string $dom_id
763  */
764  function create_alias($dom_id, $m, $alias, $type = "", $dontcheck = false) {
765  global $msg, $mail;
766  $msg->log("mail", "create_alias", "creating $m alias for $alias type $type");
767 
768  $mail_id = $mail->create($dom_id, $m, $type, $dontcheck);
769  if (!$mail_id) {
770  return false;
771  }
772  $this->set_details($mail_id, 0, 0, $alias, "dovecot", $dontcheck);
773  return true;
774  }
775 
776 
777  /**
778  * A wrapper used by mailman class to create it's needed addresses
779  * @ param : $mail_id , the mysql id of the mail address we want to delete
780  * of the email for the current acccount.
781  */
782  function del_wrapper($mail_id) {
783  global $msg;
784  $msg->log("mail", "del_wrapper");
785  $this->delete($mail_id);
786  }
787 
788 
789  /**
790  * Export the mail information of an account
791  * @return: str, string containing the complete configuration
792  * of the email for the current acccount.
793  */
794  function alternc_export_conf() {
795  global $msg;
796  $msg->log("mail", "export");
797  $domain = $this->enum_domains();
798  $str = "<mail>\n";
799  foreach ($domain as $d) {
800  $str.=" <domain>\n <name>" . xml_entities($d["domain"]) . "</name>\n";
801  $s = $this->enum_domain_mails($d["id"]);
802  if (count($s)) {
803  while (list($key, $val) = each($s)) {
804  $str.=" <address>\n";
805  $str.=" <name>" . xml_entities($val["address"]) . "</name>\n";
806  $str.=" <enabled>" . xml_entities($val["enabled"]) . "</enabled>\n";
807  if (is_array($val["islocal"])) {
808  $str.=" <islocal>1</islocal>\n";
809  $str.=" <quota>" . $val["quota"] . "</quota>\n";
810  $str.=" <path>" . $val["path"] . "</path>\n";
811  } else {
812  $str.=" <islocal>0</islocal>\n";
813  }
814  if (!empty($val["recipients"])) {
815  $r = explode("\n", $val["recipients"]);
816  foreach ($r as $recip) {
817  $str.=" <recipients>" . $recip . "<recipients>\n";
818  }
819  }
820  $str.=" </address>\n";
821  }
822  }
823  $str.=" </domain>\n";
824  }
825  $str.="</mail>\n";
826  return $str;
827  }
828 
829 
830  /**
831  * Return the list of allowed slave accounts (secondary-mx)
832  * @return array
833  */
834  function enum_slave_account() {
835  global $db;
836  $db->query("SELECT login,pass FROM mxaccount;");
837  $res = array();
838  while ($db->next_record()) {
839  $res[] = $db->Record;
840  }
841  if (!count($res)) {
842  return false;
843  }
844  return $res;
845  }
846 
847 
848  /**
849  * Check for a slave account (secondary mx)
850  * @param string $login the login to check
851  * @param string $pass the password to check
852  * @return boolean TRUE if the password is correct, or FALSE if an error occurred.
853  */
854  function check_slave_account($login, $pass) {
855  global $db;
856  $db->query("SELECT * FROM mxaccount WHERE login= ? AND pass= ?;", array($login, $pass));
857  if ($db->next_record()) {
858  return true;
859  }
860  return false;
861  }
862 
863 
864  /**
865  * Out (echo) the complete hosted domain list :
866  */
867  function echo_domain_list($format = null) {
868  global $db;
869  $db->query("SELECT domaine FROM domaines WHERE gesmx=1 ORDER BY domaine");
870  $lst = array();
871  $tt = "";
872  while ($db->next_record()) {
873  $lst[] = $db->f("domaine");
874  $tt.=$db->f("domaine");
875  }
876 
877  // Generate an integrity check
878  $obj = array('integrity' => md5($tt), 'items' => $lst);
879 
880  switch ($format) {
881  case "json":
882  return json_encode($obj);
883  default:
884  foreach ($lst as $l) {
885  echo $l . "\n";
886  }
887  return true;
888  } // switch
889  }
890 
891 
892  /**
893  * Add a slave account that will be allowed to access the mxdomain list
894  * @param string $login the login to add
895  * @param string $pass the password to add
896  * @return boolean TRUE if the account has been created, or FALSE if an error occurred.
897  */
898  function add_slave_account($login, $pass) {
899  global $db, $msg;
900  $db->query("SELECT * FROM mxaccount WHERE login= ? ;", array($login));
901  if ($db->next_record()) {
902  $msg->raise("ERROR", "mail", _("The slave MX account was not found"));
903  return false;
904  }
905  $db->query("INSERT INTO mxaccount (login,pass) VALUES (?, ?);", array($login, $pass));
906  return true;
907  }
908 
909 
910  /**
911  * Remove a slave account
912  * @param string $login the login to delete
913  */
914  function del_slave_account($login) {
915  global $db;
916  $db->query("DELETE FROM mxaccount WHERE login= ? ;", array($login));
917  return true;
918  }
919 
920 
921  // ------------------------------------------------------------
922  /**
923  * hook function called by AlternC when a domain is created for
924  * the current user account using the SLAVE DOMAIN feature
925  * This function create a CATCHALL to the master domain
926  * @param string $domain_id Domain that has just been created
927  * @param string $target_domain Master domain
928  * @access private
929  */
930  function hook_dom_add_slave_domain($domain_id, $target_domain) {
931  global $msg;
932  $msg->log("mail", "hook_dom_add_slave_domain", $domain_id);
933  $this->catchall_set($domain_id, '@' . $target_domain);
934  return true;
935  }
936 
937 
938  // ------------------------------------------------------------
939  /**
940  * hook function called by AlternC when a domain is created for
941  * the current user account
942  * This function create a postmaster mail which is an alias to LOGIN @ FQDN
943  * wich is a dynamic alias to the alternc's account mail
944  * @param string $domain_id Domain that has just been created
945  * @access private
946  */
947  function hook_dom_add_mx_domain($domain_id) {
948  global $msg, $mem, $db, $L_FQDN;
949  $msg->log("mail", "hook_dom_add_mx_domain", $domain_id);
950 
951  $db->query("SELECT value FROM variable where name='mailname_bounce';");
952  if (!$db->next_record()) {
953  $msg->raise("ERROR", "mail", _("Problem: can't create default bounce mail"));
954  return false;
955  }
956  $mailname = $db->f("value");
957  // set spf & dmarc for this domain
958  $db->query("SELECT domaine,compte FROM domaines WHERE id= ?;", array($domain_id));
959  if ($db->next_record()) {
960  $domaine=$db->Record["domaine"];
961  $compte=$db->Record["compte"];
962  $this->set_dns_autoconf($domaine,$compte);
963  if ($spf = variable_get("default_spf_value")) {
964  $this->set_dns_spf($domaine, $spf);
965  }
966  if ($dmarc = variable_get("default_dmarc_value")) {
967  $this->set_dns_dmarc($domaine, $dmarc);
968  }
969  }
970  return $this->create_alias($domain_id, 'postmaster', $mem->user['login'] . '@' . $mailname);
971  }
972 
973 
974  // ------------------------------------------------------------
975  /**
976  * hook function called by variables when a variable is changed
977  * @access private
978  */
979  function hook_variable_set($name, $old, $new) {
980  global $msg, $db;
981  $msg->log("mail", "hook_variable_set($name,$old,$new)");
982 
983  if ($name == "default_spf_value") {
984  $new = trim($new);
985  $old = trim($old);
986  $db->query("SELECT domaine,login,compte FROM domaines, membres WHERE gesdns=1 AND gesmx=1 and membres.uid=domaines.compte;");
987  $res=array();
988  while ($db->next_record()) $res[]=$db->Record;
989  foreach ($res as $record) {
990  $this->set_dns_spf($record["domaine"], $new, $old, $record["compte"], $record["login"]);
991  }
992  }
993 
994  if ($name == "default_dmarc_value") {
995  $new = trim($new);
996  $old = trim($old);
997  $db->query("SELECT domaine,login,compte FROM domaines, membres WHERE gesdns=1 AND gesmx=1 and membres.uid=domaines.compte;");
998  $res=array();
999  while ($db->next_record()) $res[]=$db->Record;
1000  foreach ($res as $record) {
1001  $this->set_dns_dmarc($record["domaine"], $new, $old, $record["compte"], $record["login"]);
1002  }
1003  }
1004  }
1005 
1006 
1007  // ------------------------------------------------------------
1008  /**
1009  * Add dns entries for autodiscover / autoconf on the domain
1010  */
1011  function set_dns_autoconf($domain,$uid=-1) {
1012  global $db, $L_FQDN, $cuid;
1013  $changed=false;
1014  if ($uid==-1) $uid=$cuid;
1015 
1016  $db->query("SELECT domaine,sub,type,valeur FROM sub_domaines WHERE domaine=? AND sub='autodiscover' AND type='autodiscover';",array($domain));
1017  if (!$db->next_record()) {
1018  $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='autodiscover', type='autodiscover', valeur='';",array($domain,$uid));
1019  $changed=true;
1020  }
1021  $db->query("SELECT domaine,sub,type,valeur FROM sub_domaines WHERE domaine=? AND sub='autoconfig' AND type='autodiscover';",array($domain));
1022  if (!$db->next_record()) {
1023  $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='autoconfig', type='autodiscover', valeur='';",array($domain,$uid));
1024  $changed=true;
1025  }
1026  if ($changed) {
1027  $db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine= ?;", array($domain));
1028  }
1029  return $changed;
1030  }
1031 
1032 
1033  // ------------------------------------------------------------
1034  /**
1035  * delete the autoconf / autodiscover vhosts when removing a domain as MX
1036  */
1037  function del_dns_autoconf($domain) {
1038  global $db, $L_FQDN, $cuid;
1039  $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='autodiscover' AND sub='autoconfig';", array($domain));
1040  $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='autodiscover' AND sub='autodiscover';", array($domain));
1041  }
1042 
1043  // ------------------------------------------------------------
1044  /**
1045  * Set or UPDATE the DNS record for the domain $dom(str) to be $spf
1046  * account's login is current and if not it's $login.
1047  * don't change spf if current value is not $old
1048  * @access private
1049  */
1050  function set_dns_spf($domain, $spf, $previous = -1, $uid = -1, $login = -1) {
1051  global $db, $cuid, $mem, $msg;
1052  $msg->debug("mail","set_dns_spf($domain, $spf, $previous, $uid, $login)");
1053  // defaults
1054  if ($uid === -1) {
1055  $uid = intval($cuid);
1056  } else {
1057  $uid = intval($uid);
1058  }
1059  if ($login === -1) {
1060  $login = $mem->user["login"];
1061  }
1062  // Search for the record in sub_domaines table
1063  $db->query("SELECT * FROM sub_domaines WHERE compte= ? AND domaine= ? AND sub='' AND type='txt' AND valeur LIKE 'v=spf1 %' AND web_action!='DELETE';", array($uid, $domain));
1064  if ($db->next_record()) {
1065  if ($previous !== -1 && $db->Record["valeur"] == "v=spf1 " . $spf) {
1066  return; // skip, no change asked.
1067  }
1068  $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE id= ? ;",array($db->Record["id"]));
1069  }
1070  $db->query("INSERT INTO sub_domaines SET compte= ?, domaine= ?, sub='', type='txt', valeur= ? , web_action='UPDATE';", array($uid, $domain, "v=spf1 " . $spf));
1071  $db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine= ?;", array($domain));
1072  }
1073 
1074  // ------------------------------------------------------------
1075  /**
1076  * delete the SPF entries in the sub_domaine table for a domain
1077  * called by del_domain or del_mx_domain by hooks :
1078  */
1079  function del_dns_spf($domain) {
1080  global $db;
1081  $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='txt' AND sub='' AND valeur LIKE 'v=spf1 %';", array($domain));
1082  }
1083 
1084 
1085  // ------------------------------------------------------------
1086  /**
1087  * Set or UPDATE the DNS record for the domain $dom(str) to be $dmarc
1088  * account's login is current and if not it's $login.
1089  * don't change dmarc if current value is not $old
1090  * @access private
1091  */
1092  function set_dns_dmarc($domain, $dmarc, $previous = -1, $uid = -1, $login = -1) {
1093  global $db, $cuid, $mem, $L_FQDN, $msg;
1094  $msg->debug("mail","set_dns_dmarc($domain, $dmarc, $previous, $uid, $login)");
1095  // defaults
1096  if ($uid === -1) {
1097  $uid = intval($cuid);
1098  } else {
1099  $uid = intval($uid);
1100  }
1101  if ($login === -1) {
1102  $login = $mem->user["login"];
1103  }
1104  $dmarc = str_replace("%%ADMINMAIL%%", "admin@" . $L_FQDN, $dmarc);
1105  $dmarc = str_replace("%%USERMAIL%%", $login . "@" . $L_FQDN, $dmarc);
1106 
1107  // Search for the record in sub_domaines table
1108  $db->query("SELECT * FROM sub_domaines WHERE compte= ? AND domaine= ? AND sub='_dmarc' AND type='txt' AND valeur LIKE lower('v=DMARC1;%') AND web_action!='DELETE';", array($uid, $domain));
1109  if ($db->next_record()) {
1110  if ($previous !== -1 && $db->Record["valeur"] == "v=DMARC1;" . $dmarc) {
1111  return; // skip, no change asked.
1112  }
1113  $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE id= ?;", array($db->Record["id"]));
1114  }
1115  $db->query("INSERT INTO sub_domaines SET compte= ?, domaine= ?, sub='_dmarc', type='txt', valeur= ?, web_action='UPDATE';", array($uid, $domain, "v=DMARC1;" . $dmarc));
1116  $db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine= ?;", array($domain));
1117  }
1118 
1119 
1120  // ------------------------------------------------------------
1121  /**
1122  * delete the DMARC entries in the sub_domaine table for a domain
1123  * called by del_domain or del_mx_domain by hooks :
1124  */
1125  function del_dns_dmarc($domain) {
1126  global $db;
1127  $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='txt' AND sub='_dmarc' AND valeur LIKE lower('v=DMARC1;%');", array($domain));
1128  }
1129 
1130 
1131  /** Manage DKIM when adding / removing a domain MX management */
1133 
1134 
1135  // ------------------------------------------------------------
1136  /**
1137  * Hook launched before doing anything dns-related
1138  */
1140  global $db;
1141  // for each domain where we don't have the MX or the DNS, remove the DKIM setup
1142  $this->shouldreloaddkim=false;
1143  $db->query("SELECT domaine,compte,gesdns,gesmx,dns_action FROM domaines WHERE dns_action!='OK';");
1144  $add=array();
1145  $del=array();
1146  while ($db->next_record()) {
1147  if ($db->Record["gesdns"]==0 || $db->Record["gesmx"]==0 || $db->Record['dns_action'] == 'DELETE') {
1148  $del[]=$db->Record;
1149  } else {
1150  $add[]=$db->Record;
1151  }
1152  }
1153  foreach($add as $domain) {
1154  $this->dkim_add($domain["domaine"],$domain["compte"]);
1155  }
1156  foreach($del as $domain) {
1157  $this->dkim_del($domain["domaine"]);
1158  }
1159  }
1160 
1161 
1162  // ------------------------------------------------------------
1163  /**
1164  * Hook launched after doing anything dns-related
1165  */
1167  if ($this->shouldreloaddkim) {
1168  exec("service opendkim reload");
1169  $this->shouldreloaddkim=false;
1170  }
1171  }
1172 
1173 
1174  // ------------------------------------------------------------
1175  /**
1176  * Add a domain into OpenDKIM configuration
1177  */
1178  function dkim_add($domain,$uid) {
1179  global $db;
1180  $target_dir = "/etc/opendkim/keys/$domain";
1181  // Create a dkim key when it's not already there :
1182  if (!file_exists($target_dir.'/alternc.txt')) {
1183  $this->shouldreloaddkim=true;
1184  if (! is_dir($target_dir)) mkdir($target_dir); // create dir
1185  $old_dir=getcwd();
1186  chdir($target_dir);
1187  // Generate the key, 2048 bits (better than 1024)
1188  // 2048 bits is also the default in recent Debian builds of opendkim
1189  // @see man opendkim-genkey
1190  exec('opendkim-genkey -b 2048 -r -d '.escapeshellarg($domain).' -s "alternc" ');
1191  chdir($old_dir);
1192  // opendkim must be owner of the key
1193  chown("$target_dir/alternc.private", 'opendkim');
1194  chgrp("$target_dir/alternc.private", 'opendkim');
1195 
1196  add_line_to_file("/etc/opendkim/KeyTable","alternc._domainkey.".$domain." ".$domain.":alternc:/etc/opendkim/keys/".$domain."/alternc.private");
1197  add_line_to_file("/etc/opendkim/SigningTable",$domain." alternc._domainkey.".$domain);
1198  }
1199 
1200  // Search for the subdomain entry, if it's not already there, create it:
1201  $db->query("SELECT id FROM sub_domaines WHERE domaine=? AND sub='alternc._domainkey';",array($domain));
1202  if (!$db->next_record()) {
1203  // Add subdomaine entry
1204  $dkim_key=$this->dkim_get_entry($domain);
1205  $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='alternc._domainkey', type='dkim', valeur=?;",array($domain,$uid,$dkim_key));
1206  // no need to do DNS_ACTION="UPDATE" => we are in the middle of a HOOK, so dns WILL BE reloaded for this domain
1207  }
1208  }
1209 
1210 
1211  // ------------------------------------------------------------
1212  /**
1213  * Delete a domain from OpenDKIM configuration
1214  */
1215  function dkim_del($domain) {
1216  global $db;
1217  $target_dir = "/etc/opendkim/keys/$domain";
1218  if (file_exists($target_dir)) {
1219  $this->shouldreloaddkim=true;
1220  @unlink("$target_dir/alternc.private");
1221  @unlink("$target_dir/alternc.txt");
1222  @rmdir($target_dir);
1223  del_line_from_file("/etc/opendkim/KeyTable","alternc._domainkey.".$domain." ".$domain.":alternc:/etc/opendkim/keys/".$domain."/alternc.private");
1224  del_line_from_file("/etc/opendkim/SigningTable",$domain." alternc._domainkey.".$domain);
1225  }
1226  $db->query("DELETE FROM sub_domaines WHERE domaine=? AND sub='alternc._domainkey';",array($domain));
1227  // No need to do DNS_ACTION="UPDATE" => we are in the middle of a HOOK
1228  }
1229 
1230 
1231  // ------------------------------------------------------------
1232  /**
1233  * return the content of the TXT information to be added into the DB for DKIM subdomains
1234  * @param $domain string the name of the domain name
1235  * @return string the TXT entry (without quotes)
1236  * or false if an error occurred
1237  **/
1238  function dkim_get_entry($domain) {
1239  global $msg;
1240  $key=file_get_contents("/etc/opendkim/keys/".$domain."/alternc.txt");
1241  // easy: monoline key
1242  if (preg_match('#alternc._domainkey IN TXT "(.*)"#',$key,$mat)) {
1243  return $mat[1];
1244  } else {
1245  // Need to parse a multiligne key:
1246  $inkey=false; $result="";
1247  $lines=explode("\n",$key);
1248  foreach($lines as $line) {
1249  if (preg_match('#alternc._domainkey\s+IN\s+TXT\s+\‍( "(.*)"#',$line,$mat)) {
1250  $result.=$mat[1]; $inkey=true; continue;
1251  }
1252  if ($inkey && preg_match('#^\s*"(.*)"\s*\‍)#',$line,$mat)) {
1253  $result.=$mat[1]; $inkey=false; break;
1254  }
1255  if ($inkey && preg_match('#^\s*"(.*)"\s*$#',$line,$mat)) {
1256  $result.=$mat[1]; $inkey=true; continue;
1257  }
1258  }
1259  if ($result)
1260  return $result;
1261  }
1262  $msg->debug("mail","dkim_get_entry($domain) failed");
1263  return false;
1264  }
1265 
1266  // @TODO hook after reloading DNS zones => if necessary, restart opendkim
1267 
1268 } /* Class m_mail */
This class handle emails (pop and/or aliases and even wrapper for internal classes) of hosted users.
Definition: m_mail.php:32
catchall_getinfos($domain_id)
Definition: m_mail.php:121
del_dns_dmarc($domain)
delete the DMARC entries in the sub_domaine table for a domain called by del_domain or del_mx_domain ...
Definition: m_mail.php:1125
set_passwd($mail_id, $pass, $canbeempty=false)
set the password of an email address.
Definition: m_mail.php:607
echo_domain_list($format=null)
Out (echo) the complete hosted domain list :
Definition: m_mail.php:867
get_details($mail_id)
function used to get every information we can on a mail
Definition: m_mail.php:402
create_alias($dom_id, $m, $alias, $type="", $dontcheck=false)
A function used to create an alias for a specific address @ param : $dom_id , the domain sql identifi...
Definition: m_mail.php:764
dkim_del($domain)
Delete a domain from OpenDKIM configuration.
Definition: m_mail.php:1215
$enum_domains
Definition: m_mail.php:67
hook_quota_get()
get_quota (hook for quota class), returns the number of used service for a quota-bound service
Definition: m_mail.php:192
get_account_by_mail_id($mail_id)
return the alternc account's ID of the mail_id
Definition: m_mail.php:488
catchall_set($domain_id, $target)
Definition: m_mail.php:165
alternc_export_conf()
Export the mail information of an account.
Definition: m_mail.php:794
hook_dom_add_mx_domain($domain_id)
hook function called by AlternC when a domain is created for the current user account This function c...
Definition: m_mail.php:947
set_dns_spf($domain, $spf, $previous=-1, $uid=-1, $login=-1)
Set or UPDATE the DNS record for the domain $dom(str) to be $spf account's login is current and if no...
Definition: m_mail.php:1050
disable($mail_id)
Disables an email address.
Definition: m_mail.php:651
set_dns_dmarc($domain, $dmarc, $previous=-1, $uid=-1, $login=-1)
Set or UPDATE the DNS record for the domain $dom(str) to be $dmarc account's login is current and if ...
Definition: m_mail.php:1092
hook_dom_add_slave_domain($domain_id, $target_domain)
hook function called by AlternC when a domain is created for the current user account using the SLAVE...
Definition: m_mail.php:930
catchall_del($domain_id)
Definition: m_mail.php:152
dkim_add($domain, $uid)
Add a domain into OpenDKIM configuration.
Definition: m_mail.php:1178
del_dns_spf($domain)
delete the SPF entries in the sub_domaine table for a domain called by del_domain or del_mx_domain by...
Definition: m_mail.php:1079
$forbiddenchars
If an email has those chars, we will ONLY allow RECIPIENTS, NOT POP/IMAP for DOVECOT !...
Definition: m_mail.php:54
alternc_password_policy()
Password policy kind used in this class (hook for admin class)
Definition: m_mail.php:209
$cache_domain_mail_size
Definition: m_mail.php:66
$total
Number of results for a pager display @access public.
Definition: m_mail.php:61
del_slave_account($login)
Remove a slave account.
Definition: m_mail.php:914
hook_mail_get_details($detail)
Definition: m_mail.php:330
hook_updatedomains_dns_pre()
Hook launched before doing anything dns-related.
Definition: m_mail.php:1139
enum_slave_account()
Return the list of allowed slave accounts (secondary-mx)
Definition: m_mail.php:834
hook_updatedomains_dns_post()
Hook launched after doing anything dns-related.
Definition: m_mail.php:1166
undelete($mail_id)
Function used to undelete a pending deletion mail from the db should be used by the web interface,...
Definition: m_mail.php:558
$specialchars
If an email has those chars, 'not nice in shell env' ;) we don't store the email in $mail/u/{user}_do...
Definition: m_mail.php:46
hook_dom_del_mx_domain($dom_id)
Hook called when the DOMAIN class will delete a domain.
Definition: m_mail.php:460
add_slave_account($login, $pass)
Add a slave account that will be allowed to access the mxdomain list.
Definition: m_mail.php:898
check_slave_account($login, $pass)
Check for a slave account (secondary mx)
Definition: m_mail.php:854
enable($mail_id)
Enables an email address.
Definition: m_mail.php:633
$srv_postfix
Definition: m_mail.php:64
create($dom_id, $mail, $type="", $dontcheck=false)
Function used to insert a new mail into the db should be used by the web interface,...
Definition: m_mail.php:350
enum_domain_mails($dom_id=null, $search="", $offset=0, $count=30, $show_systemmails=false)
function used to list every mail address hosted on a domain.
Definition: m_mail.php:285
set_details($mail_id, $islocal, $quotamb, $recipients, $delivery="dovecot", $dontcheck=false)
Function used to update an email settings should be used by the web interface, not by third-party pro...
Definition: m_mail.php:675
$srv_dovecot
Definition: m_mail.php:65
$shouldreloaddkim
Manage DKIM when adding / removing a domain MX management.
Definition: m_mail.php:1132
dkim_get_entry($domain)
return the content of the TXT information to be added into the DB for DKIM subdomains
Definition: m_mail.php:1238
del_dns_autoconf($domain)
delete the autoconf / autodiscover vhosts when removing a domain as MX
Definition: m_mail.php:1037
hook_variable_set($name, $old, $new)
hook function called by variables when a variable is changed @access private
Definition: m_mail.php:979
get_total_size_for_domain($domain)
Definition: m_mail.php:102
$isitmy_cache
Definition: m_mail.php:426
$domains
domain list for this account @access private
Definition: m_mail.php:38
add_wrapper($dom_id, $m, $delivery)
A wrapper used by mailman class to create it's needed addresses @ param : $dom_id ,...
Definition: m_mail.php:744
m_mail()
Constructeur.
Definition: m_mail.php:73
set_dns_autoconf($domain, $uid=-1)
Add dns entries for autodiscover / autoconf on the domain.
Definition: m_mail.php:1011
is_it_my_mail($mail_id)
Check if an email is mine ...
Definition: m_mail.php:436
enum_domains($uid=-1)
Returns the list of mail-hosting domains for a user.
Definition: m_mail.php:218
available($mail)
available: tells if an email address can be installed in the server check the domain part (is it mine...
Definition: m_mail.php:254
del_wrapper($mail_id)
A wrapper used by mailman class to create it's needed addresses @ param : $mail_id ,...
Definition: m_mail.php:782
hook_menu()
Hook called by menu class to add the email menu to the left pane.
Definition: m_mail.php:83
$res
Definition: index.php:111