mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-29 18:18:56 +01:00
810 lines
25 KiB
PHP
810 lines
25 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-13 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.
|
||
|
*
|
||
|
* Aliases, forwards, forward-only and quota attribute can be stored in same multivalued attribute
|
||
|
* with different prefixes.
|
||
|
*
|
||
|
* Please do NOT copy this class! Extend it and set the constants different.
|
||
|
*
|
||
|
* Please note: schema names muse use correct case (eg. "inetOrgPerson"),
|
||
|
* while attribute name muse use lowercase, as LDAP returns them as keys in lowercase!
|
||
|
*/
|
||
|
class emailadmin_smtp_ldap extends emailadmin_smtp
|
||
|
{
|
||
|
/**
|
||
|
* Name of schema, has to be in the right case!
|
||
|
*/
|
||
|
const SCHEMA = 'inetOrgPerson';
|
||
|
|
||
|
/**
|
||
|
* Filter for users
|
||
|
*/
|
||
|
const USER_FILTER = '(objectClass=posixAccount)';
|
||
|
|
||
|
/**
|
||
|
* Name of schema for groups, has to be in the right case!
|
||
|
*/
|
||
|
const GROUP_SCHEMA = 'posixGroup';
|
||
|
|
||
|
/**
|
||
|
* Attribute to enable mail for an account, OR false if existence of ALIAS_ATTR is enough for mail delivery
|
||
|
*/
|
||
|
const MAIL_ENABLE_ATTR = false;
|
||
|
|
||
|
/**
|
||
|
* Value for MAIL_ENABLED to use local mail address
|
||
|
*/
|
||
|
const MAIL_ENABLED_USE_MAIL = '@mail';
|
||
|
|
||
|
/**
|
||
|
* Attribute for aliases OR false to use mail
|
||
|
*/
|
||
|
const ALIAS_ATTR = false;
|
||
|
|
||
|
/**
|
||
|
* Caseinsensitive prefix for aliases (eg. "smtp:"), aliases get added with it and only aliases with it are reported
|
||
|
*/
|
||
|
const ALIAS_PREFIX = '';
|
||
|
|
||
|
/**
|
||
|
* 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;
|
||
|
|
||
|
/**
|
||
|
* Caseinsensitive prefix for forwards (eg. "forward:"), forwards get added with it and only forwards with it are reported
|
||
|
*/
|
||
|
const FORWARD_PREFIX = '';
|
||
|
|
||
|
/**
|
||
|
* Attribute to only forward mail, OR false if not available
|
||
|
*/
|
||
|
const FORWARD_ONLY_ATTR = false;
|
||
|
|
||
|
/**
|
||
|
* Value of forward-only attribute, if empty any value will switch forward only on (checked with =*)
|
||
|
*/
|
||
|
const FORWARD_ONLY = 'forwardOnly';
|
||
|
|
||
|
/**
|
||
|
* 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;
|
||
|
|
||
|
/**
|
||
|
* Caseinsensitive prefix for quota (eg. "quota:"), quota get added with it and only quota with it are reported
|
||
|
*/
|
||
|
const QUOTA_PREFIX = '';
|
||
|
|
||
|
/**
|
||
|
* Internal quota in MB is multiplicated with this factor before stored in LDAP
|
||
|
*/
|
||
|
const QUOTA_FACTOR = 1048576;
|
||
|
|
||
|
/**
|
||
|
* Attribute for user name
|
||
|
*/
|
||
|
const USER_ATTR = 'uid';
|
||
|
|
||
|
/**
|
||
|
* Attribute for numeric user id (optional)
|
||
|
*/
|
||
|
const USERID_ATTR = 'uidnumber';
|
||
|
|
||
|
/**
|
||
|
* Base for all searches, defaults to $GLOBALS['egw_info']['server']['ldap_context'] and can be set via setBase($base)
|
||
|
*
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $search_base;
|
||
|
|
||
|
/**
|
||
|
* Special search filter for getUserData only
|
||
|
*
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $search_filter;
|
||
|
|
||
|
/**
|
||
|
* Log all LDAP writes / actions to error_log
|
||
|
*/
|
||
|
var $debug = false;
|
||
|
|
||
|
/**
|
||
|
* from here on implementation, please do NOT copy but extend it!
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Constructor
|
||
|
*
|
||
|
* @param string $defaultDomain=null
|
||
|
*/
|
||
|
function __construct($defaultDomain=null)
|
||
|
{
|
||
|
parent::__construct($defaultDomain);
|
||
|
|
||
|
if (empty($this->search_base))
|
||
|
{
|
||
|
$this->setBase($GLOBALS['egw_info']['server']['ldap_context']);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return description for EMailAdmin
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function description()
|
||
|
{
|
||
|
return 'LDAP ('.static::SCHEMA.')';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set ldap search filter for aliases and forwards (getUserData)
|
||
|
*
|
||
|
* @param string $filter
|
||
|
*/
|
||
|
function setFilter($filter)
|
||
|
{
|
||
|
$this->search_filter = $filter;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set ldap search base, default $GLOBALS['egw_info']['server']['ldap_context']
|
||
|
*
|
||
|
* @param string $base
|
||
|
*/
|
||
|
function setBase($base)
|
||
|
{
|
||
|
$this->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 = $this->getLdapConnection();
|
||
|
|
||
|
$filter = static::USER_ATTR."=".ldap::quote($_hookValues['account_lid']);
|
||
|
|
||
|
if (!($sri = @ldap_search($ds, $this->search_base, $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(static::SCHEMA,$objectClasses) && !in_array(strtolower(static::SCHEMA),$objectClasses))
|
||
|
{
|
||
|
$objectClasses[] = static::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 (static::ALIAS_ATTR && static::REQUIRE_MAIL_AS_ALIAS)
|
||
|
{
|
||
|
$newData[static::ALIAS_ATTR] = static::ALIAS_PREFIX.$mailLocalAddress;
|
||
|
}
|
||
|
// does schema support enabling/disabling mail via attribute
|
||
|
if (static::MAIL_ENABLE_ATTR)
|
||
|
{
|
||
|
$newData[static::MAIL_ENABLE_ATTR] = static::MAIL_ENABLED == self::MAIL_ENABLED_USE_MAIL ?
|
||
|
$mailLocalAddress : static::MAIL_ENABLE_ATTR;
|
||
|
}
|
||
|
// does schema support an explicit mailbox name --> set it
|
||
|
if (static::MAILBOX_ATTR)
|
||
|
{
|
||
|
$newData[static::MAILBOX_ATTR] = self::mailbox_addr($_hookValues);
|
||
|
}
|
||
|
|
||
|
// allow extending classes to add extra data
|
||
|
$this->addAccountExtra($_hookValues, $allValues[0], $newData);
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add additional values to addAccount and setUserData ($_hookValues['location'])
|
||
|
*
|
||
|
* @param array $_hookValues
|
||
|
* @param array $allValues existing data of account as returned by ldap query
|
||
|
* @param array $newData data to update
|
||
|
*/
|
||
|
function addAccountExtra(array $_hookValues, array $allValues, array &$newData)
|
||
|
{
|
||
|
unset($_hookValues, $allValues, $newData); // not used, but required by function signature
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get all email addresses of an account
|
||
|
*
|
||
|
* @param string $_accountName
|
||
|
* @return array
|
||
|
*/
|
||
|
function getAccountEmailAddress($_accountName)
|
||
|
{
|
||
|
$emailAddresses = array();
|
||
|
$ds = $this->getLdapConnection();
|
||
|
$filter = '(&'.static::USER_FILTER.'('.static::USER_ATTR.'='.ldap::quote($_accountName).'))';
|
||
|
$attributes = array('dn', 'mail', static::ALIAS_ATTR);
|
||
|
$sri = @ldap_search($ds, $this->search_base, $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 (static::ALIAS_ATTR && isset($allValues[0][static::ALIAS_ATTR]))
|
||
|
{
|
||
|
foreach(self::getAttributePrefix($allValues[0][static::ALIAS_ATTR], static::ALIAS_PREFIX) as $value)
|
||
|
{
|
||
|
$emailAddresses[] = array(
|
||
|
'name' => $realName,
|
||
|
'address' => $value,
|
||
|
'type' => 'alternate'
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ($this->debug) error_log(__METHOD__."('$_accountName') 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(
|
||
|
'mailbox' => array(),
|
||
|
'forward' => array(),
|
||
|
|
||
|
);
|
||
|
|
||
|
$ldap = $this->getLdapConnection();
|
||
|
|
||
|
if (is_numeric($user) && static::USERID_ATTR)
|
||
|
{
|
||
|
$filter = '('.static::USERID_ATTR.'='.(int)$user.')';
|
||
|
}
|
||
|
elseif (strpos($user, '@') === false)
|
||
|
{
|
||
|
if (is_numeric($user)) $user = $GLOBALS['egw']->accounts->id2name($user);
|
||
|
$filter = '(&'.static::USER_FILTER.'('.static::USER_ATTR.'='.ldap::quote($user).'))';
|
||
|
}
|
||
|
else // email address --> build filter by attributes defined in config
|
||
|
{
|
||
|
list($namepart, $domain) = explode('@', $user);
|
||
|
if (!empty($this->search_filter))
|
||
|
{
|
||
|
$filter = strtr($this->search_filter, array(
|
||
|
'%s' => ldap::quote($user),
|
||
|
'%u' => ldap::quote($namepart),
|
||
|
'%d' => ldap::quote($domain),
|
||
|
));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$to_or = array('(mail='.ldap::quote($user).')');
|
||
|
if ($match_uid_at_domain) $to_or[] = '('.static::USER_ATTR.'='.ldap::quote($namepart).')';
|
||
|
if (static::ALIAS_ATTR)
|
||
|
{
|
||
|
$to_or[] = '('.static::ALIAS_ATTR.'='.static::ALIAS_PREFIX.ldap::quote($user).')';
|
||
|
}
|
||
|
$filter = count($to_or) > 1 ? '(|'.explode('', $to_or).')' : $to_or[0];
|
||
|
|
||
|
// if an enable attribute is set, only return enabled accounts
|
||
|
if (static::MAIL_ENABLE_ATTR)
|
||
|
{
|
||
|
$filter = '(&('.static::MAIL_ENABLE_ATTR.'='.
|
||
|
(static::MAIL_ENABLED ? static::MAIL_ENABLED : '*').")$filter)";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
$attributes = array_values(array_diff(array(
|
||
|
'mail', 'objectclass', static::USER_ATTR, static::MAIL_ENABLE_ATTR, static::ALIAS_ATTR,
|
||
|
static::MAILBOX_ATTR, static::FORWARD_ATTR, static::FORWARD_ONLY_ATTR, static::QUOTA_ATTR,
|
||
|
), array(false, '')));
|
||
|
|
||
|
$sri = ldap_search($ldap, $this->search_base, $filter, $attributes);
|
||
|
|
||
|
if ($sri)
|
||
|
{
|
||
|
$allValues = ldap_get_entries($ldap, $sri);
|
||
|
if ($this->debug) error_log(__METHOD__."('$user') --> ldap_search(, '$this->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(static::GROUP_SCHEMA, $values['objectclass']))
|
||
|
{
|
||
|
$accountStatus = emailadmin_smtp::MAIL_ENABLED;
|
||
|
$deliveryMode = emailadmin_smtp::FORWARD_ONLY;
|
||
|
}
|
||
|
else // for users we have to check the attributes
|
||
|
{
|
||
|
if (static::MAIL_ENABLE_ATTR)
|
||
|
{
|
||
|
$accountStatus = isset($values[static::MAIL_ENABLE_ATTR]) &&
|
||
|
(static::MAIL_ENABLED === self::MAIL_ENABLED_USE_MAIL && !empty($values[static::MAIL_ENABLE_ATTR][0]) ||
|
||
|
static::MAIL_ENABLED && !strcasecmp($values[static::MAIL_ENABLE_ATTR][0], static::MAIL_ENABLED) ||
|
||
|
!static::MAIL_ENABLED && $values[static::ALIAS_ATTR ? static::ALIAS_ATTR : 'mail']['count'] > 0) ?
|
||
|
emailadmin_smtp::MAIL_ENABLED : '';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$accountStatus = $values[static::ALIAS_ATTR ? static::ALIAS_ATTR : 'mail']['count'] > 0 ?
|
||
|
emailadmin_smtp::MAIL_ENABLED : '';
|
||
|
}
|
||
|
if (static::FORWARD_ONLY_ATTR)
|
||
|
{
|
||
|
if (static::FORWARD_ONLY) // check caseinsensitiv for existence of that value
|
||
|
{
|
||
|
$deliveryMode = self::getAttributePrefix($values[static::FORWARD_ONLY_ATTR], static::FORWARD_ONLY) ?
|
||
|
emailadmin_smtp::FORWARD_ONLY : '';
|
||
|
}
|
||
|
else // check for existence of any value
|
||
|
{
|
||
|
$deliveryMode = $values[static::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[static::USER_ATTR][] = $values[static::USER_ATTR][0];
|
||
|
if (static::MAILBOX_ATTR && isset($values[static::MAILBOX_ATTR]))
|
||
|
{
|
||
|
$userData['mailbox'][] = $values[static::MAILBOX_ATTR][0];
|
||
|
}
|
||
|
}
|
||
|
if (static::FORWARD_ATTR && $values[static::FORWARD_ATTR])
|
||
|
{
|
||
|
$userData['forward'] = array_merge($userData['forward'],
|
||
|
self::getAttributePrefix($values[static::FORWARD_ATTR], static::FORWARD_PREFIX, false));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// regular user-data can only be from users, NOT groups
|
||
|
if (in_array(static::GROUP_SCHEMA, $values['objectclass'])) continue;
|
||
|
|
||
|
$userData['mailLocalAddress'] = $values['mail'][0];
|
||
|
$userData['accountStatus'] = $accountStatus;
|
||
|
|
||
|
if (static::ALIAS_ATTR)
|
||
|
{
|
||
|
$userData['mailAlternateAddress'] = self::getAttributePrefix($values[static::ALIAS_ATTR], static::ALIAS_PREFIX);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$userData['mailAlternateAddress'] = (array)$values['mail'];
|
||
|
unset($userData['mailAlternateAddress']['count']);
|
||
|
unset($userData['mailAlternateAddress'][0]);
|
||
|
$userData['mailAlternateAddress'] = array_values($userData['mailAlternateAddress']);
|
||
|
}
|
||
|
|
||
|
if (static::FORWARD_ATTR)
|
||
|
{
|
||
|
$userData['mailForwardingAddress'] = self::getAttributePrefix($values[static::FORWARD_ATTR], static::FORWARD_PREFIX);
|
||
|
}
|
||
|
|
||
|
if (static::MAILBOX_ATTR) $userData['mailMessageStore'] = $values[static::MAILBOX_ATTR][0];
|
||
|
|
||
|
$userData['deliveryMode'] = $deliveryMode;
|
||
|
|
||
|
// eg. suse stores all email addresses as aliases
|
||
|
if (static::REQUIRE_MAIL_AS_ALIAS &&
|
||
|
($k = array_search($userData['mailLocalAddress'],$userData['mailAlternateAddress'])) !== false)
|
||
|
{
|
||
|
unset($userData['mailAlternateAddress'][$k]);
|
||
|
}
|
||
|
|
||
|
if (static::QUOTA_ATTR && isset($values[static::QUOTA_ATTR]))
|
||
|
{
|
||
|
$userData['quotaLimit'] = self::getAttributePrefix($values[static::QUOTA_ATTR], static::QUOTA_PREFIX);
|
||
|
$userData['quotaLimit'] = array_shift($userData['quotaLimit']);
|
||
|
$userData['quotaLimit'] = $userData['quotaLimit'] ? $userData['quotaLimit'] / static::QUOTA_FACTOR : null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
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)
|
||
|
{
|
||
|
unset($_forwarding_only); // not used
|
||
|
|
||
|
if (static::USERID_ATTR)
|
||
|
{
|
||
|
$filter = static::USERID_ATTR.'='.(int)$_uidnumber;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$uid = $GLOBALS['egw']->accounts->id2name($_uidnumber);
|
||
|
$filter = static::USER_ATTR.'='.ldap::quote($uid);
|
||
|
}
|
||
|
$ldap = $this->getLdapConnection();
|
||
|
|
||
|
if (!($sri = @ldap_search($ldap, $this->search_base, $filter)))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
$allValues = ldap_get_entries($ldap, $sri);
|
||
|
|
||
|
$accountDN = $allValues[0]['dn'];
|
||
|
$uid = $allValues[0][static::USER_ATTR][0];
|
||
|
$objectClasses = $allValues[0]['objectclass'];
|
||
|
|
||
|
unset($objectClasses['count']);
|
||
|
|
||
|
if(!in_array(static::SCHEMA,$objectClasses) && !in_array(strtolower(static::SCHEMA),$objectClasses))
|
||
|
{
|
||
|
$objectClasses[] = static::SCHEMA;
|
||
|
$newData['objectclass'] = $objectClasses;
|
||
|
}
|
||
|
|
||
|
sort($_mailAlternateAddress);
|
||
|
sort($_mailForwardingAddress);
|
||
|
|
||
|
$newData['mail'] = $_mailLocalAddress;
|
||
|
// does schema have explicit alias attribute
|
||
|
if (static::ALIAS_ATTR)
|
||
|
{
|
||
|
self::setAttributePrefix($newData[static::ALIAS_ATTR], $_mailAlternateAddress, static::ALIAS_PREFIX);
|
||
|
|
||
|
// all email must be stored as alias for suse
|
||
|
if (static::REQUIRE_MAIL_AS_ALIAS && !in_array($_mailLocalAddress,(array)$_mailAlternateAddress))
|
||
|
{
|
||
|
self::setAttributePrefix($newData[static::ALIAS_ATTR], $_mailLocalAddress, static::ALIAS_PREFIX);
|
||
|
}
|
||
|
}
|
||
|
// or de we add them - if existing - to mail attr
|
||
|
elseif ($_mailAlternateAddress)
|
||
|
{
|
||
|
self::setAttributePrefix($newData['mail'], $_mailAlternateAddress, static::ALIAS_PREFIX);
|
||
|
}
|
||
|
// does schema support to store forwards
|
||
|
if (static::FORWARD_ATTR)
|
||
|
{
|
||
|
self::setAttributePrefix($newData[static::FORWARD_ATTR], $_mailForwardingAddress, static::FORWARD_PREFIX);
|
||
|
}
|
||
|
// does schema support only forwarding incomming mail
|
||
|
if (static::FORWARD_ONLY_ATTR)
|
||
|
{
|
||
|
self::setAttributePrefix($newData[static::FORWARD_ONLY_ATTR],
|
||
|
$_deliveryMode ? (static::FORWARD_ONLY ? static::FORWARD_ONLY : 'forwardOnly') : array());
|
||
|
}
|
||
|
// does schema support an explicit mailbox name --> set it with $uid@$domain
|
||
|
if (static::MAILBOX_ATTR && empty($allValues[0][static::MAILBOX_ATTR][0]))
|
||
|
{
|
||
|
$newData[static::MAILBOX_ATTR] = $this->mailbox_addr(array(
|
||
|
'account_id' => $_uidnumber,
|
||
|
'account_lid' => $uid,
|
||
|
'account_email' => $_mailLocalAddress,
|
||
|
));
|
||
|
}
|
||
|
if (static::QUOTA_ATTR)
|
||
|
{
|
||
|
self::setAttributePrefix($newData[static::QUOTA_ATTR],
|
||
|
(int)$_quota > 0 ? (int)$_quota*static::QUOTA_FACTOR : array(), static::QUOTA_PREFIX);
|
||
|
}
|
||
|
// does schema support enabling/disabling mail via attribute
|
||
|
if (static::MAIL_ENABLE_ATTR)
|
||
|
{
|
||
|
$newData[static::MAIL_ENABLE_ATTR] = $_accountStatus ?
|
||
|
(static::MAIL_ENABLED == self::MAIL_ENABLED_USE_MAIL ? $_mailLocalAddress : static::MAIL_ENABLED) : array();
|
||
|
}
|
||
|
// if we have no mail-enabled attribute, but require primary mail in aliases-attr
|
||
|
// we do NOT write aliases, if mail is not enabled
|
||
|
if (!$_accountStatus && !static::MAIL_ENABLE_ATTR && static::REQUIRE_MAIL_AS_ALIAS)
|
||
|
{
|
||
|
$newData[static::ALIAS_ATTR] = array();
|
||
|
}
|
||
|
// does schema support an explicit mailbox name --> set it, $_setMailbox is given
|
||
|
if (static::MAILBOX_ATTR && $_setMailbox)
|
||
|
{
|
||
|
$newData[static::MAILBOX_ATTR] = $_setMailbox;
|
||
|
}
|
||
|
|
||
|
$this->addAccountExtra(array('location' => 'setUserData'), $allValues[0], $newData);
|
||
|
|
||
|
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 = $this->getLdapConnection();
|
||
|
if (static::USERID_ATTR)
|
||
|
{
|
||
|
$filter = '(&'.static::USER_FILTER.'('.static::USERID_ATTR.'='.(int)$_accountID.'))';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$uid = $GLOBALS['egw']->accounts->id2name($_accountID);
|
||
|
$filter = '(&'.static::USER_FILTER.'('.static::USER_ATTR.'='.ldap::quote($uid).'))';
|
||
|
}
|
||
|
$attributes = array('dn', static::FORWARD_ATTR, 'objectclass');
|
||
|
if (static::FORWARD_ONLY_ATTR)
|
||
|
{
|
||
|
$attributes[] = static::FORWARD_ONLY_ATTR;
|
||
|
}
|
||
|
$sri = ldap_search($ds, $this->search_base, $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(static::SCHEMA,$objectClasses))
|
||
|
{
|
||
|
$newData['objectclass'][] = static::SCHEMA;
|
||
|
}
|
||
|
if (static::FORWARD_ATTR)
|
||
|
{
|
||
|
// copy all non-forward data (different prefix) to newData, all existing forwards to $forwards
|
||
|
$newData[static::FORWARD_ATTR] = $allValues[0][static::FORWARD_ATTR];
|
||
|
$forwards = self::getAttributePrefix($newData[static::FORWARD_ATTR], static::FORWARD_PREFIX);
|
||
|
|
||
|
if(!empty($_forwardingAddress))
|
||
|
{
|
||
|
if($forwards)
|
||
|
{
|
||
|
if (!is_array($_forwardingAddress))
|
||
|
{
|
||
|
// replace the first forwarding address (old behavior)
|
||
|
$forwards[0] = $_forwardingAddress;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// replace all forwarding Addresses
|
||
|
$forwards = $_forwardingAddress;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$forwards = (array)$_forwardingAddress;
|
||
|
}
|
||
|
if (static::FORWARD_ONLY_ATTR)
|
||
|
{
|
||
|
self::getAttributePrefix($newData[static::FORWARD_ONLY_ATTR], static::FORWARD_ONLY);
|
||
|
self::setAttributePrefix($newData[static::FORWARD_ONLY_ATTR],
|
||
|
$_keepLocalCopy == 'yes' ? array() : static::FORWARD_ONLY);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$forwards = array();
|
||
|
}
|
||
|
// merge in again all new set forwards incl. opt. prefix
|
||
|
self::getAttributePrefix($newData[static::FORWARD_ATTR], $forwards, static::FORWARD_PREFIX);
|
||
|
}
|
||
|
if ($this->debug) error_log(__METHOD__.'('.array2string(func_get_args()).") --> ldap_mod_replace(,'{$allValues[0]['dn']}',".array2string($newData).')');
|
||
|
|
||
|
return ldap_modify ($ds, $allValues[0]['dn'], $newData);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get configured mailboxes of a domain
|
||
|
*
|
||
|
* @param boolean $return_inactive return mailboxes NOT marked as accountStatus=active too
|
||
|
* @return array uid => name-part of mailMessageStore
|
||
|
*/
|
||
|
function getMailboxes($return_inactive)
|
||
|
{
|
||
|
$ds = $this->getLdapConnection();
|
||
|
$filter = array("(mail=*)");
|
||
|
$attrs = array(static::USER_ATTR, 'mail');
|
||
|
if (static::MAILBOX_ATTR)
|
||
|
{
|
||
|
$filter[] = '('.static::MAILBOX_ATTR.'=*)';
|
||
|
$attrs[] = static::MAILBOX_ATTR;
|
||
|
}
|
||
|
if (!$return_inactive && static::MAIL_ENABLE_ATTR)
|
||
|
{
|
||
|
$filter[] = '('.static::MAIL_ENABLE_ATTR.'='.static::MAIL_ENABLED.')';
|
||
|
}
|
||
|
if (count($filter) > 1)
|
||
|
{
|
||
|
$filter = '(&'.implode('', $filter).')';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$filter = $filter[0];
|
||
|
}
|
||
|
if (!($sr = @ldap_search($ds, $this->search_base, $filter, $attrs)))
|
||
|
{
|
||
|
//error_log("Error ldap_search(\$ds, '$base', '$filter')!");
|
||
|
return array();
|
||
|
}
|
||
|
$entries = ldap_get_entries($ds, $sr);
|
||
|
|
||
|
unset($entries['count']);
|
||
|
|
||
|
$mailboxes = array();
|
||
|
foreach($entries as $entry)
|
||
|
{
|
||
|
if ($entry[static::USER_ATTR][0] == 'anonymous') continue; // anonymous is never a mail-user!
|
||
|
list($mailbox) = explode('@', $entry[static::MAILBOX_ATTR ? static::MAILBOX_ATTR : 'mail'][0]);
|
||
|
$mailboxes[$entry[static::USER_ATTR][0]] = $mailbox;
|
||
|
}
|
||
|
return $mailboxes;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set values in a given LDAP attribute using an optional prefix
|
||
|
*
|
||
|
* @param array &$attribute on return array with values set and existing values preseved
|
||
|
* @param string|array $values value(s) to set
|
||
|
* @param string $prefix='' prefix to use or ''
|
||
|
*/
|
||
|
protected static function setAttributePrefix(&$attribute, $values, $prefix='')
|
||
|
{
|
||
|
//$attribute_in = $attribute;
|
||
|
if (!isset($attribute)) $attribute = array();
|
||
|
if (!is_array($attribute)) $attribute = array($attribute);
|
||
|
|
||
|
foreach((array)$values as $value)
|
||
|
{
|
||
|
$attribute[] = $prefix.$value;
|
||
|
}
|
||
|
//error_log(__METHOD__."(".array2string($attribute_in).", ".array2string($values).", '$prefix') attribute=".array2string($attribute));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get values having an optional prefix from a given LDAP attribute
|
||
|
*
|
||
|
* @param array &$attribute only "count" and prefixed values get removed, get's reindexed, if values have been removed
|
||
|
* @param string $prefix='' prefix to use or ''
|
||
|
* @param boolean $remove=true remove returned values from $attribute
|
||
|
* @return array with values (prefix removed) or array() if nothing found
|
||
|
*/
|
||
|
protected static function getAttributePrefix(&$attribute, $prefix='', $remove=true)
|
||
|
{
|
||
|
//$attribute_in = $attribute;
|
||
|
$values = array();
|
||
|
|
||
|
if (isset($attribute))
|
||
|
{
|
||
|
unset($attribute['count']);
|
||
|
|
||
|
foreach($attribute as $key => $value)
|
||
|
{
|
||
|
if (!$prefix || stripos($value, $prefix) === 0)
|
||
|
{
|
||
|
if ($remove) unset($attribute[$key]);
|
||
|
$values[] = substr($value, strlen($prefix));
|
||
|
}
|
||
|
}
|
||
|
// reindex $attribute, if neccessary
|
||
|
if ($values && $attribute) $attribute = array_values($attribute);
|
||
|
}
|
||
|
//error_log(__METHOD__."(".array2string($attribute_in).", '$prefix', $remove) attribute=".array2string($attribute).' returning '.array2string($values));
|
||
|
return $values;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return LDAP connection
|
||
|
*/
|
||
|
protected function getLdapConnection()
|
||
|
{
|
||
|
static $ldap=null;
|
||
|
|
||
|
if (is_null($ldap)) $ldap = $GLOBALS['egw']->ldap->ldapConnect();
|
||
|
|
||
|
return $ldap;
|
||
|
}
|
||
|
}
|