diff --git a/doc/rpm-build/post_install.php b/doc/rpm-build/post_install.php
index 6100f62c8f..739e52083b 100755
--- a/doc/rpm-build/post_install.php
+++ b/doc/rpm-build/post_install.php
@@ -11,7 +11,7 @@
if (php_sapi_name() !== 'cli') // security precaution: forbit calling post_install as web-page
{
- die('
rpm_post_install.php must NOT be called as web-page --> exiting !!!
');
+ die('post_install.php must NOT be called as web-page --> exiting !!!
');
}
$verbose = false;
$config = array(
@@ -55,6 +55,8 @@ $config = array(
'ldap_context' => 'ou=accounts,$base',
'ldap_search_filter' => '(uid=%user)',
'ldap_group_context' => 'ou=groups,$base',
+ 'ldap_encryption_type' => '',
+ 'sambaadmin/sambaSID'=> '', // SID for sambaadmin
'mailserver' => '',
'smtpserver' => 'localhost,25',
'smtp' => '', // see setup-cli.php --help config
@@ -88,8 +90,11 @@ function set_distro_defaults($distro=null)
global $config;
if (is_null($distro))
{
- $distro = file_exists('/etc/SuSE-release') ? 'suse' : (file_exists('/etc/debian_version') ? 'debian' :
- (file_exists('/etc/mandriva-release') ? 'mandriva' : 'rh'));
+ $distro = file_exists('/etc/SuSE-release') ? 'suse' :
+ (file_exists('/etc/mandriva-release') ? 'mandriva' :
+ (file_exists('/etc/lsb-release') && preg_match('/^DISTRIB_ID="Univention"$/mi',
+ file_get_contents('/etc/lsb-release')) ? 'univention' :
+ (file_exists('/etc/debian_version') ? 'debian' : 'rh')));
}
switch (($config['distro'] = $distro))
{
@@ -141,6 +146,9 @@ function set_distro_defaults($distro=null)
$config['ldap_context'] = 'ou=People,$base';
$config['ldap_group_context'] = 'ou=Group,$base';
break;
+ case 'univention':
+ set_univention_defaults();
+ break;
default:
$config['distro'] = 'rh';
// fall through
@@ -293,6 +301,7 @@ if (!file_exists($config['header']) || filesize($config['header']) < 200) // def
foreach(array(
'domain','ldap_suffix','ldap_host','ldap_admin','ldap_admin_pw', // non-egw params: only used for create
'ldap_base','ldap_root_dn','ldap_root_pw','ldap_context','ldap_search_filter','ldap_group_context', // egw params
+ 'ldap_encryption_type', 'sambaadmin/sambaSID',
) as $name)
{
if (strpos($value=$config[$name],'$') !== false)
@@ -551,7 +560,7 @@ function usage($error=null)
foreach($config as $name => $default)
{
if (in_array($name, array('postfix','cyrus'))) continue; // do NOT report deprecated options
- if (in_array($name,array('config_passwd','db_pass','admin_passwd','ldap_root_pw')))
+ if (in_array($name,array('config_passwd','db_pass','admin_passwd','ldap_root_pw')) && strlen($config[$name]) == 16)
{
$default = '<16 char random string>';
}
@@ -724,3 +733,98 @@ function fix_perms()
system('/bin/chmod 700 /tmp/egw_cache');
}
}
+
+/**
+ * Set Univention UCS specific defaults
+ *
+ * Defaults are read from ucr registry and /etc/*.secret files
+ */
+function set_univention_defaults()
+{
+ global $config;
+
+ set_distro_defaults('debian');
+ $config['distro'] = 'univention';
+
+ // mysql settings
+ $config['db_root_pw'] = _ucr_secret('mysql');
+
+ // check if ucs ldap server is configured
+ if (_ucr_get('ldap/server/ip'))
+ {
+ // ldap settings, see http://docs.univention.de/developer-reference-3.2.html#join:secret
+ $config['ldap_suffix'] = $config['ldap_base'] = _ucr_get('ldap/base');
+ $config['ldap_host'] = 'tls://'._ucr_get('ldap/server/ip').':'._ucr_get('ldap/server/port');
+ $config['ldap_admin'] = $config['ldap_root'] = 'cn=admin,$suffix';
+ $config['ldap_admin_pw'] = $config['ldap_root_pw'] = _ucr_secret('ldap');
+ $config['ldap_context'] = 'cn=users,$base';
+ $config['ldap_group_context'] = 'cn=groups,$base';
+ $config['ldap_search_filter'] = '(uid=%user)';
+
+ // ldap password hash (our default blowfish_crypt seems not to work)
+ $config['ldap_encryption_type'] = 'sha512_crypt';
+
+ $config['account_min_id'] = 1200; // UCS use 11xx for internal users/groups
+
+ $config['account-auth'] = 'univention,ldap';
+
+ // set sambaadmin sambaSID
+ $config['sambaadmin/sambaSID'] = exec('/usr/bin/univention-ldapsearch -x "(objectclass=sambadomain)" sambaSID|sed -n "s/sambaSID: \(.*\)/\1/p"');
+
+ // mailserver, see setup-cli.php --help config
+ if (($mailserver = exec('/usr/bin/univention-ldapsearch -x "(univentionAppID=mailserver_*)" univentionAppInstalledOnServer|sed -n "s/univentionAppInstalledOnServer: \(.*\)/\1/p"')) &&
+ _ucr_get('mail/cyrus/imap') == 'yes' && ($domains=_ucr_get('mail/hosteddomains')))
+ {
+ if (!is_array($domains)) $domains = explode("\n", $domains);
+ $domain = array_shift($domains);
+ $config['smtpserver'] = "$mailserver,465,,,yes,tls";
+ $config['smtp'] = 'no,emailadmin_smtp_ldap_univention';
+ $config['mailserver'] = "$mailserver,993,$domain,email,tls";
+ $config['imap'] = /*'cyrus,'._ucr_secret('cyrus')*/','.',emailadmin_imap_cyrus';
+ $config['folder'] = 'INBOX/Sent,INBOX/Trash,INBOX/Drafts,INBOX/Templates,INBOX/Spam';
+ if (($sieve_port = _ucr_get('mail/cyrus/sieve/port')))
+ {
+ $config['sieve'] = "$mailserver,$sieve_port,starttls";
+ }
+ }
+ }
+}
+
+/**
+ * Get a value from Univention registry
+ *
+ * @param string $name
+ * @return string
+ */
+function _ucr_get($name)
+{
+ static $values=null;
+ if (!isset($values))
+ {
+ $output = $matches = null;
+ exec('/usr/sbin/ucr dump', $output);
+ foreach($output as $line)
+ {
+ if (preg_match("/^([^:]+): (.*)\n?$/", $line, $matches))
+ {
+ $values[$matches[1]] = $matches[2];
+ }
+ }
+ }
+ return $values[$name];
+}
+
+/**
+ * Read one Univention secret/password eg. _ucr_secret('mysql')
+ *
+ * @param string $name
+ * @return string|boolean
+ */
+function _ucr_secret($name)
+{
+ if (!file_exists($filename = '/etc/'.basename($name).'.secret'))
+ {
+ return false;
+ }
+ return trim(file_get_contents($filename));
+}
diff --git a/emailadmin/inc/class.emailadmin_hooks.inc.php b/emailadmin/inc/class.emailadmin_hooks.inc.php
index 8626445a7f..6655d646a9 100644
--- a/emailadmin/inc/class.emailadmin_hooks.inc.php
+++ b/emailadmin/inc/class.emailadmin_hooks.inc.php
@@ -110,16 +110,17 @@ class emailadmin_hooks
try {
$account = new emailadmin_account($params);
- if ($account->acc_imap_type != 'emailadmin_imap' && ($imap = $account->imapServer(true)) &&
- is_a($imap, 'emailadmin_imap') && get_class($imap) != 'emailadmin_imap')
- {
- $imap->$method($data);
- }
if ($account->acc_smtp_type != 'emailadmin_smtp' && ($smtp = $account->smtpServer(true)) &&
is_a($smtp, 'emailadmin_smtp') && get_class($smtp) != 'emailadmin_smtp')
{
$smtp->$method($data);
}
+ if ($account->acc_imap_type != 'emailadmin_imap' && $account->acc_admin_username &&
+ $account->acc_admin_password && ($imap = $account->imapServer(true)) &&
+ is_a($imap, 'emailadmin_imap') && get_class($imap) != 'emailadmin_imap')
+ {
+ $imap->$method($data);
+ }
}
catch(Exception $e) {
_egw_log_exception($e);
diff --git a/emailadmin/inc/class.emailadmin_imapbase.inc.php b/emailadmin/inc/class.emailadmin_imapbase.inc.php
index 76affe112a..8cfbbda0c5 100644
--- a/emailadmin/inc/class.emailadmin_imapbase.inc.php
+++ b/emailadmin/inc/class.emailadmin_imapbase.inc.php
@@ -1018,6 +1018,7 @@ class emailadmin_imapbase
$_specialUseFolders[$this->icServer->ImapServerId][$this->icServer->acc_folder_sent]='Sent';
//if (!empty($this->icServer->acc_folder_template) && !isset($_specialUseFolders[$this->icServer->ImapServerId][$this->icServer->acc_folder_template]))
$_specialUseFolders[$this->icServer->ImapServerId][$this->icServer->acc_folder_template]='Templates';
+ $_specialUseFolders[$this->icServer->ImapServerId][$this->icServer->acc_folder_junk]='Junk';
//error_log(__METHOD__.' ('.__LINE__.') '.array2string($_specialUseFolders));//.'<->'.array2string($this->icServer));
self::$specialUseFolders = $_specialUseFolders[$this->icServer->ImapServerId];
egw_cache::setCache(egw_cache::INSTANCE,'email','specialUseFolders'.trim($GLOBALS['egw_info']['user']['account_id']),$_specialUseFolders, $expiration=60*60*24*5);
@@ -2158,11 +2159,17 @@ class emailadmin_imapbase
}
try
{
- $rv = $this->icServer->createMailbox($newFolderName);
+ $opts = array();
+ // if new created folder is a specal-use-folder, mark it as such, so other clients know to use it too
+ if (isset(self::$specialUseFolders[$newFolderName]))
+ {
+ $opts['special_use'] = self::$specialUseFolders[$newFolderName];
+ }
+ $rv = $this->icServer->createMailbox($newFolderName, $opts);
}
catch (Exception $e)
{
- error_log(__METHOD__.' ('.__LINE__.') '.' create Folder '.$newFolderName.'->'.$e->getMessage().' Namespace:'.array2string($this->icServer->getNameSpaces()).function_backtrace());
+ error_log(__METHOD__.' ('.__LINE__.') '.' create Folder '.$newFolderName.'->'.$e->getMessage().' ('.$e->details.') Namespace:'.array2string($this->icServer->getNameSpaces()).function_backtrace());
return false;
}
try
@@ -2459,7 +2466,8 @@ class emailadmin_imapbase
//subscribed folders may be used in getFolderStatus
egw_cache::setCache(egw_cache::INSTANCE,'email','subscribedFolders'.trim($GLOBALS['egw_info']['user']['account_id']),$subscribedFoldersForCache,$expiration=60*60*1);
//echo "
FolderNameSpace To Process:";_debug_array($foldersNameSpace);
- $autoFolderObjects = array();
+ $autoFolderObjects = $folders = array();
+ $autofolder_exists = array();
foreach( array('personal', 'others', 'shared') as $type) {
if(isset($foldersNameSpace[$type])) {
if($_subscribedOnly) {
@@ -2520,12 +2528,23 @@ class emailadmin_imapbase
$folders[$folderName] = $folderObject;
}
//error_log(__METHOD__.' ('.__LINE__.') '.':'.$folderObject->folderName);
+ if (!isset(self::$specialUseFolders)) $this->getSpecialUseFolders ();
+ if (isset(self::$specialUseFolders[$folderName]))
+ {
+ $autofolder_exists[$folderName] = self::$specialUseFolders[$folderName];
+ }
}
}
}
if (is_array($autoFolderObjects) && !empty($autoFolderObjects)) {
uasort($autoFolderObjects,array($this,"sortByAutoFolderPos"));
}
+ // check if some standard folders are missing and need to be created
+ if (count($autofolder_exists) < count(self::$autoFolders) && $this->check_create_autofolders($autofolder_exists))
+ {
+ // if new folders have been created, re-read folders ignoring the cache
+ return $this->getFolderObjects($_subscribedOnly, $_getCounters, $_alwaysGetDefaultFolders, false); // false = do NOT use cache
+ }
if (is_array($folders)) uasort($folders,array($this,"sortByDisplayName"));
//$folders2return = array_merge($autoFolderObjects,$folders);
//_debug_array($folders2return); #exit;
@@ -2542,12 +2561,33 @@ class emailadmin_imapbase
return $folders2return[$this->icServer->ImapServerId];
}
+ /**
+ * Check if all automatic folders exist and create them if not
+ *
+ * @param array $autofolders_exists existing folders, no need to check their existance again
+ * @return int number of new folders created
+ */
+ function check_create_autofolders(array $autofolders_exists=array())
+ {
+ $num_created = 0;
+ foreach(self::$autoFolders as $folder)
+ {
+ $created = false;
+ if (!in_array($folder, $autofolders_exists) && $this->_getSpecialUseFolder($folder, true, $created) &&
+ $created && $folder != 'Outbox')
+ {
+ $num_created++;
+ }
+ }
+ return $num_created;
+ }
+
/**
* search Value In FolderObjects
*
* Helper function to search for a specific value within the foldertree objects
* @param string $needle
- * @param array $haystack, array of folderobjects
+ * @param array $haystack array of folderobjects
* @return MIXED false or key
*/
static function searchValueInFolderObjects($needle, $haystack)
@@ -2682,18 +2722,21 @@ class emailadmin_imapbase
* abstraction layer for getDraftFolder, getTemplateFolder, getTrashFolder and getSentFolder
* @param string $type the type to fetch (Drafts|Template|Trash|Sent)
* @param boolean $_checkexistance, trigger check for existance
+ * @param boolean& $created =null on return true: if folder was just created, false if not
* @return mixed string or false
*/
- function _getSpecialUseFolder($_type, $_checkexistance=TRUE)
+ function _getSpecialUseFolder($_type, $_checkexistance=TRUE, &$created=null)
{
static $types = array(
- 'Drafts'=>array('prefName'=>'draftFolder','profileKey'=>'acc_folder_draft','autoFolderName'=>'Drafts'),
- 'Template'=>array('prefName'=>'templateFolder','profileKey'=>'acc_folder_template','autoFolderName'=>'Templates'),
- 'Trash'=>array('prefName'=>'trashFolder','profileKey'=>'acc_folder_trash','autoFolderName'=>'Trash'),
- 'Sent'=>array('prefName'=>'sentFolder','profileKey'=>'acc_folder_sent','autoFolderName'=>'Sent'),
- 'Junk'=>array('prefName'=>'junkFolder','profileKey'=>'acc_folder_junk','autoFolderName'=>'Junk'),
- 'Outbox'=>array('prefName'=>'outboxFolder','profileKey'=>'acc_folder_outbox','autoFolderName'=>'Outbox'),
+ 'Drafts' => array('profileKey'=>'acc_folder_draft','autoFolderName'=>'Drafts'),
+ 'Template' => array('profileKey'=>'acc_folder_template','autoFolderName'=>'Templates'),
+ 'Trash' => array('profileKey'=>'acc_folder_trash','autoFolderName'=>'Trash'),
+ 'Sent' => array('profileKey'=>'acc_folder_sent','autoFolderName'=>'Sent'),
+ 'Junk' => array('profileKey'=>'acc_folder_junk','autoFolderName'=>'Junk'),
+ 'Outbox' => array('profileKey'=>'acc_folder_outbox','autoFolderName'=>'Outbox'),
);
+ if ($_type == 'Templates') $_type = 'Template'; // for some reason self::$autofolders uses 'Templates'!
+ $created = false;
if (!isset($types[$_type]))
{
error_log(__METHOD__.' ('.__LINE__.') '.' '.$_type.' not supported for '.__METHOD__);
@@ -2716,7 +2759,7 @@ class emailadmin_imapbase
if ($_folderName && $_checkexistance && $_folderName !='none' && !$this->folderExists($_folderName,true)) {
try
{
- $this->createFolder('', $_folderName, true);
+ if (($_folderName = $this->createFolder('', $_folderName, true))) $created = true;
}
catch(Exception $e)
{
diff --git a/emailadmin/inc/class.emailadmin_smtp_ldap.inc.php b/emailadmin/inc/class.emailadmin_smtp_ldap.inc.php
index ee3b78dc02..8535305055 100644
--- a/emailadmin/inc/class.emailadmin_smtp_ldap.inc.php
+++ b/emailadmin/inc/class.emailadmin_smtp_ldap.inc.php
@@ -48,6 +48,11 @@ class emailadmin_smtp_ldap extends emailadmin_smtp
*/
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
*/
@@ -224,7 +229,8 @@ class emailadmin_smtp_ldap extends emailadmin_smtp
// does schema support enabling/disabling mail via attribute
if (static::MAIL_ENABLE_ATTR)
{
- $newData[static::MAIL_ENABLE_ATTR] = static::MAIL_ENABLED;
+ $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)
@@ -232,6 +238,9 @@ class emailadmin_smtp_ldap extends emailadmin_smtp
$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',".
@@ -241,6 +250,18 @@ class emailadmin_smtp_ldap extends emailadmin_smtp
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
*
@@ -377,7 +398,8 @@ class emailadmin_smtp_ldap extends emailadmin_smtp
if (static::MAIL_ENABLE_ATTR)
{
$accountStatus = isset($values[static::MAIL_ENABLE_ATTR]) &&
- (static::MAIL_ENABLED && !strcasecmp($values[static::MAIL_ENABLE_ATTR][0], static::MAIL_ENABLED) ||
+ (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 : '';
}
@@ -567,7 +589,8 @@ class emailadmin_smtp_ldap extends emailadmin_smtp
// does schema support enabling/disabling mail via attribute
if (static::MAIL_ENABLE_ATTR)
{
- $newData[static::MAIL_ENABLE_ATTR] = $_accountStatus ? static::MAIL_ENABLED : array();
+ $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
@@ -580,6 +603,9 @@ class emailadmin_smtp_ldap extends emailadmin_smtp
{
$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);
diff --git a/emailadmin/inc/class.emailadmin_smtp_univention.inc.php b/emailadmin/inc/class.emailadmin_smtp_univention.inc.php
new file mode 100644
index 0000000000..d3a4934dbb
--- /dev/null
+++ b/emailadmin/inc/class.emailadmin_smtp_univention.inc.php
@@ -0,0 +1,102 @@
+
+ * @copyright (c) 2014 by Ralf Becker
+ * @version $Id4$
+ */
+
+/**
+ * Postfix with Univention mailAccount schema
+ */
+class emailadmin_smtp_univention extends emailadmin_smtp_ldap
+{
+ /**
+ * Capabilities of this class (pipe-separated): default, forward
+ */
+ const CAPABILITIES = 'default|forward';
+
+ /**
+ * Name of schema, has to be in the right case!
+ */
+ const SCHEMA = 'univentionMail';
+
+ /**
+ * Attribute to enable mail for an account, OR false if existence of ALIAS_ATTR is enough for mail delivery
+ */
+ const MAIL_ENABLE_ATTR = 'mailprimaryaddress';
+
+ /**
+ * Attribute value to enable mail for an account, OR false if existense of attribute is enough to enable account
+ */
+ const MAIL_ENABLED = self::MAIL_ENABLED_USE_MAIL;
+
+ /**
+ * Attribute for aliases OR false to use mail
+ */
+ const ALIAS_ATTR = 'mailalternativeaddress';
+
+ /**
+ * 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;
+
+ /**
+ * Attribute for quota limit of user in MB
+ */
+ const QUOTA_ATTR = 'univentionmailuserquota';
+
+ /**
+ * Internal quota in MB is multiplicated with this factor before stored in LDAP
+ */
+ const QUOTA_FACTOR = 1;
+
+ /**
+ * Log all LDAP writes / actions to error_log
+ */
+ var $debug = false;
+
+ /**
+ * Add additional values to addAccount
+ *
+ * @param array $_hookValues
+ * @param array $allValues
+ * @param array $newData
+ */
+ function addAccountExtra(array $_hookValues, array $allValues, array &$newData)
+ {
+ unset($_hookValues); // not used, but required by function signature
+
+ if (empty($allValues['univentionmailhomeserver'][0]) && $newData[self::MAIL_ENABLE_ATTR])
+ {
+ $newData['univentionMailHomeServer'] = $this->host;
+
+ if (strpos($newData['univentionMailHomeServer'], '://'))
+ {
+ $newData['univentionMailHomeServer'] = parse_url($newData['univentionMailHomeServer'], PHP_URL_HOST);
+ }
+ }
+ }
+}
diff --git a/phpgwapi/inc/class.accounts_ldap.inc.php b/phpgwapi/inc/class.accounts_ldap.inc.php
index 9163532bfa..d64246a540 100644
--- a/phpgwapi/inc/class.accounts_ldap.inc.php
+++ b/phpgwapi/inc/class.accounts_ldap.inc.php
@@ -53,7 +53,7 @@ class accounts_ldap
*/
var $user_context;
/**
- * LDAP search filter for user accounts, eg. (uid=%name)
+ * LDAP search filter for user accounts, eg. (uid=%user)
*
* @var string
*/
@@ -82,16 +82,27 @@ class accounts_ldap
'user' => array(
'top','person','organizationalperson','inetorgperson','posixaccount','shadowaccount'
),
- 'user-if-supported' => array( // these classes get added, only if the server supports them
- 'mozillaabpersonalpha','mozillaorgperson','evolutionperson'
+ 'user-if-supported' => array( // these classes get added, if server supports them
+ 'mozillaabpersonalpha', 'mozillaorgperson', 'evolutionperson',
+ 'univentionperson', array('univentionobject', 'univentionObjectType' => 'users/user'),
),
'group' => array(
'top','posixgroup','groupofnames'
+ ),
+ 'group-if-supported' => array( // these classes get added, if servers supports them
+ 'univentiongroup', array('univentionobject', 'univentionObjectType' => 'groups/group'),
)
);
/**
* Classes allowing to set a mail-address for a group and specify the memberaddresses as forwarding addresses
*
+ * $objectclass => $forward
+ * $objectclass => [$forward, $extra_attr, $mail_attr, $keep_objectclass]
+ * $forward : name of attribute to set forwards for members mail addresses, false if not used/required
+ * $extra_attr : required attribute (eg. 'uid'), which need to be set, default none
+ * $mail_attr : name of attribute for mail-address, if not 'mail'
+ * $keep_objectclass : true to not remove objectclass, if not mail set
+ *
* @var array
*/
var $group_mail_classes = array(
@@ -99,6 +110,7 @@ class accounts_ldap
'dbmailuser' => array('mailforwardingaddress','uid'),
'qmailuser' => array('mailforwardingaddress','uid'),
'mailaccount' => 'mailalias',
+ 'univentiongroup' => array(false, false, 'mailprimaryaddress', true),
);
/**
@@ -200,10 +212,7 @@ class accounts_ldap
else
{
$old = ldap::result2array($old[0]);
- foreach($old['objectclass'] as $n => $class)
- {
- $old['objectclass'][$n] = strtolower($class);
- }
+ $old['objectclass'] = array_map('strtolower', $old['objectclass']);
$key = false;
if ($is_group && ($key = array_search('namedobject',$old['objectclass'])) !== false ||
$is_group && ($old['cn'] != $data_utf8['account_lid'] || substr($old['dn'],0,3) != 'cn=') ||
@@ -243,13 +252,20 @@ class accounts_ldap
{
$to_write['objectclass'] = $old ? $old['objectclass'] : array();
}
- if (!$old && !$is_group) // for new accounts add additional addressbook object classes, if supported by server
+ if (!$old) // for new accounts add additional addressbook object classes, if supported by server
{ // as setting them later might loose eg. password, if we are not allowed to read them
- foreach($this->requiredObjectClasses['user-if-supported'] as $additional)
+ foreach($this->requiredObjectClasses[$is_group?'group-if-supported':'user-if-supported'] as $additional)
{
+ $add = array();
+ if (is_array($additional))
+ {
+ $add = $additional;
+ $additional = array_shift($add);
+ }
if ($this->ldapServerInfo->supportsObjectClass($additional))
{
$to_write['objectclass'][] = $additional;
+ if ($add) $to_write += $add;
}
}
}
@@ -269,20 +285,22 @@ class accounts_ldap
$to_write = $this->_merge_group($to_write,$data_utf8);
$data['account_type'] = 'g';
- $groupOfNames = in_array('groupofnames',$old ? $old['objectclass'] : $to_write['objectclass']);
- if (!$old && $groupOfNames || $members)
+ $objectclass = $old ? $old['objectclass'] : $to_write['objectclass'];
+ if ($members || !$old && array_intersect(array('groupofnames','groupofuniquenames','univentiongroup'), $objectclass))
{
- $to_write = array_merge($to_write,$this->set_members($members,
- $data['account_id'],$groupOfNames,$dn));
+ $to_write = array_merge($to_write, $this->set_members($members, $data['account_id'], $objectclass, $dn));
}
// check if we should set a mail address and forwards for each member
foreach($this->group_mail_classes as $objectclass => $forward)
{
+ $extra_attr = false;
+ $mail_attr = 'mail';
+ $keep_objectclass = false;
+ if (is_array($forward)) list($forward,$extra_attr,$mail_attr,$keep_objectclass) = $forward;
+
if ($this->ldapServerInfo->supportsObjectClass($objectclass) &&
- ($old && in_array($objectclass,$old['objectclass']) || $data_utf8['account_email'] || $old['mail']))
+ ($old && in_array($objectclass,$old['objectclass']) || $data_utf8['account_email'] || $old[$mail_attr]))
{
- $extra_attr = false;
- if (is_array($forward)) list($forward,$extra_attr) = $forward;
if ($data_utf8['account_email']) // setting an email
{
if (!in_array($objectclass,$old ? $old['objectclass'] : $to_write['objectclass']))
@@ -291,23 +309,27 @@ class accounts_ldap
$to_write['objectclass'][] = $objectclass;
}
if ($extra_attr) $to_write[$extra_attr] = $data_utf8['account_lid'];
- $to_write['mail'] = $data_utf8['account_email'];
+ $to_write[$mail_attr] = $data_utf8['account_email'];
- if (!$members) $members = $this->members($data['account_id']);
- $to_write[$forward] = array();
- foreach (array_keys($members) as $member)
+ if ($forward)
{
- if (($email = $this->id2name($member,'account_email')))
+ if (!$members) $members = $this->members($data['account_id']);
+ $to_write[$forward] = array();
+ foreach (array_keys($members) as $member)
{
- $to_write[$forward][] = $email;
+ if (($email = $this->id2name($member,'account_email')))
+ {
+ $to_write[$forward][] = $email;
+ }
}
}
}
elseif($old) // remove the mail and forwards only for existing entries
{
- $to_write['mail'] = $to_write[$forward] = array();
+ $to_write[$mail_attr] = array();
+ if ($forward) $to_write[$forward] = array();
if ($extra_attr) $to_write[$extra_attr] = array();
- if (($key = array_search($objectclass,$old['objectclass'])))
+ if (!$keep_objectclass && ($key = array_search($objectclass,$old['objectclass'])))
{
$to_write['objectclass'] = $old['objectclass'];
unset($to_write['objectclass'][$key]);
@@ -360,8 +382,6 @@ class accounts_ldap
if ($err)
{
error_log(__METHOD__."() ldap_".($old ? 'modify' : 'add')."(,'$dn',".array2string($to_write).") --> ldap_error()=".ldap_error($this->ds));
- echo "ldap_".($old ? 'modify' : 'add')."(,$dn,".print_r($to_write,true).")\n";
- echo ldap_error($this->ds);
return false;
}
}
@@ -410,8 +430,23 @@ class accounts_ldap
*/
protected function _read_group($account_id)
{
+ $mail_attr = 'mail';
+ $group = array();
+ if (!is_object($this->ldapServerInfo))
+ {
+ $this->ldapServerInfo = $this->ldap->getLDAPServerInfo($this->frontend->config['ldap_host']);
+ }
+ foreach($this->group_mail_classes as $objectclass => $attrs)
+ {
+ if ($this->ldapServerInfo->supportsObjectClass($objectclass))
+ {
+ $group['mailAllowed'] = $objectclass;
+ if (is_array($attrs) && $attrs[2]) $mail_attr = $attrs[2];
+ break;
+ }
+ }
$sri = ldap_search($this->ds, $this->group_context,'(&(objectClass=posixGroup)(gidnumber=' . abs($account_id).'))',
- array('dn','gidnumber','cn','objectclass','mail'));
+ array('dn', 'gidnumber', 'cn', 'objectclass', $mail_attr, 'memberuid'));
$ldap_data = ldap_get_entries($this->ds, $sri);
if (!$ldap_data['count'])
@@ -419,8 +454,9 @@ class accounts_ldap
return false; // group not found
}
$data = translation::convert($ldap_data[0],'utf-8');
+ unset($data['objectclass']['count']);
- $group = array(
+ $group += array(
'account_dn' => $data['dn'],
'account_id' => -$data['gidnumber'][0],
'account_lid' => $data['cn'][0],
@@ -428,21 +464,24 @@ class accounts_ldap
'account_firstname' => $data['cn'][0],
'account_lastname' => lang('Group'),
'account_fullname' => lang('Group').' '.$data['cn'][0],
- 'groupOfNames' => in_array('groupOfNames',$data['objectclass']),
- 'account_email' => $data['mail'][0],
+ 'objectclass' => array_map('strtolower', $data['objectclass']),
+ 'account_email' => $data[$mail_attr][0],
+ 'members' => array(),
);
- if (!is_object($this->ldapServerInfo))
+
+ if (isset($data['memberuid']))
{
- $this->ldapServerInfo = $this->ldap->getLDAPServerInfo($this->frontend->config['ldap_host']);
- }
- foreach(array_keys($this->group_mail_classes) as $objectclass)
- {
- if ($this->ldapServerInfo->supportsObjectClass($objectclass))
+ unset($data['memberuid']['count']);
+
+ foreach($data['memberuid'] as $lid)
{
- $group['mailAllowed'] = $objectclass;
- break;
+ if (($id = $this->name2id($lid, 'account_lid', 'u')))
+ {
+ $group['members'][$id] = $lid;
+ }
}
}
+
return $group;
}
@@ -745,7 +784,7 @@ class accounts_ldap
'account_modified' => isset($allVals['modifytimestamp'][0]) ? self::accounts_ldap2ts($allVals['modifytimestamp'][0]) : null,
'account_primary_group' => (string)-$allVals['gidnumber'][0],
);
- error_log(__METHOD__."() ldap=".array2string($allVals)." --> account=".array2string($account));
+ //error_log(__METHOD__."() ldap=".array2string($allVals)." --> account=".array2string($account));
if ($param['active'] && !$this->frontend->is_active($account))
{
if (isset($totalcount)) --$totalcount;
@@ -755,7 +794,7 @@ class accounts_ldap
// return objectclass(es)
if ($param['objectclass'])
{
- $account['objectclass'] = $allVals['objectclass'];
+ $account['objectclass'] = array_map('strtolower', $allVals['objectclass']);
unset($account['objectclass']['count']);
}
$accounts[$account['account_id']] = $account;
@@ -1005,9 +1044,11 @@ class accounts_ldap
$members = array();
if (isset($group[0]['memberuid']))
{
+ unset($group[0]['memberuid']['count']);
+
foreach($group[0]['memberuid'] as $lid)
{
- if (($id = $this->name2id($lid)))
+ if (($id = $this->name2id($lid, 'account_lid', 'u')))
{
$members[$id] = $lid;
}
@@ -1054,34 +1095,47 @@ class accounts_ldap
*
* @param array $members array with uidnumber or uid's
* @param int $gid gidnumber of group to set
- * @param boolean $groupOfNames =null should we set the member attribute of groupOfNames (default detect it)
+ * @param array $objectclass =null should we set the member and uniqueMember attributes (groupOf(Unique)Names|univentionGroup) (default detect it)
* @param string $use_cn =null if set $cn is used instead $gid and the attributes are returned, not written to ldap
+ * @param boolean $uniqueMember =null should we set the uniqueMember attribute (default detect it)
* @return boolean/array false on failure, array or true otherwise
*/
- function set_members($members,$gid,$groupOfNames=null,$use_cn=null)
+ function set_members($members, $gid, array $objectclass=null, $use_cn=null)
{
//echo "accounts_ldap::set_members(".print_r($members,true).",$gid)
\n";
if (!($cn = $use_cn) && !($cn = $this->id2name($gid))) return false;
- // do that group is a groupOfNames?
- if (is_null($groupOfNames)) $groupOfNames = $this->id2name($gid,'groupOfNames');
+ // do that group is a groupOf(Unique)Names or univentionGroup?
+ if (is_null($objectclass)) $objectclass = $this->id2name($gid,'objectclass');
$to_write = array('memberuid' => array());
foreach((array)$members as $key => $member)
{
+ $member_dn = $this->id2name($member, 'account_dn');
if (is_numeric($member)) $member = $this->id2name($member);
if ($member)
{
$to_write['memberuid'][] = $member;
- if ($groupOfNames) $to_write['member'][] = 'uid='.$member.','.$this->user_context;
+ if (in_array('groupofnames', $objectclass))
+ {
+ $to_write['member'][] = $member_dn;
+ }
+ if (array_intersect(array('groupofuniquenames','univentiongroup'), $objectclass))
+ {
+ $to_write['uniquemember'][] = $member_dn;
+ }
}
}
- if ($groupOfNames && !$to_write['member'])
+ // hack as groupOfNames requires the member attribute
+ if (in_array('groupofnames', $objectclass) && !$to_write['member'])
{
- // hack as groupOfNames requires the member attribute
$to_write['member'][] = 'uid=dummy'.','.$this->user_context;
}
+ if (array_intersect(array('groupofuniquenames','univentiongroup'), $objectclass) && !$to_write['uniquemember'])
+ {
+ $to_write['uniquemember'][] = 'uid=dummy'.','.$this->user_context;
+ }
if ($use_cn) return $to_write;
// set the member email addresses as forwards
@@ -1091,10 +1145,13 @@ class accounts_ldap
if (is_array($forward)) list($forward,$extra_attr) = $forward;
if ($extra_attr && ($uid = $this->id2name($gid))) $to_write[$extra_attr] = $uid;
- $to_write[$forward] = array();
- foreach($members as $key => $member)
+ if ($forward)
{
- if (($email = $this->id2name($member,'account_email'))) $to_write[$forward][] = $email;
+ $to_write[$forward] = array();
+ foreach($members as $key => $member)
+ {
+ if (($email = $this->id2name($member,'account_email'))) $to_write[$forward][] = $email;
+ }
}
}
if (!ldap_modify($this->ds,'cn='.ldap::quote($cn).','.$this->group_context,$to_write))
diff --git a/phpgwapi/inc/class.accounts_univention.inc.php b/phpgwapi/inc/class.accounts_univention.inc.php
new file mode 100644
index 0000000000..bfafc8a426
--- /dev/null
+++ b/phpgwapi/inc/class.accounts_univention.inc.php
@@ -0,0 +1,82 @@
+
+ *
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @package api
+ * @subpackage accounts
+ * @version $Id$
+ */
+
+/**
+ * Univention LDAP Backend for accounts
+ *
+ * This backend is mostly identical to LDAP backend and need to be configured in the same way.
+ * Only difference is that new users get created via univention-directory-manager CLI program,
+ * to generate necesary Kerberos stuff.
+ */
+class accounts_univention extends accounts_ldap
+{
+ /**
+ * Name of binary to call
+ */
+ const DIRECTORY_MANAGER_BIN = '/usr/sbin/univention-directory-manager';
+
+ /**
+ * Saves / adds the data of one account
+ *
+ * If no account_id is set in data the account is added and the new id is set in $data.
+ *
+ * @param array $data array with account-data
+ * @return int|boolean the account_id or false on error
+ */
+ function save(&$data)
+ {
+ if (!$data['account_id'] && $data['account_type'] !== 'g' && self::available())
+ {
+ $params = array(
+ 'users/user','create',
+ '--binddn', $this->frontend->config['ldap_root_dn'],
+ '--bindpwd', 5=>$this->frontend->config['ldap_root_pw'],
+ '--position', $this->frontend->config['ldap_context'],
+ '--set', 'username='.$data['account_lid'],
+ '--set', 'firstname='.$data['account_firstname'],
+ '--set', 'lastname='.$data['account_lastname'],
+ );
+ if ($data['account_email'])
+ {
+ $params[] = '--set'; $params[] = 'mailPrimaryAddress='.$data['account_email'];
+ }
+ if (!empty($data['account_passwd']))
+ {
+ $params[] = '--set'; $params[] = 'password='.$data['account_passwd'];
+ }
+ $cmd = self::DIRECTORY_MANAGER_BIN.' '.implode(' ', array_map('escapeshellarg', $params));
+ $output_arr = $ret = $matches = null;
+ exec($cmd, $output_arr, $ret);
+ $output = explode("\n", $output_arr);
+ if ($ret || !preg_match('/^Object created: (uid=.*)$/mui', $output, $matches))
+ {
+ $params[5] = '********'; // mask out password!
+ $cmd = self::DIRECTORY_MANAGER_BIN.' '.implode(' ', array_map('escapeshellarg', $params));
+ throw new egw_exception_wrong_userinput($cmd."\nreturned\n".$output);
+ }
+ $data['account_dn'] = $matches[1];
+ $data['account_id'] = $this->name2id($matches[1], 'account_dn', 'u');
+ }
+ return parent::save($data);
+ }
+
+ /**
+ * Check if our function depending on an external binary is available
+ *
+ * @return boolean
+ */
+ public static function available()
+ {
+ return file_exists(self::DIRECTORY_MANAGER_BIN) && is_executable(self::DIRECTORY_MANAGER_BIN);
+ }
+}
diff --git a/setup/inc/class.setup_cmd_config.inc.php b/setup/inc/class.setup_cmd_config.inc.php
index 26383250d2..6cfc89d613 100644
--- a/setup/inc/class.setup_cmd_config.inc.php
+++ b/setup/inc/class.setup_cmd_config.inc.php
@@ -82,10 +82,15 @@ class setup_cmd_config extends setup_cmd
{
if (substr($name, 0, 4) == 'acc_') continue;
+ $app = 'phpgwapi';
+ if (strpos($name, '/') !== false)
+ {
+ list($app, $name) = explode('/', $name);
+ }
self::$egw_setup->db->insert(self::$egw_setup->config_table,array(
'config_value' => $value,
),array(
- 'config_app' => 'phpgwapi',
+ 'config_app' => $app,
'config_name' => $name,
),__LINE__,__FILE__);
}
@@ -194,6 +199,7 @@ class setup_cmd_config extends setup_cmd
'--ldap-context' => 'ldap_context',
'--ldap-search-filter' => 'ldap_search_filter',
'--ldap-group-context' => 'ldap_group_context',
+ '--sambaadmin-sid' => 'sambaadmin/sambaSID',
'--allow-remote-admin' => 'allow_remote_admin',
'--install-id' => 'install_id',
'--ads-host' => 'ads_host',
@@ -397,10 +403,14 @@ class setup_cmd_config extends setup_cmd
{
if (is_array($data) && isset($data['allowed']))
{
- if ($data['name'] == 'auth_type')
+ switch ($data['name'])
{
- $options[$data['name']] = self::auth_types();
- continue;
+ case 'auth_type':
+ $options[$data['name']] = self::auth_types();
+ continue 2;
+ case 'account_repository':
+ $options[$data['name']] = self::account_repositries();
+ continue 2;
}
foreach($data['allowed'] as $label => $value)
{
@@ -460,6 +470,37 @@ class setup_cmd_config extends setup_cmd
return $auth_types;
}
+ /**
+ * Read auth-types (existing auth backends) from filesystem and fix our $options array
+ *
+ * @return array
+ */
+ static function account_repositories()
+ {
+ static $account_repositories = array(
+ 'sql' => 'SQL',
+ 'ldap' => 'LDAP',
+ 'ads' => 'Active Directory',
+ );
+ static $scan_done = null;
+ if (!$scan_done++)
+ {
+ // now add auth backends found in filesystem
+ foreach(scandir(EGW_INCLUDE_ROOT.'/phpgwapi/inc') as $file)
+ {
+ $matches = null;
+ if (preg_match('/^class\.accounts_([a-z]+)\.inc\.php$/', $file, $matches) &&
+ !isset($account_repositories[$matches[1]]) &&
+ class_exists($class='accounts_'.$matches[1]) &&
+ (!is_callable($callable=$class.'::available') || call_user_func($callable)))
+ {
+ $account_repositories[$matches[1]] = ucfirst($matches[1]);
+ }
+ }
+ }
+ return $account_repositories;
+ }
+
/**
* Return the defaults from the $options array
*
diff --git a/setup/inc/hook_config.inc.php b/setup/inc/hook_config.inc.php
index 6bc2912a04..0779a36d69 100644
--- a/setup/inc/hook_config.inc.php
+++ b/setup/inc/hook_config.inc.php
@@ -185,6 +185,17 @@ function auth_type_activesync($config)
return _options_from(setup_cmd_config::auth_types(),$config['auth_type_activesync']);
}
+/**
+ * Make account-repository-types from setup_cmd_config available
+ *
+ * @param array $config
+ * @return string
+ */
+function account_repository($config)
+{
+ return _options_from(setup_cmd_config::account_repositories(), $config['account_repository']);
+}
+
/**
* Returns options string
*
diff --git a/setup/templates/default/config.tpl b/setup/templates/default/config.tpl
index 389349ce96..65723c6abd 100644
--- a/setup/templates/default/config.tpl
+++ b/setup/templates/default/config.tpl
@@ -173,9 +173,7 @@
{lang_Select_where_you_want_to_store/retrieve_user_accounts}: |
|