diff --git a/emailadmin/inc/class.emailadmin_smtp_ldap.inc.php b/emailadmin/inc/class.emailadmin_smtp_ldap.inc.php new file mode 100644 index 0000000000..fa3663a381 --- /dev/null +++ b/emailadmin/inc/class.emailadmin_smtp_ldap.inc.php @@ -0,0 +1,416 @@ + + * @copyright (c) 2010 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +include_once(EGW_INCLUDE_ROOT.'/emailadmin/inc/class.defaultsmtp.inc.php'); + +/** + * 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 defaultsmtp +{ + /** + * 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 value to enable mail for an account, OR false if existense of attribute is enough to enable account + */ + const MAIL_ENABLED = 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 value to only forward mail + */ + const FORWARD_ONLY = false; + + /** + * Attribute for mailbox, to which mail gets delivered OR false if not supported + */ + const MAILBOX_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, + ); + + /** + * from here on implementation, please do NOT copy but extend it! + */ + + /** + * 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 with $uid@$domain + if ($this->config['mailbox_attr']) + { + $newData[$this->config['mailbox_attr']] = $_hookValues['account_lid'].'@'.$this->defaultDomain; + } + if ($this->debug) error_log(__METHOD__.'('.array2string(func_get_args()).") --> ldap_mod_replace(,'$accountDN',".array2string($newData).')'); + + return ldap_mod_replace($ds, $accountDN, $newData); + } + + /** + * 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']['firstname'] . (!empty($GLOBALS['egw_info']['user']['firstname']) ? ' ' : '') . $GLOBALS['egw_info']['user']['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' => $allValues[0]['mail'][0], + '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 + * + * @param int $_uidnumber numerical user-id + * @return array + */ + function getUserData($_uidnumber) + { + $userData = array(); + + $ldap = $GLOBALS['egw']->ldap->ldapConnect(); + + if (($sri = @ldap_search($ldap,$GLOBALS['egw_info']['server']['ldap_context'],'(uidnumber='.(int)$_uidnumber.')',array($this->config['schema'])))) + { + $allValues = ldap_get_entries($ldap, $sri); + if ($this->debug) error_log(__METHOD__."($_uidnumber) --> ldap_search(,{$GLOBALS['egw_info']['server']['ldap_context']},'(uidnumber=$_uidnumber)') --> ldap_get_entries=".array2string($allValues[0])); + + if ($allValues['count'] > 0) + { + $userData['mailLocalAddress'] = $allValues[0]['mail'][0]; + if ($this->config['alias_attr']) + { + $userData['mailAlternateAddress'] = (array)$allValues[0][$this->config['alias_attr']]; + unset($userData['mailAlternateAddress']['count']); + } + else + { + $userData['mailAlternateAddress'] = (array)$allValues[0]['mail']; + unset($userData['mailAlternateAddress']['count']); + unset($userData['mailAlternateAddress'][0]); + $userData['mailAlternateAddress'] = array_values($userData['mailAlternateAddress']); + } + if ($this->config['mail_enable_attr']) + { + $userData['accountStatus'] = isset($allValues[0][$this->config['mail_enable_attr']]) && + ($this->config['mail_enabled'] && $allValues[0][$this->config['mail_enable_attr']][0] == $this->config['mail_enabled'] || + !$this->config['mail_enabled'] && $allValues[0][$this->config['alias_attr']]['count'] > 0) ? 'active' : ''; + } + else + { + $userData['accountStatus'] = $allValues[0][$this->config['alias_attr']]['count'] > 0 ? 'active' : ''; + } + $userData['mailForwardingAddress'] = $this->config['forward_attr'] ? $allValues[0][$this->config['forward_attr']] : array(); + unset($userData['mailForwardingAddress']['count']); + + $userData['deliveryProgramPath'] = $allValues[0][$this->config['mailbox_attr']][0]; + + if ($this->config['forward_only_attr']) + { + $userData['deliveryMode'] = isset($allValues[0][$this->config['forward_only_attr']]) && + ($this->config['forward_only'] && $allValues[0][$this->config['forward_only_attr']][0] == $this->config['forward_only'] || + !$this->config['forward_only'] && $allValues[0][$this->config['forward_only_attr']]['count'] > 0) ? 'forwardOnly' : ''; + } + 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->debug) error_log(__METHOD__."('$_uidnumber') 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 + * @return boolean true on success, false on error writing to ldap + */ + function setUserData($_uidnumber, $_mailAlternateAddress, $_mailForwardingAddress, $_deliveryMode, $_accountStatus, $_mailLocalAddress) + { + $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']) + { + $newData[$this->config['mailbox_attr']] = $uid.'@'.$this->defaultDomain; + } + 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); + + $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']); + $newData[$this->config['forward_attr']][0] = $_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); + } + } +}