2006-04-26 12:28:53 +00:00
< ? php
2015-09-25 15:42:00 +00:00
2006-04-26 12:28:53 +00:00
/*
2012-08-28 08:59:05 +00:00
----------------------------------------------------------------------
LICENSE
2015-09-25 15:42:00 +00:00
2012-08-28 08:59:05 +00:00
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 .
2015-09-25 15:42:00 +00:00
2012-08-28 08:59:05 +00:00
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 .
2015-09-25 15:42:00 +00:00
2012-08-28 08:59:05 +00:00
To read the license please visit http :// www . gnu . org / copyleft / gpl . html
----------------------------------------------------------------------
2017-10-06 21:42:39 +00:00
*/
2009-09-08 05:29:38 +00:00
2006-04-26 12:28:53 +00:00
/**
2015-09-25 15:42:00 +00:00
* This class handle emails ( pop and / or aliases and even wrapper for internal
* classes ) of hosted users .
*
* 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 .
2017-10-08 17:31:34 +00:00
*
* @ copyright AlternC - Team 2000 - 2017 https :// alternc . com /
2015-09-25 15:42:00 +00:00
*/
2006-04-26 12:28:53 +00:00
class m_mail {
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* domain list for this account
2015-09-25 15:42:00 +00:00
* @ access private
*/
var $domains ;
2017-10-06 21:42:39 +00:00
/**
* If an email has those chars , 'not nice in shell env' ;)
2015-09-25 15:42:00 +00:00
* we don ' t store the email in $mail / u / { user } _domain , but in $mail / _ / { address_id } _domain
* @ access private
*/
var $specialchars = array ( '"' , " ' " , '\\' , '/' );
2017-10-06 21:42:39 +00:00
/**
* If an email has those chars , we will ONLY allow RECIPIENTS , NOT POP / IMAP for DOVECOT !
2015-09-25 15:42:00 +00:00
* Since Dovecot doesn ' t allow those characters
* @ access private
*/
var $forbiddenchars = array ( '"' , " ' " , '\\' , '/' , '?' , '!' , '*' , '$' , '|' , '#' , '+' );
2017-10-06 21:42:39 +00:00
/**
* Number of results for a pager display
2015-09-25 15:42:00 +00:00
* @ access public
*/
var $total ;
2017-10-06 21:42:39 +00:00
2015-09-25 15:42:00 +00:00
// Human server name for help
2018-06-22 15:37:04 +00:00
var $srv_postfix ;
var $srv_dovecot ;
2015-09-25 15:42:00 +00:00
var $cache_domain_mail_size = array ();
var $enum_domains = array ();
/**
* Constructeur
*/
function m_mail () {
global $L_FQDN ;
2018-06-24 17:05:59 +00:00
$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' ));
$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' ));
2015-09-25 15:42:00 +00:00
}
2006-04-26 12:28:53 +00:00
2017-10-06 21:42:39 +00:00
/**
* Hook called by menu class to add the email menu to the left pane
*/
2015-09-25 15:42:00 +00:00
function hook_menu () {
$obj = array (
'title' => _ ( " Email Addresses " ),
'link' => 'toggle' ,
'pos' => 30 ,
'links' => array (),
);
foreach ( $this -> enum_domains () as $d ) {
$obj [ 'links' ][] = array (
'txt' => htmlentities ( $d [ " domaine " ]) . ' ' . htmlentities ( " ( " . $d [ " nb_mail " ] . " ) " ),
'url' => " mail_list.php?domain_id= " . urlencode ( $d [ 'id' ]),
);
}
2012-08-23 18:46:47 +00:00
2015-09-25 15:42:00 +00:00
return $obj ;
2014-03-07 08:36:28 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
2015-09-25 15:42:00 +00:00
function get_total_size_for_domain ( $domain ) {
global $db ;
if ( empty ( $this -> cache_domain_mail_size )) {
2017-08-15 14:11:57 +00:00
$db -> query ( " SELECT SUBSTRING_INDEX(user,'@', -1) as domain, SUM(quota_dovecot) AS sum FROM dovecot_quota group by domain ; " );
2015-09-25 15:42:00 +00:00
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 ;
2012-10-20 13:16:14 +00:00
}
2015-09-25 15:42:00 +00:00
/**
* @ 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' => '' ,
);
2016-05-17 16:57:01 +00:00
$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 ));
2015-09-25 15:42:00 +00:00
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 ;
2012-10-20 13:16:14 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
2015-09-25 15:42:00 +00:00
/**
* @ 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' ]);
2012-10-20 14:41:45 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
2015-09-25 15:42:00 +00:00
/**
* @ param string $domain_id
* @ param string $target
*/
function catchall_set ( $domain_id , $target ) {
2017-08-15 14:11:57 +00:00
global $msg ;
2015-09-25 15:42:00 +00:00
$target = rtrim ( $target );
2017-08-15 14:11:57 +00:00
if ( strlen ( $target ) > 0 && substr_count ( $target , '@' ) == 0 ) { // Pas de @
2015-09-25 15:42:00 +00:00
$target = '@' . $target ;
}
2017-10-06 21:42:39 +00:00
if ( substr ( $target , 0 , 1 ) == '@' ) { // the first character is @
2015-09-25 15:42:00 +00:00
// FIXME validate domain
2017-10-06 21:42:39 +00:00
} else { // it MUST be an email
2015-09-25 15:42:00 +00:00
if ( ! filter_var ( $target , FILTER_VALIDATE_EMAIL )) {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " The email you entered is syntaxically incorrect " ));
2015-09-25 15:42:00 +00:00
return false ;
}
}
$this -> catchall_del ( $domain_id );
return $this -> create_alias ( $domain_id , '' , $target , " catchall " , true );
2012-04-06 10:10:36 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* get_quota ( hook for quota class ), returns the number of used
2015-09-25 15:42:00 +00:00
* 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 () {
2017-08-17 19:32:21 +00:00
global $db , $msg , $cuid , $quota ;
2018-06-27 22:27:10 +00:00
$msg -> debug ( " mail " , " getquota " );
2015-09-25 15:42:00 +00:00
$q = Array ( " name " => " mail " , " description " => _ ( " Email addresses " ), " used " => 0 );
2016-05-17 16:57:01 +00:00
$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 ));
2015-09-25 15:42:00 +00:00
if ( $db -> next_record ()) {
$q [ 'used' ] = $db -> f ( " cnt " );
2017-08-17 19:32:21 +00:00
$q [ 'sizeondisk' ] = $quota -> get_size_mail_sum_user ( $cuid ) / 1024 ;
2015-09-25 15:42:00 +00:00
}
return $q ;
}
2017-10-06 21:42:39 +00:00
/**
* Password policy kind used in this class ( hook for admin class )
2015-09-25 15:42:00 +00:00
* @ return array an array of policykey => " policy name (for humans) "
*/
function alternc_password_policy () {
return array ( " pop " => _ ( " Email account password " ));
}
2017-10-06 21:42:39 +00:00
/**
* Returns the list of mail - hosting domains for a user
2015-09-25 15:42:00 +00:00
* @ return array indexed array of hosted domains
*/
function enum_domains ( $uid = - 1 ) {
2017-08-15 14:11:57 +00:00
global $db , $msg , $cuid ;
2018-06-27 22:27:10 +00:00
$msg -> debug ( " mail " , " enum_domains " );
2015-09-25 15:42:00 +00:00
if ( $uid == - 1 ) {
$uid = $cuid ;
}
2017-10-06 21:42:39 +00:00
$db -> query ( "
2012-10-22 08:58:53 +00:00
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
2016-05-17 16:57:01 +00:00
d . compte = ?
2013-03-01 16:12:43 +00:00
and d . gesmx = 1
2012-10-22 08:58:53 +00:00
GROUP BY
d . id
ORDER BY
d . domaine
;
2016-05-17 16:57:01 +00:00
" , array( $uid ));
2015-09-25 15:42:00 +00:00
$this -> enum_domains = array ();
while ( $db -> next_record ()) {
$this -> enum_domains [] = $db -> Record ;
}
return $this -> enum_domains ;
2012-08-25 15:54:28 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* available : tells if an email address can be installed in the server
2015-09-25 15:42:00 +00:00
* 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 ) {
2017-08-15 14:11:57 +00:00
global $db , $msg , $dom ;
$msg -> log ( " mail " , " available " );
2015-09-25 15:42:00 +00:00
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 )) {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " The email you entered is syntaxically incorrect " ));
2015-09-25 15:42:00 +00:00
return false ;
}
// Check the availability
2016-05-17 16:57:01 +00:00
$db -> query ( " SELECT a.id FROM address a WHERE a.domain_id= ? AND a.address= ?; " , array ( $dom_id , $login ));
2015-09-25 15:42:00 +00:00
if ( $db -> next_record ()) {
return false ;
} else {
return true ;
}
2012-08-25 15:54:28 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* function used to list every mail address hosted on a domain .
2015-09-25 15:42:00 +00:00
* @ 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 ) {
2017-08-15 14:11:57 +00:00
global $db , $msg , $hooks ;
2018-06-27 22:27:10 +00:00
$msg -> debug ( " mail " , " enum_domains_mail " );
2015-09-25 15:42:00 +00:00
2016-05-18 10:51:03 +00:00
$query_args = array ( $dom_id );
$search = trim ( $search );
$where = " a.domain_id = ? " ;
2015-09-25 15:42:00 +00:00
if ( $search ) {
2016-05-18 10:51:03 +00:00
$where .= " AND (a.address LIKE ? OR r.recipients LIKE ? ) " ;
array_push ( $query_args , " % " . $search . " % " , " % " . $search . " % " );
2015-09-25 15:42:00 +00:00
}
if ( ! $show_systemmails ) {
2016-05-18 10:51:03 +00:00
$where .= " AND type='' " ;
2015-09-25 15:42:00 +00:00
}
2016-05-18 10:51:03 +00:00
$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 );
2015-09-25 15:42:00 +00:00
$db -> next_record ();
$this -> total = $db -> f ( " total " );
if ( $count != - 1 ) {
2017-10-06 21:42:39 +00:00
$offset = intval ( $offset );
$count = intval ( $count );
2016-05-18 16:41:27 +00:00
$limit = " LIMIT $offset , $count " ;
2015-09-25 15:42:00 +00:00
} else {
$limit = " " ;
}
2017-08-15 14:11:57 +00:00
$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
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
2016-05-18 10:51:03 +00:00
WHERE " . $where . " AND d . id = a . domain_id " . $limit . " ; " , $query_args );
2015-09-25 15:42:00 +00:00
if ( ! $db -> next_record ()) {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " No email found for this query " ));
2015-09-25 15:42:00 +00:00
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 ;
2012-08-19 20:50:47 +00:00
}
2012-08-23 18:46:47 +00:00
2015-09-25 15:42:00 +00:00
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' ]));
}
2012-04-06 10:10:36 +00:00
}
2012-08-23 18:46:47 +00:00
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* Function used to insert a new mail into the db
2015-09-25 15:42:00 +00:00
* 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 ,
2017-08-15 14:11:57 +00:00
* or false if an error occured ( $msg is filled accordingly )
2015-09-25 15:42:00 +00:00
*/
function create ( $dom_id , $mail , $type = " " , $dontcheck = false ) {
2017-08-15 14:11:57 +00:00
global $msg , $db , $quota , $dom , $hooks ;
$msg -> log ( " mail " , " create " , $mail );
2015-09-25 15:42:00 +00:00
// Validate the domain id
if ( ! ( $domain = $dom -> get_domain_byid ( $dom_id ))) {
return false ;
}
2012-08-19 20:50:47 +00:00
2015-09-25 15:42:00 +00:00
// Validate the email syntax:
$m = $mail . " @ " . $domain ;
if ( ! filter_var ( $m , FILTER_VALIDATE_EMAIL ) && ! $dontcheck ) {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " The email you entered is syntaxically incorrect " ));
2015-09-25 15:42:00 +00:00
return false ;
}
2012-08-23 18:46:47 +00:00
2015-09-25 15:42:00 +00:00
// 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:
2016-03-03 10:28:26 +00:00
if (( $type == " " ) &&! $quota -> cancreate ( " mail " )) {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ALERT " , " mail " , _ ( " You cannot create email addresses: your quota is over " ));
2015-09-25 15:42:00 +00:00
return false ;
}
// Already exists?
2016-05-17 16:57:01 +00:00
$db -> query ( " SELECT * FROM address WHERE domain_id= ? AND address= ? ; " , array ( $dom_id , $mail ));
2015-09-25 15:42:00 +00:00
if ( $db -> next_record ()) {
2017-10-06 21:42:39 +00:00
if ( $db -> f ( " type " ) == " mailman " )
$msg -> raise ( " ERROR " , " mail " , _ ( " This email address already exists in mailman " ));
else
$msg -> raise ( " ERROR " , " mail " , _ ( " This email address already exists " ));
2017-08-15 14:11:57 +00:00
2015-09-25 15:42:00 +00:00
return false ;
}
// Create it now
2016-05-17 16:57:01 +00:00
$db -> query ( " INSERT INTO address (domain_id, address,type) VALUES (?, ?, ?); " , array ( $dom_id , $mail , $type ));
2015-09-25 15:42:00 +00:00
if ( ! ( $id = $db -> lastid ())) {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " An unexpected error occured when creating the email " ));
2015-09-25 15:42:00 +00:00
return false ;
}
return $id ;
2012-08-24 13:37:56 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* function used to get every information we can on a mail
2015-09-25 15:42:00 +00:00
* @ param $mail_id integer
* @ return array a hashtable with all the informations for that email
*/
function get_details ( $mail_id ) {
2017-08-15 14:11:57 +00:00
global $db , $msg , $hooks ;
2018-06-27 22:27:10 +00:00
$msg -> debug ( " mail " , " get_details " );
2015-09-25 15:42:00 +00:00
$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 :
2017-08-15 14:11:57 +00:00
$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 ));
2015-09-25 15:42:00 +00:00
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 ;
2012-08-25 14:04:38 +00:00
}
2015-09-25 15:42:00 +00:00
private $isitmy_cache = array ();
2017-10-06 21:42:39 +00:00
/**
* Check if an email is mine ...
2015-09-25 15:42:00 +00:00
*
* @ param $mail_id integer the number of the email to check
* @ return string the complete email address if that ' s mine , false if not
2017-08-15 14:11:57 +00:00
* ( $msg is filled accordingly )
2015-09-25 15:42:00 +00:00
*/
function is_it_my_mail ( $mail_id ) {
2017-08-15 14:11:57 +00:00
global $msg , $db , $cuid ;
2015-09-25 15:42:00 +00:00
$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 ];
}
2016-05-17 16:57:01 +00:00
$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 ));
2015-09-25 15:42:00 +00:00
if ( $db -> next_record ()) {
return $this -> isitmy_cache [ $mail_id ] = $db -> f ( " email " );
} else {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " This email is not yours, you can't change anything on it " ));
2015-09-25 15:42:00 +00:00
return $this -> isitmy_cache [ $mail_id ] = false ;
}
2015-06-17 17:56:51 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* Hook called when the DOMAIN class will delete a domain .
2017-08-12 14:31:46 +00:00
* OR when the DOMAIN class tells us we don ' t host the emails of this domain anymore .
* @ param $dom the ID of the domain to delete
2017-08-15 14:11:57 +00:00
* @ return boolean if the email has been properly deleted
2017-08-21 15:50:57 +00:00
* or false if an error occured ( $msg is filled accordingly )
2015-09-25 15:42:00 +00:00
*/
function hook_dom_del_mx_domain ( $dom_id ) {
global $db ;
$list = $this -> enum_domain_mails ( $dom_id , " " , 0 , - 1 );
if ( is_array ( $list )) {
foreach ( $list as $one ) {
$this -> delete ( $one [ " id " ]);
}
}
2017-01-24 16:52:29 +00:00
$db -> query ( " SELECT domaine FROM domaines WHERE id= ? ; " , array ( $dom_id ));
2018-10-23 17:21:22 +00:00
if ( ! $db -> next_record ()) {
return false ;
2015-09-25 15:42:00 +00:00
}
2018-10-23 17:21:22 +00:00
$domain = $db -> Record [ " domaine " ];
$db -> query ( " UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND (type='defmx' OR type='defmx2'); " , array ( $domain ));
$this -> del_dns_dmarc ( $domain );
$this -> del_dns_spf ( $domain );
2018-10-23 17:27:54 +00:00
$this -> del_dns_autoconf ( $domain );
2018-10-23 17:21:22 +00:00
$this -> dkim_del ( $domain );
2018-07-08 20:03:27 +00:00
2018-10-23 17:21:22 +00:00
$db -> query ( " UPDATE domaines SET dns_action='UPDATE' WHERE id= ? ; " , array ( $dom_id ));
2015-09-25 15:42:00 +00:00
return true ;
2012-08-24 13:37:56 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* return the alternc account ' s ID of the mail_id
*/
2015-09-25 15:42:00 +00:00
function get_account_by_mail_id ( $mail_id ) {
global $db ;
2016-05-17 16:57:01 +00:00
$db -> query ( " select compte as uid from domaines d, address a where a.domain_id = d.id and a.id = ? ; " , array ( $mail_id ));
2015-09-25 15:42:00 +00:00
if ( ! $db -> next_record ()) {
return false ;
}
return $db -> f ( 'uid' );
2012-08-24 13:37:56 +00:00
}
2013-02-07 15:48:28 +00:00
2017-10-06 21:42:39 +00:00
/**
* Function used to delete a mail from the db
2015-09-25 15:42:00 +00:00
* 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
2017-08-15 14:11:57 +00:00
* or false if an error occured ( $msg is filled accordingly )
2015-09-25 15:42:00 +00:00
*/
function delete ( $mail_id ) {
2017-08-15 14:11:57 +00:00
global $msg , $db , $hooks ;
$msg -> log ( " mail " , " delete " );
2015-09-25 15:42:00 +00:00
$mail_id = intval ( $mail_id );
if ( ! $mail_id ) {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " The email you entered is syntaxically incorrect " ));
2015-09-25 15:42:00 +00:00
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:
2016-05-17 16:57:01 +00:00
$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 ));
2015-09-25 15:42:00 +00:00
if ( ! $db -> next_record ()) {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " The email %s does not exist, it can't be deleted " ), $mail );
2015-09-25 15:42:00 +00:00
return false ;
}
if ( $db -> f ( " mail_action " ) != " OK " || ( $db -> f ( " islocal " ) && $db -> f ( " mailbox_action " ) != " OK " )) { // will be deleted soon ...
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " The email %s is already marked for deletion, it can't be deleted " ), $mail );
2015-09-25 15:42:00 +00:00
return false ;
}
$mail_id = $db -> f ( " id " );
if ( $db -> f ( " islocal " )) {
// If it's a pop/imap mailbox, mark it for deletion
2016-05-17 16:57:01 +00:00
$db -> query ( " UPDATE address SET mail_action='DELETE', enabled=0 WHERE id= ?; " , array ( $mail_id ));
$db -> query ( " UPDATE mailbox SET mail_action='DELETE' WHERE address_id= ?; " , array ( $mail_id ));
2015-09-25 15:42:00 +00:00
} else {
// If it's only aliases, delete it NOW.
2016-05-17 16:57:01 +00:00
$db -> query ( " DELETE FROM address WHERE id= ? ; " , array ( $mail_id ));
$db -> query ( " DELETE FROM mailbox WHERE address_id= ? ; " , array ( $mail_id ));
$db -> query ( " DELETE FROM recipient WHERE address_id= ? ; " , array ( $mail_id ));
2015-09-25 15:42:00 +00:00
}
return true ;
2012-08-24 13:37:56 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* Function used to undelete a pending deletion mail from the db
2015-09-25 15:42:00 +00:00
* 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
2017-08-15 14:11:57 +00:00
* or false if an error occured ( $msg is filled accordingly )
2015-09-25 15:42:00 +00:00
*/
function undelete ( $mail_id ) {
2017-08-15 14:11:57 +00:00
global $msg , $db ;
$msg -> log ( " mail " , " undelete " );
2015-09-25 15:42:00 +00:00
$mail_id = intval ( $mail_id );
if ( ! $mail_id ) {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " The email you entered does not exist " ));
2015-09-25 15:42:00 +00:00
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:
2016-05-17 16:57:01 +00:00
$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 ));
2015-09-25 15:42:00 +00:00
if ( ! $db -> next_record ()) {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " The email %s does not exist, it can't be undeleted " ), $mail );
2015-09-25 15:42:00 +00:00
return false ;
}
if ( $db -> f ( " type " ) != " " ) { // Technically special : mailman, sympa ...
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " The email %s is special, it can't be undeleted " ), $mail );
2015-09-25 15:42:00 +00:00
return false ;
}
if ( $db -> f ( " mailbox_action " ) != " DELETE " || $db -> f ( " mail_action " ) != " DELETE " ) { // will be deleted soon ...
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ALERT " , " mail " , _ ( " Sorry, deletion of email %s is already in progress, or not marked for deletion, it can't be undeleted " ), $mail );
2015-09-25 15:42:00 +00:00
return false ;
}
$mail_id = $db -> f ( " id " );
if ( $db -> f ( " islocal " )) {
// If it's a pop/imap mailbox, mark it for deletion
2016-05-17 16:57:01 +00:00
$db -> query ( " UPDATE address SET mail_action='OK', `enabled`=1 WHERE id= ?; " , array ( $mail_id ));
$db -> query ( " UPDATE mailbox SET mail_action='OK' WHERE address_id= ? ; " , array ( $mail_id ));
2015-09-25 15:42:00 +00:00
return true ;
} else {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " -- Program Error -- The email %s can't be undeleted " ), $mail );
2015-09-25 15:42:00 +00:00
return false ;
}
2012-08-24 13:37:56 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* set the password of an email address .
2015-09-25 15:42:00 +00:00
* @ 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 .
*/
2017-08-15 14:11:57 +00:00
function set_passwd ( $mail_id , $pass , $canbeempty = false ) {
global $db , $msg , $admin ;
$msg -> log ( " mail " , " setpasswd " );
2015-09-25 15:42:00 +00:00
if ( ! ( $email = $this -> is_it_my_mail ( $mail_id ))) {
return false ;
}
2017-08-15 14:11:57 +00:00
if ( ! $admin -> checkPolicy ( " pop " , $email , $pass , $canbeempty )) {
2015-09-25 15:42:00 +00:00
return false ;
}
2017-09-12 12:49:33 +00:00
if ( $canbeempty && empty ( $pass )) {
2018-04-17 01:02:45 +00:00
return $db -> query ( " UPDATE address SET password= ? where id = ? ; " ,
array ( null , $mail_id ));
} else if ( ! $db -> query ( " UPDATE address SET password= ? where id = ? ; " ,
array ( _dovecot_hash ( $pass ), $mail_id ))) {
2015-09-25 15:42:00 +00:00
return false ;
}
return true ;
2012-08-24 13:37:56 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* Enables an email address .
2015-09-25 15:42:00 +00:00
* @ param $mail_id integer Email ID
* @ return boolean true if the email has been enabled .
*/
function enable ( $mail_id ) {
2017-08-15 14:11:57 +00:00
global $db , $msg ;
$msg -> log ( " mail " , " enable " );
2015-09-25 15:42:00 +00:00
if ( ! ( $email = $this -> is_it_my_mail ( $mail_id ))) {
return false ;
}
2016-05-17 16:57:01 +00:00
if ( ! $db -> query ( " UPDATE address SET `enabled`=1 where id= ? ; " , array ( $mail_id ))) {
2015-09-25 15:42:00 +00:00
return false ;
}
return true ;
2012-08-24 13:37:56 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* Disables an email address .
2015-09-25 15:42:00 +00:00
* @ param $mail_id integer Email ID
* @ return boolean true if the email has been enabled .
*/
function disable ( $mail_id ) {
2017-08-15 14:11:57 +00:00
global $db , $msg ;
$msg -> log ( " mail " , " disable " );
2015-09-25 15:42:00 +00:00
if ( ! ( $email = $this -> is_it_my_mail ( $mail_id ))) {
return false ;
}
2016-05-17 16:57:01 +00:00
if ( ! $db -> query ( " UPDATE address SET `enabled`=0 where id= ? ; " , array ( $mail_id ))) {
2015-09-25 15:42:00 +00:00
return false ;
}
return true ;
2012-08-24 13:37:56 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* Function used to update an email settings
2015-09-25 15:42:00 +00:00
* 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
2017-08-15 14:11:57 +00:00
* or false if an error occured ( $msg is filled accordingly )
2015-09-25 15:42:00 +00:00
*/
function set_details ( $mail_id , $islocal , $quotamb , $recipients , $delivery = " dovecot " , $dontcheck = false ) {
2017-08-15 14:11:57 +00:00
global $msg , $db ;
$msg -> log ( " mail " , " set_details " );
2015-09-25 15:42:00 +00:00
if ( ! ( $me = $this -> get_details ( $mail_id ))) {
return false ;
}
if ( $me [ " islocal " ] && ! $islocal ) {
// delete pop
2016-05-17 16:57:01 +00:00
$db -> query ( " UPDATE mailbox SET mail_action='DELETE' WHERE address_id= ? ; " , array ( $mail_id ));
2015-09-25 15:42:00 +00:00
}
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 ) {
2017-10-06 16:04:36 +00:00
$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 " ));
2015-09-25 15:42:00 +00:00
return false ;
}
}
foreach ( $this -> specialchars as $str ) {
if ( strpos ( $me [ " address " ], $str ) !== false ) {
$path = ALTERNC_MAIL . " /_/ " . $me [ " id " ] . " _ " . $me [ " domain " ];
break ;
}
}
2016-05-17 16:57:01 +00:00
$db -> query ( " INSERT INTO mailbox SET address_id= ? , delivery= ?, path= ? ; " , array ( $mail_id , $delivery , $path ));
2015-09-25 15:42:00 +00:00
}
if ( $me [ " islocal " ] && $islocal && $me [ " mailbox_action " ] == " DELETE " ) {
2016-05-17 16:57:01 +00:00
$db -> query ( " UPDATE mailbox SET mail_action='OK' WHERE mail_action='DELETE' AND address_id= ? ; " , array ( $mail_id ));
2015-09-25 15:42:00 +00:00
}
if ( $islocal ) {
if ( $quotamb != 0 && $quotamb < ( intval ( $me [ " used " ] / 1024 / 1024 ) + 1 )) {
$quotamb = intval ( $me [ " used " ] / 1024 / 1024 ) + 1 ;
2017-10-06 16:04:36 +00:00
$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 " ));
2015-09-25 15:42:00 +00:00
}
2016-05-17 16:57:01 +00:00
$db -> query ( " UPDATE mailbox SET quota= ? WHERE address_id= ? ; " , array ( $quotamb , $mail_id ));
2015-09-25 15:42:00 +00:00
}
$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
2017-10-06 21:42:39 +00:00
&& $m != ( $me [ " address " ] . " @ " . $me [ " domain " ])) { // And not myself (no loop allowed easily ;) )
2015-09-25 15:42:00 +00:00
$red .= $m . " \n " ;
}
}
2016-05-17 16:57:01 +00:00
$db -> query ( " DELETE FROM recipient WHERE address_id= ? ; " , array ( $mail_id ));
2015-09-25 15:42:00 +00:00
if ( isset ( $red ) && $red ) {
2016-05-17 16:57:01 +00:00
$db -> query ( " INSERT INTO recipient SET address_id= ?, recipients= ? ; " , array ( $mail_id , $red ));
2015-09-25 15:42:00 +00:00
}
2017-10-06 21:42:39 +00:00
if ( ! $islocal && ! $red ) {
$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. " ));
}
2015-09-25 15:42:00 +00:00
return true ;
2012-08-24 13:37:56 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* A wrapper used by mailman class to create it ' s needed addresses
2015-09-25 15:42:00 +00:00
* @ 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 ) {
2017-08-15 14:11:57 +00:00
global $msg , $mail ;
$msg -> log ( " mail " , " add_wrapper " , " creating $delivery $m address " );
2015-09-25 15:42:00 +00:00
$mail_id = $mail -> create ( $dom_id , $m , $delivery );
$this -> set_details ( $mail_id , 1 , 0 , '' , $delivery );
// FIXME return error code
2012-08-24 13:37:56 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* A function used to create an alias for a specific address
2015-09-25 15:42:00 +00:00
* @ 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 ) {
2017-08-15 14:11:57 +00:00
global $msg , $mail ;
$msg -> log ( " mail " , " create_alias " , " creating $m alias for $alias type $type " );
2015-09-25 15:42:00 +00:00
$mail_id = $mail -> create ( $dom_id , $m , $type , $dontcheck );
if ( ! $mail_id ) {
return false ;
}
$this -> set_details ( $mail_id , 0 , 0 , $alias , " dovecot " , $dontcheck );
return true ;
2012-08-24 13:37:56 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* A wrapper used by mailman class to create it ' s needed addresses
2015-09-25 15:42:00 +00:00
* @ 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 ) {
2017-08-15 14:11:57 +00:00
global $msg ;
$msg -> log ( " mail " , " del_wrapper " );
2015-09-25 15:42:00 +00:00
$this -> delete ( $mail_id );
2012-08-24 18:21:00 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* Export the mail information of an account
2015-09-25 15:42:00 +00:00
* @ return : str , string containing the complete configuration
* of the email for the current acccount .
*/
function alternc_export_conf () {
2017-08-15 14:11:57 +00:00
global $msg ;
$msg -> log ( " mail " , " export " );
2015-09-25 15:42:00 +00:00
$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 ;
2012-08-24 18:21:00 +00:00
}
2015-09-25 15:42:00 +00:00
/**
* Return the list of allowed slave accounts ( secondary - mx )
* @ return array
*/
function enum_slave_account () {
global $db ;
$db -> query ( " SELECT login,pass FROM mxaccount; " );
$res = array ();
while ( $db -> next_record ()) {
$res [] = $db -> Record ;
}
if ( ! count ( $res )) {
return false ;
}
return $res ;
2012-08-24 18:21:00 +00:00
}
2015-09-25 15:42:00 +00:00
/**
* 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 ;
2016-05-17 16:57:01 +00:00
$db -> query ( " SELECT * FROM mxaccount WHERE login= ? AND pass= ?; " , array ( $login , $pass ));
2015-09-25 15:42:00 +00:00
if ( $db -> next_record ()) {
return true ;
}
return false ;
2012-08-24 18:21:00 +00:00
}
2015-09-25 15:42:00 +00:00
2017-10-06 21:42:39 +00:00
/**
* Out ( echo ) the complete hosted domain list :
2015-09-25 15:42:00 +00:00
*/
function echo_domain_list ( $format = null ) {
global $db ;
$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 " );
}
2017-10-06 21:42:39 +00:00
// Generate an integrity check
2015-09-25 15:42:00 +00:00
$obj = array ( 'integrity' => md5 ( $tt ), 'items' => $lst );
switch ( $format ) {
2017-10-06 21:42:39 +00:00
case " json " :
return json_encode ( $obj );
default :
foreach ( $lst as $l ) {
echo $l . " \n " ;
}
return true ;
2015-09-25 15:42:00 +00:00
} // switch
2012-08-24 18:21:00 +00:00
}
2015-09-25 15:42:00 +00:00
/**
* 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 ) {
2017-08-15 14:11:57 +00:00
global $db , $msg ;
2016-05-17 16:57:01 +00:00
$db -> query ( " SELECT * FROM mxaccount WHERE login= ? ; " , array ( $login ));
2015-09-25 15:42:00 +00:00
if ( $db -> next_record ()) {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " The slave MX account was not found " ));
2015-09-25 15:42:00 +00:00
return false ;
}
2016-05-17 16:57:01 +00:00
$db -> query ( " INSERT INTO mxaccount (login,pass) VALUES (?, ?); " , array ( $login , $pass ));
2015-09-25 15:42:00 +00:00
return true ;
2012-08-24 18:21:00 +00:00
}
2015-09-25 15:42:00 +00:00
/**
* Remove a slave account
* @ param string $login the login to delete
*/
function del_slave_account ( $login ) {
global $db ;
2016-05-17 16:57:01 +00:00
$db -> query ( " DELETE FROM mxaccount WHERE login= ? ; " , array ( $login ));
2015-09-25 15:42:00 +00:00
return true ;
2012-08-31 16:00:47 +00:00
}
2015-09-25 15:42:00 +00:00
2018-07-08 20:03:27 +00:00
// ------------------------------------------------------------
2017-10-06 21:42:39 +00:00
/**
* hook function called by AlternC when a domain is created for
2015-09-25 15:42:00 +00:00
* 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 ) {
2017-08-15 14:11:57 +00:00
global $msg ;
$msg -> log ( " mail " , " hook_dom_add_slave_domain " , $domain_id );
2015-09-25 15:42:00 +00:00
$this -> catchall_set ( $domain_id , '@' . $target_domain );
2012-08-31 16:00:47 +00:00
return true ;
}
2015-09-25 15:42:00 +00:00
2018-07-08 20:03:27 +00:00
// ------------------------------------------------------------
2017-10-06 21:42:39 +00:00
/**
* hook function called by AlternC when a domain is created for
2015-09-25 15:42:00 +00:00
* 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 ) {
2018-07-08 20:03:27 +00:00
global $msg , $mem , $db , $L_FQDN ;
2017-08-15 14:11:57 +00:00
$msg -> log ( " mail " , " hook_dom_add_mx_domain " , $domain_id );
2015-09-25 15:42:00 +00:00
$db -> query ( " SELECT value FROM variable where name='mailname_bounce'; " );
if ( ! $db -> next_record ()) {
2017-10-06 16:04:36 +00:00
$msg -> raise ( " ERROR " , " mail " , _ ( " Problem: can't create default bounce mail " ));
2015-09-25 15:42:00 +00:00
return false ;
}
$mailname = $db -> f ( " value " );
// set spf & dmarc for this domain
2018-07-08 20:03:27 +00:00
$db -> query ( " SELECT domaine,compte FROM domaines WHERE id= ?; " , array ( $domain_id ));
2015-09-25 15:42:00 +00:00
if ( $db -> next_record ()) {
2018-07-08 20:03:27 +00:00
$this -> set_dns_autoconf ( $db -> Record [ " domaine " ], $db -> Record [ " compte " ]);
2015-09-25 15:42:00 +00:00
if ( $spf = variable_get ( " default_spf_value " )) {
$this -> set_dns_spf ( $db -> Record [ " domaine " ], $spf );
}
if ( $dmarc = variable_get ( " default_dmarc_value " )) {
$this -> set_dns_dmarc ( $db -> Record [ " domaine " ], $dmarc );
}
}
return $this -> create_alias ( $domain_id , 'postmaster' , $mem -> user [ 'login' ] . '@' . $mailname );
2013-04-23 12:58:59 +00:00
}
2015-09-25 15:42:00 +00:00
2018-07-08 20:03:27 +00:00
// ------------------------------------------------------------
2017-10-06 21:42:39 +00:00
/**
* hook function called by variables when a variable is changed
2015-09-25 15:42:00 +00:00
* @ access private
*/
function hook_variable_set ( $name , $old , $new ) {
2017-08-15 14:11:57 +00:00
global $msg , $db ;
2018-07-08 20:03:27 +00:00
$msg -> log ( " mail " , " hook_variable_set( $name , $old , $new ) " );
2015-09-25 15:42:00 +00:00
if ( $name == " default_spf_value " ) {
$new = trim ( $new );
$old = trim ( $old );
$db -> query ( " SELECT domaine,login,compte FROM domaines, membres WHERE gesdns=1 AND gesmx=1 and membres.uid=domaines.compte; " );
2016-08-26 13:51:01 +00:00
$res = array ();
2016-03-02 13:17:32 +00:00
while ( $db -> next_record ()) $res [] = $db -> Record ;
foreach ( $res as $record ) {
$this -> set_dns_spf ( $record [ " domaine " ], $new , $old , $record [ " compte " ], $record [ " login " ]);
2015-09-25 15:42:00 +00:00
}
}
2013-04-23 12:58:59 +00:00
2015-09-25 15:42:00 +00:00
if ( $name == " default_dmarc_value " ) {
$new = trim ( $new );
$old = trim ( $old );
$db -> query ( " SELECT domaine,login,compte FROM domaines, membres WHERE gesdns=1 AND gesmx=1 and membres.uid=domaines.compte; " );
2016-08-26 13:51:01 +00:00
$res = array ();
2016-03-02 13:17:32 +00:00
while ( $db -> next_record ()) $res [] = $db -> Record ;
foreach ( $res as $record ) {
$this -> set_dns_dmarc ( $record [ " domaine " ], $new , $old , $record [ " compte " ], $record [ " login " ]);
2015-09-25 15:42:00 +00:00
}
}
2012-08-31 16:00:47 +00:00
}
2015-09-25 15:42:00 +00:00
2018-07-08 20:03:27 +00:00
// ------------------------------------------------------------
/**
* Add dns entries for autodiscover / autoconf on the domain
*/
function set_dns_autoconf ( $domain , $uid =- 1 ) {
global $db , $L_FQDN , $cuid ;
$changed = false ;
if ( $uid ==- 1 ) $uid = $cuid ;
$db -> query ( " SELECT domaine,sub,type,valeur FROM sub_domaines WHERE domaine=? AND sub='autodiscover' AND type='autodiscover'; " , array ( $domain ));
if ( ! $db -> next_record ()) {
2018-07-17 15:39:14 +00:00
$db -> query ( " INSERT INTO sub_domaines SET domaine=?, compte=?, sub='autodiscover', type='autodiscover', valeur=''; " , array ( $domain , $uid ));
2018-07-08 20:03:27 +00:00
$changed = true ;
}
$db -> query ( " SELECT domaine,sub,type,valeur FROM sub_domaines WHERE domaine=? AND sub='autoconfig' AND type='autodiscover'; " , array ( $domain ));
if ( ! $db -> next_record ()) {
2018-07-17 15:39:14 +00:00
$db -> query ( " INSERT INTO sub_domaines SET domaine=?, compte=?, sub='autoconfig', type='autodiscover', valeur=''; " , array ( $domain , $uid ));
2018-07-08 20:03:27 +00:00
$changed = true ;
}
if ( $changed ) {
$db -> query ( " UPDATE domaines SET dns_action='UPDATE' WHERE domaine= ?; " , array ( $domain ));
}
return $changed ;
}
2015-09-25 15:42:00 +00:00
2018-10-23 17:27:54 +00:00
// ------------------------------------------------------------
/**
* delete the autoconf / autodiscover vhosts when removing a domain as MX
*/
function del_dns_autoconf ( $domain ) {
global $db , $L_FQDN , $cuid ;
$db -> query ( " UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='autodiscover' AND sub='autoconfig'; " , array ( $domain ));
$db -> query ( " UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='autodiscover' AND sub='autodiscover'; " , array ( $domain ));
}
2018-07-08 20:03:27 +00:00
// ------------------------------------------------------------
2017-10-06 21:42:39 +00:00
/**
* Set or UPDATE the DNS record for the domain $dom ( str ) to be $spf
2015-09-25 15:42:00 +00:00
* account 's login is current and if not it' s $login .
* don ' t change spf if current value is not $old
* @ access private
*/
function set_dns_spf ( $domain , $spf , $previous = - 1 , $uid = - 1 , $login = - 1 ) {
global $db , $cuid , $mem ;
// defaults
if ( $uid === - 1 ) {
$uid = intval ( $cuid );
} else {
$uid = intval ( $uid );
}
if ( $login === - 1 ) {
$login = $mem -> user [ " login " ];
}
// Search for the record in sub_domaines table
2016-05-17 16:57:01 +00:00
$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 ));
2015-09-25 15:42:00 +00:00
if ( $db -> next_record ()) {
if ( $previous !== - 1 && $db -> Record [ " valeur " ] == " v=spf1 " . $spf ) {
return ; // skip, no change asked.
}
2016-05-17 16:57:01 +00:00
$db -> query ( " UPDATE sub_domaines SET web_action='DELETE' WHERE id= ? ; " , array ( $db -> Record [ " id " ]));
2015-09-25 15:42:00 +00:00
}
2016-05-17 16:57:01 +00:00
$db -> query ( " INSERT INTO sub_domaines SET compte= ?, domaine= ?, sub='', type='txt', valeur= ? , web_action='UPDATE'; " , array ( $uid , $domain , " v=spf1 " . $spf ));
$db -> query ( " UPDATE domaines SET dns_action='UPDATE' WHERE domaine= ?; " , array ( $domain ));
2015-09-25 15:42:00 +00:00
}
2018-10-23 17:21:22 +00:00
// ------------------------------------------------------------
/**
* delete the SPF entries in the sub_domaine table for a domain
* called by del_domain or del_mx_domain by hooks :
*/
function del_dns_spf ( $domain ) {
global $db ;
$db -> query ( " UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='txt' AND sub='' AND valeur LIKE 'v=spf1 %'; " , array ( $domain ));
}
2015-09-25 15:42:00 +00:00
2018-07-08 20:03:27 +00:00
// ------------------------------------------------------------
2017-10-06 21:42:39 +00:00
/**
* Set or UPDATE the DNS record for the domain $dom ( str ) to be $dmarc
2015-09-25 15:42:00 +00:00
* account 's login is current and if not it' s $login .
* don ' t change dmarc if current value is not $old
* @ access private
*/
function set_dns_dmarc ( $domain , $dmarc , $previous = - 1 , $uid = - 1 , $login = - 1 ) {
global $db , $cuid , $mem , $L_FQDN ;
// defaults
if ( $uid === - 1 ) {
$uid = intval ( $cuid );
} else {
$uid = intval ( $uid );
}
if ( $login === - 1 ) {
$login = $mem -> user [ " login " ];
}
$dmarc = str_replace ( " %%ADMINMAIL%% " , " admin@ " . $L_FQDN , $dmarc );
$dmarc = str_replace ( " %%USERMAIL%% " , $login . " @ " . $L_FQDN , $dmarc );
// Search for the record in sub_domaines table
2016-05-17 16:57:01 +00:00
$db -> query ( " SELECT * FROM sub_domaines WHERE compte= ? AND domaine= ? AND sub='_dmarc' AND type='txt' AND valeur LIKE 'v=dmarc1;%' AND web_action!='DELETE'; " , array ( $uid , $domain ));
2015-09-25 15:42:00 +00:00
if ( $db -> next_record ()) {
if ( $previous !== - 1 && $db -> Record [ " valeur " ] == " v=dmarc1; " . $dmarc ) {
return ; // skip, no change asked.
}
2016-05-17 16:57:01 +00:00
$db -> query ( " UPDATE sub_domaines SET web_action='DELETE' WHERE id= ?; " , array ( $db -> Record [ " id " ]));
2015-09-25 15:42:00 +00:00
}
2016-05-17 16:57:01 +00:00
$db -> query ( " INSERT INTO sub_domaines SET compte= ?, domaine= ?, sub='_dmarc', type='txt', valeur= ?, web_action='UPDATE'; " , array ( $uid , $domain , " v=dmarc1; " . $dmarc ));
$db -> query ( " UPDATE domaines SET dns_action='UPDATE' WHERE domaine= ?; " , array ( $domain ));
2013-03-14 08:41:43 +00:00
}
2015-09-25 15:42:00 +00:00
2018-10-23 17:21:22 +00:00
// ------------------------------------------------------------
/**
* delete the DMARC entries in the sub_domaine table for a domain
* called by del_domain or del_mx_domain by hooks :
*/
function del_dns_dmarc ( $domain ) {
global $db ;
$db -> query ( " UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='txt' AND sub='' AND valeur LIKE 'v=dmarc1 %'; " , array ( $domain ));
}
2018-07-08 20:03:27 +00:00
/** Manage DKIM when adding / removing a domain MX management */
var $shouldreloaddkim ;
// ------------------------------------------------------------
/**
* Hook launched before doing anything dns - related
*/
function hook_updatedomains_dns_pre () {
global $db ;
// for each domain where we don't have the MX or the DNS, remove the DKIM setup
$this -> shouldreloaddkim = false ;
2018-07-17 14:59:54 +00:00
$db -> query ( " SELECT domaine,compte,gesdns,gesmx FROM domaines WHERE dns_action!='OK'; " );
2018-07-08 20:03:27 +00:00
$add = array ();
$del = array ();
while ( $db -> next_record ()) {
if ( $db -> Record [ " gesdns " ] == 0 || $db -> Record [ " gesmx " ] == 0 ) {
2018-07-17 14:59:54 +00:00
$del [] = $db -> Record ;
2018-07-08 20:03:27 +00:00
} else {
2018-07-17 14:59:54 +00:00
$add [] = $db -> Record ;
2018-07-08 20:03:27 +00:00
}
}
foreach ( $add as $domain ) {
2018-07-17 14:59:54 +00:00
$this -> dkim_add ( $domain [ " domaine " ], $domain [ " compte " ]);
2018-07-08 20:03:27 +00:00
}
foreach ( $del as $domain ) {
2018-10-23 17:21:22 +00:00
$this -> dkim_del ( $domain [ " domaine " ]);
2018-07-08 20:03:27 +00:00
}
}
// ------------------------------------------------------------
/**
* Hook launched after doing anything dns - related
*/
function hook_updatedomains_dns_post () {
if ( $this -> shouldreloaddkim ) {
exec ( " service opendkim reload " );
$this -> shouldreloaddkim = false ;
}
}
// ------------------------------------------------------------
/**
* Add a domain into OpenDKIM configuration
*/
2018-07-17 14:59:54 +00:00
function dkim_add ( $domain , $uid ) {
2018-07-08 20:03:27 +00:00
global $db ;
$target_dir = " /etc/opendkim/keys/ $domain " ;
2018-07-18 09:02:50 +00:00
// Create a dkim key when it's not already there :
if ( ! file_exists ( $target_dir . '/alternc.txt' )) {
$this -> shouldreloaddkim = true ;
if ( ! is_dir ( $target_dir )) mkdir ( $target_dir ); // create dir
// Generate the key, 1200 bits (better than 1024)
$old_dir = getcwd ();
chdir ( $target_dir );
exec ( 'opendkim-genkey -b 1200 -r -d ' . escapeshellarg ( $domain ) . ' -s "alternc" ' );
chdir ( $old_dir );
// opendkim must be owner of the key
chown ( " $target_dir /alternc.private " , 'opendkim' );
chgrp ( " $target_dir /alternc.private " , 'opendkim' );
add_line_to_file ( " /etc/opendkim/KeyTable " , " alternc._domainkey. " . $domain . " " . $domain . " :alternc:/etc/opendkim/keys/ " . $domain . " /alternc.private " );
add_line_to_file ( " /etc/opendkim/SigningTable " , $domain . " alternc._domainkey. " . $domain );
}
// Search for the subdomain entry, if it's not already there, create it:
$db -> query ( " SELECT id FROM sub_domaines WHERE domaine=? AND sub='alternc._domainkey'; " , array ( $domain ));
if ( ! $db -> next_record ()) {
// Add subdomaine entry
$dkim_key = $this -> dkim_get_entry ( $domain );
$db -> query ( " INSERT INTO sub_domaines SET domaine=?, compte=?, sub='alternc._domainkey', type='dkim', valeur=?; " , array ( $domain , $uid , $dkim_key ));
// no need to do DNS_ACTION="UPDATE" => we are in the middle of a HOOK, so dns WILL BE reloaded for this domain
}
2018-07-08 20:03:27 +00:00
}
// ------------------------------------------------------------
/**
* Delete a domain from OpenDKIM configuration
*/
2018-10-23 17:21:22 +00:00
function dkim_del ( $domain ) {
2018-10-23 17:23:14 +00:00
global $db ;
2018-07-08 20:03:27 +00:00
$target_dir = " /etc/opendkim/keys/ $domain " ;
if ( file_exists ( $target_dir )) {
$this -> shouldreloaddkim = true ;
@ unlink ( " $target_dir /alternc_private " );
@ unlink ( " $target_dir /alternc.txt " );
@ rmdir ( $target_dir );
del_line_from_file ( " /etc/opendkim/KeyTable " , " alternc._domainkey. " . $domain . " " . $domain . " :alternc:/etc/opendkim/keys/ " . $domain . " /alternc.private " );
del_line_from_file ( " /etc/opendkim/SigningTable " , $domain . " alternc._domainkey. " . $domain );
}
2018-07-18 09:02:50 +00:00
$db -> query ( " DELETE FROM sub_domaines WHERE domaine=? AND sub='alternc._domainkey'; " , array ( $domain ));
// No need to do DNS_ACTION="UPDATE" => we are in the middle of a HOOK
2018-07-08 20:03:27 +00:00
}
// ------------------------------------------------------------
/**
* return the content of the TXT information to be added into the DB for DKIM subdomains
* @ param $domain string the name of the domain name
* @ return string the TXT entry ( without quotes )
* or false if an error occurred
**/
function dkim_get_entry ( $domain ) {
2018-07-17 15:39:14 +00:00
global $msg ;
2018-07-08 20:03:27 +00:00
$key = file_get_contents ( " /etc/opendkim/keys/ " . $domain . " /alternc.txt " );
2018-07-17 15:39:14 +00:00
// easy: monoline key
2018-07-08 20:03:27 +00:00
if ( preg_match ( '#alternc._domainkey IN TXT "(.*)"#' , $key , $mat )) {
return $mat [ 1 ];
2018-07-17 15:39:14 +00:00
} else {
// Need to parse a multiligne key:
$inkey = false ; $result = " " ;
$lines = explode ( " \n " , $key );
foreach ( $lines as $line ) {
2018-07-17 15:48:15 +00:00
if ( preg_match ( '#alternc._domainkey\s+IN\s+TXT\s+\( "(.*)"#' , $line , $mat )) {
2018-07-17 15:39:14 +00:00
$result .= $mat [ 1 ]; $inkey = true ; continue ;
}
2018-07-17 15:48:15 +00:00
if ( $inkey && preg_match ( '#^\s*"(.*)"\s*\)#' , $line , $mat )) {
2018-07-17 15:39:14 +00:00
$result .= $mat [ 1 ]; $inkey = false ; break ;
}
2018-07-17 15:48:15 +00:00
if ( $inkey && preg_match ( '#^\s*"(.*)"\s*$#' , $line , $mat )) {
2018-07-17 15:39:14 +00:00
$result .= $mat [ 1 ]; $inkey = true ; continue ;
}
}
if ( $result )
return $result ;
2018-07-08 20:03:27 +00:00
}
2018-07-17 15:39:14 +00:00
$msg -> debug ( " mail " , " dkim_get_entry( $domain ) failed " );
2018-07-08 20:03:27 +00:00
return false ;
}
// @TODO hook after reloading DNS zones => if necessary, restart opendkim
2012-04-06 10:10:36 +00:00
2017-10-06 21:42:39 +00:00
} /* Class m_mail */