mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-11 17:20:53 +01:00
565 lines
19 KiB
PHP
565 lines
19 KiB
PHP
<?php
|
|
/**
|
|
* EGroupware EMailAdmin: generic base class for SMTP configuration via LDAP
|
|
*
|
|
* @link http://www.stylite.de
|
|
* @package emailadmin
|
|
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
|
* @copyright (c) 2010-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
* @version $Id$
|
|
*/
|
|
|
|
/**
|
|
* Generic base class for SMTP configuration via LDAP
|
|
*
|
|
* This class uses just inetOrgPerson schema to store primary mail address and aliases
|
|
*
|
|
* Aliases are stored as aditional mail Attributes. The primary mail address is the first one.
|
|
* This schema does NOT support forwarding or disabling of an account for mail.
|
|
*
|
|
* Please do NOT copy this class! Extend it and set the constants different
|
|
* (incl. protected config var as long as we can not require PHP5.3 for LSB).
|
|
*/
|
|
class emailadmin_smtp_ldap extends emailadmin_smtp
|
|
{
|
|
/**
|
|
* Name of schema, has to be in the right case!
|
|
*/
|
|
const SCHEMA = 'inetOrgPerson';
|
|
|
|
/**
|
|
* Attribute to enable mail for an account, OR false if existence of ALIAS_ATTR is enough for mail delivery
|
|
*/
|
|
const MAIL_ENABLE_ATTR = false;
|
|
|
|
/**
|
|
* Attribute for aliases OR false to use mail
|
|
*/
|
|
const ALIAS_ATTR = false;
|
|
|
|
/**
|
|
* Primary mail address required as an alias too: true or false
|
|
*/
|
|
const REQUIRE_MAIL_AS_ALIAS=false;
|
|
|
|
/**
|
|
* Attribute for forwards OR false if not possible
|
|
*/
|
|
const FORWARD_ATTR = false;
|
|
|
|
/**
|
|
* Attribute to only forward mail, OR false if not available
|
|
*/
|
|
const FORWARD_ONLY_ATTR = false;
|
|
|
|
/**
|
|
* Attribute for mailbox, to which mail gets delivered OR false if not supported
|
|
*/
|
|
const MAILBOX_ATTR = false;
|
|
|
|
/**
|
|
* Attribute for quota limit of user in MB
|
|
*/
|
|
const QUOTA_ATTR = false;
|
|
|
|
/**
|
|
* Log all LDAP writes / actions to error_log
|
|
*/
|
|
var $debug = false;
|
|
|
|
/**
|
|
* LDAP schema configuration
|
|
*
|
|
* Parent can NOT use constants direct as we have no late static binding in currenlty required PHP 5.2
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $config = array(
|
|
'schema' => self::SCHEMA,
|
|
'mail_enable_attr' => self::MAIL_ENABLE_ATTR,
|
|
'mail_enabled' => self::MAIL_ENABLED,
|
|
'alias_attr' => self::ALIAS_ATTR,
|
|
'require_mail_as_alias' => self::REQUIRE_MAIL_AS_ALIAS,
|
|
'forward_attr' => self::FORWARD_ATTR,
|
|
'forward_only_attr' => self::FORWARD_ONLY_ATTR,
|
|
'forward_only' => self::FORWARD_ONLY,
|
|
'mailbox_attr' => self::MAILBOX_ATTR,
|
|
'quota_attr' => self::QUOTA_ATTR,
|
|
'search_filter' => null,
|
|
'search_base' => null,
|
|
);
|
|
|
|
/**
|
|
* from here on implementation, please do NOT copy but extend it!
|
|
*/
|
|
|
|
/**
|
|
* Set ldap search filter for aliases and forwards
|
|
*
|
|
* @param string $filter
|
|
*/
|
|
function setFilter($filter)
|
|
{
|
|
$this->config['search_filter'] = $filter;
|
|
}
|
|
|
|
/**
|
|
* Set ldap search base for aliases and forwards
|
|
*
|
|
* @param string $base
|
|
*/
|
|
function setBase($base)
|
|
{
|
|
$this->config['search_base'] = $base;
|
|
}
|
|
|
|
/**
|
|
* Hook called on account creation
|
|
*
|
|
* @param array $_hookValues values for keys 'account_email', 'account_firstname', 'account_lastname', 'account_lid'
|
|
* @return boolean true on success, false on error writing to ldap
|
|
*/
|
|
function addAccount($_hookValues)
|
|
{
|
|
$mailLocalAddress = $_hookValues['account_email'] ? $_hookValues['account_email'] :
|
|
common::email_address($_hookValues['account_firstname'],
|
|
$_hookValues['account_lastname'],$_hookValues['account_lid'],$this->defaultDomain);
|
|
|
|
$ds = $GLOBALS['egw']->ldap->ldapConnect();
|
|
|
|
$filter = "uid=".$_hookValues['account_lid'];
|
|
|
|
if (!($sri = @ldap_search($ds,$GLOBALS['egw_info']['server']['ldap_context'],$filter)))
|
|
{
|
|
return false;
|
|
}
|
|
$allValues = ldap_get_entries($ds, $sri);
|
|
$accountDN = $allValues[0]['dn'];
|
|
$objectClasses = $allValues[0]['objectclass'];
|
|
unset($objectClasses['count']);
|
|
|
|
// add our mail schema, if not already set
|
|
if(!in_array($this->config['schema'],$objectClasses) && !in_array(strtolower($this->config['schema']),$objectClasses))
|
|
{
|
|
$objectClasses[] = $this->config['schema'];
|
|
}
|
|
// the new code for postfix+cyrus+ldap
|
|
$newData = array(
|
|
'mail' => $mailLocalAddress,
|
|
'objectclass' => $objectClasses
|
|
);
|
|
// does schema have explicit alias attribute AND require mail added as alias too
|
|
if ($this->config['alias_attr'] && $this->config['require_mail_as_alias'] && $this->config['alias_attr'])
|
|
{
|
|
$newData[$this->config['alias_attr']] = $mailLocalAddress;
|
|
}
|
|
// does schema support enabling/disabling mail via attribute
|
|
if ($this->config['mail_enable_attr'])
|
|
{
|
|
$newData[$this->config['mail_enable_attr']] = $this->config['mail_enabled'];
|
|
}
|
|
// does schema support an explicit mailbox name --> set it
|
|
if ($this->config['mailbox_attr'])
|
|
{
|
|
$newData[$this->config['mailbox_attr']] = self::mailbox_addr($_hookValues);
|
|
}
|
|
|
|
if (!($ret = ldap_mod_replace($ds, $accountDN, $newData)) || $this->debug)
|
|
{
|
|
error_log(__METHOD__.'('.array2string(func_get_args()).") --> ldap_mod_replace(,'$accountDN',".
|
|
array2string($newData).') returning '.array2string($ret).
|
|
(!$ret?' ('.ldap_error($ds).')':''));
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* Get all email addresses of an account
|
|
*
|
|
* @param string $_accountName
|
|
* @return array
|
|
*/
|
|
function getAccountEmailAddress($_accountName)
|
|
{
|
|
$emailAddresses = array();
|
|
$ds = $GLOBALS['egw']->ldap->ldapConnect();
|
|
$filter = sprintf("(&(uid=%s)(objectclass=posixAccount))",$_accountName);
|
|
$attributes = array('dn','mail',$this->config['alias_attr']);
|
|
$sri = @ldap_search($ds, $GLOBALS['egw_info']['server']['ldap_context'], $filter, $attributes);
|
|
|
|
if ($sri)
|
|
{
|
|
$realName = trim($GLOBALS['egw_info']['user']['account_firstname'] . (!empty($GLOBALS['egw_info']['user']['account_firstname']) ? ' ' : '') . $GLOBALS['egw_info']['user']['account_lastname']);
|
|
$allValues = ldap_get_entries($ds, $sri);
|
|
|
|
if(isset($allValues[0]['mail']))
|
|
{
|
|
foreach($allValues[0]['mail'] as $key => $value)
|
|
{
|
|
if ($key === 'count') continue;
|
|
|
|
$emailAddresses[] = array (
|
|
'name' => $realName,
|
|
'address' => $value,
|
|
'type' => !$key ? 'default' : 'alternate',
|
|
);
|
|
}
|
|
}
|
|
if ($this->config['alias_attr'] && isset($allValues[0][$this->config['alias_attr']]))
|
|
{
|
|
foreach($allValues[0][$this->config['alias_attr']] as $key => $value)
|
|
{
|
|
if ($key === 'count') continue;
|
|
|
|
$emailAddresses[] = array(
|
|
'name' => $realName,
|
|
'address' => $value,
|
|
'type' => 'alternate'
|
|
);
|
|
}
|
|
}
|
|
}
|
|
if ($this->debug) error_log(__METHOD__."('$_acountName') returning ".array2string($emailAddresses));
|
|
|
|
return $emailAddresses;
|
|
}
|
|
|
|
/**
|
|
* Get the data of a given user
|
|
*
|
|
* Multiple accounts may match, if an email address is specified.
|
|
* In that case only mail routing fields "uid", "mailbox" and "forward" contain values
|
|
* from all accounts!
|
|
*
|
|
* @param int|string $user numerical account-id, account-name or email address
|
|
* @param boolean $match_uid_at_domain=true true: uid@domain matches, false only an email or alias address matches
|
|
* @return array with values for keys 'mailLocalAddress', 'mailAlternateAddress' (array), 'mailForwardingAddress' (array),
|
|
* 'accountStatus' ("active"), 'quotaLimit' and 'deliveryMode' ("forwardOnly")
|
|
*/
|
|
function getUserData($user, $match_uid_at_domain=false)
|
|
{
|
|
$userData = array();
|
|
|
|
$ldap = $GLOBALS['egw']->ldap->ldapConnect();
|
|
|
|
if (is_numeric($user))
|
|
{
|
|
$filter = '(uidnumber='.(int)$user.')';
|
|
}
|
|
elseif (strpos($user, '@') === false)
|
|
{
|
|
$filter = '(uid='.ldap::quote($user).')';
|
|
}
|
|
else // email address --> build filter by attributes defined in config
|
|
{
|
|
list($namepart, $domain) = explode('@', $user);
|
|
if (!empty($this->config['search_filter']))
|
|
{
|
|
$filter = strtr($this->config['search_filter'], array(
|
|
'%s' => ldap::quote($user),
|
|
'%u' => ldap::quote($namepart),
|
|
'%d' => ldap::quote($domain),
|
|
));
|
|
}
|
|
else
|
|
{
|
|
$filter = array('(mail='.ldap::quote($user).')');
|
|
if ($match_uid_at_domain) $filter[] = '(uid='.ldap::quote($namepart).')';
|
|
if ($this->config['alias_attr'])
|
|
{
|
|
$filter[] = '('.$this->config['alias_attr'].'='.ldap::quote($user).')';
|
|
}
|
|
$filter = count($filter) > 1 ? '(|'.explode('', $filter).')' : $filter[0];
|
|
|
|
// if an enable attribute is set, only return enabled accounts
|
|
if ($this->config['mail_enable_attr'])
|
|
{
|
|
$filter = '(&('.$this->config['mail_enable_attr'].'='.
|
|
($this->config['mail_enabled'] ? $this->config['mail_enabled'] : '*').")$filter)";
|
|
}
|
|
}
|
|
}
|
|
$base = empty($this->config['search_base']) ?
|
|
$GLOBALS['egw_info']['server']['ldap_context'] : $this->config['search_base'];
|
|
$sri = ldap_search($ldap, $base, $filter, array($this->config['schema']));
|
|
|
|
if ($sri)
|
|
{
|
|
$allValues = ldap_get_entries($ldap, $sri);
|
|
if ($this->debug) error_log(__METHOD__."('$user') --> ldap_search(, '$base', '$filter') --> ldap_get_entries=".array2string($allValues[0]));
|
|
|
|
foreach($allValues as $key => $values)
|
|
{
|
|
if ($key === 'count') continue;
|
|
|
|
// groups are always active (if they have an email) and allways forwardOnly
|
|
if (in_array('posixGroup', $values['objectclass']))
|
|
{
|
|
$accountStatus = emailadmin_smtp::MAIL_ENABLED;
|
|
$deliveryMode = emailadmin_smtp::FORWARD_ONLY;
|
|
}
|
|
else // for users we have to check the attributes
|
|
{
|
|
if ($this->config['mail_enable_attr'])
|
|
{
|
|
$accountStatus = isset($values[$this->config['mail_enable_attr']]) &&
|
|
($this->config['mail_enabled'] && !strcasecmp($values[$this->config['mail_enable_attr']][0], $this->config['mail_enabled']) ||
|
|
!$this->config['mail_enabled'] && $values[$this->config['alias_attr']]['count'] > 0) ? emailadmin_smtp::MAIL_ENABLED : '';
|
|
}
|
|
else
|
|
{
|
|
$accountStatus = $values[$this->config['alias_attr']]['count'] > 0 ? emailadmin_smtp::MAIL_ENABLED : '';
|
|
}
|
|
if ($this->config['forward_only_attr'])
|
|
{
|
|
$deliveryMode = isset($values[$this->config['forward_only_attr']]) &&
|
|
($this->config['forward_only'] && !strcasecmp($values[$this->config['forward_only_attr']][0], $this->config['forward_only']) ||
|
|
!$this->config['forward_only'] && $values[$this->config['forward_only_attr']]['count'] > 0) ? emailadmin_smtp::FORWARD_ONLY : '';
|
|
}
|
|
else
|
|
{
|
|
$deliveryMode = '';
|
|
}
|
|
}
|
|
|
|
// collect mail routing data (can be from multiple (active) accounts and groups!)
|
|
if ($accountStatus)
|
|
{
|
|
// groups never have a mailbox, accounts can have a deliveryMode of "forwardOnly"
|
|
if ($deliveryMode != emailadmin_smtp::FORWARD_ONLY)
|
|
{
|
|
$userData['uid'][] = $values['uid'][0];
|
|
if ($this->config['mailbox_attr'] && isset($values[$this->config['mailbox_attr']]))
|
|
{
|
|
$userData['mailbox'][] = $values[$this->config['mailbox_attr']][0];
|
|
}
|
|
}
|
|
if ($this->config['forward_attr'] && $values[$this->config['forward_attr']])
|
|
{
|
|
$userData['forward'] = array_merge((array)$userData['forward'], $values[$this->config['forward_attr']]);
|
|
unset($userData['forward']['count']);
|
|
}
|
|
}
|
|
|
|
// regular user-data can only be from users, NOT groups
|
|
if (in_array('posixGroup', $values['objectclass'])) continue;
|
|
|
|
$userData['mailLocalAddress'] = $values['mail'][0];
|
|
$userData['accountStatus'] = $accountStatus;
|
|
|
|
if ($this->config['alias_attr'])
|
|
{
|
|
$userData['mailAlternateAddress'] = (array)$values[$this->config['alias_attr']];
|
|
unset($userData['mailAlternateAddress']['count']);
|
|
}
|
|
else
|
|
{
|
|
$userData['mailAlternateAddress'] = (array)$values['mail'];
|
|
unset($userData['mailAlternateAddress']['count']);
|
|
unset($userData['mailAlternateAddress'][0]);
|
|
$userData['mailAlternateAddress'] = array_values($userData['mailAlternateAddress']);
|
|
}
|
|
|
|
$userData['mailForwardingAddress'] = $this->config['forward_attr'] ? $values[$this->config['forward_attr']] : array();
|
|
unset($userData['mailForwardingAddress']['count']);
|
|
|
|
if ($this->config['mailbox_attr']) $userData['mailMessageStore'] = $values[$this->config['mailbox_attr']][0];
|
|
|
|
if ($this->config['forward_only_attr'])
|
|
{
|
|
$userData['deliveryMode'] = isset($values[$this->config['forward_only_attr']]) &&
|
|
($this->config['forward_only'] && !strcasecmp($values[$this->config['forward_only_attr']][0], $this->config['forward_only']) ||
|
|
!$this->config['forward_only'] && $values[$this->config['forward_only_attr']]['count'] > 0) ? emailadmin_smtp::FORWARD_ONLY : '';
|
|
}
|
|
else
|
|
{
|
|
$userData['deliveryMode'] = '';
|
|
}
|
|
// eg. suse stores all email addresses as aliases
|
|
if ($this->config['require_mail_as_alias'] &&
|
|
($k = array_search($userData['mailLocalAddress'],$userData['mailAlternateAddress'])) !== false)
|
|
{
|
|
unset($userData['mailAlternateAddress'][$k]);
|
|
}
|
|
|
|
if ($this->config['quota_attr'] && isset($values[$this->config['quota_attr']]))
|
|
{
|
|
$userData['quotaLimit'] = $values[$this->config['quota_attr']][0] / 1048576;
|
|
}
|
|
}
|
|
}
|
|
if ($this->debug) error_log(__METHOD__."('$user') returning ".array2string($userData));
|
|
|
|
return $userData;
|
|
}
|
|
|
|
/**
|
|
* Set the data of a given user
|
|
*
|
|
* @param int $_uidnumber numerical user-id
|
|
* @param array $_mailAlternateAddress
|
|
* @param array $_mailForwardingAddress
|
|
* @param string $_deliveryMode
|
|
* @param string $_accountStatus
|
|
* @param string $_mailLocalAddress
|
|
* @param int $_quota in MB
|
|
* @param boolean $_forwarding_only=false not used as we have our own addAccount method
|
|
* @param string $_setMailbox=null used only for account migration
|
|
* @return boolean true on success, false on error writing to ldap
|
|
*/
|
|
function setUserData($_uidnumber, array $_mailAlternateAddress, array $_mailForwardingAddress, $_deliveryMode,
|
|
$_accountStatus, $_mailLocalAddress, $_quota, $_forwarding_only=false, $_setMailbox=null)
|
|
{
|
|
$filter = 'uidnumber='.(int)$_uidnumber;
|
|
|
|
$ldap = $GLOBALS['egw']->ldap->ldapConnect();
|
|
|
|
if (!($sri = @ldap_search($ldap,$GLOBALS['egw_info']['server']['ldap_context'],$filter)))
|
|
{
|
|
return false;
|
|
}
|
|
$allValues = ldap_get_entries($ldap, $sri);
|
|
|
|
$accountDN = $allValues[0]['dn'];
|
|
$uid = $allValues[0]['uid'][0];
|
|
$objectClasses = $allValues[0]['objectclass'];
|
|
|
|
unset($objectClasses['count']);
|
|
|
|
if(!in_array($this->config['schema'],$objectClasses) && !in_array(strtolower($this->config['schema']),$objectClasses))
|
|
{
|
|
$objectClasses[] = $this->config['schema'];
|
|
$newData['objectclass'] = $objectClasses;
|
|
}
|
|
|
|
sort($_mailAlternateAddress);
|
|
sort($_mailForwardingAddress);
|
|
|
|
$newData['mail'] = $_mailLocalAddress;
|
|
// does schema have explicit alias attribute
|
|
if ($this->config['alias_attr'])
|
|
{
|
|
$newData[$this->config['alias_attr']] = (array)$_mailAlternateAddress;
|
|
|
|
// all email must be stored as alias for suse
|
|
if ($this->config['require_mail_as_alias'] && !in_array($_mailLocalAddress,(array)$_mailAlternateAddress))
|
|
{
|
|
$newData[$this->config['alias_attr']][] = $_mailLocalAddress;
|
|
}
|
|
}
|
|
// or de we add them - if existing - to mail attr
|
|
elseif ($_mailAlternateAddress)
|
|
{
|
|
$newData['mail'] = array_merge((array)$newData['mail'],(array)$_mailAlternateAddress);
|
|
}
|
|
// does schema support to store forwards
|
|
if ($this->config['forward_attr'])
|
|
{
|
|
$newData[$this->config['forward_attr']] = (array)$_mailForwardingAddress;
|
|
}
|
|
// does schema support only forwarding incomming mail
|
|
if ($this->config['forward_only_attr'])
|
|
{
|
|
$newData[$this->config['forward_only_attr']] = $_deliveryMode ? $this->config['forward_only'] : array();
|
|
}
|
|
// does schema support enabling/disabling mail via attribute
|
|
if ($this->config['mail_enable_attr'])
|
|
{
|
|
$newData[$this->config['mail_enable_attr']] = $_accountStatus ? $this->config['mail_enabled'] : array();
|
|
}
|
|
// does schema support an explicit mailbox name --> set it with $uid@$domain
|
|
if ($this->config['mailbox_attr'] && empty($allValues[0][$this->config['mailbox_attr']][0]))
|
|
{
|
|
$newData[$this->config['mailbox_attr']] = $this->mailbox_addr(array(
|
|
'account_id' => $_uidnumber,
|
|
'account_lid' => $uid,
|
|
'account_email' => $_mailLocalAddress,
|
|
));
|
|
}
|
|
if ($this->config['quota_attr'])
|
|
{
|
|
$newData[$this->config['quota_attr']] = (int)$_quota >= 0 ? (int)$_quota*1048576 : array();
|
|
}
|
|
// does schema support an explicit mailbox name --> set it, $_setMailbox is given
|
|
if ($this->config['mailbox_attr'] && $_setMailbox)
|
|
{
|
|
$newData[$this->config['mailbox_attr']] = $_setMailbox;
|
|
}
|
|
if ($this->debug) error_log(__METHOD__.'('.array2string(func_get_args()).") --> ldap_mod_replace(,'$accountDN',".array2string($newData).')');
|
|
|
|
return ldap_mod_replace($ldap, $accountDN, $newData);
|
|
}
|
|
|
|
/**
|
|
* Saves the forwarding information
|
|
*
|
|
* @param int $_accountID
|
|
* @param string $_forwardingAddress
|
|
* @param string $_keepLocalCopy 'yes'
|
|
* @return boolean true on success, false on error writing to ldap
|
|
*/
|
|
function saveSMTPForwarding($_accountID, $_forwardingAddress, $_keepLocalCopy)
|
|
{
|
|
$ds = $GLOBALS['egw']->ldap->ldapConnect();
|
|
$filter = sprintf('(&(uidnumber=%d)(objectclass=posixAccount))',$_accountID);
|
|
$attributes = array('dn',$this->config['forward_attr'],'objectclass');
|
|
if ($this->config['forward_only_attr'])
|
|
{
|
|
$attributes[] = $this->config['forward_only_attr'];
|
|
}
|
|
$sri = ldap_search($ds, $GLOBALS['egw_info']['server']['ldap_context'], $filter, $attributes);
|
|
|
|
if ($sri)
|
|
{
|
|
$newData = array();
|
|
$allValues = ldap_get_entries($ds, $sri);
|
|
$objectClasses = $allValues[0]['objectclass'];
|
|
$newData['objectclass'] = $allValues[0]['objectclass'];
|
|
|
|
unset($newData['objectclass']['count']);
|
|
|
|
if(!in_array($this->config['schema'],$objectClasses))
|
|
{
|
|
$newData['objectclass'][] = $this->config['schema'];
|
|
}
|
|
if ($this->config['forward_attr'])
|
|
{
|
|
if(!empty($_forwardingAddress))
|
|
{
|
|
if(is_array($allValues[0][$this->config['forward_attr']]))
|
|
{
|
|
$newData[$this->config['forward_attr']] = $allValues[0][$this->config['forward_attr']];
|
|
unset($newData[$this->config['forward_attr']]['count']);
|
|
if (!is_array($_forwardingAddress))
|
|
{
|
|
// replace the first forwarding address (old behavior)
|
|
$newData[$this->config['forward_attr']][0] = $_forwardingAddress;
|
|
}
|
|
else
|
|
{
|
|
// replace all forwarding Addresses
|
|
$newData[$this->config['forward_attr']] = $_forwardingAddress;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$newData[$this->config['forward_attr']] = (array)$_forwardingAddress;
|
|
}
|
|
if ($this->config['forward_only_attr'])
|
|
{
|
|
$newData['deliverymode'] = $_keepLocalCopy == 'yes' ? array() : $this->config['forward_only'];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$newData[$this->config['forward_attr']] = array();
|
|
}
|
|
}
|
|
if ($this->debug) error_log(__METHOD__.'('.array2string(func_get_args()).") --> ldap_mod_replace(,'$accountDN',".array2string($newData).')');
|
|
|
|
return ldap_modify ($ds, $allValues[0]['dn'], $newData);
|
|
}
|
|
}
|
|
}
|