Alternc  3.2
Alternc logiel libre pour l'hébergement
 All Data Structures Namespaces Files Functions Variables Pages
m_mail.php
Go to the documentation of this file.
1 <?php
2 /*
3  ----------------------------------------------------------------------
4  AlternC - Web Hosting System
5  Copyright (C) 2000-2012 by the AlternC Development Team.
6  https://alternc.org/
7  ----------------------------------------------------------------------
8  LICENSE
9 
10  This program is free software; you can redistribute it and/or
11  modify it under the terms of the GNU General Public License (GPL)
12  as published by the Free Software Foundation; either version 2
13  of the License, or (at your option) any later version.
14 
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU General Public License for more details.
19 
20  To read the license please visit http://www.gnu.org/copyleft/gpl.html
21  ----------------------------------------------------------------------
22  Purpose of file: Manage Email accounts and aliases.
23  ----------------------------------------------------------------------
24 */
25 
26 /**
27 * This class handle emails (pop and/or aliases and even wrapper for internal
28 * classes) of hosted users.
29 *
30 * @copyright AlternC-Team 2012-09-01 http://alternc.com/
31 * This class is directly using the following alternc MySQL tables:
32 * address = any used email address will be defined here, mailbox = pop/imap mailboxes, recipient = redirection from an email to another
33 * and indirectly the domain class, to know domain names from their id in the DB.
34 * This class is also defining a few hooks, search ->invoke in the code.
35 */
36 class m_mail {
37 
38 
39  /* ----------------------------------------------------------------- */
40  /** domain list for this account
41  * @access private
42  */
43  var $domains;
44 
45 
46  /* ----------------------------------------------------------------- */
47  /** If an email has those chars, 'not nice in shell env' ;)
48  * we don't store the email in $mail/u/{user}_domain, but in $mail/_/{address_id}_domain
49  * @access private
50  */
51  var $specialchars=array('"',"'",'\\','/');
52 
53 
54  /* ----------------------------------------------------------------- */
55  /** If an email has those chars, we will ONLY allow RECIPIENTS, NOT POP/IMAP for DOVECOT !
56  * Since Dovecot doesn't allow those characters
57  * @access private
58  */
59  var $forbiddenchars=array('"',"'",'\\','/','?','!','*','$','|','#','+');
60 
61 
62  /* ----------------------------------------------------------------- */
63  /** Number of results for a pager display
64  * @access public
65  */
66  var $total;
67 
68 
69  // Human server name for help
71  var $srv_smtp;
73  var $srv_imap;
75  var $srv_pop3;
77 
78  var $cache_domain_mail_size = array();
79  /* ----------------------------------------------------------------- */
80  /**
81  * Constructeur
82  */
83  function m_mail() {
84  $this->srv_submission = variable_get('mail_human_submission', '%%FQDN%%','Human name for mail server (submission protocol)', array(array('desc'=>'Name','type'=>'string')));
85  $this->srv_smtp = variable_get('mail_human_smtp', '%%FQDN%%','Human name for mail server (SMTP protocol)', array(array('desc'=>'Name','type'=>'string')));
86  $this->srv_smtps = variable_get('mail_human_smtps', '%%FQDN%%','Human name for mail server (SMTPS protocol)', array(array('desc'=>'Name','type'=>'string')));
87  $this->srv_imap = variable_get('mail_human_imap', '%%FQDN%%','Human name for IMAP mail server', array(array('desc'=>'Name','type'=>'string')));
88  $this->srv_imaps = variable_get('mail_human_imaps', '%%FQDN%%','Human name for IMAPS mail server', array(array('desc'=>'Name','type'=>'string')));
89  $this->srv_pop3 = variable_get('mail_human_pop3', '%%FQDN%%','Human name for POP3 mail server', array(array('desc'=>'Name','type'=>'string')));
90  $this->srv_pop3s = variable_get('mail_human_pop3s', '%%FQDN%%','Human name for POP3s mail server', array(array('desc'=>'Name','type'=>'string')));
91  }
92 
93  function hook_menu() {
94  $obj = array(
95  'title' => _("Email Addresses"),
96  'ico' => 'images/mail.png',
97  'link' => 'toggle',
98  'pos' => 30,
99  'links' => array(),
100  ) ;
101 
102  foreach ($this->enum_domains() as $d) {
103  $obj['links'][] =
104  array (
105  'txt' => htmlentities($d["domaine"]).'&nbsp;'.htmlentities("(".$d["nb_mail"].")"),
106  'url' => "mail_list.php?domain_id=".urlencode($d['id']),
107  );
108  }
109 
110  return $obj;
111  }
112 
114  global $db;
115  if (empty($this->cache_domain_mail_size)) {
116  $db->query("SELECT SUBSTRING_INDEX(user,'@', -1) as domain, SUM(quota_dovecot) AS sum FROM dovecot_view group by domain ;");
117  while ($db->next_record() ) {
118  $dd = $db->f('domain');
119  $this->cache_domain_mail_size[ $dd ] = $db->f('sum');
120  }
121  }
122  if ( isset( $this->cache_domain_mail_size[$domain]) ) return $this->cache_domain_mail_size[$domain];
123  return 0;
124  }
125 
126  // FIXME documenter
127  function catchall_getinfos($domain_id) {
128  global $dom, $db;
129  $rr=array(
130  'mail_id'=>'',
131  'domain' =>$dom->get_domain_byid($domain_id),
132  'target' => '',
133  'type' => '',
134  );
135 
136  $db->query("select r.recipients as dst, a.id mail_id from address a, recipient r where a.domain_id = $domain_id and r.address_id = a.id and a.address='';");
137  if ($db->next_record()) {
138  $rr['target'] = $db->f('dst');
139  $rr['mail_id'] = $db->f('mail_id');
140  }
141 
142  // Does it redirect to a specific mail or to a domain
143  if (empty($rr['target'])) {
144  $rr['type']='none';
145  } elseif (substr($rr['target'],0,1)=='@') {
146  $rr['type']='domain';
147  } else {
148  $rr['type']='mail';
149  }
150 
151  return $rr;
152  }
153 
154  function catchall_del($domain_id) {
155  $catch = $this->catchall_getinfos($domain_id);
156  if (empty($catch['mail_id'])) return false;
157  return $this->delete($catch['mail_id']);
158  }
159 
160  function catchall_set($domain_id, $target) {
161  // target :
162  $target=rtrim($target);
163  if ( substr_count($target,'@') == 0 ) { // Pas de @
164  $target = '@'.$target;
165  }
166 
167  if ( substr($target,0,1) == '@' ) { // le premier caractere est un @
168  // FIXME validate domain
169  } else { // ca doit ĂȘtre un mail
170  if (!filter_var($target,FILTER_VALIDATE_EMAIL)) {
171  $err->raise("mail",_("The email you entered is syntaxically incorrect"));
172  return false;
173  }
174  }
175  $this->catchall_del($domain_id);
176  return $this->create_alias($domain_id, '', $target, "catchall", true);
177  }
178 
179 
180  /* ----------------------------------------------------------------- */
181  /** get_quota (hook for quota class), returns the number of used
182  * service for a quota-bound service
183  * @param $name string the named quota we want
184  * @return the number of used service for the specified quota,
185  * or false if I'm not the one for the named quota
186  */
187  function hook_quota_get() {
188  global $db,$err,$cuid;
189  $err->log("mail","getquota");
190  $q=Array("name"=>"mail", "description"=>_("Email addresses"), "used"=>0);
191  $db->query("SELECT COUNT(*) AS cnt FROM address a, domaines d WHERE a.domain_id=d.id AND d.compte=$cuid AND a.type='';");
192  if ($db->next_record()) {
193  $q['used']=$db->f("cnt");
194  }
195  return $q;
196  }
197 
198 
199  /* ----------------------------------------------------------------- */
200  /** Password policy kind used in this class (hook for admin class)
201  * @return array an array of policykey => "policy name (for humans)"
202  */
204  return array("pop"=>_("Email account password"));
205  }
206 
207 
208  /* ----------------------------------------------------------------- */
209  /** Returns the list of mail-hosting domains for a user
210  * @return array indexed array of hosted domains
211  */
212  function enum_domains($uid=-1) {
213  global $db,$err,$cuid;
214  $err->log("mail","enum_domains");
215  if ($uid == -1) { $uid = $cuid; }
216  $db->query("
217 SELECT
218  d.id,
219  d.domaine,
220  IFNULL( COUNT(a.id), 0) as nb_mail
221 FROM
222  domaines d LEFT JOIN address a ON (d.id=a.domain_id AND a.type='')
223 WHERE
224  d.compte = $uid
225  and d.gesmx = 1
226 GROUP BY
227  d.id
228 ORDER BY
229  d.domaine
230 ;
231 ");
232  $this->enum_domains=array();
233  while($db->next_record()){
234  $this->enum_domains[]=$db->Record;
235  }
236  return $this->enum_domains;
237  }
238 
239 
240  /* ----------------------------------------------------------------- */
241  /** available: tells if an email address can be installed in the server
242  * check the domain part (is it mine too), the syntax, and the availability.
243  * @param $mail string email to check
244  * @return boolean true if the email can be installed on the server
245  */
246  function available($mail){
247  global $db,$err,$dom;
248  $err->log("mail","available");
249  list($login,$domain)=explode("@",$mail,2);
250  // Validate the domain ownership & syntax
251  if (!($dom_id=$dom->get_domain_byname($domain))) {
252  return false;
253  }
254  // Validate the email syntax:
255  if (!filter_var($mail,FILTER_VALIDATE_EMAIL)) {
256  $err->raise("mail",_("The email you entered is syntaxically incorrect"));
257  return false;
258  }
259  // Check the availability
260  $db->query("SELECT a.id FROM address a WHERE a.domain_id=".$dom_id." AND a.address='".addslashes($login)."';");
261  if ($db->next_record()) {
262  return false;
263  } else {
264  return true;
265  }
266  }
267 
268 
269  /* ----------------------------------------------------------------- */
270  /* function used to list every mail address hosted on a domain.
271  * @param $dom_id integer the domain id.
272  * @param $search string search that string in recipients or address.
273  * @param $offset integer skip THAT much emails in the result.
274  * @param $count integer return no more than THAT much emails. -1 for ALL. Offset is ignored then.
275  * @result an array of each mail hosted under the domain.
276  */
277  function enum_domain_mails($dom_id = null, $search="", $offset=0, $count=30, $show_systemmails=false){
278  global $db,$err,$cuid,$hooks;
279  $err->log("mail","enum_domains_mail");
280 
281  $search=trim($search);
282 
283  $where="a.domain_id=$dom_id";
284  if ($search) $where.=" AND (a.address LIKE '%".addslashes($search)."%' OR r.recipients LIKE '%".addslashes($search)."%')";
285  if (!$show_systemmails) $where.=" AND type='' ";
286  $db->query("SELECT count(a.id) AS total FROM address a LEFT JOIN recipient r ON r.address_id=a.id WHERE $where;");
287  $db->next_record();
288  $this->total=$db->f("total");
289  if ($count!=-1) $limit="LIMIT $offset,$count"; else $limit="";
290  $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, m.bytes AS used, NOT ISNULL(m.id) AS islocal, a.type, r.recipients, m.lastlogin, a.domain_id
291  FROM (address a LEFT JOIN mailbox m ON m.address_id=a.id) LEFT JOIN recipient r ON r.address_id=a.id, domaines d
292  WHERE $where AND d.id=a.domain_id $limit ;");
293  if (! $db->next_record()) {
294  $err->raise("mail",_("No email found for this query"));
295  return false;
296  }
297  $res=array();
298  do {
299  $details=$db->Record;
300  // if necessary, fill the typedata with data from hooks ...
301  if ($details["type"]) {
302  $result=$hooks->invoke("hook_mail_get_details",array($details)); // Will fill typedata if necessary
303  $details["typedata"]=implode("<br />",$result);
304  }
305  $res[]=$details;
306  } while ($db->next_record());
307  return $res;
308  }
309 
310 
311  function hook_mail_get_details($detail) {
312  if ($detail['type']=='catchall') 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']));
313  }
314 
315 
316  /* ----------------------------------------------------------------- */
317  /** Function used to insert a new mail into the db
318  * should be used by the web interface, not by third-party programs.
319  *
320  * This function calls the hook "hooks_mail_cancreate"
321  * which must return FALSE if the user can't create this email, and raise and error accordingly
322  *
323  * @param $dom_id integer A domain_id (owned by the user)
324  * (will be the part at the right of the @ in the email)
325  * @param $mail string the left part of the email to create (something@dom_id)
326  * @return an hashtable containing the database id of the newly created mail,
327  * or false if an error occured ($err is filled accordingly)
328  */
329  function create($dom_id, $mail,$type="",$dontcheck=false){
330  global $err,$db,$cuid,$quota,$dom,$hooks;
331  $err->log("mail","create",$mail);
332 
333  // Validate the domain id
334  if (!($domain=$dom->get_domain_byid($dom_id))) {
335  return false;
336  }
337 
338  // Validate the email syntax:
339  $m=$mail."@".$domain;
340  if (!filter_var($m,FILTER_VALIDATE_EMAIL) && !$dontcheck) {
341  $err->raise("mail",_("The email you entered is syntaxically incorrect"));
342  return false;
343  }
344 
345  // Call other classes to check we can create it:
346  $cancreate=$hooks->invoke("hook_mail_cancreate",array($dom_id,$mail));
347  if (in_array(false,$cancreate,true)) {
348  return false;
349  }
350 
351  // Check the quota:
352  if (!$quota->cancreate("mail")) {
353  $err->raise("mail",_("You cannot create email addresses: your quota is over"));
354  return false;
355  }
356  // Already exists?
357  $db->query("SELECT * FROM address WHERE domain_id=".$dom_id." AND address='".addslashes($mail)."';");
358  if ($db->next_record()) {
359  $err->raise("mail",_("This email address already exists"));
360  return false;
361  }
362  // Create it now
363  $db->query("INSERT INTO address (domain_id, address,type) VALUES ($dom_id, '".addslashes($mail)."','$type');");
364  if (!($id=$db->lastid())) {
365  $err->raise("mail",_("An unexpected error occured when creating the email"));
366  return false;
367  }
368  return $id;
369  }
370 
371 
372  /* ----------------------------------------------------------------- */
373  /** function used to get every information we can on a mail
374  * @param $mail_id integer
375  * @return array a hashtable with all the informations for that email
376  */
377  function get_details($mail_id) {
378  global $db, $err, $cuid, $hooks;
379  $err->log("mail","get_details");
380 
381  $mail_id=intval($mail_id);
382  // Validate that this email is owned by me...
383  if (!($mail=$this->is_it_my_mail($mail_id))) {
384  return false;
385  }
386 
387  // We fetch all the informations for that email: these will fill the hastable :
388  $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, m.bytes AS used, NOT ISNULL(m.id) AS islocal, a.type, r.recipients, m.lastlogin, a.mail_action, m.mail_action AS mailbox_action FROM (address a LEFT JOIN mailbox m ON m.address_id=a.id) LEFT JOIN recipient r ON r.address_id=a.id, domaines d WHERE a.id=".$mail_id." AND d.id=a.domain_id;");
389  if (! $db->next_record()) return false;
390  $details=$db->Record;
391  // if necessary, fill the typedata with data from hooks ...
392  if ($details["type"]) {
393  $result=$hooks->invoke("hook_mail_get_details",array($mail_id)); // Will fill typedata if necessary
394  $details["typedata"]=implode("<br />",$result);
395  }
396  return $details;
397  }
398 
399 
400  private $isitmy_cache=array();
401 
402  /* ----------------------------------------------------------------- */
403  /** Check if an email is mine ...
404  *
405  * @param $mail_id integer the number of the email to check
406  * @return string the complete email address if that's mine, false if not
407  * ($err is filled accordingly)
408  */
410  global $err,$db,$cuid;
411  $mail_id=intval($mail_id);
412  // cache it (may be called more than one time in the same page).
413  if (isset($this->isitmy_cache[$mail_id])) return $this->isitmy_cache[$mail_id];
414 
415  $db->query("SELECT concat(a.address,'@',d.domaine) AS email FROM address a, domaines d WHERE d.id=a.domain_id AND a.id=$mail_id AND d.compte=$cuid;");
416  if ($db->next_record()) {
417  return $this->isitmy_cache[$mail_id]=$db->f("email");
418  } else {
419  $err->raise("mail",_("This email is not yours, you can't change anything on it"));
420  return $this->isitmy_cache[$mail_id]=false;
421  }
422  }
423 
424 
425  /* ----------------------------------------------------------------- */
426  /** Hook called when the DOMAIN class will delete a domain.
427  *
428  * @param $dom integer the number of the email to delete
429  * @return true if the email has been properly deleted
430  * or false if an error occured ($err is filled accordingly)
431  */
432  function hook_dom_del_mx_domain($dom_id) {
433  $list=$this->enum_domain_mails($dom_id,"",0,-1);
434  if (is_array($list)) {
435  foreach($list as $one) {
436  $this->delete($one["id"]);
437  }
438  }
439  return true;
440  }
441 
442  // return the alternc account's ID of the mail_id
444  global $db,$err;
445  $db->query("select compte as uid from domaines d, address a where a.domain_id = d.id and a.id = $mail_id");
446  if ( !$db->next_record()) {
447  return false;
448  }
449  return $db->f('uid');
450  }
451 
452 
453  /* ----------------------------------------------------------------- */
454  /** Function used to delete a mail from the db
455  * should be used by the web interface, not by third-party programs.
456  *
457  * @param $mail_id integer the number of the email to delete
458  * @return true if the email has been properly deleted
459  * or false if an error occured ($err is filled accordingly)
460  */
461  function delete($mail_id){
462  global $err,$db,$cuid,$quota,$dom,$hooks;
463  $err->log("mail","delete");
464 
465  $mail_id=intval($mail_id);
466 
467  if (!$mail_id) {
468  $err->raise("mail",_("The email you entered is syntaxically incorrect"));
469  return false;
470  }
471  // Validate that this email is owned by me...
472  if (!($mail=$this->is_it_my_mail($mail_id))) {
473  return false;
474  }
475 
477  $hooks->invoke('hook_mail_delete', array($mail_id, $mailinfos['address'].'@'.$mailinfos['domain'] ));
478 
479  // Search for that address:
480  $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='$mail_id';");
481  if (!$db->next_record()) {
482  $err->raise("mail",_("The email %s does not exist, it can't be deleted"),$mail);
483  return false;
484  }
485  if ($db->f("mail_action")!="OK" || ($db->f("islocal") && $db->f("mailbox_action")!="OK")) { // will be deleted soon ...
486  $err->raise("mail",_("The email %s is already marked for deletion, it can't be deleted"),$mail);
487  return false;
488  }
489  $mail_id=$db->f("id");
490 
491  if ($db->f("islocal")) {
492  // If it's a pop/imap mailbox, mark it for deletion
493  $db->query("UPDATE address SET mail_action='DELETE', enabled=0 WHERE id='$mail_id';");
494  $db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id='$mail_id';");
495  $err->raise("mail",_("The email %s has been marked for deletion"),$mail);
496  } else {
497  // If it's only aliases, delete it NOW.
498  $db->query("DELETE FROM address WHERE id='$mail_id';");
499  $db->query("DELETE FROM mailbox WHERE address_id='$mail_id';");
500  $db->query("DELETE FROM recipient WHERE address_id='$mail_id';");
501  $err->raise("mail",_("The email %s has been successfully deleted"),$mail);
502  }
503  return true;
504  }
505 
506 
507  /* ----------------------------------------------------------------- */
508  /** Function used to undelete a pending deletion mail from the db
509  * should be used by the web interface, not by third-party programs.
510  *
511  * @param $mail_id integer the email id
512  * @return true if the email has been properly undeleted
513  * or false if an error occured ($err is filled accordingly)
514  */
515  function undelete($mail_id){
516  global $err,$db,$cuid,$quota,$dom,$hooks;
517  $err->log("mail","undelete");
518 
519  $mail_id=intval($mail_id);
520 
521  if (!$mail_id) {
522  $err->raise("mail",_("The email you entered is syntaxically incorrect"));
523  return false;
524  }
525  // Validate that this email is owned by me...
526  if (!($mail=$this->is_it_my_mail($mail_id))) {
527  return false;
528  }
529 
530  // Search for that address:
531  $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='$mail_id';");
532  if (!$db->next_record()) {
533  $err->raise("mail",_("The email %s does not exist, it can't be undeleted"),$mail);
534  return false;
535  }
536  if ($db->f("type")!="") { // Technically special : mailman, sympa ...
537  $err->raise("mail",_("The email %s is special, it can't be undeleted"),$mail);
538  return false;
539  }
540  if ($db->f("mailbox_action")!="DELETE" || $db->f("mail_action")!="DELETE") { // will be deleted soon ...
541  $err->raise("mail",_("Sorry, deletion of email %s is already in progress, or not marked for deletion, it can't be undeleted"),$mail);
542  return false;
543  }
544  $mail_id=$db->f("id");
545 
546  if ($db->f("islocal")) {
547  // If it's a pop/imap mailbox, mark it for deletion
548  $db->query("UPDATE address SET mail_action='OK', `enabled`=1 WHERE id='$mail_id';");
549  $db->query("UPDATE mailbox SET mail_action='OK' WHERE address_id='$mail_id';");
550  $err->raise("mail",_("The email %s has been undeleted"),$mail);
551  return true;
552  } else {
553  $err->raise("mail",_("-- Program Error -- The email %s can't be undeleted"),$mail);
554  return false;
555  }
556  }
557 
558 
559  /* ----------------------------------------------------------------- */
560  /** set the password of an email address.
561  * @param $mail_id integer email ID
562  * @param $pass string the new password.
563  * @return boolean true if the password has been set, false else, raise an error.
564  */
565  function set_passwd($mail_id,$pass){
566  global $db,$err,$admin;
567  $err->log("mail","setpasswd");
568 
569  if (!($email=$this->is_it_my_mail($mail_id))) return false;
570  if (!$admin->checkPolicy("pop",$email,$pass)) return false;
571  if (!$db->query("UPDATE address SET password='"._md5cr($pass)."' where id=$mail_id;")) return false;
572  return true;
573  }
574 
575 
576  /* ----------------------------------------------------------------- */
577  /** Enables an email address.
578  * @param $mail_id integer Email ID
579  * @return boolean true if the email has been enabled.
580  */
581  function enable($mail_id){
582  global $db,$err;
583  $err->log("mail","enable");
584  if (!($email=$this->is_it_my_mail($mail_id))) return false;
585  if (!$db->query("UPDATE address SET `enabled`=1 where id=$mail_id;")) return false;
586  return true;
587  }
588 
589 
590  /* ----------------------------------------------------------------- */
591  /** Disables an email address.
592  * @param $mail_id integer Email ID
593  * @return boolean true if the email has been enabled.
594  */
595  function disable($mail_id){
596  global $db,$err;
597  $err->log("mail","disable");
598  if (!($email=$this->is_it_my_mail($mail_id))) return false;
599  if (!$db->query("UPDATE address SET `enabled`=0 where id=$mail_id;")) return false;
600  return true;
601  }
602 
603 
604  /* ----------------------------------------------------------------- */
605  /** Function used to update an email settings
606  * should be used by the web interface, not by third-party programs.
607  *
608  * @param $mail_id integer the number of the email to delete
609  * @param $islocal boolean is it a POP/IMAP mailbox ?
610  * @param $quotamb integer if islocal=1, quota in MB
611  * @param $recipients string recipients, one mail per line.
612  * @return true if the email has been properly edited
613  * or false if an error occured ($err is filled accordingly)
614  */
615  function set_details($mail_id, $islocal, $quotamb, $recipients,$delivery="dovecot",$dontcheck=false) {
616  global $err,$db,$cuid,$quota,$dom,$hooks;
617  $delivery=mysql_real_escape_string($delivery);
618  $err->log("mail","set_details");
619  if (!($me=$this->get_details($mail_id))) {
620  return false;
621  }
622  if ($me["islocal"] && !$islocal) {
623  // delete pop
624  $db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id=".$mail_id.";");
625  }
626  if (!$me["islocal"] && $islocal) {
627  // create pop
628  $path="";
629  if($delivery=="dovecot"){
630  $path=ALTERNC_MAIL."/".substr($me["address"]."_",0,1)."/".$me["address"]."_".$me["domain"];
631  }
632  foreach($this->forbiddenchars as $str) {
633  if (strpos($me["address"],$str)!==false) {
634  $err->raise("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"));
635  return false;
636  break;
637  }
638  }
639  foreach($this->specialchars as $str) {
640  if (strpos($me["address"],$str)!==false) {
641  $path=ALTERNC_MAIL."/_/".$me["id"]."_".$me["domain"];
642  break;
643  }
644  }
645  $db->query("INSERT INTO mailbox SET address_id=$mail_id, delivery='$delivery', path='".addslashes($path)."';");
646  }
647  if ($me["islocal"] && $islocal && $me["mailbox_action"]=="DELETE") {
648  $db->query("UPDATE mailbox SET mail_action='OK' WHERE mail_action='DELETE' AND address_id=".$mail_id.";");
649  }
650 
651  if ($islocal) {
652  if ($quotamb!=0 && $quotamb<(intval($me["used"]/1024/1024)+1)) {
653  $quotamb=intval($me["used"]/1024/1024)+1;
654  $err->raise("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"));
655  }
656  $db->query("UPDATE mailbox SET quota=".intval($quotamb)." WHERE address_id=".$mail_id.";");
657  }
658 
659  $r=explode("\n",$recipients);
660  $red="";
661  foreach($r as $m) {
662  $m=trim($m);
663  if ($m && ( filter_var($m,FILTER_VALIDATE_EMAIL) || $dontcheck) // Recipient Email is valid
664  && $m!=($me["address"]."@".$me["domain"])) { // And not myself (no loop allowed easily ;) )
665  $red.=$m."\n";
666  }
667  }
668  $db->query("DELETE FROM recipient WHERE address_id=".$mail_id.";");
669  if ($m) {
670  $db->query("INSERT INTO recipient SET address_id=".$mail_id.", recipients='".addslashes($red)."';");
671  }
672  return true;
673  }
674 
675  /* ----------------------------------------------------------------- */
676  /** A wrapper used by mailman class to create it's needed addresses
677  * @ param : $dom_id , the domain id associated to a given address
678  * @ param : $m , the left part of the mail address being created
679  * @ param : $delivery , the delivery used to deliver the mail
680  */
681 
682  function add_wrapper($dom_id,$m,$delivery){
683  global $err,$db,$mail;
684  $err->log("mail","add_wrapper","creating $delivery $m address");
685 
686  $mail_id=$mail->create($dom_id,$m,$delivery);
687  $this->set_details($mail_id,1,0,'',$delivery);
688  // FIXME return error code
689  }
690 
691  /* ----------------------------------------------------------------- */
692  /** A function used to create an alias for a specific address
693  * @ param : $dom_id , the domain sql identifier
694  * @ param : $m , the alias we want to create
695  * @ param : $alias , the already existing aliased address
696  * @ param : $type, the type of the alias created
697  */
698  function create_alias($dom_id,$m,$alias,$type="",$dontcheck=false) {
699  global $err,$db,$mail;
700  $err->log("mail","create_alias","creating $m alias for $alias type $type");
701 
702  $mail_id=$mail->create($dom_id,$m,$type,$dontcheck);
703  $this->set_details($mail_id,0,0,$alias,"dovecot",$dontcheck);
704  // FIXME return error code
705  }
706 
707 
708 
709  /* ----------------------------------------------------------------- */
710  /** A wrapper used by mailman class to create it's needed addresses
711  * @ param : $mail_id , the mysql id of the mail address we want to delete
712  * of the email for the current acccount.
713  */
715  global $err,$db;
716  $err->log("mail","del_wrapper");
717  $this->delete($mail_id);
718  }
719 
720  /* ----------------------------------------------------------------- */
721  /** Export the mail information of an account
722  * @return: str, string containing the complete configuration
723  * of the email for the current acccount.
724  */
725  function alternc_export_conf() {
726  global $db,$err,$mail_localbox;
727  $err->log("mail","export");
728  $domain=$this->enum_domains();
729  $str="<mail>\n";
730  $onepop=false;
731  foreach ($domain as $d) {
732  $str.=" <domain>\n <name>".xml_entities($d["domain"])."</name>\n";
733  $s=$this->enum_domain_mails($d["id"]);
734  if (count($s)) {
735  while (list($key,$val)=each($s)){
736  $test=$this->get_details($val['id']);
737  $str.=" <address>\n";
738  $str.=" <name>".xml_entities($val["address"])."</name>\n";
739  $str.=" <enabled>".xml_entities($val["enabled"])."</enabled>\n";
740  if(is_array($val["islocal"])){
741  $str.=" <islocal>1</islocal>\n";
742  $str.=" <quota>".$val["quota"]."</quota>\n";
743  $str.=" <path>".$val["path"]."</path>\n";
744  }else{
745  $str.=" <islocal>0</islocal>\n";
746  }
747  if(!empty($val["recipients"])){
748  $r=explode("\n",$val["recipients"]);
749  foreach($r as $recip){
750  $str.=" <recipients>".$recip."<recipients>\n";
751  }
752  }
753  $str.=" </address>\n";
754  }
755  }
756  $str.=" </domain>\n";
757  }
758  $str.="</mail>\n";
759  return $str;
760  }
761 
762 
763  /* ----------------------------------------------------------------- */
764  /**
765  * Return the list of allowed slave accounts (secondary-mx)
766  * @return array
767  */
768  function enum_slave_account() {
769  global $db,$err;
770  $db->query("SELECT login,pass FROM mxaccount;");
771  $res=array();
772  while ($db->next_record()) {
773  $res[]=$db->Record;
774  }
775  if (!count($res)) return false;
776  return $res;
777  }
778 
779  /* ----------------------------------------------------------------- */
780  /**
781  * Check for a slave account (secondary mx)
782  * @param string $login the login to check
783  * @param string $pass the password to check
784  * @return boolean TRUE if the password is correct, or FALSE if an error occurred.
785  */
786  function check_slave_account($login,$pass) {
787  global $db,$err;
788  $login=mysql_real_escape_string($login);
789  $pass=mysql_real_escape_string($pass);
790  $db->query("SELECT * FROM mxaccount WHERE login='$login' AND pass='$pass';");
791  if ($db->next_record()) {
792  return true;
793  }
794  return false;
795  }
796 
797 
798  /* ----------------------------------------------------------------- */
799  /** Out (echo) the complete hosted domain list :
800  */
801  function echo_domain_list($format=null) {
802  global $db,$err;
803  $db->query("SELECT domaine FROM domaines WHERE gesmx=1 ORDER BY domaine");
804  $lst=array();
805  $tt="";
806  while ($db->next_record()) {
807  $lst[]=$db->f("domaine");
808  $tt.=$db->f("domaine");
809  }
810 
811  # Generate an integrity check
812  $obj=array('integrity'=>md5($tt),'items'=>$lst);
813 
814  switch($format) {
815  case "json":
816  return json_encode($obj);
817  break;
818  default:
819  foreach ($lst as $l) { echo $l."\n"; }
820  return true;
821  break;
822  } // switch
823  }
824 
825 
826  /* ----------------------------------------------------------------- */
827  /**
828  * Add a slave account that will be allowed to access the mxdomain list
829  * @param string $login the login to add
830  * @param string $pass the password to add
831  * @return boolean TRUE if the account has been created, or FALSE if an error occurred.
832  */
833  function add_slave_account($login,$pass) {
834  global $db,$err;
835  $login=mysql_real_escape_string($login);
836  $pass=mysql_real_escape_string($pass);
837  $db->query("SELECT * FROM mxaccount WHERE login='$login'");
838  if ($db->next_record()) {
839  $err->raise("mail",_("The slave MX account was not found"));
840  return false;
841  }
842  $db->query("INSERT INTO mxaccount (login,pass) VALUES ('$login','$pass')");
843  return true;
844  }
845 
846 
847  /* ----------------------------------------------------------------- */
848  /**
849  * Remove a slave account
850  * @param string $login the login to delete
851  */
852  function del_slave_account($login) {
853  global $db,$err;
854  $login=mysql_real_escape_string($login);
855  $db->query("DELETE FROM mxaccount WHERE login='$login'");
856  return true;
857  }
858 
859  /* ----------------------------------------------------------------- */
860  /** hook function called by AlternC when a domain is created for
861  * the current user account using the SLAVE DOMAIN feature
862  * This function create a CATCHALL to the master domain
863  * @param string $domain_id Domain that has just been created
864  * @param string $target_domain Master domain
865  * @access private
866  */
867  function hook_dom_add_slave_domain($domain_id,$target_domain) {
868  global $err;
869  $err->log("mail","hook_dom_add_slave_domain",$domain_id);
870  $this->catchall_set($domain_id,'@'.$target_domain);
871  return true;
872  }
873 
874  /* ----------------------------------------------------------------- */
875  /** hook function called by AlternC when a domain is created for
876  * the current user account
877  * This function create a postmaster mail which is an alias to LOGIN @ FQDN
878  * wich is a dynamic alias to the alternc's account mail
879  * @param string $domain_id Domain that has just been created
880  * @access private
881  */
882  function hook_dom_add_mx_domain($domain_id) {
883  global $err, $mem, $L_FQDN,$db;
884  $err->log("mail","hook_dom_add_mx_domain",$domain_id);
885 
886  $db->query("SELECT value FROM variable where name='mailname_bounce';");
887  if (!$db->next_record()) {
888  $err->raise("mail",_("The email %s does not exist, it can't be deleted"),$mail);
889  return false;
890  }
891  $mailname=$db->f("value");
892 
893  return $this->create_alias($domain_id, 'postmaster', $mem->user['login'].'@'.$mailname );
894  }
895 
896 
897 
898  /* ----------------------------------------------------------------- */
899  /** hook function called by AlternC-upnp to know which open
900  * tcp or udp ports this class requires or suggests
901  * @return array a key => value list of port protocol name mandatory values
902  * @access private
903  */
904  function hook_upnp_list() {
905  return array(
906  "imap" => array("port" => 143, "protocol" => "tcp", "mandatory" => 1),
907  "imaps" => array("port" => 993, "protocol" => "tcp", "mandatory" => 1),
908  "pop" => array("port" => 110, "protocol" => "tcp", "mandatory" => 1),
909  "pops" => array("port" => 995, "protocol" => "tcp", "mandatory" => 1),
910  "smtp" => array("port" => 25, "protocol" => "tcp", "mandatory" => 1),
911  "sieve" => array("port" => 2000, "protocol" => "tcp", "mandatory" => 1),
912  "submission" => array("port" => 587, "protocol" => "tcp", "mandatory" => 0),
913  );
914  }
915 
916 
917 
918 } /* Class m_mail */
919 
920