<?php
/*
  ----------------------------------------------------------------------
  AlternC - Web Hosting System
  Copyright (C) 2000-2012 by the AlternC Development Team.
  https://alternc.org/
  ----------------------------------------------------------------------
  LICENSE
  
  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License (GPL)
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  To read the license please visit http://www.gnu.org/copyleft/gpl.html
  ----------------------------------------------------------------------
  Purpose of file: Manage Email accounts and aliases.
  ----------------------------------------------------------------------
*/

/**
* This class handle emails (pop and/or aliases and even wrapper for internal
* classes) of hosted users.
*
* @copyright    AlternC-Team 2012-09-01 http://alternc.com/
* This class is directly using the following alternc MySQL tables:
* address = any used email address will be defined here, mailbox = pop/imap mailboxes, recipient = redirection from an email to another
* and indirectly the domain class, to know domain names from their id in the DB.
* This class is also defining a few hooks, search ->invoke in the code.
*/
class m_mail {


  /* ----------------------------------------------------------------- */
  /** domain list for this account
   * @access private
   */
  var $domains;


  /* ----------------------------------------------------------------- */
  /** If an email has those chars, 'not nice in shell env' ;) 
   * we don't store the email in $mail/u/{user}_domain, but in $mail/_/{address_id}_domain
   * @access private
   */
  var $specialchars=array('"',"'",'\\','/');


  /* ----------------------------------------------------------------- */
  /** If an email has those chars, we will ONLY allow RECIPIENTS, NOT POP/IMAP for DOVECOT !
   * Since Dovecot doesn't allow those characters
   * @access private
   */
  var $forbiddenchars=array('"',"'",'\\','/','?','!','*','$','|','#','+');


  /* ----------------------------------------------------------------- */
  /** Number of results for a pager display
   * @access public
   */
  var $total;

  
  // Human server name for help
  var $srv_submission;
  var $srv_smtp;
  var $srv_smtps;
  var $srv_imap;
  var $srv_imaps;
  var $srv_pop3;
  var $srv_pop3s;

  var $cache_domain_mail_size = array();
  var $enum_domains=array();
  /* ----------------------------------------------------------------- */
  /** 
   * Constructeur
   */
  function m_mail() {
    $this->srv_submission = variable_get('mail_human_submission', '%%FQDN%%','Human name for mail server (submission protocol), leave empty to disable help', array('desc'=>'Name','type'=>'string'));
    $this->srv_smtp       = variable_get('mail_human_smtp',       '%%FQDN%%','Human name for mail server (SMTP protocol), leave empty to disable help', array('desc'=>'Name','type'=>'string'));
    $this->srv_smtps      = variable_get('mail_human_smtps',      '%%FQDN%%','Human name for mail server (SMTPS protocol), leave empty to disable help', array('desc'=>'Name','type'=>'string'));
    $this->srv_imap       = variable_get('mail_human_imap',       '%%FQDN%%','Human name for IMAP mail server, leave empty to disable help', array('desc'=>'Name','type'=>'string'));
    $this->srv_imaps      = variable_get('mail_human_imaps',      '%%FQDN%%','Human name for IMAPS mail server, leave empty to disable help', array('desc'=>'Name','type'=>'string'));
    $this->srv_pop3       = variable_get('mail_human_pop3',       '%%FQDN%%','Human name for POP3 mail server, leave empty to disable help', array('desc'=>'Name','type'=>'string'));
    $this->srv_pop3s      = variable_get('mail_human_pop3s',      '%%FQDN%%','Human name for POP3s mail server, leave empty to disable help', array('desc'=>'Name','type'=>'string'));
  }

  function hook_menu() {
    $obj = array(
      'title'       => _("Email Addresses"),
      'ico'         => 'images/mail.png',
      'link'        => 'toggle',
      'pos'         => 30,
      'links'       => array(),
     ) ;

     foreach ($this->enum_domains() as $d) {
       $obj['links'][] =
         array (
           'txt' => htmlentities($d["domaine"]).'&nbsp;'.htmlentities("(".$d["nb_mail"].")"),
           'url' => "mail_list.php?domain_id=".urlencode($d['id']),
         );
     }

     return $obj;
  }

  function get_total_size_for_domain($domain) {
    global $db;
    if (empty($this->cache_domain_mail_size)) {
      $db->query("SELECT SUBSTRING_INDEX(user,'@', -1) as domain, SUM(quota_dovecot) AS sum FROM dovecot_view group by domain ;");
      while ($db->next_record() ) {
        $dd = $db->f('domain');
        $this->cache_domain_mail_size[ $dd ] = $db->f('sum');
      }
    }
    if ( isset( $this->cache_domain_mail_size[$domain]) ) return $this->cache_domain_mail_size[$domain];
    return 0;
  }

  // FIXME documenter

  /**
   * @param string $domain_id
   */
  function catchall_getinfos($domain_id) {
    global $dom, $db;
    $rr=array(
      'mail_id'=>'',
      'domain' =>$dom->get_domain_byid($domain_id),
      'target' => '',
      'type'   => '',
      );
    
    $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='';");
    if ($db->next_record()) {
      $rr['target'] = $db->f('dst');
      $rr['mail_id'] = $db->f('mail_id');
    }

    // Does it redirect to a specific mail or to a domain
    if (empty($rr['target'])) {
      $rr['type']='none';
    } elseif (substr($rr['target'],0,1)=='@') {
      $rr['type']='domain';
    } else {
      $rr['type']='mail';
    }
    
    return $rr;
  }

  /**
   * @param string $domain_id
   */
  function catchall_del($domain_id) {
    $catch = $this->catchall_getinfos($domain_id);
    if (empty($catch['mail_id'])) return false;
    return $this->delete($catch['mail_id']);
  }

  /**
   * @param string $domain_id
   * @param string $target
   */
  function catchall_set($domain_id, $target) {
    global $err;
    // target :
    $target=rtrim($target);
    if ( substr_count($target,'@') == 0 ) { // Pas de @
      $target = '@'.$target;
    } 

    if ( substr($target,0,1) == '@' ) { // le premier caractere est un @
      // FIXME validate domain
    } else { // ca doit ĂȘtre un mail
      if (!filter_var($target,FILTER_VALIDATE_EMAIL)) {
        $err->raise("mail",_("The email you entered is syntaxically incorrect"));
        return false;
      }
    }
    $this->catchall_del($domain_id);
    return $this->create_alias($domain_id, '', $target, "catchall", true);
  }


  /* ----------------------------------------------------------------- */
  /** get_quota (hook for quota class), returns the number of used 
   * service for a quota-bound service
   * @param $name string the named quota we want
   * @return the number of used service for the specified quota, 
   * or false if I'm not the one for the named quota
   */
  function hook_quota_get() {
    global $db,$err,$cuid;
    $err->log("mail","getquota");
    $q=Array("name"=>"mail", "description"=>_("Email addresses"), "used"=>0);
    $db->query("SELECT COUNT(*) AS cnt FROM address a, domaines d WHERE a.domain_id=d.id AND d.compte=$cuid AND a.type='';");
    if ($db->next_record()) {
      $q['used']=$db->f("cnt");
    }
    return $q;
  }


  /* ----------------------------------------------------------------- */
  /** Password policy kind used in this class (hook for admin class)
   * @return array an array of policykey => "policy name (for humans)"
   */
  function alternc_password_policy() {
    return array("pop"=>_("Email account password"));
  }


  /* ----------------------------------------------------------------- */
  /** Returns the list of mail-hosting domains for a user
   * @return array indexed array of hosted domains
   */
  function enum_domains($uid=-1) {
      global $db,$err,$cuid;
      $err->log("mail","enum_domains");
      if ($uid == -1) { $uid = $cuid; }
      $db->query("
SELECT
  d.id,
  d.domaine,
  IFNULL( COUNT(a.id), 0) as nb_mail
FROM
  domaines d LEFT JOIN address a ON (d.id=a.domain_id AND a.type='')
WHERE
  d.compte = $uid
  and d.gesmx = 1
GROUP BY
  d.id
ORDER BY
  d.domaine
;
");
      $this->enum_domains=array();
      while($db->next_record()){
          $this->enum_domains[]=$db->Record;
      }
      return $this->enum_domains;
  }


  /* ----------------------------------------------------------------- */
  /** available: tells if an email address can be installed in the server
   * check the domain part (is it mine too), the syntax, and the availability.
   * @param $mail string email to check
   * @return boolean true if the email can be installed on the server 
   */  
  function available($mail){
    global $db,$err,$dom;
    $err->log("mail","available");    
    list($login,$domain)=explode("@",$mail,2);
    // Validate the domain ownership & syntax
    if (!($dom_id=$dom->get_domain_byname($domain))) {
      return false;
    }
    // Validate the email syntax:
    if (!filter_var($mail,FILTER_VALIDATE_EMAIL)) {
      $err->raise("mail",_("The email you entered is syntaxically incorrect"));
      return false;
    }
    // Check the availability
    $db->query("SELECT a.id FROM address a WHERE a.domain_id=".$dom_id." AND a.address='".addslashes($login)."';");
    if ($db->next_record()) {
      return false;
    } else {
      return true;
    }
  }


  /* ----------------------------------------------------------------- */
  /* function used to list every mail address hosted on a domain.
   * @param $dom_id integer the domain id.
   * @param $search string search that string in recipients or address.
   * @param $offset integer skip THAT much emails in the result.
   * @param $count integer return no more than THAT much emails. -1 for ALL. Offset is ignored then.
   * @result an array of each mail hosted under the domain.
   */
  function enum_domain_mails($dom_id = null, $search="", $offset=0, $count=30, $show_systemmails=false){
    global $db,$err,$cuid,$hooks;
    $err->log("mail","enum_domains_mail");

    $search=trim($search);

    $where="a.domain_id=$dom_id";
    if ($search) $where.=" AND (a.address LIKE '%".addslashes($search)."%' OR r.recipients LIKE '%".addslashes($search)."%')";
    if (!$show_systemmails) $where.=" AND type='' ";
    $db->query("SELECT count(a.id) AS total FROM address a LEFT JOIN recipient r ON r.address_id=a.id WHERE $where;");
    $db->next_record();
    $this->total=$db->f("total");
    if ($count!=-1) $limit="LIMIT $offset,$count"; else $limit="";
    $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  
         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 $where AND d.id=a.domain_id $limit ;");
    if (! $db->next_record()) {
      $err->raise("mail",_("No email found for this query"));
      return array();
    }
    $res=array();
    do {
      $details=$db->Record;
      // if necessary, fill the typedata with data from hooks ...
      if ($details["type"]) {
	      $result=$hooks->invoke("hook_mail_get_details",array($details)); // Will fill typedata if necessary
	      $details["typedata"]=implode("<br />",$result);
      }
      $res[]=$details;
    } while ($db->next_record());
    return $res;
  }


  function hook_mail_get_details($detail) {
    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']));
  }


  /* ----------------------------------------------------------------- */
  /** Function used to insert a new mail into the db
   * should be used by the web interface, not by third-party programs.
   *
   * This function calls the hook "hooks_mail_cancreate"
   * which must return FALSE if the user can't create this email, and raise and error accordingly
   * 
   * @param $dom_id integer A domain_id (owned by the user) 
   * (will be the part at the right of the @ in the email)
   * @param $mail string the left part of the email to create (something@dom_id)
   * @return an hashtable containing the database id of the newly created mail, 
   * or false if an error occured ($err is filled accordingly)
   */ 
  function create($dom_id, $mail,$type="",$dontcheck=false){
    global $err,$db,$cuid,$quota,$dom,$hooks;
    $err->log("mail","create",$mail);

    // Validate the domain id
    if (!($domain=$dom->get_domain_byid($dom_id))) {
      return false;
    }

    // Validate the email syntax:
    $m=$mail."@".$domain;
    if (!filter_var($m,FILTER_VALIDATE_EMAIL) && !$dontcheck) {
      $err->raise("mail",_("The email you entered is syntaxically incorrect"));
      return false;
    }

    // Call other classes to check we can create it:
    $cancreate=$hooks->invoke("hook_mail_cancreate",array($dom_id,$mail));
    if (in_array(false,$cancreate,true)) {
      return false;
    }

    // Check the quota:
    if (!$quota->cancreate("mail")) {
      $err->raise("mail",_("You cannot create email addresses: your quota is over"));
      return false;
    }
    // Already exists?
    $db->query("SELECT * FROM address WHERE domain_id=".$dom_id." AND address='".addslashes($mail)."';");
    if ($db->next_record()) {
      $err->raise("mail",_("This email address already exists"));
      return false;
    }
    // Create it now
    $db->query("INSERT INTO address (domain_id, address,type) VALUES ($dom_id, '".addslashes($mail)."','$type');");
    if (!($id=$db->lastid())) {
      $err->raise("mail",_("An unexpected error occured when creating the email"));
      return false;
    }
    return $id;
  }


  /* ----------------------------------------------------------------- */
  /** function used to get every information we can on a mail 
  * @param $mail_id integer
  * @return array a hashtable with all the informations for that email
  */
  function get_details($mail_id) {
    global $db, $err, $cuid, $hooks;
    $err->log("mail","get_details");

    $mail_id=intval($mail_id);
    // Validate that this email is owned by me...
    if (!($mail=$this->is_it_my_mail($mail_id))) {
      return false;
    }

    // We fetch all the informations for that email: these will fill the hastable : 
    $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;");
    if (! $db->next_record()) return false;
    $details=$db->Record;
    // if necessary, fill the typedata with data from hooks ...
    if ($details["type"]) {
      $result=$hooks->invoke("hook_mail_get_details",array($mail_id)); // Will fill typedata if necessary
      $details["typedata"]=implode("<br />",$result);
    }
    return $details;
  }


  private $isitmy_cache=array(); 

  /* ----------------------------------------------------------------- */
  /** Check if an email is mine ...
   *
   * @param $mail_id integer the number of the email to check
   * @return string the complete email address if that's mine, false if not
   * ($err is filled accordingly)
   */ 
  function is_it_my_mail($mail_id){
    global $err,$db,$cuid;
    $mail_id=intval($mail_id);
    // cache it (may be called more than one time in the same page).
    if (isset($this->isitmy_cache[$mail_id])) return $this->isitmy_cache[$mail_id];

    $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;");
    if ($db->next_record()) {
      return $this->isitmy_cache[$mail_id]=$db->f("email");
    } else {
      $err->raise("mail",_("This email is not yours, you can't change anything on it"));
      return $this->isitmy_cache[$mail_id]=false;
    }
  }


  /* ----------------------------------------------------------------- */
  /** Hook called when the DOMAIN class will delete a domain.
   *
   * @param $dom integer the number of the email to delete
   * @return boolean if the email has been properly deleted 
   * or false if an error occured ($err is filled accordingly)
   */ 
  function hook_dom_del_mx_domain($dom_id) {
    $list=$this->enum_domain_mails($dom_id,"",0,-1);
    if (is_array($list)) {
      foreach($list as $one) {
	$this->delete($one["id"]);
      }
    }
    return true;
  }

  // return the alternc account's ID of the mail_id
  function get_account_by_mail_id($mail_id) {
    global $db,$err;
    $db->query("select compte as uid from domaines d, address a where a.domain_id = d.id and a.id = $mail_id");
    if ( !$db->next_record()) {
      return false;
    }  
    return $db->f('uid');
  }


  /* ----------------------------------------------------------------- */
  /** Function used to delete a mail from the db
   * should be used by the web interface, not by third-party programs.
   *
   * @param $mail_id integer the number of the email to delete
   * @return boolean if the email has been properly deleted 
   * or false if an error occured ($err is filled accordingly)
   */ 
  function delete($mail_id){
    global $err,$db,$cuid,$quota,$dom,$hooks;
    $err->log("mail","delete");

    $mail_id=intval($mail_id);

    if (!$mail_id)  {
      $err->raise("mail",_("The email you entered is syntaxically incorrect"));
      return false;
    }
    // Validate that this email is owned by me...
    if (!($mail=$this->is_it_my_mail($mail_id))) {
      return false;
    }

    $mailinfos=$this->get_details($mail_id);
    $hooks->invoke('hook_mail_delete', array($mail_id, $mailinfos['address'].'@'.$mailinfos['domain'] ));

    // Search for that address:
    $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';");
    if (!$db->next_record()) {
      $err->raise("mail",_("The email %s does not exist, it can't be deleted"),$mail);
      return false;
    }
    if ($db->f("mail_action")!="OK" || ($db->f("islocal") && $db->f("mailbox_action")!="OK")) { // will be deleted soon ...
      $err->raise("mail",_("The email %s is already marked for deletion, it can't be deleted"),$mail);
      return false;
    }
    $mail_id=$db->f("id");

    if ($db->f("islocal")) {
      // If it's a pop/imap mailbox, mark it for deletion
      $db->query("UPDATE address SET mail_action='DELETE', enabled=0 WHERE id='$mail_id';");
      $db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id='$mail_id';");
      $err->raise("mail",_("The email %s has been marked for deletion"),$mail);
    } else {
      // If it's only aliases, delete it NOW.
      $db->query("DELETE FROM address WHERE id='$mail_id';");
      $db->query("DELETE FROM mailbox WHERE address_id='$mail_id';");
      $db->query("DELETE FROM recipient WHERE address_id='$mail_id';");
      $err->raise("mail",_("The email %s has been successfully deleted"),$mail);
    }
    return true;
  }


  /* ----------------------------------------------------------------- */
  /** Function used to undelete a pending deletion mail from the db
   * should be used by the web interface, not by third-party programs.
   *
   * @param $mail_id integer the email id
   * @return boolean if the email has been properly undeleted 
   * or false if an error occured ($err is filled accordingly)
   */ 
  function undelete($mail_id){
    global $err,$db,$cuid,$quota,$dom,$hooks;
    $err->log("mail","undelete");

    $mail_id=intval($mail_id);

    if (!$mail_id)  {
      $err->raise("mail",_("The email you entered is syntaxically incorrect"));
      return false;
    }
    // Validate that this email is owned by me...
    if (!($mail=$this->is_it_my_mail($mail_id))) {
      return false;
    }

    // Search for that address:
    $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';");
    if (!$db->next_record()) {
      $err->raise("mail",_("The email %s does not exist, it can't be undeleted"),$mail);
      return false;
    }
    if ($db->f("type")!="") { // Technically special : mailman, sympa ... 
      $err->raise("mail",_("The email %s is special, it can't be undeleted"),$mail);
      return false;
    }
    if ($db->f("mailbox_action")!="DELETE" || $db->f("mail_action")!="DELETE") { // will be deleted soon ...
      $err->raise("mail",_("Sorry, deletion of email %s is already in progress, or not marked for deletion, it can't be undeleted"),$mail);
      return false;
    }
    $mail_id=$db->f("id");

    if ($db->f("islocal")) {
      // If it's a pop/imap mailbox, mark it for deletion
      $db->query("UPDATE address SET mail_action='OK', `enabled`=1 WHERE id='$mail_id';");
      $db->query("UPDATE mailbox SET mail_action='OK' WHERE address_id='$mail_id';");
      $err->raise("mail",_("The email %s has been undeleted"),$mail);
      return true;
    } else {
      $err->raise("mail",_("-- Program Error -- The email %s can't be undeleted"),$mail);
      return false;
    }
  }


  /* ----------------------------------------------------------------- */
  /** set the password of an email address.
   * @param $mail_id integer email ID 
   * @param $pass string the new password.
   * @return boolean true if the password has been set, false else, raise an error.
   */  
  function set_passwd($mail_id,$pass){
    global $db,$err,$admin;
    $err->log("mail","setpasswd");

    if (!($email=$this->is_it_my_mail($mail_id))) return false;
    if (!$admin->checkPolicy("pop",$email,$pass)) return false;
    if (!$db->query("UPDATE address SET password='"._md5cr($pass)."' where id=$mail_id;")) return false;
    return true;
  }


  /* ----------------------------------------------------------------- */
  /** Enables an email address.
   * @param $mail_id integer Email ID
   * @return boolean true if the email has been enabled.
   */  
  function enable($mail_id){
    global $db,$err;
    $err->log("mail","enable");
    if (!($email=$this->is_it_my_mail($mail_id))) return false;
    if (!$db->query("UPDATE address SET `enabled`=1 where id=$mail_id;")) return false;
    return true;
  }


  /* ----------------------------------------------------------------- */
  /** Disables an email address.
   * @param $mail_id integer Email ID
   * @return boolean true if the email has been enabled.
   */ 
  function disable($mail_id){
    global $db,$err;
    $err->log("mail","disable");
    if (!($email=$this->is_it_my_mail($mail_id))) return false;
    if (!$db->query("UPDATE address SET `enabled`=0 where id=$mail_id;")) return false;
    return true;
  }


  /* ----------------------------------------------------------------- */
  /** Function used to update an email settings
   * should be used by the web interface, not by third-party programs.
   *
   * @param $mail_id integer the number of the email to delete
   * @param integer $islocal boolean is it a POP/IMAP mailbox ?
   * @param integer $quotamb integer if islocal=1, quota in MB
   * @param string $recipients string recipients, one mail per line.
   * @return boolean if the email has been properly edited
   * or false if an error occured ($err is filled accordingly)
   */ 
  function set_details($mail_id, $islocal, $quotamb, $recipients,$delivery="dovecot",$dontcheck=false) {
    global $err,$db,$cuid,$quota,$dom,$hooks;
    $delivery=mysql_real_escape_string($delivery);
    $err->log("mail","set_details");
    if (!($me=$this->get_details($mail_id))) {
      return false;
    }
    if ($me["islocal"] && !$islocal) {
      // delete pop
      $db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id=".$mail_id.";");
    } 
    if (!$me["islocal"] && $islocal) {
      // create pop
      $path="";
      if($delivery=="dovecot"){
        $path=ALTERNC_MAIL."/".substr($me["address"]."_",0,1)."/".$me["address"]."_".$me["domain"];
      }
      foreach($this->forbiddenchars as $str) {
	    if (strpos($me["address"],$str)!==false) {
	      $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"));
          return false;
          break;
        }
      }
      foreach($this->specialchars as $str) {
	    if (strpos($me["address"],$str)!==false) {
	      $path=ALTERNC_MAIL."/_/".$me["id"]."_".$me["domain"];
	      break;
  	    } 
      }
      $db->query("INSERT INTO mailbox SET address_id=$mail_id, delivery='$delivery', path='".addslashes($path)."';");
    }
    if ($me["islocal"] && $islocal && $me["mailbox_action"]=="DELETE") {
      $db->query("UPDATE mailbox SET mail_action='OK' WHERE mail_action='DELETE' AND address_id=".$mail_id.";");
    }

    if ($islocal) {
      if ($quotamb!=0 && $quotamb<(intval($me["used"]/1024/1024)+1)) {
	$quotamb=intval($me["used"]/1024/1024)+1;
	$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"));
      }
      $db->query("UPDATE mailbox SET quota=".intval($quotamb)." WHERE address_id=".$mail_id.";");
    }

    $recipients=preg_replace('/[\r\t\s]/', "\n", $recipients); // Handle space AND new line
    $r=explode("\n",$recipients);
    $red="";
    foreach($r as $m) {
      $m=trim($m);
      if ($m && ( filter_var($m,FILTER_VALIDATE_EMAIL) || $dontcheck)  // Recipient Email is valid
	  && $m!=($me["address"]."@".$me["domain"])) {  // And not myself (no loop allowed easily ;) )
	$red.=$m."\n";
      }
    }
    $db->query("DELETE FROM recipient WHERE address_id=".$mail_id.";");
    if (isset($m) && $m) {
      $db->query("INSERT INTO recipient SET address_id=".$mail_id.", recipients='".addslashes($red)."';");
    }
    return true;
  }

  /* ----------------------------------------------------------------- */
  /** A wrapper used by mailman class to create it's needed addresses 
   * @ param : $dom_id , the domain id associated to a given address
   * @ param : $m , the left part of the  mail address being created
   * @ param : $delivery , the delivery used to deliver the mail
   */

  function add_wrapper($dom_id,$m,$delivery){
    global $err,$db,$mail;
    $err->log("mail","add_wrapper","creating $delivery $m address");

    $mail_id=$mail->create($dom_id,$m,$delivery);
    $this->set_details($mail_id,1,0,'',$delivery);
    // FIXME return error code
  }

  /* ----------------------------------------------------------------- */
  /** A function used to create an alias for a specific address
   * @ param : $dom_id , the domain sql identifier
   * @ param : $m , the alias we want to create
   * @ param : $alias , the already existing aliased address
   * @ param : $type, the type of the alias created
   * @param string $m
   * @param string $alias
   * @param string $dom_id
   */
  function create_alias($dom_id,$m,$alias,$type="",$dontcheck=false) {
    global $err,$db,$mail;
    $err->log("mail","create_alias","creating $m alias for $alias type $type");

    $mail_id=$mail->create($dom_id,$m,$type,$dontcheck);
    $this->set_details($mail_id,0,0,$alias,"dovecot",$dontcheck);
    // FIXME return error code
  }



  /* ----------------------------------------------------------------- */
  /** A wrapper used by mailman class to create it's needed addresses 
   * @ param : $mail_id , the mysql id of the mail address we want to delete
   * of the email for the current acccount.
   */
  function del_wrapper($mail_id){
    global $err,$db;
    $err->log("mail","del_wrapper");
    $this->delete($mail_id);
  }

  /* ----------------------------------------------------------------- */
  /** Export the mail information of an account 
   * @return: str, string containing the complete configuration 
   * of the email for the current acccount.
   */
  function alternc_export_conf() {
    global $db,$err,$mail_localbox;
    $err->log("mail","export");
    $domain=$this->enum_domains();
    $str="<mail>\n";
    foreach ($domain as $d) {
      $str.="  <domain>\n    <name>".xml_entities($d["domain"])."</name>\n";
      $s=$this->enum_domain_mails($d["id"]);
      if (count($s)) {
	while (list($key,$val)=each($s)){
	  $str.="    <address>\n";
	  $str.="      <name>".xml_entities($val["address"])."</name>\n";
	  $str.="      <enabled>".xml_entities($val["enabled"])."</enabled>\n";
	  if(is_array($val["islocal"])){
	    $str.="      <islocal>1</islocal>\n";
             $str.="      <quota>".$val["quota"]."</quota>\n";
             $str.="      <path>".$val["path"]."</path>\n";
           }else{
             $str.="      <islocal>0</islocal>\n";
          }
          if(!empty($val["recipients"])){
	    $r=explode("\n",$val["recipients"]);
              foreach($r as $recip){
                $str.="      <recipients>".$recip."<recipients>\n";
              }
          }
       $str.="    </address>\n";
     }
       }     
       $str.="  </domain>\n";
     }
     $str.="</mail>\n";
     return $str;
   }
 

  /* ----------------------------------------------------------------- */
  /**
   * Return the list of allowed slave accounts (secondary-mx)
   * @return array
   */
  function enum_slave_account() {
    global $db,$err;
    $db->query("SELECT login,pass FROM mxaccount;");
    $res=array();
    while ($db->next_record()) {
        $res[]=$db->Record;
    }
    if (!count($res)) return false;
    return $res;
  }

  /* ----------------------------------------------------------------- */
  /**
   * Check for a slave account (secondary mx)
   * @param string $login the login to check
   * @param string $pass the password to check
   * @return boolean TRUE if the password is correct, or FALSE if an error occurred.
   */
  function check_slave_account($login,$pass) {
    global $db,$err;
    $login=mysql_real_escape_string($login);
    $pass=mysql_real_escape_string($pass);
    $db->query("SELECT * FROM mxaccount WHERE login='$login' AND pass='$pass';");
    if ($db->next_record()) {
        return true;
    }
    return false;
  }


  /* ----------------------------------------------------------------- */
  /** Out (echo) the complete hosted domain list : 
   */
  function echo_domain_list($format=null) {
    global $db,$err;
    $db->query("SELECT domaine FROM domaines WHERE gesmx=1 ORDER BY domaine");
    $lst=array();
    $tt="";
    while ($db->next_record()) {
      $lst[]=$db->f("domaine");
      $tt.=$db->f("domaine");
    }

    # Generate an integrity check 
    $obj=array('integrity'=>md5($tt),'items'=>$lst);

    switch($format) {
      case "json":
        return json_encode($obj);
        break;
      default:
        foreach ($lst as $l) { echo $l."\n"; }
        return true;
        break;
    } // switch
  }


  /* ----------------------------------------------------------------- */
  /**
   * Add a slave account that will be allowed to access the mxdomain list
   * @param string $login the login to add
   * @param string $pass the password to add
   * @return boolean TRUE if the account has been created, or FALSE if an error occurred.
   */
  function add_slave_account($login,$pass) {
    global $db,$err;
    $login=mysql_real_escape_string($login);
    $pass=mysql_real_escape_string($pass);
    $db->query("SELECT * FROM mxaccount WHERE login='$login'");
    if ($db->next_record()) {
      $err->raise("mail",_("The slave MX account was not found"));
      return false;
    }
    $db->query("INSERT INTO mxaccount (login,pass) VALUES ('$login','$pass')");
    return true;
  }


  /* ----------------------------------------------------------------- */
  /**
   * Remove a slave account
   * @param string $login the login to delete
   */
  function del_slave_account($login) {
    global $db,$err;
    $login=mysql_real_escape_string($login);
    $db->query("DELETE FROM mxaccount WHERE login='$login'");
    return true;
  }

  /* ----------------------------------------------------------------- */
  /** hook function called by AlternC when a domain is created for
   * the current user account using the SLAVE DOMAIN feature
   * This function create a CATCHALL to the master domain
   * @param string $domain_id Domain that has just been created
   * @param string $target_domain Master domain 
   * @access private
   */
  function hook_dom_add_slave_domain($domain_id,$target_domain) { 
    global $err;
    $err->log("mail","hook_dom_add_slave_domain",$domain_id);
    $this->catchall_set($domain_id,'@'.$target_domain); 
    return true;
  }

  /* ----------------------------------------------------------------- */
  /** hook function called by AlternC when a domain is created for
   * the current user account 
   * This function create a postmaster mail which is an alias to LOGIN @ FQDN
   * wich is a dynamic alias to the alternc's account mail
   * @param string $domain_id Domain that has just been created
   * @access private
   */
   function hook_dom_add_mx_domain($domain_id) {
    global $err, $mem, $L_FQDN,$db;
    $err->log("mail","hook_dom_add_mx_domain",$domain_id);

    $db->query("SELECT value FROM variable where name='mailname_bounce';");
    if (!$db->next_record()) {
      $err->raise("mail",_("Problem: can't create default bounce mail"));
      return false;
    }
    $mailname=$db->f("value");

    return $this->create_alias($domain_id, 'postmaster', $mem->user['login'].'@'.$mailname );
  }



  /* ----------------------------------------------------------------- */
  /** hook function called by AlternC-upnp to know which open 
   * tcp or udp ports this class requires or suggests
   * @return array a key => value list of port protocol name mandatory values
   * @access private
   */
  function hook_upnp_list() {
    return array(
		 "imap" => array("port" => 143, "protocol" => "tcp", "mandatory" => 1),
		 "imaps" => array("port" => 993, "protocol" => "tcp", "mandatory" => 1),
		 "pop" => array("port" => 110, "protocol" => "tcp", "mandatory" => 1),
		 "pops" => array("port" => 995, "protocol" => "tcp", "mandatory" => 1),
		 "smtp" => array("port" => 25, "protocol" => "tcp", "mandatory" => 1),
		 "sieve" => array("port" => 2000, "protocol" => "tcp", "mandatory" => 1),
		 "submission" => array("port" => 587, "protocol" => "tcp", "mandatory" => 0),
		 );
  }

 

} /* Class m_mail */