From: Gunnar Wrobel
Date: Wed, 11 Feb 2009 19:53:25 +0000 (+0000)
Subject: Initial import of Kolab_Sever from Horde cvs HEAD.
X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=36d4183828de3842e1672121797533c82dc93f61;p=horde.git
Initial import of Kolab_Sever from Horde cvs HEAD.
---
diff --git a/framework/Kolab_Server/doc/Horde/Kolab/Server/usage.txt b/framework/Kolab_Server/doc/Horde/Kolab/Server/usage.txt
new file mode 100644
index 000000000..6980b2715
--- /dev/null
+++ b/framework/Kolab_Server/doc/Horde/Kolab/Server/usage.txt
@@ -0,0 +1,113 @@
+The "Horde_Kolab_Server" package allows you to access the Kolab
+Server user database.
+
+
+Installation of the package
+===========================
+
+The package is being distributed as a standard PEAR package by the
+Horde project. As long as you have PEAR installed, installation should
+be straight forward.
+
+ pear channel-discover pear.horde.org
+ pear install --force channel://pear.horde.org/Horde_Kolab_Server
+
+"pear" will probably complain about the library (and its dependencies)
+not being marked stable yet but the "--force" option allows to ignore
+these warnings.
+
+
+Using the package
+===========================
+
+This section will present some examples on how to fetch information
+from the Kolab Server database.
+
+Currently the package only provides a LDAP and a test driver as
+possible backends. The test driver is for internal testing purposes
+only and emulates a LDAP system without requiring access to a real
+LDAP database. So most developers will use the LDAP back end. In order
+to enable the driver some basic configuration is required.
+
+Configurations for Horde are always set in the global "$conf"
+variable. A possible configuration for "Horde_Kolab_Server" could look
+like this:
+
+ global $conf;
+ $conf['kolab']['server']['driver'] = 'ldap';
+ $conf['kolab']['server']['params']['server'] = 'example.com';
+ $conf['kolab']['server']['params']['base_dn'] = 'dc=example,dc=com';
+ $conf['kolab']['server']['params']['bind_dn'] = 'cn=nobody,cn=internal,dc=example,dc=com';
+ $conf['kolab']['server']['params']['bind_pw'] = 'MY_VERY_SECRET_PASSWORD';
+
+With this base configuration the developer can load the code and
+initialize the server object:
+
+ require_once 'Horde/Kolab/Server.php';
+ $server = Horde_Kolab_Server::singleton();
+
+This server handler serves two primary purposes:
+
+ * Identifying the ID of an object on basis of a given attribute
+ * Fetching an object from the database based on the ID
+
+A common request to the Kolab LDAP database is to return the ID of a
+user given a mail address. The "$server" object will return the "DN"
+("distinguished name") of the object in the LDAP database:
+
+ $dn = $server->dnForMailAddress('wrobel@example.com');
+
+The returned "DN" could look like this:
+
+ var_dump($dn);
+
+ string(32) "cn=Gunnar Wrobel,dc=example,dc=com"
+
+For alternative methods of retrieving a particular "DN" you should
+consult the detailed package documentation (see below).
+
+This "DN" can now be used to fetch the object from the database:
+
+ $object = $server->fetch($dn);
+
+The "$server" object will always return an object of class
+"Horde_Kolab_Server_Object". But you will receive a sub class of this
+parent class that matches the type of the object. For the example
+given above the server handler returns an object of type
+"Horde_Kolab_Server_Object_user":
+
+ var_dump(get_class($object));
+
+ string(30) "Horde_Kolab_Server_Object_user"
+
+Depending on the sub class such an object presents different
+features. One primary method is common to all the objects though:
+Fetching attribute values with "get()".
+
+ $attr = $object->get(KOLAB_ATTR_CN);
+
+"KOLAB_ATTR_CN" is being used to retrieve the "common name" of the
+object:
+
+ var_dump($attr);
+
+ string(13) "Gunnar Wrobel"
+
+For additional details about the different objects and attribute types
+you should consult the detailed package documentation (see below).
+
+
+Detailed package documentation
+==============================
+
+A detailed documentation based on the code comments and extracted via
+phpDocumentor can be found at
+http://dev.horde.org/api/framework/. Simply select the package
+"Kolab_Server" in the package selection box in the upper right
+corner.
+
+
+ToDo
+====
+
+Currently the package only implements read access. At some point it should allow write access, too. This is required to convert the Kolab webadmin to use this package.
diff --git a/framework/Kolab_Server/examples/Horde/Kolab/Server/server.php b/framework/Kolab_Server/examples/Horde/Kolab/Server/server.php
new file mode 100644
index 000000000..55c7c3029
--- /dev/null
+++ b/framework/Kolab_Server/examples/Horde/Kolab/Server/server.php
@@ -0,0 +1,35 @@
+dnForMailAddress('wrobel@example.com');
+var_dump($dn);
+
+/** Fetch the corresponding object */
+$object = $server->fetch($dn);
+var_dump(get_class($object));
+
+/** Display object attributes */
+var_dump($object->get(KOLAB_ATTR_CN));
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/IMAP.php b/framework/Kolab_Server/lib/Horde/Kolab/IMAP.php
new file mode 100644
index 000000000..9c4bcc52b
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/IMAP.php
@@ -0,0 +1,139 @@
+
+ * @author Thomas Jarosch
+ * @package Kolab_Storage
+ */
+class Horde_Kolab_IMAP {
+
+ /**
+ * IMAP server to connect to.
+ *
+ * @var string
+ */
+ var $_server;
+
+ /**
+ * IMAP server port to connect to.
+ *
+ * @var int
+ */
+ var $_port;
+
+ /**
+ * IMAP connection.
+ *
+ * @var mixed
+ */
+ var $_imap;
+
+ /**
+ * Connection reuse detection signature.
+ *
+ * @var string
+ */
+ var $_reuse_detection;
+
+ /**
+ * Constructor.
+ *
+ * @param string $server Server to connect to
+ * @param int $port Port to connect to
+ */
+ function Horde_Kolab_IMAP($server, $port)
+ {
+ $this->_server = $server;
+ $this->_port = $port;
+ }
+
+ /**
+ * Attempts to return a reference to a concrete Horde_Kolab_IMAP instance.
+ * It will only create a new instance if no Horde_Kolab_IMAP instance
+ * exists.
+ *
+ * @static
+ *
+ * @param string $server Server name
+ * @param int $port Port
+ * @param boolean $annotation_required Do we actually need
+ * the annotation calls?
+ *
+ * @return Horde_Kolab_IMAP|PEAR_Error The concrete reference.
+ */
+ function &singleton($server, $port, $annotation_required = true)
+ {
+ static $instances = array();
+
+ /**
+ * There are Kolab specific PHP functions available that make the IMAP
+ * access more efficient. If these are detected, or if they are not
+ * required for the current operation, the PHP IMAP implementation
+ * should be used.
+ *
+ * The c-client Kolab driver provides quicker IMAP routines so is
+ * preferable whenever possible.
+ */
+ if ($annotation_required) {
+ if (function_exists('imap_status_current')
+ && function_exists('imap_getannotation')) {
+ $driver = 'cclient';
+ } else {
+ $driver = 'pear';
+ }
+ } else {
+ $driver = 'cclient';
+ }
+
+ if (isset($GLOBALS['KOLAB_TESTING'])) {
+ $driver = 'test';
+ }
+
+ $signature = "$server|$port|$driver";
+ if (!isset($instances[$signature])) {
+ $instances[$signature] = &Horde_Kolab_IMAP::factory($server, $port, $driver);
+ }
+
+ return $instances[$signature];
+ }
+
+ /**
+ * Attempts to return a concrete Horde_Kolab_IMAP instance based on the
+ * available PHP functionality.
+ *
+ * @param string $server Server name.
+ * @param int $port Server port.
+ * @param string $driver Which driver should we use?
+ *
+ * @return Horde_Kolab_IMAP|PEAR_Error The newly created concrete
+ * Horde_Kolab_IMAP instance.
+ */
+ function &factory($server, $port, $driver = 'cclient')
+ {
+ @include_once dirname(__FILE__) . '/IMAP/' . $driver . '.php';
+
+ $class = 'Horde_Kolab_IMAP_' . $driver;
+ if (class_exists($class)) {
+ $driver = &new $class($server, $port);
+ } else {
+ return PEAR::raiseError(sprintf(_("Failed to load Kolab IMAP driver %s"), $driver));
+ }
+
+ return $driver;
+ }
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/IMAP/cclient.php b/framework/Kolab_Server/lib/Horde/Kolab/IMAP/cclient.php
new file mode 100644
index 000000000..88d3e9ffe
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/IMAP/cclient.php
@@ -0,0 +1,778 @@
+
+ * @author Thomas Jarosch
+ * @package Kolab_Storage
+ */
+class Horde_Kolab_IMAP_cclient extends Horde_Kolab_IMAP {
+
+ /**
+ * Basic IMAP connection string.
+ *
+ * @var string
+ */
+ var $_base_mbox;
+
+ /**
+ * IMAP connection string that includes the folder.
+ *
+ * @var string
+ */
+ var $_mbox;
+
+ /**
+ * The signature of the current connection.
+ *
+ * @var string
+ */
+ var $_signature;
+
+ /**
+ * IMAP user name.
+ *
+ * @var string
+ */
+ var $_login;
+
+ /**
+ * IMAP password.
+ *
+ * @var string
+ */
+ var $_password;
+
+ /**
+ * Connects to the IMAP server.
+ *
+ * @param string $login The user account name.
+ * @param string $password The user password.
+ * @param boolean $tls Should TLS be used for the connection?
+ *
+ * @return boolean|PEAR_Error True in case the connection was opened
+ * successfully.
+ */
+ function connect($login, $password, $tls = false)
+ {
+ $options = '';
+ if (!$tls) {
+ $options = '/notls';
+ }
+
+ $mbox = '{' . $this->_server . ':' . $this->_port
+ . $options . '}';
+
+ $this->_signature = "$mbox|$login|$password";
+ if ($this->_signature == $this->_reuse_detection) {
+ return true;
+ }
+
+ $this->_mbox = $this->_base_mbox = $mbox;
+ $this->_login = $login;
+ $this->_password = $password;
+ $this->_imap = null;
+
+ $this->_reuse_detection = $this->_signature;
+
+ return true;
+ }
+
+ /**
+ * Lazy connect to the IMAP server.
+ *
+ * @return mixed True in case the connection was opened successfully, a
+ * PEAR error otherwise.
+ */
+ function _connect()
+ {
+ $result = @imap_open($this->_base_mbox, $this->_login, $this->_password, OP_HALFOPEN);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Server: %s. Error: %s"), $this->_server, @imap_last_error()));
+ }
+ $this->_imap = $result;
+ return true;
+ }
+
+ /**
+ * Disconnects from the IMAP server. If not really necessary this
+ * should not be called. Once the page got served the connections
+ * should be closed anyhow and if there is a chance to reuse the
+ * connection it should be used.
+ *
+ * @return mixed True in case the connection was closed successfully, a
+ * PEAR error otherwise.
+ */
+ function disconnect()
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $this->_reuse_detection = null;
+
+ $result = @imap_close($this->_imap);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Server: %s. Error: %s"), $this->_server, @imap_last_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Opens the given folder.
+ *
+ * @param string $folder The folder to open.
+ *
+ * @return mixed True in case the folder was opened successfully, a PEAR
+ * error otherwise.
+ */
+ function select($folder)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $this->_mbox = $this->_base_mbox . $folder;
+
+ $result = @imap_reopen($this->_imap, $this->_mbox);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $folder, @imap_last_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Does the given folder exist?
+ *
+ * @param string $folder The folder to check.
+ *
+ * @return mixed True in case the folder exists, false otherwise
+ */
+ function exists($folder)
+ {
+ $folders = $this->getMailboxes();
+ if (is_a($folders, 'PEAR_Error')) {
+ return $folders;
+ }
+ return in_array($folder, $folders);
+ }
+
+ /**
+ * Create the specified folder.
+ *
+ * @param string $folder The folder to create.
+ *
+ * @return mixed True in case the operation was successfull, a
+ * PEAR error otherwise.
+ */
+ function create($folder)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $mbox = $this->_base_mbox . $folder;
+ $result = @imap_createmailbox($this->_imap, $mbox);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $folder, @imap_last_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Delete the specified folder.
+ *
+ * @param string $folder The folder to delete.
+ *
+ * @return mixed True in case the operation was successfull, a
+ * PEAR error otherwise.
+ */
+ function delete($folder)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $mbox = $this->_base_mbox . $folder;
+ $result = @imap_deletemailbox($this->_imap, $mbox);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $folder, @imap_last_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Rename the specified folder.
+ *
+ * @param string $old The folder to rename.
+ * @param string $new The new name of the folder.
+ *
+ * @return mixed True in case the operation was successfull, a
+ * PEAR error otherwise.
+ */
+ function rename($old, $new)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $result = @imap_renamemailbox($this->_imap,
+ $this->_base_mbox . $old,
+ $this->_base_mbox . $new);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $old, @imap_last_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the status of the current folder.
+ *
+ * @return array An array that contains 'uidvalidity' and 'uidnext'.
+ */
+ function status()
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $status = @imap_status_current($this->_imap, SA_MESSAGES | SA_UIDVALIDITY | SA_UIDNEXT);
+ if (!$status) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $this->_mbox, @imap_last_error()));
+ }
+
+ return array('uidvalidity' => $status->uidvalidity,
+ 'uidnext' => $status->uidnext);
+ }
+
+ /**
+ * Returns the uids of the messages in this folder.
+ *
+ * @return mixed The message ids or a PEAR error in case of an error.
+ */
+ function getUids()
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $uids = @imap_search($this->_imap, 'UNDELETED', SE_UID);
+ if (!is_array($uids)) {
+ $uids = array();
+ }
+
+ return $uids;
+ }
+
+ /**
+ * Searches the current folder using the given list of search criteria.
+ *
+ * @param string $search_list A list of search criteria.
+ *
+ * @return mixed The list of matching message ids or a PEAR error in case
+ * of an error.
+ */
+ function search($search_list)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $result = @imap_search($this->_imap, $search_list, SE_UID);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $this->_mbox, @imap_last_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Searches the headers of the messages. c-client does not allow using
+ * "HEADER" as it is possible with Net/IMAP, so we need a workaround.
+ *
+ * @param string $field The name of the header field.
+ * @param string $value The value that field should match.
+ *
+ * @return mixed The list of matching message ids or a PEAR error in case
+ * of an error.
+ */
+ function searchHeaders($field, $value)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $uids = $this->getUids();
+ if (is_a($uids, 'PEAR_Error')) {
+ return $uids;
+ }
+
+ $result = array();
+ foreach ($uids as $uid) {
+ $header = $this->getMessageHeader($uid, false);
+ if (is_a($header, 'PEAR_Error')) {
+ return $header;
+ }
+ $header_array = MIME_Headers::parseHeaders($header);
+ if (isset($header_array[$field]) && $header_array[$field] == $value) {
+ $result[] = $uid;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Retrieves the message headers for a given message id.
+ *
+ * @param integer $uid The message id.
+ * @param boolean $peek_for_body Prefetch the body.
+ *
+ * @return mixed The message header or a PEAR error in case of an error.
+ */
+ function getMessageHeader($uid, $peek_for_body = true)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $flags = FT_UID;
+ if ($peek_for_body) {
+ $flags |= FT_PREFETCHTEXT;
+ }
+
+ $result = @imap_fetchheader($this->_imap, $uid, $flags);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Message: %s. Error: %s"), $uid, @imap_last_error()));
+ }
+
+ return $result;
+ }
+
+ /**
+ * Retrieves the message body for a given message id.
+ *
+ * @param integer $uid The message id.
+ *
+ * @return mixed The message body or a PEAR error in case of an error.
+ */
+ function getMessageBody($uid)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $result = @imap_body($this->_imap, $uid, FT_UID);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Message: %s. Error: %s"), $uid, @imap_last_error()));
+ }
+
+ return $result;
+ }
+
+ /**
+ * Retrieves the full message text for a given message id.
+ *
+ * @param integer $uid The message id.
+ *
+ * @return mixed The message text or a PEAR error in case of an error.
+ */
+ function getMessage($uid)
+ {
+ $header = $this->getMessageHeader($uid);
+ if (is_a($header, 'PEAR_Error')) {
+ return $header;
+ }
+
+ $body = $this->getMessageBody($uid);
+ if (is_a($body, 'PEAR_Error')) {
+ return $body;
+ }
+
+ return $header . $body;
+ }
+
+ /**
+ * Retrieves a list of mailboxes on the server.
+ *
+ * @return mixed The list of mailboxes or a PEAR error in case of an
+ * error.
+ */
+ function getMailboxes()
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $folders = array();
+
+ $result = @imap_list($this->_imap, $this->_base_mbox, '*');
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $this->_base_mbox, @imap_last_error()));
+ }
+
+ $server_len = strlen($this->_base_mbox);
+ foreach ($result as $folder) {
+ if (substr($folder, 0, $server_len) == $this->_base_mbox) {
+ $folders[] = substr($folder, $server_len);
+ }
+ }
+
+ return $folders;
+ }
+
+ /**
+ * Fetches the annotation on a folder.
+ *
+ * @param string $entries The entry to fetch.
+ * @param string $value The specific value to fetch.
+ * @param string $mailbox_name The name of the folder.
+ *
+ * @return mixed The annotation value or a PEAR error in case of an error.
+ */
+ function getAnnotation($entries, $value, $mailbox_name)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ static $annotations = array();
+
+ $signature = "$this->_signature|$entries|$value|$mailbox_name";
+
+ if (!isset($annotations[$signature])) {
+ $result = @imap_getannotation($this->_imap, $mailbox_name, $entries, $value);
+ if (isset($result[$value])) {
+ $annotations[$signature] = $result[$value];
+ } else {
+ $annotations[$signature] = '';
+ }
+ }
+
+ return $annotations[$signature];
+ }
+
+ /**
+ * Sets the annotation on a folder.
+ *
+ * @param string $entries The entry to set.
+ * @param array $values The values to set
+ * @param string $mailbox_name The name of the folder.
+ *
+ * @return mixed True if successfull, a PEAR error otherwise.
+ */
+ function setAnnotation($entries, $values, $mailbox_name)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ foreach ($values as $key => $value) {
+ $result = @imap_setannotation($this->_imap, $mailbox_name, $entries, $key, $value);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $mailbox_name, @imap_last_error()));
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Retrieve the access rights from a folder
+ *
+ * @param string $folder The folder to retrieve the ACLs from.
+ *
+ * @return mixed An array of rights if successfull, a PEAR error
+ * otherwise.
+ */
+ function getACL($folder)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $result = @imap_getacl($this->_imap, $folder);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $folder, @imap_last_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Retrieve the access rights from a folder not owned by the current user
+ *
+ * @param string $folder The folder to retrieve the ACLs from.
+ *
+ * @return mixed An array of rights if successfull, a PEAR error
+ * otherwise.
+ */
+ function getMyRights($folder)
+ {
+ if (!function_exists('imap_myrights')) {
+ return PEAR::raiseError(sprintf(_("PHP does not support imap_myrights."), $folder, @imap_last_error()));
+ }
+
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $result = @imap_myrights($this->_imap, $folder);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $folder, @imap_last_error()));
+ }
+ return $result;
+
+ }
+
+ /**
+ * Set the access rights for a folder
+ *
+ * @param string $folder The folder to retrieve the ACLs from.
+ * @param string $user The user to set the ACLs for
+ * @param string $acl The ACLs
+ *
+ * @return mixed True if successfull, a PEAR error otherwise.
+ */
+ function setACL($folder, $user, $acl)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $result = @imap_setacl($this->_imap, $folder, $user, $acl);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $folder, @imap_last_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Delete the access rights for a user.
+ *
+ * @param string $folder The folder that should be modified.
+ * @param string $user The user that should get the ACLs removed
+ *
+ * @return mixed True if successfull, a PEAR error otherwise.
+ */
+ function deleteACL($folder, $user)
+ {
+ return $this->setACL($folder, $user, '');
+ }
+
+ /**
+ * Appends a message to the current folder.
+ *
+ * @param string $msg The message to append.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function appendMessage($msg)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $result = @imap_append($this->_imap, $this->_mbox, $msg);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $this->_mbox, @imap_last_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Copies a message to a new folder.
+ *
+ * @param integer $uid IMAP message id.
+ * @param string $new_folder Target folder.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function copyMessage($uid, $new_folder)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $result = @imap_mail_copy($this->_imap, $uid, $new_folder, CP_UID);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $new_folder, @imap_last_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Moves a message to a new folder.
+ *
+ * @param integer $uid IMAP message id.
+ * @param string $new_folder Target folder.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function moveMessage($uid, $new_folder)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $result = @imap_mail_move($this->_imap, $uid, $new_folder, CP_UID);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Folder: %s. Error: %s"), $new_folder, @imap_last_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Deletes messages from the current folder.
+ *
+ * @param integer $uids IMAP message ids.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function deleteMessages($uids)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ if (!is_array($uids)) {
+ $uids = array($uids);
+ }
+
+ foreach($uids as $uid) {
+ $result = @imap_delete($this->_imap, $uid, FT_UID);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Message: %s. Error: %s"), $uid, @imap_last_error()));
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Undeletes a message in the current folder.
+ *
+ * @param integer $uid IMAP message id.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function undeleteMessages($uid)
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $result = @imap_undelete($this->_imap, $uid, FT_UID);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Message: %s. Error: %s"), $uid, @imap_last_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Expunges messages in the current folder.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function expunge()
+ {
+ if (!isset($this->_imap)) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $result = @imap_expunge($this->_imap);
+ if (!$result) {
+ return PEAR::raiseError(sprintf(_("IMAP error. Message: %s. Error: %s"), $this->_mbox, @imap_last_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Return the currently selected mailbox
+ *
+ * @return string The mailbox currently selected
+ */
+ function current()
+ {
+ return $this->_mbox;
+ }
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/IMAP/pear.php b/framework/Kolab_Server/lib/Horde/Kolab/IMAP/pear.php
new file mode 100644
index 000000000..49b911ece
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/IMAP/pear.php
@@ -0,0 +1,520 @@
+= 1.0.3 of Net_IMAP (i.e. a
+ * version that includes support for the ANNOTATEMORE IMAP extension). The
+ * latest version of Net_IMAP can be obtained from
+ * http://pear.php.net/get/Net_IMAP
+ */
+require_once 'Net/IMAP.php';
+
+/**
+ * The Horde_Kolab_IMAP_Connection_pear class connects to an IMAP server using the
+ * Net_IMAP PEAR package.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/IMAP/pear.php,v 1.2 2009/01/06 17:49:25 jan Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Gunnar Wrobel
+ * @author Thomas Jarosch
+ * @package Kolab_Storage
+ */
+class Horde_Kolab_IMAP_pear extends Horde_Kolab_IMAP {
+
+ /**
+ * The signature of the current connection
+ *
+ * @var string
+ */
+ var $_signature;
+
+ /**
+ * Connects to the IMAP server.
+ *
+ * @param string $login The user account name.
+ * @param string $password The user password.
+ * @param boolean $tls Should TLS be used for the connection?
+ *
+ * @return mixed True in case the connection was opened successfully, a
+ * PEAR error otherwise.
+ */
+ function connect($login, $password, $tls = false)
+ {
+ $this->_signature = $this->_server . '|' . $this->_port . "|$login|$password|$tls";
+
+ // Reuse existing connection?
+ if ($this->_signature == $this->_reuse_detection) {
+ return true;
+ }
+
+ $this->_imap = &new Net_IMAP($this->_server, $this->_port);
+ $result = $this->_imap->login($login, $password, true, false);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ $this->_reuse_detection = $this->_signature;
+
+ return true;
+ }
+
+ /**
+ * Disconnects from the IMAP server.
+ *
+ * @return mixed True in case the connection was closed successfully, a
+ * PEAR error otherwise.
+ */
+ function disconnect()
+ {
+ $this->_reuse_detection = null;
+ return $this->_imap->disconnect();
+ }
+
+ /**
+ * Opens the given folder.
+ *
+ * @param string $folder The folder to open
+ *
+ * @return mixed True in case the folder was opened successfully, a PEAR
+ * error otherwise.
+ */
+ function select($folder)
+ {
+ return $this->_imap->selectMailbox($folder);
+ }
+
+ /**
+ * Does the given folder exist?
+ *
+ * @param string $folder The folder to check.
+ *
+ * @return mixed True in case the folder exists, false otherwise
+ */
+ function exists($folder)
+ {
+ return $this->_imap->mailboxExist($folder);
+ }
+
+ /**
+ * Create the specified folder.
+ *
+ * @param string $folder The folder to create.
+ *
+ * @return mixed True in case the operation was successfull, a
+ * PEAR error otherwise.
+ */
+ function create($folder)
+ {
+ return $this->_imap->createMailbox($folder);
+ }
+
+ /**
+ * Delete the specified folder.
+ *
+ * @param string $folder The folder to delete.
+ *
+ * @return mixed True in case the operation was successfull, a
+ * PEAR error otherwise.
+ */
+ function delete($folder)
+ {
+ return $this->_imap->deleteMailbox($folder);
+ }
+
+ /**
+ * Rename the specified folder.
+ *
+ * @param string $old The folder to rename.
+ * @param string $new The new name of the folder.
+ *
+ * @return mixed True in case the operation was successfull, a
+ * PEAR error otherwise.
+ */
+ function rename($old, $new)
+ {
+ return $this->_imap->renameMailbox($old, $new);
+ }
+
+ /**
+ * Returns the status of the current folder.
+ *
+ * @return array An array that contains 'uidvalidity' and 'uidnext'.
+ */
+ function status()
+ {
+ $result = array();
+
+ $mailbox = $this->_imap->getCurrentMailbox();
+
+ // Net_IMAP is not very efficent here
+ $ret = $this->_imap->cmdStatus($mailbox, 'UIDVALIDITY');
+ $result['uidvalidity'] = $ret['PARSED']['STATUS']['ATTRIBUTES']['UIDVALIDITY'];
+
+ $ret = $this->_imap->cmdStatus($mailbox, 'UIDNEXT');
+ $result['uidnext'] = $ret['PARSED']['STATUS']['ATTRIBUTES']['UIDNEXT'];
+
+ return $result;
+ }
+
+ /**
+ * Returns the message ids of the messages in this folder.
+ *
+ * @return array The message ids.
+ */
+ function getUids()
+ {
+ $uids = $this->_imap->search('UNDELETED', true);
+ if (!is_array($uids)) {
+ $uids = array();
+ }
+ return $uids;
+ }
+
+ /**
+ * Searches the current folder using the given list of search criteria.
+ *
+ * @param string $search_list A list of search criteria.
+ *
+ * @return mixed The list of matching message ids or a PEAR error in case
+ * of an error.
+ */
+ function search($search_list, $uidSearch = true)
+ {
+ return $this->_imap->search($search_list, $uidSearch);
+ }
+
+ /**
+ * Searches the headers of the messages.
+ *
+ * @param string $field The name of the header field.
+ * @param string $value The value that field should match.
+ *
+ * @return mixed The list of matching message ids or a PEAR error in case
+ * of an error.
+ */
+ function searchHeaders($field, $value)
+ {
+ return $this->_imap->search('HEADER "' . $field . '" "' . $value . '"', true);
+ }
+
+ /**
+ * Retrieves the message headers for a given message id.
+ *
+ * @param int $uid The message id.
+ * @param boolean $peek_for_body Prefetch the body.
+ *
+ * @return mixed The message header or a PEAR error in case of an error.
+ */
+ function getMessageHeader($uid, $peek_for_body = true)
+ {
+ $ret = $this->_imap->cmdUidFetch($uid, 'BODY[HEADER]');
+ if (String::upper($ret['RESPONSE']['CODE']) != 'OK') {
+ return PEAR::raiseError(sprintf(_("Failed fetching headers of IMAP message %s. Error was %s"),
+ $uid,
+ $ret['RESPONSE']['CODE'] . ', ' . $ret['RESPONSE']['STR_CODE']));
+ }
+
+ if (isset($ret['PARSED'])) {
+ foreach ($ret['PARSED'] as $msg) {
+ if (isset($msg['EXT']['BODY[HEADER]']['CONTENT'])) {
+ return $msg['EXT']['BODY[HEADER]']['CONTENT'];
+ }
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Retrieves the message body for a given message id.
+ *
+ * @param integet $uid The message id.
+ *
+ * @return mixed The message body or a PEAR error in case of an error.
+ */
+ function getMessageBody($uid)
+ {
+ $ret = $this->_imap->cmdUidFetch($uid, 'BODY[TEXT]');
+ if (String::upper($ret['RESPONSE']['CODE']) != 'OK') {
+ return PEAR::raiseError(sprintf(_("Failed fetching body of IMAP message %s. Error was %s"),
+ $uid,
+ $ret['RESPONSE']['CODE'] . ', ' . $ret['RESPONSE']['STR_CODE']));
+ }
+
+ if (isset($ret['PARSED'])) {
+ foreach ($ret['PARSED'] as $msg) {
+ if (isset($msg['EXT']['BODY[TEXT]']['CONTENT'])) {
+ return $msg['EXT']['BODY[TEXT]']['CONTENT'];
+ }
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Retrieves the full message text for a given message id.
+ *
+ * @param integer $uid The message id.
+ *
+ * @return mixed The message text or a PEAR error in case of an error.
+ */
+ function getMessage($uid)
+ {
+ $ret = $this->_imap->cmdUidFetch($uid, 'RFC822');
+ if (String::upper($ret['RESPONSE']['CODE']) != 'OK') {
+ return PEAR::raiseError(sprintf(_("Failed fetching IMAP message %s. Error was %s"),
+ $uid,
+ $ret['RESPONSE']['CODE'] . ', ' . $ret['RESPONSE']['STR_CODE']));
+ }
+
+ if (isset($ret['PARSED'])) {
+ foreach ($ret['PARSED'] as $msg) {
+ if (isset($msg['EXT']['RFC822']['CONTENT'])) {
+ return $msg['EXT']['RFC822']['CONTENT'];
+ }
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Retrieves a list of mailboxes on the server.
+ *
+ * @return mixed The list of mailboxes or a PEAR error in case of an
+ * error.
+ */
+ function getMailboxes()
+ {
+ return $this->_imap->getMailboxes();
+ }
+
+ /**
+ * Fetches the annotation on a folder.
+ *
+ * @param string $entries The entry to fetch.
+ * @param string $value The specific value to fetch.
+ * @param string $mailbox_name The name of the folder.
+ *
+ * @return mixed The annotation value or a PEAR error in case of an error.
+ */
+ function getAnnotation($entries, $value, $mailbox_name)
+ {
+ static $annotations = array();
+
+ $signature = "$this->_signature|$entries|$value|$mailbox_name";
+
+ if (!isset($annotations[$signature])) {
+ $annotations[$signature] = $this->_imap->getAnnotation($entries, $value, $mailbox_name);
+ }
+
+ return $annotations[$signature];
+ }
+
+ /**
+ * Sets the annotation on a folder.
+ *
+ * @param string $entries The entry to set.
+ * @param array $values The values to set
+ * @param string $mailbox_name The name of the folder.
+ *
+ * @return mixed True if successfull, a PEAR error otherwise.
+ */
+ function setAnnotation($entries, $values, $mailbox_name)
+ {
+ return $this->_imap->setAnnotation($entries, $values, $mailbox_name);
+ }
+
+ /**
+ * Retrieve the access rights from a folder
+ *
+ * @param string $folder The folder to retrieve the ACLs from.
+ *
+ * @return mixed An array of rights if successfull, a PEAR error
+ * otherwise.
+ */
+ function getACL($folder)
+ {
+ $result = $this->_imap->getACL($folder);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ $acl = array();
+ foreach ($result as $user) {
+ $acl[$user['USER']] = $user['RIGHTS'];
+ }
+ return $acl;
+ }
+
+ /**
+ * Retrieve the access rights on a folder not owned by the current user
+ *
+ * @param string $folder The folder to retrieve the ACLs from.
+ *
+ * @return mixed An array of rights if successfull, a PEAR error
+ * otherwise.
+ */
+ function getMyRights($folder)
+ {
+ $result = $this->_imap->getMyRights($folder);
+ return $result;
+ }
+
+ /**
+ * Set the access rights for a folder
+ *
+ * @param string $folder The folder to retrieve the ACLs from.
+ * @param string $user The user to set the ACLs for
+ * @param string $acl The ACLs
+ *
+ * @return mixed True if successfull, a PEAR error otherwise.
+ */
+ function setACL($folder, $user, $acl)
+ {
+ return $this->_imap->setACL($folder, $user, $acl);
+ }
+
+ /**
+ * Delete the access rights for a user.
+ *
+ * @param string $folder The folder that should be modified.
+ * @param string $user The user that should get the ACLs removed
+ *
+ * @return mixed True if successfull, a PEAR error otherwise.
+ */
+ function deleteACL($folder, $user)
+ {
+ return $this->_imap->deleteACL($folder, $user);
+ }
+
+ /**
+ * Appends a message to the current folder.
+ *
+ * @param string $msg The message to append.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function appendMessage($msg)
+ {
+ return $this->_imap->appendMessage($msg);
+ }
+
+ /**
+ * Copies a message to a new folder.
+ *
+ * @param integer $uid IMAP message id.
+ * @param string $new_folder Target folder.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function copyMessage($uid, $new_folder)
+ {
+ $ret = $this->_imap->cmdUidCopy($uid, $new_folder);
+ if (String::upper($ret['RESPONSE']['CODE']) != 'OK') {
+ return PEAR::raiseError(sprintf(_("IMAP error. Message: %s. Error: %s"),
+ $uid,
+ $ret['RESPONSE']['CODE'] . ', ' . $ret['RESPONSE']['STR_CODE']));
+ }
+ return true;
+ }
+
+ /**
+ * Moves a message to a new folder.
+ *
+ * @param integer $uid IMAP message id.
+ * @param string $new_folder Target folder.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function moveMessage($uid, $new_folder)
+ {
+ $result = $this->copyMessage($uid, $new_folder);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ $result = $this->deleteMessages($uid);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ $result = $this->expunge();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ return true;
+ }
+
+ /**
+ * Deletes messages from the current folder.
+ *
+ * @param integer $uids IMAP message ids.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function deleteMessages($uids)
+ {
+ if (!is_array($uids)) {
+ $uids = array($uids);
+ }
+
+ foreach ($uids as $uid) {
+ $ret = $this->_imap->cmdUidStore($uid, '+FLAGS.SILENT', '\Deleted');
+ if (String::upper($ret['RESPONSE']['CODE']) != 'OK') {
+ return PEAR::raiseError(sprintf(_("IMAP error. Message: %s. Error: %s"),
+ $uid,
+ $ret['RESPONSE']['CODE'] . ', ' . $ret['RESPONSE']['STR_CODE']));
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Undeletes a message in the current folder.
+ *
+ * @param integer $uid IMAP message id.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function undeleteMessages($uid)
+ {
+ $ret = $this->_imap->cmdUidStore($uid, '-FLAGS.SILENT', '\Deleted');
+ if (String::upper($ret['RESPONSE']['CODE']) != 'OK') {
+ return PEAR::raiseError(sprintf(_("IMAP error. Message: %s. Error: %s"),
+ $uid,
+ $ret['RESPONSE']['CODE'] . ', ' . $ret['RESPONSE']['STR_CODE']));
+ }
+ return true;
+ }
+
+ /**
+ * Expunges messages in the current folder.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function expunge()
+ {
+ return $this->_imap->expunge();
+ }
+
+ /**
+ * Return the currently selected mailbox
+ *
+ * @return string The mailbox currently selected
+ */
+ function current()
+ {
+ return $this->_imap->getCurrentMailbox();
+ }
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/IMAP/test.php b/framework/Kolab_Server/lib/Horde/Kolab/IMAP/test.php
new file mode 100644
index 000000000..ee8d0c423
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/IMAP/test.php
@@ -0,0 +1,728 @@
+
+ * @package Kolab_Storage
+ */
+class Horde_Kolab_IMAP_test extends Horde_Kolab_IMAP {
+
+ /**
+ * If we are supposed to be connected this holds the user
+ * credentials and some connection details.
+ *
+ * @var string
+ */
+ var $_connected;
+
+ /**
+ * Login of the current user
+ *
+ * @var string
+ */
+ var $_user;
+
+ /**
+ * The data of the mailbox currently opened
+ *
+ * @var array
+ */
+ var $_mbox = null;
+
+ /**
+ * The name of the mailbox currently opened
+ *
+ * @var array
+ */
+ var $_mboxname = null;
+
+ /**
+ * Prepare the dummy server.
+ *
+ * @param string $login The user account name.
+ * @param string $password The user password.
+ * @param boolean $tls Should TLS be used for the connection?
+ *
+ * @return mixed True in case the connection was opened successfully, a
+ * PEAR error otherwise.
+ */
+ function connect($login, $password, $tls = false)
+ {
+ if (!is_array($GLOBALS['KOLAB_TESTING'])) {
+ /* Simulate an empty IMAP server */
+ $GLOBALS['KOLAB_TESTING'] = array();
+ }
+
+ $tls = ($tls) ? 'tls' : 'notls';
+ $this->_connected = $login . ':' . $password . ':' . $tls;
+ $this->_user = $login;
+ $this->_mbox = null;
+ $this->_mboxname = null;
+ }
+
+ /**
+ * Disconnects from the IMAP server.
+ *
+ * @return mixed True in case the connection was closed successfully, a
+ * PEAR error otherwise.
+ */
+ function disconnect()
+ {
+ $this->_connected = null;
+ }
+
+ function _parseFolder($folder)
+ {
+ if (substr($folder, 0, 5) == 'INBOX') {
+ $user = split('@', $this->_user);
+ return 'user/' . $user[0] . substr($folder, 5);
+ }
+ return $folder;
+ }
+
+ /**
+ * Opens the given folder.
+ *
+ * @param string $folder The folder to open
+ *
+ * @return mixed True in case the folder was opened successfully, a PEAR
+ * error otherwise.
+ */
+ function select($folder)
+ {
+ $folder = $this->_parseFolder($folder);
+ if (!isset($GLOBALS['KOLAB_TESTING'][$folder])) {
+ return PEAR::raiseError(sprintf("IMAP folder %s does not exist!", $folder));
+ }
+ $this->_mbox = &$GLOBALS['KOLAB_TESTING'][$folder];
+ $this->_mboxname = $folder;
+ return true;
+ }
+
+ /**
+ * Does the given folder exist?
+ *
+ * @param string $folder The folder to check.
+ *
+ * @return mixed True in case the folder exists, false otherwise
+ */
+ function exists($folder)
+ {
+ $folder = $this->_parseFolder($folder);
+ if (!isset($GLOBALS['KOLAB_TESTING'][$folder])) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Create the specified folder.
+ *
+ * @param string $folder The folder to create.
+ *
+ * @return mixed True in case the operation was successfull, a
+ * PEAR error otherwise.
+ */
+ function create($folder)
+ {
+ $folder = $this->_parseFolder($folder);
+ if (isset($GLOBALS['KOLAB_TESTING'][$folder])) {
+ return PEAR::raiseError(sprintf("IMAP folder %s does already exist!", $folder));
+ }
+ $GLOBALS['KOLAB_TESTING'][$folder] = array(
+ 'status' => array(
+ 'uidvalidity' => time(),
+ 'uidnext' => 1),
+ 'mails' => array(),
+ 'permissions' => array(),
+ 'annotations' => array(),
+ );
+ return true;
+ }
+
+ /**
+ * Delete the specified folder.
+ *
+ * @param string $folder The folder to delete.
+ *
+ * @return mixed True in case the operation was successfull, a
+ * PEAR error otherwise.
+ */
+ function delete($folder)
+ {
+ $folder = $this->_parseFolder($folder);
+ if (!isset($GLOBALS['KOLAB_TESTING'][$folder])) {
+ return PEAR::raiseError(sprintf("IMAP folder %s does not exist!", $folder));
+ }
+ unset($GLOBALS['KOLAB_TESTING'][$folder]);
+ return true;
+ }
+
+ /**
+ * Rename the specified folder.
+ *
+ * @param string $old The folder to rename.
+ * @param string $new The new name of the folder.
+ *
+ * @return mixed True in case the operation was successfull, a
+ * PEAR error otherwise.
+ */
+ function rename($old, $new)
+ {
+ $old = $this->_parseFolder($old);
+ $new = $this->_parseFolder($new);
+
+ if (!isset($GLOBALS['KOLAB_TESTING'][$old])) {
+ return PEAR::raiseError(sprintf("IMAP folder %s does not exist!", $old));
+ }
+ if (isset($GLOBALS['KOLAB_TESTING'][$new])) {
+ return PEAR::raiseError(sprintf("IMAP folder %s does already exist!", $new));
+ }
+ $GLOBALS['KOLAB_TESTING'][$new] = $GLOBALS['KOLAB_TESTING'][$old];
+ unset($GLOBALS['KOLAB_TESTING'][$old]);
+ return true;
+ }
+
+ /**
+ * Returns the status of the current folder.
+ *
+ * @return array An array that contains 'uidvalidity' and 'uidnext'.
+ */
+ function status()
+ {
+ if (!$this->_mbox) {
+ return PEAR::raiseError("No IMAP folder selected!");
+ }
+ return $this->_mbox['status'];
+ }
+
+ /**
+ * Returns the message ids of the messages in this folder.
+ *
+ * @return array The message ids.
+ */
+ function getUids()
+ {
+ if (!$this->_mbox) {
+ return PEAR::raiseError("No IMAP folder selected!");
+ }
+ $uids = array();
+ foreach ($this->_mbox['mails'] as $uid => $mail) {
+ if (!($mail['flags'] & KOLAB_IMAP_FLAG_DELETED)) {
+ $uids[] = $uid;
+ }
+ }
+ return $uids;
+ }
+
+ /**
+ * Searches the current folder using the given list of search criteria.
+ *
+ * @param string $search_list A list of search criteria.
+ *
+ * @return mixed The list of matching message ids or a PEAR error in case
+ * of an error.
+ */
+ function search($search_list, $uidSearch = true)
+ {
+ if (!$this->_mbox) {
+ return PEAR::raiseError("No IMAP folder selected!");
+ }
+ $uids = array();
+ if (substr($search_list, 0, 7) == 'SUBJECT') {
+ $needle = '^Subject: ' . substr($search_list, 8);
+ foreach ($this->_mbox['mails'] as $uid => $mail) {
+ if (preg_match($needle, $mail['header'])) {
+ $uids[] = $uid;
+ }
+ }
+ } else if (substr($search_list, 0, 6) == 'HEADER') {
+ preg_match('([^ ]*) ([^ ]*)', substr($search_list, 7), $matches);
+ $needle = '^' . $matches[0] . ': ' . $matches[1];
+ foreach ($this->_mbox['mails'] as $uid => $mail) {
+ if (preg_match($needle, $mail['header'])) {
+ $uids[] = $uid;
+ }
+ }
+
+ }
+ return $uids;
+ }
+
+ /**
+ * Searches the headers of the messages.
+ *
+ * @param string $field The name of the header field.
+ * @param string $value The value that field should match.
+ *
+ * @return mixed The list of matching message ids or a PEAR error in case
+ * of an error.
+ */
+ function searchHeaders($field, $value)
+ {
+ return $this->search('HEADER "' . $field . '" "' . $value . '"', true);
+ }
+
+ /**
+ * Retrieves the message headers for a given message id.
+ *
+ * @param int $uid The message id.
+ * @param boolean $peek_for_body Prefetch the body.
+ *
+ * @return mixed The message header or a PEAR error in case of an error.
+ */
+ function getMessageHeader($uid, $peek_for_body = true)
+ {
+ if (!$this->_mbox) {
+ return PEAR::raiseError("No IMAP folder selected!");
+ }
+ if (!isset($this->_mbox['mails'][$uid])) {
+ return PEAR::raiseError(sprintf("No IMAP message %s!", $uid));
+ }
+ return $this->_mbox['mails'][$uid]['header'];
+ }
+
+ /**
+ * Retrieves the message body for a given message id.
+ *
+ * @param integet $uid The message id.
+ *
+ * @return mixed The message body or a PEAR error in case of an error.
+ */
+ function getMessageBody($uid)
+ {
+ if (!$this->_mbox) {
+ return PEAR::raiseError("No IMAP folder selected!");
+ }
+ if (!isset($this->_mbox['mails'][$uid])) {
+ return PEAR::raiseError(sprintf("No IMAP message %s!", $uid));
+ }
+ return $this->_mbox['mails'][$uid]['body'];
+ }
+
+ /**
+ * Retrieves the full message text for a given message id.
+ *
+ * @param integer $uid The message id.
+ *
+ * @return mixed The message text or a PEAR error in case of an error.
+ */
+ function getMessage($uid)
+ {
+ if (!$this->_mbox) {
+ return PEAR::raiseError("No IMAP folder selected!");
+ }
+ if (!isset($this->_mbox['mails'][$uid])) {
+ return PEAR::raiseError(sprintf("No IMAP message %s!", $uid));
+ }
+ return $this->_mbox['mails'][$uid]['header'] . $this->_mbox['mails'][$uid]['body'];
+ }
+
+ /**
+ * Retrieves a list of mailboxes on the server.
+ *
+ * @return mixed The list of mailboxes or a PEAR error in case of an
+ * error.
+ */
+ function getMailboxes()
+ {
+ $mboxes = array_keys($GLOBALS['KOLAB_TESTING']);
+ $user = split('@', $this->_user);
+ $pattern = '#^user/' . $user[0] . '#';
+ $result = array();
+ foreach ($mboxes as $mbox) {
+ $result[] = preg_replace($pattern, 'INBOX', $mbox);
+ }
+ return $result;
+ }
+
+ /**
+ * Fetches the annotation on a folder.
+ *
+ * @param string $entries The entry to fetch.
+ * @param string $value The specific value to fetch.
+ * @param string $mailbox_name The name of the folder.
+ *
+ * @return mixed The annotation value or a PEAR error in case of an error.
+ */
+ function getAnnotation($entries, $value, $mailbox_name)
+ {
+ $mailbox_name = $this->_parseFolder($mailbox_name);
+ $old_mbox = null;
+ if ($mailbox_name != $this->_mboxname) {
+ $old_mbox = $this->_mboxname;
+ $result = $this->select($mailbox_name);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ if (!isset($this->_mbox['annotations'][$entries])
+ || !isset($this->_mbox['annotations'][$entries][$value])) {
+ return false;
+ }
+ $annotation = $this->_mbox['annotations'][$entries][$value];
+ if ($old_mbox) {
+ $this->select($old_mbox);
+ }
+ return $annotation;
+ }
+
+ /**
+ * Sets the annotation on a folder.
+ *
+ * @param string $entries The entry to set.
+ * @param array $values The values to set
+ * @param string $mailbox_name The name of the folder.
+ *
+ * @return mixed True if successfull, a PEAR error otherwise.
+ */
+ function setAnnotation($entries, $values, $mailbox_name)
+ {
+ $mailbox_name = $this->_parseFolder($mailbox_name);
+ $old_mbox = null;
+ if ($mailbox_name != $this->_mboxname) {
+ $old_mbox = $this->_mboxname;
+ $result = $this->select($mailbox_name);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ if (!isset($this->_mbox['annotations'][$entries])) {
+ $this->_mbox['annotations'][$entries] = array();
+ }
+ foreach ($values as $key => $value) {
+ $this->_mbox['annotations'][$entries][$key] = $value;
+ }
+ if ($old_mbox) {
+ $result = $this->select($old_mbox);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Retrieve the access rights from a folder
+ *
+ * @param string $folder The folder to retrieve the ACLs from.
+ *
+ * @return mixed An array of rights if successfull, a PEAR error
+ * otherwise.
+ */
+ function getACL($folder)
+ {
+ $folder = $this->_parseFolder($folder);
+ $old_mbox = null;
+ if ($folder != $this->_mboxname) {
+ $old_mbox = $this->_mboxname;
+ $result = $this->select($folder);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ $acl = $this->_mbox['permissions'];
+ if ($old_mbox) {
+ $result = $this->select($old_mbox);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ return $acl;
+ }
+
+ /**
+ * Retrieve the access rights on a folder not owned by the current user
+ *
+ * @param string $folder The folder to retrieve the ACLs from.
+ *
+ * @return mixed An array of rights if successfull, a PEAR error
+ * otherwise.
+ */
+ function getMyRights($folder)
+ {
+ $folder = $this->_parseFolder($folder);
+ $old_mbox = null;
+ if ($folder != $this->_mboxname) {
+ $old_mbox = $this->_mboxname;
+ $result = $this->select($folder);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ $acl = '';
+ if (isset($this->_mbox['permissions'][$this->_user])) {
+ $acl = $this->_mbox['permissions'][$this->_user];
+ }
+ if ($old_mbox) {
+ $result = $this->select($old_mbox);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ return $acl;
+ }
+
+ /**
+ * Set the access rights for a folder
+ *
+ * @param string $folder The folder to retrieve the ACLs from.
+ * @param string $user The user to set the ACLs for
+ * @param string $acl The ACLs
+ *
+ * @return mixed True if successfull, a PEAR error otherwise.
+ */
+ function setACL($folder, $user, $acl)
+ {
+ $folder = $this->_parseFolder($folder);
+ $old_mbox = null;
+ if ($folder != $this->_mboxname) {
+ $old_mbox = $this->_mboxname;
+ $result = $this->select($folder);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ $this->_mbox['permissions'][$user] = $acl;
+ if ($old_mbox) {
+ $result = $this->select($old_mbox);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Delete the access rights for a user.
+ *
+ * @param string $folder The folder that should be modified.
+ * @param string $user The user that should get the ACLs removed
+ *
+ * @return mixed True if successfull, a PEAR error otherwise.
+ */
+ function deleteACL($folder, $user)
+ {
+ $folder = $this->_parseFolder($folder);
+ $old_mbox = null;
+ if ($folder != $this->_mboxname) {
+ $old_mbox = $this->_mboxname;
+ $result = $this->select($folder);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ unset($this->_mbox['permissions'][$user]);
+ if ($old_mbox) {
+ $result = $this->select($old_mbox);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Appends a message to the current folder.
+ *
+ * @param string $msg The message to append.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function appendMessage($msg)
+ {
+ $split = strpos($msg, "\r\n\r\n");
+ $mail = array('header' => substr($msg, 0, $split + 2),
+ 'body' => substr($msg, $split + 3));
+ return $this->_appendMessage($mail);
+ }
+
+ /**
+ * Appends a message to the current folder.
+ *
+ * @param array $msg The message to append.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function _appendMessage($msg)
+ {
+ if (!$this->_mbox) {
+ return PEAR::raiseError("No IMAP folder selected!");
+ }
+ $mail = array();
+ $mail['flags'] = 0;
+ $mail['header'] = $msg['header'];
+ $mail['body'] = $msg['body'];
+
+
+ $this->_mbox['mails'][$this->_mbox['status']['uidnext']] = $mail;
+ $this->_mbox['status']['uidnext']++;
+ return true;
+ }
+
+ /**
+ * Copies a message to a new folder.
+ *
+ * @param integer $uid IMAP message id.
+ * @param string $new_folder Target folder.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function copyMessage($uid, $new_folder)
+ {
+ $new_folder = $this->_parseFolder($new_folder);
+ if (!$this->_mbox) {
+ return PEAR::raiseError("No IMAP folder selected!");
+ }
+ if (!isset($this->_mbox['mails'][$uid])) {
+ return PEAR::raiseError(sprintf("No IMAP message %s!", $uid));
+ }
+ $mail = $this->_mbox['mails'][$uid];
+
+ $old_mbox = null;
+ $result = $this->select($new_folder);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ $this->_appendMessage($mail);
+ if ($old_mbox) {
+ $result = $this->select($old_mbox);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Moves a message to a new folder.
+ *
+ * @param integer $uid IMAP message id.
+ * @param string $new_folder Target folder.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function moveMessage($uid, $new_folder)
+ {
+ $new_folder = $this->_parseFolder($new_folder);
+ if (!$this->_mbox) {
+ return PEAR::raiseError("No IMAP folder selected!");
+ }
+ if (!isset($this->_mbox['mails'][$uid])) {
+ return PEAR::raiseError(sprintf("No IMAP message %s!", $uid));
+ }
+ $mail = $this->_mbox['mails'][$uid];
+ unset($this->_mbox['mails'][$uid]);
+
+ $old_mbox = null;
+ $result = $this->select($new_folder);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ $this->_appendMessage($mail);
+ if ($old_mbox) {
+ $result = $this->select($old_mbox);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Deletes messages from the current folder.
+ *
+ * @param integer $uids IMAP message ids.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function deleteMessages($uids)
+ {
+ if (!$this->_mbox) {
+ return PEAR::raiseError("No IMAP folder selected!");
+ }
+
+ if (!is_array($uids)) {
+ $uids = array($uids);
+ }
+
+ foreach ($uids as $uid) {
+
+ if (!isset($this->_mbox['mails'][$uid])) {
+ return PEAR::raiseError(sprintf("No IMAP message %s!", $uid));
+ }
+ $this->_mbox['mails'][$uid]['flags'] |= KOLAB_IMAP_FLAG_DELETED;
+ }
+ return true;
+ }
+
+ /**
+ * Undeletes a message in the current folder.
+ *
+ * @param integer $uid IMAP message id.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function undeleteMessages($uid)
+ {
+ if (!$this->_mbox) {
+ return PEAR::raiseError("No IMAP folder selected!");
+ }
+
+ if (!isset($this->_mbox['mails'][$uid])) {
+ return PEAR::raiseError(sprintf("No IMAP message %s!", $uid));
+ }
+ $this->_mbox['mails'][$uid]['flags'] &= ~KOLAB_IMAP_FLAG_DELETED;
+ return true;
+ }
+
+ /**
+ * Expunges messages in the current folder.
+ *
+ * @return mixed True or a PEAR error in case of an error.
+ */
+ function expunge()
+ {
+ if (!$this->_mbox) {
+ return PEAR::raiseError("No IMAP folder selected!");
+ }
+
+ $remaining = array();
+ foreach ($this->_mbox['mails'] as $uid => $mail) {
+ if (!($mail['flags'] & KOLAB_IMAP_FLAG_DELETED)) {
+ $remaining[$uid] = $mail;
+ }
+ }
+ $this->_mbox['mails'] = $remaining;
+ return true;
+ }
+
+ /**
+ * Return the currently selected mailbox
+ *
+ * @return string The mailbox currently selected
+ */
+ function current()
+ {
+ return $this->_mboxname;
+ }
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server.php b/framework/Kolab_Server/lib/Horde/Kolab/Server.php
new file mode 100644
index 000000000..d8089b7e1
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server.php
@@ -0,0 +1,673 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/** We need PEAR */
+require_once 'PEAR.php';
+
+/** Provide access to the Kolab specific objects. */
+require_once 'Horde/Kolab/Server/Object.php';
+
+/** Define types of return values. */
+define('KOLAB_SERVER_RESULT_SINGLE', 1);
+define('KOLAB_SERVER_RESULT_STRICT', 2);
+define('KOLAB_SERVER_RESULT_MANY', 3);
+
+/**
+ * This class provides methods to deal with Kolab objects stored in
+ * the Kolab object db.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server.php,v 1.9 2009/01/08 07:12:43 wrobel Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server {
+
+ /**
+ * Server parameters.
+ *
+ * @var array
+ */
+ var $_params = array();
+
+ /**
+ * The UID of the current user.
+ *
+ * @var string
+ */
+ var $uid;
+
+ /**
+ * Valid Kolab object types
+ *
+ * @var array
+ */
+ var $valid_types = array(
+ KOLAB_OBJECT_ADDRESS,
+ KOLAB_OBJECT_ADMINISTRATOR,
+ KOLAB_OBJECT_DISTLIST,
+ KOLAB_OBJECT_DOMAINMAINTAINER,
+ KOLAB_OBJECT_GROUP,
+ KOLAB_OBJECT_MAINTAINER,
+ KOLAB_OBJECT_SERVER,
+ KOLAB_OBJECT_SHAREDFOLDER,
+ KOLAB_OBJECT_USER,
+ );
+
+ /**
+ * Construct a new Horde_Kolab_Server object.
+ *
+ * @param array $params Parameter array.
+ */
+ function Horde_Kolab_Server($params = array())
+ {
+ $this->_params = $params;
+ if (isset($params['uid'])) {
+ $this->uid = $params['uid'];
+ }
+ }
+
+ /**
+ * Attempts to return a concrete Horde_Kolab_Server instance based
+ * on $driver.
+ *
+ * @param mixed $driver The type of concrete Horde_Kolab_Server subclass to
+ * return.
+ * @param array $params A hash containing any additional
+ * configuration or connection parameters a subclass
+ * might need.
+ *
+ * @return Horde_Kolab_Server|PEAR_Error The newly created concrete
+ * Horde_Kolab_Server instance.
+ */
+ function &factory($driver, $params = array())
+ {
+ $driver = basename($driver);
+ if (empty($driver) || $driver == 'none') {
+ $db = new Horde_Kolab_Server($params);
+ return $db;
+ }
+
+ if (file_exists(dirname(__FILE__) . '/Server/' . $driver . '.php')) {
+ include_once dirname(__FILE__) . '/Server/' . $driver . '.php';
+ }
+
+ $class = 'Horde_Kolab_Server_' . $driver;
+ if (class_exists($class)) {
+ $db = new $class($params);
+ } else {
+ $db = PEAR::raiseError('Class definition of ' . $class . ' not found.');
+ }
+
+ return $db;
+ }
+
+ /**
+ * Attempts to return a reference to a concrete Horde_Kolab_Server
+ * instance based on $driver. It will only create a new instance
+ * if no Horde_Kolab_Server instance with the same parameters currently
+ * exists.
+ *
+ * This method must be invoked as:
+ * $var = &Horde_Kolab_Server::singleton()
+ *
+ * @param array $params An array of optional login parameters. May
+ * contain "uid" (for the login uid), "user"
+ * (if the uid is not yet known), and "pass"
+ * (for a password).
+ *
+ * @return Horde_Kolab_Server|PEAR_Error The concrete Horde_Kolab_Server
+ * reference.
+ */
+ function &singleton($params = null)
+ {
+ global $conf;
+
+ static $instances = array();
+
+ if (isset($conf['kolab']['server']['driver'])) {
+ $driver = $conf['kolab']['server']['driver'];
+ if (isset($conf['kolab']['server']['params'])) {
+ $server_params = $conf['kolab']['server']['params'];
+ } else {
+ $server_params = array();
+ }
+ } else if (isset($conf['kolab']['ldap']['server'])
+ && isset($conf['kolab']['ldap']['basedn'])
+ && isset($conf['kolab']['ldap']['phpdn'])
+ && isset($conf['kolab']['ldap']['phppw'])) {
+ $driver = 'ldap';
+
+ $server_params = array('server' => $conf['kolab']['ldap']['server'],
+ 'base_dn' => $conf['kolab']['ldap']['basedn'],
+ 'uid' => $conf['kolab']['ldap']['phpdn'],
+ 'pass' => $conf['kolab']['ldap']['phppw']);
+ } else {
+ $driver = null;
+ $server_params = array();
+ }
+
+ if (!empty($params)) {
+ if (isset($params['user'])) {
+ $tmp_server = &Horde_Kolab_Server::factory($driver, $server_params);
+
+ $uid = $tmp_server->uidForIdOrMail($params['user']);
+ if (is_a($uid, 'PEAR_Error')) {
+ return PEAR::raiseError(sprintf(_("Failed identifying the UID of the Kolab user %s. Error was: %s"),
+ $params['user'],
+ $uid->getMessage()));
+ }
+ $server_params['uid'] = $uid;
+ }
+ if (isset($params['pass'])) {
+ if (isset($server_params['pass'])) {
+ $server_params['search_pass'] = $server_params['pass'];
+ }
+ $server_params['pass'] = $params['pass'];
+ }
+ if (isset($params['uid'])) {
+ if (isset($server_params['uid'])) {
+ $server_params['search_uid'] = $server_params['pass'];
+ }
+ $server_params['uid'] = $params['uid'];
+ }
+ }
+
+ $sparam = $server_params;
+ $sparam['pass'] = isset($sparam['pass']) ? md5($sparam['pass']) : '';
+ $signature = serialize(array($driver, $sparam));
+ if (empty($instances[$signature])) {
+ $instances[$signature] = &Horde_Kolab_Server::factory($driver,
+ $server_params);
+ }
+
+ return $instances[$signature];
+ }
+
+ /**
+ * Return the root of the UID values on this server.
+ *
+ * @return string The base UID on this server (base DN on ldap).
+ */
+ function getBaseUid()
+ {
+ return '';
+ }
+
+ /**
+ * Fetch a Kolab object.
+ *
+ * @param string $uid The UID of the object to fetch.
+ * @param string $type The type of the object to fetch.
+ *
+ * @return Kolab_Object|PEAR_Error The corresponding Kolab object.
+ */
+ function &fetch($uid = null, $type = null)
+ {
+ if (!isset($uid)) {
+ $uid = $this->uid;
+ }
+ if (empty($type)) {
+ $type = $this->_determineType($uid);
+ if (is_a($type, 'PEAR_Error')) {
+ return $type;
+ }
+ } else {
+ if (!in_array($type, $this->valid_types)) {
+ return PEAR::raiseError(sprintf(_("Invalid Kolab object type \"%s\"."),
+ $type));
+ }
+ }
+
+ $object = &Horde_Kolab_Server_Object::factory($type, $uid, $this);
+ return $object;
+ }
+
+ /**
+ * Add a Kolab object.
+ *
+ * @param array $info The object to store.
+ *
+ * @return Kolab_Object|PEAR_Error The newly created Kolab object.
+ */
+ function &add($info)
+ {
+ if (!isset($info['type'])) {
+ return PEAR::raiseError('The type of a new object must be specified!');
+ }
+ if (!in_array($info['type'], $this->valid_types)) {
+ return PEAR::raiseError(sprintf(_("Invalid Kolab object type \"%s\"."),
+ $type));
+ }
+
+ $uid = $this->generateUid($info['type'], $info);
+ if (is_a($uid, 'PEAR_Error')) {
+ return $uid;
+ }
+
+ $object = &Horde_Kolab_Server_Object::factory($info['type'], $uid, $this);
+ if (is_a($object, 'PEAR_Error')) {
+ return $object;
+ }
+
+ if ($object->exists()) {
+ return PEAR::raiseError('The object does already exist!');
+ }
+
+ $result = $object->save($info);
+ if (is_a($result, 'PEAR_Error')) {
+ return PEAR::raiseError(sprintf('Adding object failed: %s',
+ $result->getMessage()));
+ }
+ return $object;
+ }
+
+ /**
+ * Update or create a Kolab object.
+ *
+ * @param string $type The type of the object to store.
+ * @param array $info Any additional information about the object to store.
+ * @param string $uid The unique id of the object to store.
+ *
+ * @return Kolab_Object|PEAR_Error The updated Kolab object.
+ */
+ function &store($type, $info, $uid = null)
+ {
+ if (!in_array($type, $this->valid_types)) {
+ return PEAR::raiseError(sprintf(_("Invalid Kolab object type \"%s\"."),
+ $type));
+ }
+ if (empty($uid)) {
+ $uid = $this->generateUid($type, $info);
+ }
+
+ $object = &Horde_Kolab_Server_Object::factory($type, $uid, $this);
+ $result = $object->save($info);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ return $object;
+ }
+
+ /**
+ * Get the groups for this object
+ *
+ * @param string $uid The UID of the object to fetch.
+ *
+ * @return array|PEAR_Error An array of group ids.
+ */
+ function getGroups($uid)
+ {
+ return array();
+ }
+
+ /**
+ * Read object data.
+ *
+ * @param string $uid The object to retrieve.
+ * @param string $attrs Restrict to these attributes.
+ *
+ * @return array|PEAR_Error An array of attributes.
+ */
+ function read($uid, $attrs = null)
+ {
+ return $this->_read($uid, $attrs);
+ }
+
+ /**
+ * Stub for reading object data.
+ *
+ * @param string $uid The object to retrieve.
+ * @param string $attrs Restrict to these attributes.
+ *
+ * @return array|PEAR_Error An array of attributes.
+ */
+ function _read($uid, $attrs = null)
+ {
+ return PEAR::raiseError(_("Not implemented!"));
+ }
+
+ /**
+ * Stub for saving object data.
+ *
+ * @param string $uid The object to save.
+ * @param string $data The data of the object.
+ *
+ * @return array|PEAR_Error An array of attributes.
+ */
+ function save($uid, $data)
+ {
+ return PEAR::raiseError(_("Not implemented!"));
+ }
+
+ /**
+ * Determine the type of a Kolab object.
+ *
+ * @param string $uid The UID of the object to examine.
+ *
+ * @return string The corresponding Kolab object type.
+ */
+ function _determineType($uid)
+ {
+ return KOLAB_OBJECT_USER;
+ }
+
+ /**
+ * Identify the primary mail attribute for the first object found
+ * with the given ID or mail.
+ *
+ * @param string $id Search for objects with this ID/mail.
+ *
+ * @return mixed|PEAR_Error The mail address or false if there was
+ * no result.
+ */
+ function mailForIdOrMail($id)
+ {
+ /* In the default class we just return the id */
+ return $id;
+ }
+
+ /**
+ * Returns a list of allowed email addresses for the given user.
+ *
+ * @param string $user The user name.
+ *
+ * @return array|PEAR_Error An array of allowed mail addresses.
+ */
+ function addrsForIdOrMail($user)
+ {
+ /* In the default class we just return the user name */
+ return $user;
+ }
+
+ /**
+ * Return the UID for a given primary mail, uid, or alias.
+ *
+ * @param string $mail A valid mail address for the user.
+ *
+ * @return mixed|PEAR_Error The UID or false if there was no result.
+ */
+ function uidForMailAddress($mail)
+ {
+ /* In the default class we just return the mail address */
+ return $mail;
+ }
+
+ /**
+ * Identify the UID for the first user found using a specified
+ * attribute value.
+ *
+ * @param string $attr The name of the attribute used for searching.
+ * @param string $value The desired value of the attribute.
+ * @param int $restrict A KOLAB_SERVER_RESULT_* result restriction.
+ *
+ * @return mixed|PEAR_Error The UID or false if there was no result.
+ */
+ function uidForAttr($attr, $value,
+ $restrict = KOLAB_SERVER_RESULT_SINGLE)
+ {
+ /* In the default class we just return false */
+ return false;
+ }
+
+ /**
+ * Identify the GID for the first group found using a specified
+ * attribute value.
+ *
+ * @param string $attr The name of the attribute used for searching.
+ * @param string $value The desired value of the attribute.
+ * @param int $restrict A KOLAB_SERVER_RESULT_* result restriction.
+ *
+ * @return mixed|PEAR_Error The GID or false if there was no result.
+ */
+ function gidForAttr($attr, $value,
+ $restrict = KOLAB_SERVER_RESULT_SINGLE)
+ {
+ /* In the default class we just return false */
+ return false;
+ }
+
+ /**
+ * Is the given UID member of the group with the given mail address?
+ *
+ * @param string $uid UID of the user.
+ * @param string $mail Search the group with this mail address.
+ *
+ * @return boolean|PEAR_Error True in case the user is in the
+ * group, false otherwise.
+ */
+ function memberOfGroupAddress($uid, $mail)
+ {
+ /* No groups in the default class */
+ return false;
+ }
+
+ /**
+ * Identify the UID for the first object found with the given ID.
+ *
+ * @param string $id Search for objects with this ID.
+ * @param int $restrict A KOLAB_SERVER_RESULT_* result restriction.
+ *
+ * @return mixed|PEAR_Error The UID or false if there was no result.
+ */
+ function uidForId($id,
+ $restrict = KOLAB_SERVER_RESULT_SINGLE)
+ {
+ return $this->uidForAttr('uid', $id);
+ }
+
+ /**
+ * Identify the UID for the first user found with the given mail.
+ *
+ * @param string $mail Search for users with this mail address.
+ * @param int $restrict A KOLAB_SERVER_RESULT_* result restriction.
+ *
+ * @return mixed|PEAR_Error The UID or false if there was no result.
+ */
+ function uidForMail($mail,
+ $restrict = KOLAB_SERVER_RESULT_SINGLE)
+ {
+ return $this->uidForAttr('mail', $mail);
+ }
+
+ /**
+ * Identify the GID for the first group found with the given mail.
+ *
+ * @param string $mail Search for groups with this mail address.
+ * @param int $restrict A KOLAB_SERVER_RESULT_* result restriction.
+ *
+ * @return mixed|PEAR_Error The GID or false if there was no result.
+ */
+ function gidForMail($mail,
+ $restrict = KOLAB_SERVER_RESULT_SINGLE)
+ {
+ return $this->gidForAttr('mail', $mail);
+ }
+
+ /**
+ * Identify the UID for the first object found with the given ID or mail.
+ *
+ * @param string $id Search for objects with this uid/mail.
+ *
+ * @return mixed|PEAR_Error The UID or false if there was no result.
+ */
+ function uidForIdOrMail($id)
+ {
+ $uid = $this->uidForAttr('uid', $id);
+ if (!$uid) {
+ $uid = $this->uidForAttr('mail', $id);
+ }
+ return $uid;
+ }
+
+ /**
+ * Identify the UID for the first object found with the given alias.
+ *
+ * @param string $mail Search for objects with this mail alias.
+ * @param int $restrict A KOLAB_SERVER_RESULT_* result restriction.
+ *
+ * @return mixed|PEAR_Error The UID or false if there was no result.
+ */
+ function uidForAlias($mail,
+ $restrict = KOLAB_SERVER_RESULT_SINGLE)
+ {
+ return $this->uidForAttr('alias', $mail);
+ }
+
+ /**
+ * Identify the UID for the first object found with the given mail
+ * address or alias.
+ *
+ * @param string $mail Search for objects with this mail address
+ * or alias.
+ *
+ * @return mixed|PEAR_Error The UID or false if there was no result.
+ */
+ function uidForMailOrAlias($mail)
+ {
+ $uid = $this->uidForAttr('alias', $mail);
+ if (!$uid) {
+ $uid = $this->uidForAttr('mail', $mail);
+ }
+ return $uid;
+ }
+
+ /**
+ * Identify the UID for the first object found with the given ID,
+ * mail or alias.
+ *
+ * @param string $id Search for objects with this ID/mail/alias.
+ *
+ * @return mixed|PEAR_Error The UID or false if there was no result.
+ */
+ function uidForMailOrIdOrAlias($id)
+ {
+ $uid = $this->uidForAttr('uid', $id);
+ if (!$uid) {
+ $uid = $this->uidForAttr('mail', $id);
+ if (!$uid) {
+ $uid = $this->uidForAttr('alias', $id);
+ }
+ }
+ return $uid;
+ }
+
+ /**
+ * Generate a hash representation for a list of objects.
+ *
+ * @param string $type The type of the objects to be listed
+ * @param array $params Additional parameters.
+ *
+ * @return array|PEAR_Error An array of Kolab objects.
+ */
+ function listHash($type, $params = null)
+ {
+ $list = $this->_listObjects($type, $params);
+ if (is_a($list, 'PEAR_Error')) {
+ return $list;
+ }
+
+ if (isset($params['attributes'])) {
+ $attributes = $params['attributes'];
+ } else {
+ $attributes = null;
+ }
+
+ $hash = array();
+ foreach ($list as $entry) {
+ $hash[] = $entry->toHash($attributes);
+ }
+
+ return $hash;
+ }
+
+ /**
+ * List all objects of a specific type
+ *
+ * @param string $type The type of the objects to be listed
+ * @param array $params Additional parameters.
+ *
+ * @return array|PEAR_Error An array of Kolab objects.
+ */
+ function listObjects($type, $params = null)
+ {
+ if (!in_array($type, $this->valid_types)) {
+ return PEAR::raiseError(sprintf(_("Invalid Kolab object type \"%s\"."),
+ $type));
+ }
+
+ return $this->_listObjects($type, $params);
+ }
+
+ /**
+ * List all objects of a specific type
+ *
+ * @param string $type The type of the objects to be listed
+ * @param array $params Additional parameters.
+ *
+ * @return array|PEAR_Error An array of Kolab objects.
+ */
+ function _listObjects($type, $params = null)
+ {
+ return array();
+ }
+
+ /**
+ * Generates a unique ID for the given information.
+ *
+ * @param string $type The type of the object to create.
+ * @param array $info Any additional information about the object to create.
+ *
+ * @return string|PEAR_Error The UID.
+ */
+ function generateUid($type, $info)
+ {
+ if (!class_exists($type)) {
+ $result = Horde_Kolab_Server_Object::loadClass($type);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $id = call_user_func(array($type, 'generateId'), $info);
+ if (is_a($id, 'PEAR_Error')) {
+ return $id;
+ }
+ return $this->_generateUid($type, $id, $info);
+ }
+
+ /**
+ * Generates a UID for the given information.
+ *
+ * @param string $type The type of the object to create.
+ * @param string $id The id of the object.
+ * @param array $info Any additional information about the object to create.
+ *
+ * @return string|PEAR_Error The UID.
+ */
+ function _generateUid($type, $id, $info)
+ {
+ return $id;
+ }
+
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Object.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object.php
new file mode 100644
index 000000000..ceb1300bf
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object.php
@@ -0,0 +1,505 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/** Define the different Kolab object types */
+define('KOLAB_OBJECT_ADDRESS', 'Horde_Kolab_Server_Object_address');
+define('KOLAB_OBJECT_ADMINISTRATOR', 'Horde_Kolab_Server_Object_administrator');
+define('KOLAB_OBJECT_DOMAINMAINTAINER', 'Horde_Kolab_Server_Object_domainmaintainer');
+define('KOLAB_OBJECT_GROUP', 'Horde_Kolab_Server_Object_group');
+define('KOLAB_OBJECT_DISTLIST', 'Horde_Kolab_Server_Object_distlist');
+define('KOLAB_OBJECT_MAINTAINER', 'Horde_Kolab_Server_Object_maintainer');
+define('KOLAB_OBJECT_SHAREDFOLDER', 'Horde_Kolab_Server_Object_sharedfolder');
+define('KOLAB_OBJECT_USER', 'Horde_Kolab_Server_Object_user');
+define('KOLAB_OBJECT_SERVER', 'Horde_Kolab_Server_Object_server');
+
+/** Define the possible Kolab object attributes */
+define('KOLAB_ATTR_UID', 'dn');
+define('KOLAB_ATTR_ID', 'id');
+define('KOLAB_ATTR_SN', 'sn');
+define('KOLAB_ATTR_CN', 'cn');
+define('KOLAB_ATTR_GIVENNAME', 'givenName');
+define('KOLAB_ATTR_FN', 'fn');
+define('KOLAB_ATTR_LNFN', 'lnfn');
+define('KOLAB_ATTR_FNLN', 'fnln');
+define('KOLAB_ATTR_MAIL', 'mail');
+define('KOLAB_ATTR_SID', 'uid');
+define('KOLAB_ATTR_ACL', 'acl');
+define('KOLAB_ATTR_MEMBER', 'member');
+define('KOLAB_ATTR_USERTYPE', 'usertype');
+define('KOLAB_ATTR_DOMAIN', 'domain');
+define('KOLAB_ATTR_FOLDERTYPE', 'kolabFolderType');
+define('KOLAB_ATTR_USERPASSWORD', 'userPassword');
+define('KOLAB_ATTR_DELETED', 'kolabDeleteFlag');
+define('KOLAB_ATTR_FREEBUSYHOST', 'kolabFreeBusyServer');
+define('KOLAB_ATTR_IMAPHOST', 'kolabImapServer');
+define('KOLAB_ATTR_HOMESERVER', 'kolabHomeServer');
+define('KOLAB_ATTR_KOLABDELEGATE','kolabDelegate');
+define('KOLAB_ATTR_IPOLICY', 'kolabInvitationPolicy');
+define('KOLAB_ATTR_QUOTA', 'cyrus-userquota');
+define('KOLAB_ATTR_FBPAST', 'kolabFreeBusyPast');
+define('KOLAB_ATTR_FBFUTURE', 'kolabFreeBusyFuture');
+define('KOLAB_ATTR_VISIBILITY', 'visible');
+
+/** Define the possible Kolab object classes */
+define('KOLAB_OC_TOP', 'top');
+define('KOLAB_OC_INETORGPERSON', 'inetOrgPerson');
+define('KOLAB_OC_KOLABINETORGPERSON', 'kolabInetOrgPerson');
+define('KOLAB_OC_HORDEPERSON', 'hordePerson');
+define('KOLAB_OC_KOLABGROUPOFNAMES', 'kolabGroupOfNames');
+define('KOLAB_OC_KOLABSHAREDFOLDER', 'kolabSharedFolder');
+
+/** Define the possible Kolab user types */
+define('KOLAB_UT_STANDARD', 0);
+define('KOLAB_UT_INTERNAL', 1);
+define('KOLAB_UT_GROUP', 2);
+define('KOLAB_UT_RESOURCE', 3);
+
+/**
+ * This class provides methods to deal with Kolab objects stored in
+ * the Kolab db.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server/Object.php,v 1.9 2009/01/08 21:00:07 wrobel Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Object {
+
+ /**
+ * Link into the Kolab server.
+ *
+ * @var Kolab_Server
+ */
+ var $_db;
+
+ /**
+ * UID of this object on the Kolab server.
+ *
+ * @var string
+ */
+ var $_uid;
+
+ /**
+ * The cached LDAP result
+ *
+ * FIXME: Include _ldap here
+ *
+ * @var mixed
+ */
+ var $_cache = false;
+
+ /** FIXME: Add an attribute cache for the get() function */
+
+ /**
+ * The LDAP filter to retrieve this object type.
+ *
+ * @var string
+ */
+ var $filter = '';
+
+ /**
+ * The group the UID must be member of so that this object really
+ * matches this class type. This may not include the root UID.
+ *
+ * @var string
+ */
+ var $required_group;
+
+ /**
+ * The LDAP attributes supported by this class.
+ *
+ * @var array
+ */
+ var $_supported_attributes = array();
+
+ /**
+ * Attributes derived from the LDAP values.
+ *
+ * @var array
+ */
+ var $_derived_attributes = array(
+ KOLAB_ATTR_ID,
+ );
+
+ /**
+ * The attributes required when creating an object of this class.
+ *
+ * @var array
+ */
+ var $_required_attributes = array();
+
+ /**
+ * The ldap classes for this type of object.
+ *
+ * @var array
+ */
+ var $_object_classes = array();
+
+ /**
+ * Sort by this attributes (must be a LDAP attribute).
+ *
+ * @var string
+ */
+ var $sort_by = KOLAB_ATTR_SN;
+
+ /**
+ * Initialize the Kolab Object. Provide either the UID or a
+ * LDAP search result.
+ *
+ * @param Horde_Kolab_Server &$db The link into the Kolab db.
+ * @param string $uid UID of the object.
+ * @param array $data A possible array of data for the object
+ */
+ function Horde_Kolab_Server_Object(&$db, $uid = null, $data = null)
+ {
+ $this->_db = &$db;
+ if (empty($uid)) {
+ if (empty($data) || !isset($data[KOLAB_ATTR_UID])) {
+ $this->_cache = PEAR::raiseError(_('Specify either the UID or a search result!'));
+ return;
+ }
+ if (is_array($data[KOLAB_ATTR_UID])) {
+ $this->_uid = $data[KOLAB_ATTR_UID][0];
+ } else {
+ $this->_uid = $data[KOLAB_ATTR_UID];
+ }
+ $this->_cache = $data;
+ } else {
+ $this->_uid = $uid;
+ }
+ }
+
+ /**
+ * Attempts to return a concrete Horde_Kolab_Server_Object instance based on
+ * $type.
+ *
+ * @param mixed $type The type of the Horde_Kolab_Server_Object subclass
+ * to return.
+ * @param string $uid UID of the object
+ * @param array &$storage A link to the Kolab_Server class handling read/write.
+ * @param array $data A possible array of data for the object
+ *
+ * @return Horde_Kolab_Server_Object|PEAR_Error The newly created concrete
+ * Horde_Kolab_Server_Object instance.
+ */
+ function &factory($type, $uid, &$storage, $data = null)
+ {
+ $result = Horde_Kolab_Server_Object::loadClass($type);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (class_exists($type)) {
+ $object = &new $type($storage, $uid, $data);
+ } else {
+ $object = PEAR::raiseError('Class definition of ' . $type . ' not found.');
+ }
+
+ return $object;
+ }
+
+ /**
+ * Attempts to load the concrete Horde_Kolab_Server_Object class based on
+ * $type.
+ *
+ * @param mixed $type The type of the Horde_Kolab_Server_Object subclass.
+ *
+ * @static
+ *
+ * @return true|PEAR_Error True if successfull.
+ */
+ function loadClass($type)
+ {
+ if (in_array($type, array(KOLAB_OBJECT_ADDRESS, KOLAB_OBJECT_ADMINISTRATOR,
+ KOLAB_OBJECT_DISTLIST, KOLAB_OBJECT_DOMAINMAINTAINER,
+ KOLAB_OBJECT_GROUP, KOLAB_OBJECT_MAINTAINER,
+ KOLAB_OBJECT_SHAREDFOLDER, KOLAB_OBJECT_USER,
+ KOLAB_OBJECT_SERVER))) {
+ $name = substr($type, 26);
+ } else {
+ return PEAR::raiseError(sprintf('Object type "%s" not supported.',
+ $type));
+ }
+
+ $name = basename($name);
+
+ if (file_exists(dirname(__FILE__) . '/Object/' . $name . '.php')) {
+ include_once dirname(__FILE__) . '/Object/' . $name . '.php';
+ }
+ }
+
+ /**
+ * Does the object exist?
+ *
+ * @return NULL
+ */
+ function exists()
+ {
+ $this->_read();
+ if (!$this->_cache || is_a($this->_cache, 'PEAR_Error')) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Read the object into the cache
+ *
+ * @return NULL
+ */
+ function _read()
+ {
+ $this->_cache = $this->_db->read($this->_uid,
+ $this->_supported_attributes);
+ }
+
+ /**
+ * Get the specified attribute of this object
+ *
+ * @param string $attr The attribute to read
+ *
+ * @return string the value of this attribute
+ */
+ function get($attr)
+ {
+ if ($attr != KOLAB_ATTR_UID) {
+ if (!in_array($attr, $this->_supported_attributes)
+ && !in_array($attr, $this->_derived_attributes)) {
+ return PEAR::raiseError(sprintf(_("Attribute \"%s\" not supported!"),
+ $attr));
+ }
+ if (!$this->_cache) {
+ $this->_read();
+ }
+ if (is_a($this->_cache, 'PEAR_Error')) {
+ return $this->_cache;
+ }
+ }
+
+ if (in_array($attr, $this->_derived_attributes)) {
+ return $this->_derive($attr);
+ }
+
+ switch ($attr) {
+ case KOLAB_ATTR_UID:
+ return $this->_getUID();
+ case KOLAB_ATTR_FN:
+ return $this->_getFn();
+ case KOLAB_ATTR_SN:
+ case KOLAB_ATTR_CN:
+ case KOLAB_ATTR_GIVENNAME:
+ case KOLAB_ATTR_MAIL:
+ case KOLAB_ATTR_SID:
+ case KOLAB_ATTR_USERPASSWORD:
+ case KOLAB_ATTR_DELETED:
+ case KOLAB_ATTR_IMAPHOST:
+ case KOLAB_ATTR_FREEBUSYHOST:
+ case KOLAB_ATTR_HOMESERVER:
+ case KOLAB_ATTR_FBPAST:
+ case KOLAB_ATTR_FBFUTURE:
+ case KOLAB_ATTR_FOLDERTYPE:
+ return $this->_get($attr, true);
+ default:
+ return $this->_get($attr, false);
+ }
+ }
+
+ /**
+ * Get the specified attribute of this object
+ *
+ * @param string $attr The attribute to read
+ * @param boolean $single Should a single value be returned
+ * or are multiple values allowed?
+ *
+ * @return string the value of this attribute
+ */
+ function _get($attr, $single = true)
+ {
+ if (isset($this->_cache[$attr])) {
+ if ($single && is_array($this->_cache[$attr])) {
+ return $this->_cache[$attr][0];
+ } else {
+ return $this->_cache[$attr];
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Derive an attribute value.
+ *
+ * @param string $attr The attribute to derive.
+ *
+ * @return mixed The value of the attribute.
+ */
+ function _derive($attr)
+ {
+ switch ($attr) {
+ case KOLAB_ATTR_ID:
+ $result = split(',', $this->_uid);
+ if (substr($result[0], 0, 3) == 'cn=') {
+ return substr($result[0], 3);
+ } else {
+ return $result[0];
+ }
+ case KOLAB_ATTR_LNFN:
+ $gn = $this->_get(KOLAB_ATTR_GIVENNAME, true);
+ $sn = $this->_get(KOLAB_ATTR_SN, true);
+ return sprintf('%s, %s', $sn, $gn);
+ case KOLAB_ATTR_FNLN:
+ $gn = $this->_get(KOLAB_ATTR_GIVENNAME, true);
+ $sn = $this->_get(KOLAB_ATTR_SN, true);
+ return sprintf('%s %s', $gn, $sn);
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Convert the object attributes to a hash.
+ *
+ * @param string $attrs The attributes to return.
+ *
+ * @return array|PEAR_Error The hash representing this object.
+ */
+ function toHash($attrs = null)
+ {
+ if (!isset($attrs)) {
+ $attrs = array();
+ }
+ $result = array();
+ foreach ($attrs as $key) {
+ $value = $this->get($key);
+ if (is_a($value, 'PEAR_Error')) {
+ return $value;
+ }
+ $result[$key] = $value;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get the UID of this object
+ *
+ * @return string the UID of this object
+ */
+ function _getUid()
+ {
+ return $this->_uid;
+ }
+
+ /**
+ * Get the "first name" attribute of this object
+ *
+ * FIXME: This should get refactored to be combined with the Id value.
+ *
+ * @return string the "first name" of this object
+ */
+ function _getFn()
+ {
+ $sn = $this->_get(KOLAB_ATTR_SN, true);
+ $cn = $this->_get(KOLAB_ATTR_CN, true);
+ return trim(substr($cn, 0, strlen($cn) - strlen($sn)));
+ }
+
+ /**
+ * Get the groups for this object
+ *
+ * @return mixed An array of group ids or a PEAR Error in case of
+ * an error.
+ */
+ function getGroups()
+ {
+ return array();
+ }
+
+ /**
+ * Returns the server url of the given type for this user.
+ *
+ * This method can be used to encapsulate multidomain support.
+ *
+ * @param string $server_type The type of server URL that should be returned.
+ *
+ * @return string|PEAR_Error The server url or empty.
+ */
+ function getServer($server_type)
+ {
+ return PEAR::raiseError('Not implemented!');
+ }
+
+ /**
+ * Generates an ID for the given information.
+ *
+ * @param array $info The data of the object.
+ *
+ * @static
+ *
+ * @return string|PEAR_Error The ID.
+ */
+ function generateId($info)
+ {
+ $id_mapfields = array('givenName', 'sn');
+ $id_format = '%s %s';
+
+ $fieldarray = array();
+ foreach ($id_mapfields as $mapfield) {
+ if (isset($info[$mapfield])) {
+ $fieldarray[] = $info[$mapfield];
+ } else {
+ $fieldarray[] = '';
+ }
+ }
+
+ return trim(vsprintf($id_format, $fieldarray), " \t\n\r\0\x0B,");
+ }
+
+ /**
+ * Saves object information.
+ *
+ * @param array $info The information about the object.
+ *
+ * @return boolean|PEAR_Error True on success.
+ */
+ function save($info)
+ {
+ foreach ($this->_required_attributes as $attribute) {
+ if (!isset($info[$attribute])) {
+ return PEAR::raiseError(sprintf('The value for "%s" is missing!',
+ $attribute));
+ }
+ }
+
+ $info['objectClass'] = $this->_object_classes;
+
+ $result = $this->_db->save($this->_uid, $info);
+ if ($result === false || is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ $this->_cache = $info;
+
+ return $result;
+ }
+};
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/address.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/address.php
new file mode 100644
index 000000000..ca81da10a
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/address.php
@@ -0,0 +1,105 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * This class provides methods to deal with global address book
+ * entries for Kolab.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server/Object/address.php,v 1.6 2009/01/08 21:00:08 wrobel Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Object_address extends Horde_Kolab_Server_Object {
+
+ /**
+ * The LDAP filter to retrieve this object type
+ *
+ * @var string
+ */
+ var $filter = '(&(objectclass=inetOrgPerson)(!(uid=*))(sn=*))';
+
+ /**
+ * The attributes supported by this class
+ *
+ * @var array
+ */
+ var $_supported_attributes = array(
+ KOLAB_ATTR_SN,
+ KOLAB_ATTR_CN,
+ KOLAB_ATTR_GIVENNAME,
+ KOLAB_ATTR_FN,
+ KOLAB_ATTR_LNFN,
+ KOLAB_ATTR_MAIL,
+ KOLAB_ATTR_DELETED,
+ );
+
+ /**
+ * Attributes derived from the LDAP values.
+ *
+ * @var array
+ */
+ var $_derived_attributes = array(
+ KOLAB_ATTR_LNFN,
+ KOLAB_ATTR_FNLN,
+ );
+
+ /**
+ * The attributes required when creating an object of this class.
+ *
+ * @var array
+ */
+ var $_required_attributes = array(
+ KOLAB_ATTR_SN,
+ KOLAB_ATTR_GIVENNAME,
+ );
+
+ /**
+ * The ldap classes for this type of object.
+ *
+ * @var array
+ */
+ var $_object_classes = array(
+ KOLAB_OC_TOP,
+ KOLAB_OC_INETORGPERSON,
+ KOLAB_OC_KOLABINETORGPERSON,
+ );
+
+ /**
+ * Convert the object attributes to a hash.
+ *
+ * @param string $attrs The attributes to return.
+ *
+ * @return array|PEAR_Error The hash representing this object.
+ */
+ function toHash($attrs = null)
+ {
+ if (!isset($attrs)) {
+ $attrs = array(
+ KOLAB_ATTR_LNFN,
+ );
+ }
+ return parent::toHash($attrs);
+ }
+
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/administrator.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/administrator.php
new file mode 100644
index 000000000..33b075015
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/administrator.php
@@ -0,0 +1,45 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+require_once 'Horde/Kolab/Server/Object/adminrole.php';
+
+/**
+ * This class provides methods to deal with administrator
+ * entries for Kolab.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server/Object/administrator.php,v 1.5 2009/01/06 17:49:26 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Object_administrator extends Horde_Kolab_Server_Object_adminrole {
+
+ /**
+ * The group the UID must be member of so that this object really
+ * matches this class type. This may not include the root UID.
+ *
+ * @var string
+ */
+ var $required_group = 'cn=admin,cn=internal';
+
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/adminrole.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/adminrole.php
new file mode 100644
index 000000000..d87aa7267
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/adminrole.php
@@ -0,0 +1,157 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * This class provides methods to deal with administrator object types.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server/Object/adminrole.php,v 1.3 2009/01/06 17:49:26 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Object_adminrole extends Horde_Kolab_Server_Object {
+
+ /**
+ * The LDAP filter to retrieve this object type
+ *
+ * @var string
+ */
+ var $filter = '(&(cn=*)(objectClass=inetOrgPerson)(!(uid=manager))(sn=*))';
+
+ /**
+ * The attributes supported by this class
+ *
+ * @var array
+ */
+ var $_supported_attributes = array(
+ KOLAB_ATTR_SN,
+ KOLAB_ATTR_CN,
+ KOLAB_ATTR_GIVENNAME,
+ KOLAB_ATTR_FN,
+ KOLAB_ATTR_SID,
+ KOLAB_ATTR_USERPASSWORD,
+ KOLAB_ATTR_DELETED,
+ );
+
+ /**
+ * The attributes required when creating an object of this class.
+ *
+ * @var array
+ */
+ var $_required_attributes = array(
+ KOLAB_ATTR_SN,
+ KOLAB_ATTR_GIVENNAME,
+ KOLAB_ATTR_USERPASSWORD,
+ KOLAB_ATTR_SID,
+ );
+
+ /**
+ * Attributes derived from the LDAP values.
+ *
+ * @var array
+ */
+ var $_derived_attributes = array(
+ KOLAB_ATTR_ID,
+ KOLAB_ATTR_LNFN,
+ );
+
+ /**
+ * The ldap classes for this type of object.
+ *
+ * @var array
+ */
+ var $_object_classes = array(
+ KOLAB_OC_TOP,
+ KOLAB_OC_INETORGPERSON,
+ KOLAB_OC_KOLABINETORGPERSON,
+ );
+
+ /**
+ * Convert the object attributes to a hash.
+ *
+ * @param string $attrs The attributes to return.
+ *
+ * @return array|PEAR_Error The hash representing this object.
+ */
+ function toHash($attrs = null)
+ {
+ if (!isset($attrs)) {
+ $attrs = array(
+ KOLAB_ATTR_SID,
+ KOLAB_ATTR_LNFN,
+ );
+ }
+ return parent::toHash($attrs);
+ }
+
+ /**
+ * Saves object information.
+ *
+ * @param array $info The information about the object.
+ *
+ * @return boolean|PEAR_Error True on success.
+ */
+ function save($info)
+ {
+ if (!isset($info['cn'])) {
+ if (!isset($info['sn']) || !isset($info['givenName'])) {
+ return PEAR::raiseError('Either the last name or the given name is missing!');
+ } else {
+ $info['cn'] = $this->generateId($info);
+ }
+ }
+
+ $admins_uid = sprintf('%s,%s', $this->required_group,
+ $this->_db->getBaseUid());
+
+ $admin_group = $this->_db->fetch($admins_uid, KOLAB_OBJECT_GROUP);
+ if (is_a($admin_group, 'PEAR_Error') || !$admin_group->exists()) {
+
+ $members = array($this->_uid);
+
+ //FIXME: This is not okay and also contains too much LDAP knowledge
+ $parts = split(',', $this->required_group);
+ list($groupname) = sscanf($parts[0], 'cn=%s');
+
+ $result = $this->_db->add(array(KOLAB_ATTR_CN => $groupname,
+ 'type' => KOLAB_OBJECT_GROUP,
+ KOLAB_ATTR_MEMBER => $members,
+ KOLAB_ATTR_VISIBILITY => false));
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ } else {
+ $result = $admin_group->isMember($this->_uid);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ if ($result === false) {
+ $members = $admin_group->getMembers();
+ $members[] = $this->_uid;
+ $admin_group->save(array(KOLAB_ATTR_MEMBER => $members));
+ }
+ }
+ return parent::save($info);
+ }
+
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/distlist.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/distlist.php
new file mode 100644
index 000000000..d41164b61
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/distlist.php
@@ -0,0 +1,52 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+require_once 'Horde/Kolab/Server/Object/group.php';
+
+/**
+ * This class provides methods to deal with distribution lists for Kolab.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server/Object/distlist.php,v 1.2 2009/01/06 17:49:26 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Object_distlist extends Horde_Kolab_Server_Object_group {
+
+ /**
+ * The LDAP filter to retrieve this object type
+ *
+ * @var string
+ */
+ var $filter = '(&(objectClass=kolabGroupOfNames)(mail=*))';
+
+
+ /**
+ * The attributes required when creating an object of this class.
+ *
+ * @var array
+ */
+ var $_required_attributes = array(
+ KOLAB_ATTR_MAIL,
+ );
+};
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/domainmaintainer.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/domainmaintainer.php
new file mode 100644
index 000000000..c4517b4a9
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/domainmaintainer.php
@@ -0,0 +1,124 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+require_once 'Horde/Kolab/Server/Object/adminrole.php';
+
+/**
+ * This class provides methods associated to Kolab domain maintainers.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server/Object/domainmaintainer.php,v 1.6 2009/01/06 17:49:26 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Object_domainmaintainer extends Horde_Kolab_Server_Object_adminrole {
+
+ /**
+ * The attributes required when creating an object of this class.
+ *
+ * @var array
+ */
+ var $_required_attributes = array(
+ KOLAB_ATTR_SN,
+ KOLAB_ATTR_GIVENNAME,
+ KOLAB_ATTR_USERPASSWORD,
+ KOLAB_ATTR_SID,
+ KOLAB_ATTR_DOMAIN,
+ );
+
+ /**
+ * Attributes derived from the LDAP values.
+ *
+ * @var array
+ */
+ var $_derived_attributes = array(
+ KOLAB_ATTR_ID,
+ KOLAB_ATTR_LNFN,
+ KOLAB_ATTR_DOMAIN,
+ );
+
+ /**
+ * The group the UID must be member of so that this object really
+ * matches this class type. This may not include the root UID.
+ *
+ * @var string
+ */
+ var $required_group = 'cn=domain-maintainer,cn=internal';
+
+ /**
+ * Convert the object attributes to a hash.
+ *
+ * @param string $attrs The attributes to return.
+ *
+ * @return array|PEAR_Error The hash representing this object.
+ */
+ function toHash($attrs = null)
+ {
+ if (!isset($attrs)) {
+ $attrs = array(
+ KOLAB_ATTR_SID,
+ KOLAB_ATTR_LNFN,
+ KOLAB_ATTR_DOMAIN,
+ );
+ }
+ return parent::toHash($attrs);
+ }
+
+ /**
+ * Saves object information.
+ *
+ * @param array $info The information about the object.
+ *
+ * @return boolean|PEAR_Error True on success.
+ */
+ function save($info)
+ {
+ foreach ($info[KOLAB_ATTR_DOMAIN] as $domain) {
+ $domain_uid = sprintf('cn=%s,cn=domain,cn=internal,%s',
+ $domain, $this->_db->getBaseUid());
+
+ //FIXME: This should be made easier by the group object
+
+ $domain_group = $this->_db->fetch($domain_uid, KOLAB_OBJECT_GROUP);
+ if (is_a($domain_group, 'PEAR_Error')) {
+ return $domain_group;
+ }
+ if (!$domain_group->exists()) {
+ $members = array($this->_uid);
+ $domain_group->save(array(KOLAB_ATTR_MEMBER => $members));
+ } else {
+ $result = $domain_group->isMember($this->_uid);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ if ($result === false) {
+ $members = $domain_group->getMembers();
+ $members[] = $this->_uid;
+ $domain_group->save(array(KOLAB_ATTR_MEMBER => $members));
+ }
+ }
+ }
+ return parent::save($info);
+ }
+
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/group.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/group.php
new file mode 100644
index 000000000..76e47365d
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/group.php
@@ -0,0 +1,239 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * This class provides methods to deal with groups for Kolab.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server/Object/group.php,v 1.5 2009/01/06 17:49:26 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Object_group extends Horde_Kolab_Server_Object {
+
+ /**
+ * The LDAP filter to retrieve this object type
+ *
+ * @var string
+ */
+ var $filter = '(objectClass=kolabGroupOfNames)';
+
+ /**
+ * The attributes supported by this class
+ *
+ * @var array
+ */
+ var $_supported_attributes = array(
+ KOLAB_ATTR_CN,
+ KOLAB_ATTR_MAIL,
+ KOLAB_ATTR_MEMBER,
+ KOLAB_ATTR_DELETED,
+ );
+
+ /**
+ * Attributes derived from the LDAP values.
+ *
+ * @var array
+ */
+ var $_derived_attributes = array(
+ KOLAB_ATTR_ID,
+ KOLAB_ATTR_VISIBILITY,
+ );
+
+ /**
+ * The attributes required when creating an object of this class.
+ *
+ * @var array
+ */
+ var $_required_attributes = array(
+ KOLAB_ATTR_CN,
+ );
+
+ /**
+ * The ldap classes for this type of object.
+ *
+ * @var array
+ */
+ var $_object_classes = array(
+ KOLAB_OC_TOP,
+ KOLAB_OC_KOLABGROUPOFNAMES,
+ );
+
+ /**
+ * Sort by this attributes (must be a LDAP attribute).
+ *
+ * @var string
+ */
+ var $sort_by = KOLAB_ATTR_MAIL;
+
+ /**
+ * Derive an attribute value.
+ *
+ * @param string $attr The attribute to derive.
+ *
+ * @return mixed The value of the attribute.
+ */
+ function _derive($attr)
+ {
+ switch ($attr) {
+ case KOLAB_ATTR_VISIBILITY:
+ return strpos($this->_uid, 'cn=internal') === false;
+ default:
+ return parent::_derive($attr);
+ }
+ }
+
+ /**
+ * Convert the object attributes to a hash.
+ *
+ * @param string $attrs The attributes to return.
+ *
+ * @return array|PEAR_Error The hash representing this object.
+ */
+ function toHash($attrs = null)
+ {
+ if (!isset($attrs)) {
+ $attrs = array(
+ KOLAB_ATTR_ID,
+ KOLAB_ATTR_MAIL,
+ KOLAB_ATTR_VISIBILITY,
+ );
+ }
+ return parent::toHash($attrs);
+ }
+
+ /**
+ * Generates an ID for the given information.
+ *
+ * @param array $info The data of the object.
+ *
+ * @static
+ *
+ * @return string|PEAR_Error The ID.
+ */
+ function generateId($info)
+ {
+ if (isset($info['mail'])) {
+ return trim($info['mail'], " \t\n\r\0\x0B,");
+ } else {
+ return trim($info['cn'], " \t\n\r\0\x0B,");
+ }
+ }
+
+ /**
+ * Saves object information.
+ *
+ * @param array $info The information about the object.
+ *
+ * @return boolean|PEAR_Error True on success.
+ */
+ function save($info)
+ {
+ if (!isset($info['cn'])) {
+ if (!isset($info['mail'])) {
+ return PEAR::raiseError('Either the mail address or the common name has to be specified for a group object!');
+ } else {
+ $info['cn'] = $info['mail'];
+ }
+ }
+ return parent::save($info);
+ }
+
+ /**
+ * Retrieve the member list for this group.
+ *
+ * @return array|PEAR_Error The list of members in this group.
+ */
+ function getMembers()
+ {
+ return $this->_get(KOLAB_ATTR_MEMBER, false);
+ }
+
+ /**
+ * Add a member to this group.
+ *
+ * @param string $member The UID of the member to add.
+ *
+ * @return array|PEAR_Error True if successful.
+ */
+ function addMember($member)
+ {
+ $members = $this->getMembers();
+ if (is_a($members, 'PEAR_Error')) {
+ return $members;
+ }
+ if (!in_array($member, $members)) {
+ $this->_cache[KOLAB_ATTR_MEMBER][] = $member;
+ } else {
+ return PEAR::raiseError(_("The UID %s is already a member of the group %s!"),
+ $member, $this->_uid);
+ }
+ return $this->save($this->_cache);
+ }
+
+ /**
+ * Delete a member from this group.
+ *
+ * @param string $member The UID of the member to delete.
+ *
+ * @return array|PEAR_Error True if successful.
+ */
+ function deleteMember($member)
+ {
+ $members = $this->getMembers();
+ if (is_a($members, 'PEAR_Error')) {
+ return $members;
+ }
+ if (in_array($member, $members)) {
+ $this->_cache[KOLAB_ATTR_MEMBER] = array_diff($this->_cache[KOLAB_ATTR_MEMBER],
+ array($member));
+ } else {
+ return PEAR::raiseError(_("The UID %s is no member of the group %s!"),
+ $member, $this->_uid);
+
+ }
+ return $this->save($this->_cache);
+ }
+
+ /**
+ * Is the specified UID member of this group?
+ *
+ * @param string $member The UID of the member to check.
+ *
+ * @return boolean|PEAR_Error True if the UID is a member of the group,
+ * false otherwise.
+ */
+ function isMember($member)
+ {
+ $members = $this->getMembers();
+ if (is_a($members, 'PEAR_Error') || !is_array($members)) {
+ return $members;
+ }
+ if (!in_array($member, $members)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/maintainer.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/maintainer.php
new file mode 100644
index 000000000..342bd7bbd
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/maintainer.php
@@ -0,0 +1,45 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+require_once 'Horde/Kolab/Server/Object/adminrole.php';
+
+/**
+ * This class provides methods to deal with maintainer
+ * entries for Kolab.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server/Object/maintainer.php,v 1.5 2009/01/06 17:49:26 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Object_maintainer extends Horde_Kolab_Server_Object_adminrole {
+
+ /**
+ * The group the UID must be member of so that this object really
+ * matches this class type. This may not include the root UID.
+ *
+ * @var string
+ */
+ var $required_group = 'cn=maintainer,cn=internal';
+
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/server.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/server.php
new file mode 100644
index 000000000..c10dcccc3
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/server.php
@@ -0,0 +1,50 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * This class provides methods to deal with Kolab server configuration.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server/Object/server.php,v 1.5 2009/01/06 17:49:26 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Object_server extends Horde_Kolab_Server_Object {
+
+ /**
+ * The LDAP filter to retrieve this object type
+ *
+ * @var string
+ */
+ var $filter = '(&((k=kolab))(objectclass=kolab))';
+
+ /**
+ * The attributes supported by this class
+ *
+ * @var array
+ */
+ var $_supported_attributes = array(
+ KOLAB_ATTR_FBPAST,
+ );
+
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/sharedfolder.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/sharedfolder.php
new file mode 100644
index 000000000..05800bc50
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/sharedfolder.php
@@ -0,0 +1,109 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * This class provides methods to deal with shared folders
+ * entries for Kolab.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server/Object/sharedfolder.php,v 1.5 2009/01/06 17:49:26 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Object_sharedfolder extends Horde_Kolab_Server_Object {
+
+ /**
+ * The LDAP filter to retrieve this object type
+ *
+ * @var string
+ */
+ var $filter = '(objectClass=kolabSharedFolder)';
+
+ /**
+ * The attributes supported by this class
+ *
+ * @var array
+ */
+ var $_supported_attributes = array(
+ KOLAB_ATTR_CN,
+ KOLAB_ATTR_DELETED,
+ KOLAB_ATTR_FOLDERTYPE,
+ KOLAB_ATTR_HOMESERVER,
+ KOLAB_ATTR_IMAPHOST,
+ KOLAB_ATTR_QUOTA,
+ KOLAB_ATTR_ACL,
+ );
+
+ /**
+ * The attributes required when creating an object of this class.
+ *
+ * @var array
+ */
+ var $_required_attributes = array(
+ KOLAB_ATTR_CN,
+ KOLAB_ATTR_HOMESERVER,
+ );
+
+ /**
+ * The ldap classes for this type of object.
+ *
+ * @var array
+ */
+ var $_object_classes = array(
+ KOLAB_OC_TOP,
+ KOLAB_OC_KOLABSHAREDFOLDER,
+ );
+
+ /**
+ * Generates an ID for the given information.
+ *
+ * @param array $info The data of the object.
+ *
+ * @static
+ *
+ * @return string|PEAR_Error The ID.
+ */
+ function generateId($info)
+ {
+ return trim($info['cn'], " \t\n\r\0\x0B,");
+ }
+
+ /**
+ * Convert the object attributes to a hash.
+ *
+ * @param string $attrs The attributes to return.
+ *
+ * @return array|PEAR_Error The hash representing this object.
+ */
+ function toHash($attrs = null)
+ {
+ if (!isset($attrs)) {
+ $attrs = array(
+ KOLAB_ATTR_CN,
+ KOLAB_ATTR_HOMESERVER,
+ KOLAB_ATTR_FOLDERTYPE,
+ );
+ }
+ return parent::toHash($attrs);
+ }
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/user.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/user.php
new file mode 100644
index 000000000..8b47e9026
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server/Object/user.php
@@ -0,0 +1,312 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * This class provides methods to deal with Kolab users stored in
+ * the Kolab db.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server/Object/user.php,v 1.11 2009/01/08 21:00:08 wrobel Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Object_user extends Horde_Kolab_Server_Object {
+
+ /**
+ * The LDAP filter to retrieve this object type
+ *
+ * @var string
+ */
+ var $filter = '(&(objectClass=kolabInetOrgPerson)(uid=*)(mail=*)(sn=*))';
+
+ /**
+ * The attributes supported by this class
+ *
+ * @var array
+ */
+ var $_supported_attributes = array(
+ KOLAB_ATTR_SN,
+ KOLAB_ATTR_CN,
+ KOLAB_ATTR_GIVENNAME,
+ KOLAB_ATTR_FN,
+ KOLAB_ATTR_SID,
+ KOLAB_ATTR_USERPASSWORD,
+ KOLAB_ATTR_MAIL,
+ KOLAB_ATTR_DELETED,
+ KOLAB_ATTR_IMAPHOST,
+ KOLAB_ATTR_FREEBUSYHOST,
+ KOLAB_ATTR_HOMESERVER,
+ KOLAB_ATTR_KOLABDELEGATE,
+ KOLAB_ATTR_IPOLICY,
+ KOLAB_ATTR_FBFUTURE,
+ );
+
+ /**
+ * Attributes derived from the LDAP values.
+ *
+ * @var array
+ */
+ var $_derived_attributes = array(
+ KOLAB_ATTR_ID,
+ KOLAB_ATTR_USERTYPE,
+ KOLAB_ATTR_LNFN,
+ KOLAB_ATTR_FNLN,
+ );
+
+ /**
+ * The attributes required when creating an object of this class.
+ *
+ * @var array
+ */
+ var $_required_attributes = array(
+ KOLAB_ATTR_SN,
+ KOLAB_ATTR_GIVENNAME,
+ KOLAB_ATTR_USERPASSWORD,
+ KOLAB_ATTR_MAIL,
+ KOLAB_ATTR_HOMESERVER,
+ );
+
+ /**
+ * The ldap classes for this type of object.
+ *
+ * @var array
+ */
+ var $_object_classes = array(
+ KOLAB_OC_TOP,
+ KOLAB_OC_INETORGPERSON,
+ KOLAB_OC_KOLABINETORGPERSON,
+ KOLAB_OC_HORDEPERSON,
+ );
+
+ /**
+ * Initialize the Kolab Object. Provide either the UID or a
+ * LDAP search result.
+ *
+ * @param Horde_Kolab_Server &$db The link into the Kolab db.
+ * @param string $dn UID of the object.
+ * @param array $data A possible array of data for the object
+ */
+ function Horde_Kolab_Server_Object_user(&$db, $dn = null, $data = null)
+ {
+ global $conf;
+
+ /** Allows to customize the supported user attributes. */
+ if (isset($conf['kolab']['server']['user_supported_attrs'])) {
+ $this->_supported_attributes = $conf['kolab']['server']['user_supported_attrs'];
+ }
+
+ /** Allows to customize the required user attributes. */
+ if (isset($conf['kolab']['server']['user_required_attrs'])) {
+ $this->_required_attributes = $conf['kolab']['server']['user_required_attrs'];
+ }
+
+ /** Allows to customize the user object classes. */
+ if (isset($conf['kolab']['server']['user_objectclasses'])) {
+ $this->_object_classes = $conf['kolab']['server']['user_object_classes'];
+ }
+
+ Horde_Kolab_Server_Object::Horde_Kolab_Server_Object($db, $dn, $data);
+ }
+
+ /**
+ * Derive an attribute value.
+ *
+ * @param string $attr The attribute to derive.
+ *
+ * @return mixed The value of the attribute.
+ */
+ function _derive($attr)
+ {
+ switch ($attr) {
+ case KOLAB_ATTR_USERTYPE:
+ if (strpos($this->_uid, 'cn=internal')) {
+ return KOLAB_UT_INTERNAL;
+ } else if (strpos($this->_uid, 'cn=group')) {
+ return KOLAB_UT_GROUP;
+ } else if (strpos($this->_uid, 'cn=resource')) {
+ return KOLAB_UT_RESOURCE;
+ } else {
+ return KOLAB_UT_STANDARD;
+ }
+ default:
+ return parent::_derive($attr);
+ }
+ }
+
+ /**
+ * Convert the object attributes to a hash.
+ *
+ * @param string $attrs The attributes to return.
+ *
+ * @return array|PEAR_Error The hash representing this object.
+ */
+ function toHash($attrs = null)
+ {
+ if (!isset($attrs)) {
+ $attrs = array(
+ KOLAB_ATTR_SID,
+ KOLAB_ATTR_FN,
+ KOLAB_ATTR_MAIL,
+ KOLAB_ATTR_USERTYPE,
+ );
+ }
+ return parent::toHash($attrs);
+ }
+
+ /**
+ * Get the groups for this object
+ *
+ * @return mixed|PEAR_Error An array of group ids, false if no groups were
+ * found.
+ */
+ function getGroups()
+ {
+ return $this->_db->getGroups($this->_uid);
+ }
+
+ /**
+ * Returns the server url of the given type for this user.
+ *
+ * This method is used to encapsulate multidomain support.
+ *
+ * @param string $server_type The type of server URL that should be returned.
+ *
+ * @return string The server url or empty on error.
+ */
+ function getServer($server_type)
+ {
+ global $conf;
+
+ switch ($server_type) {
+ case 'freebusy':
+ $server = $this->get(KOLAB_ATTR_FREEBUSYHOST);
+ if (!is_a($server, 'PEAR_Error') && !empty($server)) {
+ return $server;
+ }
+ $server = $this->getServer('homeserver');
+ if (is_a($server, 'PEAR_Error')) {
+ return $server;
+ }
+ if (empty($server)) {
+ $server = $_SERVER['SERVER_NAME'];
+ }
+ if (isset($conf['kolab']['freebusy']['server'])) {
+ return $conf['kolab']['freebusy']['server'];
+ }
+ if (isset($conf['kolab']['server']['freebusy_url_format'])) {
+ return sprintf($conf['kolab']['server']['freebusy_url_format'],
+ $server);
+ } else {
+ return 'https://' . $server . '/freebusy';
+ }
+ case 'imap':
+ $server = $this->get(KOLAB_ATTR_IMAPHOST);
+ if (!is_a($server, 'PEAR_Error') && !empty($server)) {
+ return $server;
+ }
+ case 'homeserver':
+ default:
+ $server = $this->get(KOLAB_ATTR_HOMESERVER);
+ if (empty($server)) {
+ $server = $_SERVER['SERVER_NAME'];
+ }
+ return $server;
+ }
+ }
+
+ /**
+ * Generates an ID for the given information.
+ *
+ * @param array $info The data of the object.
+ *
+ * @static
+ *
+ * @return string|PEAR_Error The ID.
+ */
+ function generateId($info)
+ {
+ global $conf;
+
+ /** The fields that should get mapped into the user ID. */
+ if (isset($conf['kolab']['server']['user_id_mapfields'])) {
+ $id_mapfields = $conf['kolab']['server']['user_id_mapfields'];
+ } else {
+ $id_mapfields = array('givenName', 'sn');
+ }
+
+ /** The user ID format. */
+ if (isset($conf['kolab']['server']['user_id_format'])) {
+ $id_format = $conf['kolab']['server']['user_id_format'];
+ } else {
+ $id_format = '%s %s';
+ }
+
+ $fieldarray = array();
+ foreach ($id_mapfields as $mapfield) {
+ if (isset($info[$mapfield])) {
+ $fieldarray[] = $info[$mapfield];
+ } else {
+ $fieldarray[] = '';
+ }
+ }
+ return trim(vsprintf($id_format, $fieldarray), " \t\n\r\0\x0B,");
+ }
+
+ /**
+ * Saves object information.
+ *
+ * @param array $info The information about the object.
+ *
+ * @return boolean|PEAR_Error True on success.
+ */
+ function save($info)
+ {
+ if (!isset($info['cn'])) {
+ if (!isset($info['sn']) || !isset($info['givenName'])) {
+ return PEAR::raiseError('Either the last name or the given name is missing!');
+ } else {
+ $info['cn'] = $this->generateId($info);
+ }
+ }
+
+ if (isset($conf['kolab']['server']['user_mapping'])) {
+ $mapped = array();
+ $map = $conf['kolab']['server']['user_mapping'];
+ foreach ($map as $key => $val) {
+ $mapped[$val] = $info[$key];
+ }
+ $info = $mapped;
+ }
+
+ if (isset($conf['kolab']['server']['user_mapping'])) {
+ $mapped = array();
+ $map = $conf['kolab']['server']['user_mapping'];
+ foreach ($map as $key => $val) {
+ $mapped[$val] = $info[$key];
+ }
+ $info = $mapped;
+ }
+
+ return parent::save($info);
+ }
+};
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/ldap.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/ldap.php
new file mode 100644
index 000000000..3e9d52981
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server/ldap.php
@@ -0,0 +1,987 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/** We need the Horde LDAP tools for this class **/
+require_once 'Horde/LDAP.php';
+
+/**
+ * This class provides methods to deal with Kolab objects stored in
+ * the standard Kolab LDAP db.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server/ldap.php,v 1.9 2009/01/08 07:12:44 wrobel Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_ldap extends Horde_Kolab_Server {
+
+ /**
+ * LDAP connection handle.
+ *
+ * @var resource
+ */
+ var $_connection;
+
+ /**
+ * Flag that indicates bound state for the LDAP connection.
+ *
+ * @var boolean
+ */
+ var $_bound;
+
+ /**
+ * The base dn .
+ *
+ * @var boolean
+ */
+ var $_base_dn;
+
+ /**
+ * Connects to the LDAP server.
+ *
+ * @param string $server LDAP server URL.
+ * @param string $base_dn LDAP server base DN.
+ *
+ * @return boolean|PEAR_Error True if the connection succeeded.
+ */
+ function _connect($server = null, $base_dn = null)
+ {
+ if (!function_exists('ldap_connect')) {
+ return PEAR::raiseError(_("Cannot connect to the Kolab LDAP server. PHP does not support LDAP!"));
+ }
+
+ if (!$server) {
+ if (isset($this->_params['server'])) {
+ $server = $this->_params['server'];
+ } else {
+ return PEAR::raiseError(_("Horde_Kolab_Server_ldap needs a server parameter!"));
+ }
+ }
+ if (!$base_dn) {
+ if (isset($this->_params['base_dn'])) {
+ $this->_base_dn = $this->_params['base_dn'];
+ } else {
+ return PEAR::raiseError(_("Horde_Kolab_Server_ldap needs a base_dn parameter!"));
+ }
+ } else {
+ $this->_base_dn = $base_dn;
+ }
+
+ $this->_connection = @ldap_connect($server);
+ if (!$this->_connection) {
+ return PEAR::raiseError(sprintf(_("Error connecting to LDAP server %s!"),
+ $server));
+ }
+
+ /* We need version 3 for Kolab */
+ if (!ldap_set_option($this->_connection, LDAP_OPT_PROTOCOL_VERSION, 3)) {
+ return PEAR::raiseError(sprintf(_("Error setting LDAP protocol on server %s to v3: %s"),
+ $server,
+ ldap_error($this->_connection)));
+ }
+
+ return true;
+ }
+
+ /**
+ * Binds the LDAP connection with a specific user and pass.
+ *
+ * @param string $dn DN to bind with
+ * @param string $pw Password associated to this DN.
+ *
+ * @return boolean|PEAR_Error Whether or not the binding succeeded.
+ */
+ function _bind($dn = false, $pw = '')
+ {
+ if (!$this->_connection) {
+ $result = $this->_connect();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ if (!$dn) {
+ if (isset($this->_params['uid'])) {
+ $dn = $this->_params['uid'];
+ } else {
+ $dn = '';
+ }
+ }
+ if (!$pw) {
+ if (isset($this->_params['pass'])) {
+ $pw = $this->_params['pass'];
+ }
+ }
+
+ $this->_bound = @ldap_bind($this->_connection, $dn, $pw);
+
+ if (!$this->_bound) {
+ return PEAR::raiseError(sprintf(_("Unable to bind to the LDAP server as %s!"),
+ $dn));
+ }
+ return true;
+ }
+
+ /**
+ * Disconnect from LDAP.
+ *
+ * @return NULL
+ */
+ function unbind()
+ {
+ $result = @ldap_unbind($this->_connection);
+ if (!$result) {
+ return PEAR::raiseError("Failed to unbind from the LDAP server!");
+ }
+
+ $this->_bound = false;
+ }
+
+ /**
+ * Search for an object.
+ *
+ * @param string $filter Filter criteria.
+ * @param array $attributes Restrict the search result to
+ * these attributes.
+ * @param string $base The base location for searching.
+ *
+ * @return array|PEAR_Error A LDAP search result.
+ */
+ function _search($filter, $attributes = null, $base = null)
+ {
+ if (!$this->_bound) {
+ $result = $this->_bind();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ if (empty($base)) {
+ $base = $this->_base_dn;
+ }
+
+ if (isset($attributes)) {
+ $result = @ldap_search($this->_connection, $base, $filter, $attributes);
+ } else {
+ $result = @ldap_search($this->_connection, $base, $filter);
+ }
+ if (!$result && $this->_errno()) {
+ return PEAR::raiseError(sprintf(_("LDAP Error: Failed to search using filter %s. Error was: %s"),
+ $filter, $this->_error()));
+ }
+ return $result;
+ }
+
+ /**
+ * Read object data.
+ *
+ * @param string $dn The object to retrieve.
+ * @param string $attrs Restrict to these attributes.
+ *
+ * @return array|PEAR_Error An array of attributes.
+ */
+ function _read($dn, $attrs = null)
+ {
+ if (!$this->_bound) {
+ $result = $this->_bind();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ if (isset($attrs)) {
+ $result = @ldap_read($this->_connection, $dn, '(objectclass=*)', $attrs);
+ } else {
+ $result = @ldap_read($this->_connection, $dn, '(objectclass=*)');
+ }
+ if (!$result && $this->_errno()) {
+ return PEAR::raiseError(sprintf(_("LDAP Error: No such object: %s: %s"),
+ $dn, $this->_error()));
+ }
+ $entry = $this->_firstEntry($result);
+ if (!$entry) {
+ ldap_free_result($result);
+ return PEAR::raiseError(sprintf(_("LDAP Error: Empty result for: %s."),
+ $dn));
+ }
+ $object = $this->_getAttributes($entry);
+ if (!$object && $this->_errno()) {
+ return PEAR::raiseError(sprintf(_("LDAP Error: No such dn: %s: %s"),
+ $dn, $this->_error()));
+ }
+ ldap_free_result($result);
+ return $object;
+ }
+
+ /**
+ * Add a new object
+ *
+ * @param string $dn The DN of the object to be added.
+ * @param array $data The attributes of the object to be added.
+ *
+ * @return boolean True if adding succeeded.
+ */
+ function _add($dn, $data)
+ {
+ if (!$this->_bound) {
+ $result = $this->_bind();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ return @ldap_add($this->_connection, $dn, $data);
+ }
+
+ /**
+ * Count the number of results.
+ *
+ * @param string $result The LDAP search result.
+ *
+ * @return int The number of records found.
+ */
+ function _count($result)
+ {
+ return @ldap_count_entries($this->_connection, $result);
+ }
+
+ /**
+ * Return the dn of an entry.
+ *
+ * @param resource $entry The LDAP entry.
+ *
+ * @return string The DN of the entry.
+ */
+ function _getDn($entry)
+ {
+ return @ldap_get_dn($this->_connection, $entry);
+ }
+
+ /**
+ * Return the attributes of an entry.
+ *
+ * @param resource $entry The LDAP entry.
+ *
+ * @return array The attributes of the entry.
+ */
+ function _getAttributes($entry)
+ {
+ return @ldap_get_attributes($this->_connection, $entry);
+ }
+
+ /**
+ * Return the first entry of a result.
+ *
+ * @param resource $result The LDAP search result.
+ *
+ * @return resource The first entry of the result.
+ */
+ function _firstEntry($result)
+ {
+ return @ldap_first_entry($this->_connection, $result);
+ }
+
+ /**
+ * Return the next entry of a result.
+ *
+ * @param resource $entry The current LDAP entry.
+ *
+ * @return resource The next entry of the result.
+ */
+ function _nextEntry($entry)
+ {
+ return @ldap_next_entry($this->_connection, $entry);
+ }
+
+ /**
+ * Return the entries of a result.
+ *
+ * @param resource $result The LDAP search result.
+ * @param int $from Only return results after this position.
+ * @param int $to Only return results until this position.
+ *
+ * @return array The entries of the result.
+ */
+ function _getEntries($result, $from = -1, $to = -1)
+ {
+ if ($from >= 0 || $to >= 0) {
+ $result = array();
+
+ $i = 0;
+ for ($entry = $this->_firstEntry($result);
+ $entry != false;
+ $entry = $this->_nextEntry($entry)) {
+ if (!$entry && $this->_errno()) {
+ return false;
+ }
+ if ($i > $from && ($i <= $to || $to == -1)) {
+ $attributes = $this->_getAttributes($entry);
+ if (!$attributes && $this->_errno()) {
+ return false;
+ }
+ $result[] = $attributes;
+ }
+ $i++;
+ }
+ return $result;
+ }
+ return @ldap_get_entries($this->_connection, $result);
+ }
+
+ /**
+ * Sort the entries of a result.
+ *
+ * @param resource $result The LDAP search result.
+ * @param string $attribute The attribute used for sorting.
+ *
+ * @return boolean True if sorting succeeded.
+ */
+ function _sort($result, $attribute)
+ {
+ return @ldap_sort($this->_connection, $result, $attribute);
+ }
+
+ /**
+ * Return the current LDAP error number.
+ *
+ * @return int The current LDAP error number.
+ */
+ function _errno()
+ {
+ return @ldap_errno($this->_connection);
+ }
+
+ /**
+ * Return the current LDAP error description.
+ *
+ * @return string The current LDAP error description.
+ */
+ function _error()
+ {
+ return @ldap_error($this->_connection);
+ }
+
+ /*
+ * ------------------------------------------------------------------
+ * The functions defined below do not call ldap_* functions directly.
+ * ------------------------------------------------------------------
+ */
+
+ /**
+ * Return the root of the UID values on this server.
+ *
+ * @return string The base UID on this server (base DN on ldap).
+ */
+ function getBaseUid()
+ {
+ return $this->_base_dn;
+ }
+
+ /**
+ * Return the DNs of a result.
+ *
+ * @param resource $result The LDAP search result.
+ * @param int $from Only return results after this position.
+ * @param int $to Only return results until this position.
+ *
+ * @return array The DNs of the result.
+ */
+ function _getDns($result, $from = -1, $to = -1)
+ {
+ $dns = array();
+ $entry = $this->_firstEntry($result);
+
+ $i = 0;
+ for ($entry = $this->_firstEntry($result);
+ $entry != false;
+ $entry = $this->_nextEntry($entry)) {
+ if ($i > $from && ($i <= $to || $to == -1)) {
+ $dn = $this->_getDn($entry);
+ if (!$dn && $this->_errno()) {
+ return false;
+ }
+ $dns[] = $dn;
+ }
+ $i++;
+ }
+ if ($this->_errno()) {
+ return false;
+ }
+ return $dns;
+ }
+
+ /**
+ * Identify the DN of the first result entry.
+ *
+ * @param array $result The LDAP search result.
+ * @param int $restrict A KOLAB_SERVER_RESULT_* result restriction.
+ *
+ * @return string|PEAR_Error The DN.
+ */
+ function _dnFromResult($result, $restrict = KOLAB_SERVER_RESULT_SINGLE)
+ {
+ switch ($restrict) {
+ case KOLAB_SERVER_RESULT_STRICT:
+ $count = $this->_count($result);
+ if (!$count) {
+ return false;
+ } else if ($count > 1) {
+ return PEAR::raiseError(sprintf(_("Found %s results when expecting only one!"),
+ $count));
+ }
+ case KOLAB_SERVER_RESULT_SINGLE:
+ $entry = $this->_firstEntry($result);
+ if (!$entry && $this->_errno()) {
+ return PEAR::raiseError(sprintf(_("Search failed. Error was: %s"),
+ $this->_error()));
+ }
+ if (!$entry) {
+ return false;
+ }
+ $dn = $this->_getDn($entry);
+ if (!$dn && $this->_errno()) {
+ return PEAR::raiseError(sprintf(_("Retrieving DN failed. Error was: %s"),
+ $this->_error()));
+ }
+ return $dn;
+ case KOLAB_SERVER_RESULT_MANY:
+ $entries = $this->_getDns($result);
+ if (!$entries && $this->_errno()) {
+ return PEAR::raiseError(sprintf(_("Search failed. Error was: %s"),
+ $this->_error()));
+ }
+ if (!$entries) {
+ return false;
+ }
+ return $entries;
+ }
+ return false;
+ }
+
+ /**
+ * Get the attributes of the first result entry.
+ *
+ * @param array $result The LDAP search result.
+ * @param array $attrs The attributes to retrieve.
+ * @param int $restrict A KOLAB_SERVER_RESULT_* result restriction.
+ *
+ * @return mixed|PEAR_Error The attributes or false if there were
+ * no results.
+ */
+ function _attrsFromResult($result, $attrs,
+ $restrict = KOLAB_SERVER_RESULT_SINGLE)
+ {
+ $entries = array();
+
+ switch ($restrict) {
+ case KOLAB_SERVER_RESULT_STRICT:
+ $count = $this->_count($result);
+ if (!$count) {
+ return false;
+ } else if ($count > 1) {
+ return PEAR::raiseError(sprintf(_("Found %s results when expecting only one!"),
+ $count));
+ }
+ case KOLAB_SERVER_RESULT_SINGLE:
+ $first = $this->_firstEntry($result);
+ if (!$first && $this->_errno()) {
+ return PEAR::raiseError(sprintf(_("Search failed. Error was: %s"),
+ $this->_error()));
+ }
+ if (!$first) {
+ return false;
+ }
+ $entry = $this->_getAttributes($first);
+ if (!$entry && $this->_errno()) {
+ return PEAR::raiseError(sprintf(_("Retrieving attributes failed. Error was: %s"),
+ $this->_error()));
+ }
+
+ $result = array();
+ foreach ($attrs as $attr) {
+ if ($entry[$attr]['count'] > 0) {
+ unset($entry[$attr]['count']);
+ $result[$attr] = $entry[$attr];
+ }
+ }
+ return $result;
+ case KOLAB_SERVER_RESULT_MANY:
+ $entries = $this->_getEntries($result);
+ if (!$entries && $this->_errno()) {
+ return PEAR::raiseError(sprintf(_("Search failed. Error was: %s"),
+ $this->_error()));
+ }
+ if (!$entries) {
+ return false;
+ }
+ unset($entries['count']);
+ $result = array();
+
+ $i = 0;
+ foreach ($entries as $entry) {
+ $result[$i] = array();
+ foreach ($attrs as $attr) {
+ if (isset($entry[$attr])) {
+ if ($entry[$attr]['count'] > 0) {
+ unset($entry[$attr]['count']);
+ $result[$i][$attr] = $entry[$attr];
+ }
+ }
+ }
+ $i++;
+ }
+ return $result;
+ }
+ return false;
+ }
+
+ /**
+ * Determine the type of a Kolab object.
+ *
+ * @param string $dn The DN of the object to examine.
+ *
+ * @return int The corresponding Kolab object type.
+ */
+ function _determineType($dn)
+ {
+ $oc = $this->_getObjectClasses($dn);
+ if (is_a($oc, 'PEAR_Error')) {
+ return $oc;
+ }
+
+ // Not a user type?
+ if (!in_array('kolabinetorgperson', $oc)) {
+ // Is it a group?
+ if (in_array('kolabgroupofnames', $oc)) {
+ return KOLAB_OBJECT_GROUP;
+ }
+ // Is it a shared Folder?
+ if (in_array('kolabsharedfolder', $oc)) {
+ return KOLAB_OBJECT_SHAREDFOLDER;
+ }
+ return PEAR::raiseError(sprintf(_("Unkown Kolab object type for DN %s."),
+ $dn));
+ }
+
+ $groups = $this->getGroups($dn);
+ if (is_a($groups, 'PEAR_Error')) {
+ return $groups;
+ }
+ if (!empty($groups)) {
+ if (in_array('cn=admin,cn=internal,' . $this->_base_dn, $groups)) {
+ return KOLAB_OBJECT_ADMINISTRATOR;
+ }
+ if (in_array('cn=maintainer,cn=internal,' . $this->_base_dn,
+ $groups)) {
+ return KOLAB_OBJECT_MAINTAINER;
+ }
+ if (in_array('cn=domain-maintainer,cn=internal,' . $this->_base_dn,
+ $groups)) {
+ return KOLAB_OBJECT_DOMAINMAINTAINER;
+ }
+ }
+
+ if (strpos($dn, 'cn=external') !== false) {
+ return KOLAB_OBJECT_ADDRESS;
+ }
+
+ return KOLAB_OBJECT_USER;
+ }
+
+ /**
+ * Get the LDAP object classes for the given DN.
+ *
+ * @param string $dn DN of the object.
+ *
+ * @return array|PEAR_Error An array of object classes.
+ */
+ function _getObjectClasses($dn)
+ {
+ $object = $this->_read($dn, array('objectClass'));
+ if (is_a($object, 'PEAR_Error')) {
+ return $object;
+ }
+ if (!isset($object['objectClass'])) {
+ return PEAR::raiseError('The result has no object classes!');
+ }
+ unset($object['count']);
+ unset($object['objectClass']['count']);
+ $result = array_map('strtolower', $object['objectClass']);
+ return $result;
+ }
+
+ /**
+ * Identify the DN for the first object found using a filter.
+ *
+ * @param string $filter The LDAP filter to use.
+ * @param int $restrict A KOLAB_SERVER_RESULT_* result restriction.
+ *
+ * @return mixed|PEAR_Error The DN or false if there was no result.
+ */
+ function _dnForFilter($filter,
+ $restrict = KOLAB_SERVER_RESULT_SINGLE)
+ {
+ $result = $this->_search($filter, array());
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ if (!$this->_count($result)) {
+ return false;
+ }
+ return $this->_dnFromResult($result, $restrict);
+ }
+
+ /**
+ * Identify attributes for the first object found using a filter.
+ *
+ * @param string $filter The LDAP filter to use.
+ * @param array $attrs The attributes to retrieve.
+ * @param int $restrict A KOLAB_SERVER_RESULT_* result restriction.
+ *
+ * @return mixed|PEAR_Error The DN or false if there was no result.
+ */
+ function _attrsForFilter($filter, $attrs,
+ $restrict = KOLAB_SERVER_RESULT_SINGLE)
+ {
+ $result = $this->_search($filter, $attrs);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ return $this->_attrsFromResult($result, $attrs, $restrict);
+ }
+
+ /**
+ * Identify the primary mail attribute for the first object found
+ * with the given ID or mail.
+ *
+ * @param string $id Search for objects with this ID/mail.
+ *
+ * @return mixed|PEAR_Error The mail address or false if there was
+ * no result.
+ */
+ function mailForIdOrMail($id)
+ {
+ $filter = '(&(objectClass=kolabInetOrgPerson)(|(uid='.
+ Horde_LDAP::quote($id) . ')(mail=' .
+ Horde_LDAP::quote($id) . ')))';
+ $result = $this->_attrsForFilter($filter, array('mail'),
+ KOLAB_SERVER_RESULT_STRICT);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ return $result['mail'][0];
+ }
+
+ /**
+ * Identify the UID for the first object found with the given ID
+ * or mail.
+ *
+ * @param string $id Search for objects with this ID/mail.
+ *
+ * @return mixed|PEAR_Error The UID or false if there was no result.
+ */
+ function uidForIdOrMail($id)
+ {
+ $filter = '(&(objectClass=kolabInetOrgPerson)(|(uid='.
+ Horde_LDAP::quote($id) . ')(mail=' .
+ Horde_LDAP::quote($id) . ')))';
+ return $this->_dnForFilter($filter, KOLAB_SERVER_RESULT_STRICT);
+ }
+
+ /**
+ * Returns a list of allowed email addresses for the given user.
+ *
+ * @param string $id The users primary mail address or ID.
+ *
+ * @return array|PEAR_Error An array of allowed mail addresses
+ */
+ function addrsForIdOrMail($id)
+ {
+ $filter = '(&(objectClass=kolabInetOrgPerson)(|(mail='
+ . Horde_LDAP::quote($id) . ')(uid='
+ . Horde_LDAP::quote($id) . ')))';
+ $result = $this->_attrsForFilter($filter, array('mail', 'alias'),
+ KOLAB_SERVER_RESULT_STRICT);
+ if (empty($result) || is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ $addrs = array_merge((array) $result['mail'], (array) $result['alias']);
+ $mail = $result['mail'][0];
+
+ $filter = '(&(objectClass=kolabInetOrgPerson)(kolabDelegate='
+ . Horde_LDAP::quote($mail) . '))';
+ $result = $this->_attrsForFilter($filter, array('mail', 'alias'),
+ KOLAB_SERVER_RESULT_MANY);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (!empty($result)) {
+ foreach ($result as $adr) {
+ if (isset($adr['mail'])) {
+ $addrs = array_merge((array) $addrs, (array) $adr['mail']);
+ }
+ if (isset($adr['alias'])) {
+ $addrs = array_merge((array) $addrs, (array) $adr['alias']);
+ }
+ }
+ }
+
+ return $addrs;
+ }
+
+ /**
+ * Return the UID for a given primary mail, ID, or alias.
+ *
+ * @param string $mail A valid mail address for the user.
+ *
+ * @return mixed|PEAR_Error The UID or false if there was no result.
+ */
+ function uidForMailAddress($mail)
+ {
+ $filter = '(&(objectClass=kolabInetOrgPerson)(|(uid='.
+ Horde_LDAP::quote($mail) . ')(mail=' .
+ Horde_LDAP::quote($mail) . ')(alias=' .
+ Horde_LDAP::quote($mail) . ')))';
+ return $this->_dnForFilter($filter);
+ }
+
+ /**
+ * Identify the UID for the first object found using a specified
+ * attribute value.
+ *
+ * @param string $attr The name of the attribute used for searching.
+ * @param string $value The desired value of the attribute.
+ * @param int $restrict A KOLAB_SERVER_RESULT_* result restriction.
+ *
+ * @return mixed|PEAR_Error The UID or false if there was no result.
+ */
+ function uidForAttr($attr, $value,
+ $restrict = KOLAB_SERVER_RESULT_SINGLE)
+ {
+ $filter = '(&(objectClass=kolabInetOrgPerson)(' . $attr .
+ '=' . Horde_LDAP::quote($value) . '))';
+ return $this->_dnForFilter($filter, $restrict);
+ }
+
+ /**
+ * Identify the GID for the first group found using a specified
+ * attribute value.
+ *
+ * @param string $attr The name of the attribute used for searching.
+ * @param string $value The desired value of the attribute.
+ * @param int $restrict A KOLAB_SERVER_RESULT_* result restriction.
+ *
+ * @return mixed|PEAR_Error The GID or false if there was no result.
+ */
+ function gidForAttr($attr, $value,
+ $restrict = KOLAB_SERVER_RESULT_SINGLE)
+ {
+ $filter = '(&(objectClass=kolabGroupOfNames)(' . $attr .
+ '=' . Horde_LDAP::quote($value) . '))';
+ return $this->_dnForFilter($filter, $restrict);
+ }
+
+ /**
+ * Is the given UID member of the group with the given mail address?
+ *
+ * @param string $uid UID of the user.
+ * @param string $mail Search the group with this mail address.
+ *
+ * @return boolen|PEAR_Error True in case the user is in the
+ * group, false otherwise.
+ */
+ function memberOfGroupAddress($uid, $mail)
+ {
+ $filter = '(&(objectClass=kolabGroupOfNames)(mail='
+ . Horde_LDAP::quote($mail) . ')(member='
+ . Horde_LDAP::quote($uid) . '))';
+ $result = $this->_dnForFilter($filter, KOLAB_SERVER_RESULT_STRICT);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ if (empty($result)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get the groups for this object.
+ *
+ * @param string $uid The UID of the object to fetch.
+ *
+ * @return array|PEAR_Error An array of group ids.
+ */
+ function getGroups($uid)
+ {
+ $filter = '(&(objectClass=kolabGroupOfNames)(member='
+ . Horde_LDAP::quote($uid) . '))';
+ $result = $this->_dnForFilter($filter, KOLAB_SERVER_RESULT_MANY);
+ if (empty($result)) {
+ return array();
+ }
+ return $result;
+ }
+
+ /**
+ * List all objects of a specific type
+ *
+ * @param string $type The type of the objects to be listed
+ * @param array $params Additional parameters.
+ *
+ * @return array|PEAR_Error An array of Kolab objects.
+ */
+ function _listObjects($type, $params = null)
+ {
+ if (empty($params['base_dn'])) {
+ $base = $this->_base_dn;
+ } else {
+ $base = $params['base_dn'];
+ }
+
+ $result = Horde_Kolab_Server_Object::loadClass($type);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ $vars = get_class_vars($type);
+ $filter = $vars['filter'];
+ $sort = $vars['sort_by'];
+
+ if (isset($params['sort'])) {
+ $sort = $params['sort'];
+ }
+
+ $result = $this->_search($filter, null, $base);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ if (empty($result)) {
+ return array();
+ }
+
+ if ($sort) {
+ $this->_sort($result, $sort);
+ }
+
+ if (isset($params['from'])) {
+ $from = $params['from'];
+ } else {
+ $from = -1;
+ }
+
+ if (isset($params['to'])) {
+ $sort = $params['to'];
+ } else {
+ $to = -1;
+ }
+
+ $entries = $this->_getDns($result, $from, $to);
+ if (!$entries && $this->_errno()) {
+ return PEAR::raiseError(sprintf(_("Search failed. Error was: %s"),
+ $this->_error()));
+ }
+ if (!$entries) {
+ return false;
+ }
+
+ if (!empty($vars['required_group'])) {
+ $required_group = $this->fetch($vars['required_group'],
+ KOLAB_OBJECT_GROUP);
+ }
+
+ $objects = array();
+ foreach ($entries as $dn) {
+ if (!empty($vars['required_group']) && $required_group->isMember($dn)) {
+ continue;
+ }
+ $result = $this->fetch($dn, $type);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ $objects[] = $result;
+ }
+ return $objects;
+ }
+
+ /**
+ * Generates a UID for the given information.
+ *
+ * @param string $type The type of the object to create.
+ * @param string $id The id of the object.
+ * @param array $info Any additional information about the object to create.
+ *
+ * @return string|PEAR_Error The DN.
+ */
+ function _generateUid($type, $id, $info)
+ {
+ switch ($type) {
+ case KOLAB_OBJECT_USER:
+ if (!isset($info['user_type']) || $info['user_type'] == 0) {
+ return sprintf('cn=%s,%s', $id, $this->_base_dn);
+ } else if ($info['user_type'] == KOLAB_UT_INTERNAL) {
+ return sprintf('cn=%s,cn=internal,%s', $id, $this->_base_dn);
+ } else if ($info['user_type'] == KOLAB_UT_GROUP) {
+ return sprintf('cn=%s,cn=groups,%s', $id, $this->_base_dn);
+ } else if ($info['user_type'] == KOLAB_UT_RESOURCE) {
+ return sprintf('cn=%s,cn=resources,%s', $id, $this->_base_dn);
+ } else {
+ return sprintf('cn=%s,%s', $id, $this->_base_dn);
+ }
+ case KOLAB_OBJECT_ADDRESS:
+ return sprintf('cn=%s,cn=external,%s', $id, $this->_base_dn);
+ case KOLAB_OBJECT_SHAREDFOLDER:
+ case KOLAB_OBJECT_ADMINISTRATOR:
+ case KOLAB_OBJECT_MAINTAINER:
+ case KOLAB_OBJECT_DOMAINMAINTAINER:
+ return sprintf('cn=%s,%s', $id, $this->_base_dn);
+ case KOLAB_OBJECT_GROUP:
+ case KOLAB_OBJECT_DISTLIST:
+ if (!isset($info['visible']) || !empty($info['visible'])) {
+ return sprintf('cn=%s,%s', $id, $this->_base_dn);
+ } else {
+ return sprintf('cn=%s,cn=internal,%s', $id, $this->_base_dn);
+ }
+ default:
+ return PEAR::raiseError(_("Not implemented!"));
+ }
+ }
+
+ /**
+ * Save an object.
+ *
+ * @param string $dn The DN of the object.
+ * @param array $data The data for the object.
+ *
+ * @return boolean|PEAR_Error True if successfull.
+ */
+ function save($dn, $data)
+ {
+ $result = $this->_add($dn, $data);
+ if (!$result && $this->_errno()) {
+ return PEAR::raiseError(sprintf(_("Failed saving object. Error was: %s"),
+ $this->_error()));
+ }
+ }
+
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/test.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/test.php
new file mode 100644
index 000000000..65c7820b0
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Server/test.php
@@ -0,0 +1,623 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/** Require the LDAP based class as our base class */
+require_once 'Horde/Kolab/Server/ldap.php';
+
+/**
+ * This class provides a class for testing the Kolab Server DB.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Server/test.php,v 1.8 2009/01/06 17:49:26 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_test extends Horde_Kolab_Server_ldap {
+
+ /**
+ * Array holding the current result set.
+ *
+ * @var array
+ */
+ var $_result;
+
+ /**
+ * Buffer for error numbers.
+ *
+ * @var int
+ */
+ var $_errno = 0;
+
+ /**
+ * Buffer for error descriptions.
+ *
+ * @var int
+ */
+ var $_error = '';
+
+ /**
+ * Attribute used for sorting.
+ *
+ * @var string
+ */
+ var $_sort_by;
+
+ /**
+ * A result cache for iterating over the result.
+ *
+ * @var array
+ */
+ var $_current_result;
+
+ /**
+ * An index into the current result for iterating.
+ *
+ * @var int
+ */
+ var $_current_index;
+
+ /**
+ * Construct a new Horde_Kolab_Server object.
+ *
+ * @param array $params Parameter array.
+ */
+ function Horde_Kolab_Server_test($params = array())
+ {
+ if (isset($params['data'])) {
+ $GLOBALS['KOLAB_SERVER_TEST_DATA'] = $params['data'];
+ } else {
+ if (!isset($GLOBALS['KOLAB_SERVER_TEST_DATA'])) {
+ $GLOBALS['KOLAB_SERVER_TEST_DATA'] = array();
+ }
+ }
+ Horde_Kolab_Server::Horde_Kolab_Server($params);
+ }
+
+ /**
+ * Binds the LDAP connection with a specific user and pass.
+ *
+ * @param string $dn DN to bind with
+ * @param string $pw Password associated to this DN.
+ *
+ * @return boolean|PEAR_Error Whether or not the binding succeeded.
+ */
+ function _bind($dn = false, $pw = '')
+ {
+ if (!$dn) {
+ if (isset($this->_params['uid'])) {
+ $dn = $this->_params['uid'];
+ } else {
+ $dn = '';
+ }
+ }
+ if (!$pw) {
+ if (isset($this->_params['pass'])) {
+ $pw = $this->_params['pass'];
+ }
+ }
+
+ if (!empty($dn)) {
+ if (!isset($GLOBALS['KOLAB_SERVER_TEST_DATA'][$dn])) {
+ return PEAR::raiseError('User does not exist!');
+ }
+
+ $this->_bound = true;
+
+ $data = $this->_read($dn, $attrs = array('userPassword'));
+ if (is_a($data, 'PEAR_Error')) {
+ $this->_bound = false;
+ return $data;
+ }
+ if (!isset($data['userPassword'])) {
+ $this->_bound = false;
+ return PEAR::raiseError('User has no password entry!');
+ }
+ $this->_bound = $data['userPassword'][0] == $pw;
+ if (!$this->_bound) {
+ return PEAR::raiseError('Incorrect password!');
+ }
+ } else if (!empty($this->_params['no_anonymous_bind'])) {
+ $this->_bound = false;
+ return PEAR::raiseError('Anonymous bind is not allowed!');
+ } else {
+ $this->_bound = true;
+ }
+ return $this->_bound;
+ }
+
+ /**
+ * Disconnect from LDAP.
+ *
+ * @return NULL
+ */
+ function unbind()
+ {
+ $this->_bound = false;
+ }
+
+ /**
+ * Parse LDAP filter.
+ * Partially derived from Net_LDAP_Filter.
+ *
+ * @param string $filter The filter string.
+ *
+ * @return array|PEAR_Error An array of the parsed filter.
+ */
+ function _parse($filter)
+ {
+ $result = array();
+ if (preg_match('/^\((.+?)\)$/', $filter, $matches)) {
+ if (in_array(substr($matches[1], 0, 1), array('!', '|', '&'))) {
+ $result['op'] = substr($matches[1], 0, 1);
+ $result['sub'] = $this->_parseSub(substr($matches[1], 1));
+ return $result;
+ } else {
+ if (stristr($matches[1], ')(')) {
+ return PEAR::raiseError("Filter parsing error: invalid filter syntax - multiple leaf components detected!");
+ } else {
+ $filter_parts = preg_split('/(?|<|>=|<=)/',
+ $matches[1], 2,
+ PREG_SPLIT_DELIM_CAPTURE);
+ if (count($filter_parts) != 3) {
+ return PEAR::raiseError("Filter parsing error: invalid filter syntax - unknown matching rule used");
+ } else {
+ $result['att'] = $filter_parts[0];
+ $result['log'] = $filter_parts[1];
+ $result['val'] = $filter_parts[2];
+ return $result;
+ }
+ }
+ }
+ } else {
+ return PEAR::raiseError(sprintf("Filter parsing error: %s - filter components must be enclosed in round brackets",
+ $filter));
+ }
+ }
+
+ /**
+ * Parse a LDAP subfilter.
+ *
+ * @param string $filter The subfilter string.
+ *
+ * @return array|PEAR_Error An array of the parsed subfilter.
+ */
+ function _parseSub($filter)
+ {
+ $result = array();
+ $level = 0;
+ $collect = '';
+ while (preg_match('/^(\(.+?\))(.*)/', $filter, $matches)) {
+ if (in_array(substr($matches[1], 0, 2), array('(!', '(|', '(&'))) {
+ $level++;
+ }
+ if ($level) {
+ $collect .= $matches[1];
+ if (substr($matches[2], 0, 1) == ')') {
+ $collect .= ')';
+ $matches[2] = substr($matches[2], 1);
+ $level--;
+ if (!$level) {
+ $result[] = $this->_parse($collect);
+ }
+ }
+ } else {
+ $result[] = $this->_parse($matches[1]);
+ }
+ $filter = $matches[2];
+ }
+ return $result;
+ }
+
+ /**
+ * Search for an object.
+ *
+ * @param string $filter Filter criteria.
+ * @param array $attributes Restrict the search result to
+ * these attributes.
+ * @param string $base DN of the search base.
+ *
+ * @return array|PEAR_Error A LDAP serach result.
+ */
+ function _search($filter, $attributes = null, $base = null)
+ {
+ if (!$this->_bound) {
+ $result = $this->_bind();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $filter = $this->_parse($filter);
+ if (is_a($filter, 'PEAR_Error')) {
+ return $filter;
+ }
+ $result = $this->_doSearch($filter, $attributes);
+ if (empty($result)) {
+ return null;
+ }
+ if ($base) {
+ $subtree = array();
+ foreach ($result as $entry) {
+ if (strpos($entry['dn'], $base)) {
+ $subtree[] = $entry;
+ }
+ }
+ $result = $subtree;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Perform the search.
+ *
+ * @param array $filter Filter criteria-
+ * @param array $attributes Restrict the search result to
+ * these attributes.
+ *
+ * @return array|PEAR_Error A LDAP serach result.
+ */
+ function _doSearch($filter, $attributes = null)
+ {
+ if (isset($filter['log'])) {
+ $result = array();
+ foreach ($GLOBALS['KOLAB_SERVER_TEST_DATA'] as $element) {
+ if (isset($element['data'][$filter['att']])) {
+ switch ($filter['log']) {
+ case '=':
+ $value = $element['data'][$filter['att']];
+ if (($filter['val'] == '*' && !empty($value))
+ || $value == $filter['val']
+ || (is_array($value)
+ && in_array($filter['val'], $value))) {
+ if (empty($attributes)) {
+ $result[] = $element;
+ } else {
+ $selection = $element;
+ foreach ($element['data'] as $attr => $value) {
+ if (!in_array($attr, $attributes)) {
+ unset($selection['data'][$attr]);
+ }
+ }
+ $result[] = $selection;
+ }
+ }
+ break;
+ default:
+ return PEAR::raiseError(_("Not implemented!"));
+ }
+ }
+ }
+ return $result;
+ } else {
+ $subresult = array();
+ $filtercount = count($filter['sub']);
+ foreach ($filter['sub'] as $subfilter) {
+ $subresult = array_merge($subresult,
+ $this->_doSearch($subfilter,
+ $attributes));
+ }
+ $result = array();
+ $dns = array();
+ foreach ($subresult as $element) {
+ $dns[] = $element['dn'];
+
+ $result[$element['dn']] = $element;
+ }
+ switch ($filter['op']) {
+ case '&':
+ $count = array_count_values($dns);
+ $selection = array();
+ foreach ($count as $dn => $value) {
+ if ($value == $filtercount) {
+ $selection[] = $result[$dn];
+ }
+ }
+ return $selection;
+ case '|':
+ return array_values($result);
+ case '!':
+ $dns = array();
+ foreach ($result as $entry) {
+ if (!in_array($entry['dn'], $dns) ) {
+ $dns[] = $entry['dn'];
+ }
+ }
+ $all_dns = array_keys($GLOBALS['KOLAB_SERVER_TEST_DATA']);
+ $diff = array_diff($all_dns, $dns);
+
+ $result = array();
+ foreach ($diff as $dn) {
+ if (empty($attributes)) {
+ $result[] = $GLOBALS['KOLAB_SERVER_TEST_DATA'][$dn];
+ } else {
+ $selection = $GLOBALS['KOLAB_SERVER_TEST_DATA'][$dn];
+ foreach ($GLOBALS['KOLAB_SERVER_TEST_DATA'][$dn]['data']
+ as $attr => $value) {
+ if (!in_array($attr, $attributes)) {
+ unset($selection['data'][$attr]);
+ }
+ }
+ $result[] = $selection;
+ }
+ }
+ return $result;
+ default:
+ return PEAR::raiseError(_("Not implemented!"));
+ }
+ }
+ }
+
+ /**
+ * Read object data.
+ *
+ * @param string $dn The object to retrieve.
+ * @param string $attrs Restrict to these attributes
+ *
+ * @return array|PEAR_Error An array of attributes.
+ */
+ function _read($dn, $attrs = null)
+ {
+ if (!$this->_bound) {
+ $result = $this->_bind();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ if (!isset($GLOBALS['KOLAB_SERVER_TEST_DATA'][$dn])) {
+ return PEAR::raiseError(sprintf("LDAP Error: No such object: %s: No such object",
+ $dn));
+ }
+ if (empty($attrs)) {
+ return $GLOBALS['KOLAB_SERVER_TEST_DATA'][$dn]['data'];
+ } else {
+ $result = array();
+ $data = $GLOBALS['KOLAB_SERVER_TEST_DATA'][$dn]['data'];
+
+ foreach ($attrs as $attr) {
+ if (isset($data[$attr])) {
+ $result[$attr] = $data[$attr];
+ array_push($result, $attr);
+ }
+ }
+ $result['count'] = 1;
+ return $result;
+ }
+ }
+
+ /**
+ * Add a new object
+ *
+ * @param string $dn The DN of the object to be added.
+ * @param array $data The attributes of the object to be added.
+ *
+ * @return boolean True if adding succeeded.
+ */
+ function _add($dn, $data)
+ {
+ if (!$this->_bound) {
+ $result = $this->_bind();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ }
+
+ $ldap_data = array();
+ foreach ($data as $key => $val) {
+ if (!is_array($val)) {
+ $val = array($val);
+ }
+ $ldap_data[$key] = array_merge(array('count' => count($val)), $val);
+ }
+
+ $GLOBALS['KOLAB_SERVER_TEST_DATA'][$dn] = array(
+ 'dn' => $dn,
+ 'data' => $ldap_data
+ );
+ }
+
+ /**
+ * Count the number of results.
+ *
+ * @param array $result The LDAP search result.
+ *
+ * @return int The number of records found.
+ */
+ function _count($result)
+ {
+ if (is_array($result)) {
+ return count($result);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Return the dn of an entry.
+ *
+ * @param array $entry The LDAP entry.
+ *
+ * @return string The DN of the entry.
+ */
+ function _getDn($entry)
+ {
+ if (is_array($entry) && isset($entry['dn'])) {
+ if (isset($entry['count'])) {
+ return $entry['dn'][0];
+ } else {
+ return $entry['dn'];
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return the attributes of an entry.
+ *
+ * @param array $entry The LDAP entry.
+ *
+ * @return array The attributes of the entry.
+ */
+ function _getAttributes($entry)
+ {
+ if (is_array($entry)) {
+ return $entry;
+ }
+ return false;
+ }
+
+ /**
+ * Return the current entry of a result.
+ *
+ * @return mixe The current entry of the result or false.
+ */
+ function _fetchEntry()
+ {
+ if (is_array($this->_current_result)
+ && $this->_current_index < count($this->_current_result)) {
+
+ $data = array_keys($this->_current_result[$this->_current_index]['data']);
+
+ $data['count'] = 1;
+ $data['dn'] = array($this->_current_result[$this->_current_index]['dn']);
+ $data['dn']['count'] = 1;
+
+ foreach ($this->_current_result[$this->_current_index]['data']
+ as $attr => $value) {
+ if (!is_array($value)) {
+ $value = array($value);
+ }
+ $data[$attr] = $value;
+ $data[$attr]['count'] = count($value);
+ }
+ $this->_current_index++;
+ return $data;
+ }
+ return false;
+ }
+
+ /**
+ * Return the first entry of a result.
+ *
+ * @param array $result The LDAP search result.
+ *
+ * @return mixed The first entry of the result or false.
+ */
+ function _firstEntry($result)
+ {
+ $this->_current_result = $result;
+ $this->_current_index = 0;
+ return $this->_fetchEntry();
+ }
+
+ /**
+ * Return the next entry of a result.
+ *
+ * @param resource $entry The current LDAP entry.
+ *
+ * @return resource The next entry of the result.
+ */
+ function _nextEntry($entry)
+ {
+ return $this->_fetchEntry();
+ }
+
+ /**
+ * Return the entries of a result.
+ *
+ * @param array $result The LDAP search result.
+ *
+ * @return mixed The entries of the result or false.
+ */
+ function _getEntries($result)
+ {
+ if (is_array($result)) {
+ $data = array();
+ $data['count'] = count($result);
+ foreach ($result as $entry) {
+ $t = $entry['data'];
+ $t['dn'] = $entry['dn'];
+ $data[] = $t;
+ }
+ return $data;
+ }
+ return false;
+ }
+
+ /**
+ * Sort the entries of a result.
+ *
+ * @param resource &$result The LDAP search result.
+ * @param string $attribute The attribute used for sorting.
+ *
+ * @return boolean True if sorting succeeded.
+ */
+ function _sort(&$result, $attribute)
+ {
+ if (empty($result)) {
+ return $result;
+ }
+
+ $this->_sort_by = $attribute;
+ usort($result, array($this, '_resultSort'));
+ return false;
+ }
+
+ /**
+ * Sort two entries.
+ *
+ * @param array $a First entry.
+ * @param array $b Second entry.
+ *
+ * @return int Comparison result.
+ */
+ function _resultSort($a, $b)
+ {
+ $x = isset($a['data'][$this->_sort_by][0])?$a['data'][$this->_sort_by][0]:'';
+ $y = isset($b['data'][$this->_sort_by][0])?$b['data'][$this->_sort_by][0]:'';
+ return strcasecmp($x, $y);
+ }
+
+
+ /**
+ * Return the current LDAP error number.
+ *
+ * @return int The current LDAP error number.
+ */
+ function _errno()
+ {
+ return $this->_errno;
+ }
+
+ /**
+ * Return the current LDAP error description.
+ *
+ * @return string The current LDAP error description.
+ */
+ function _error()
+ {
+ return $this->_error;
+ }
+
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Session.php b/framework/Kolab_Server/lib/Horde/Kolab/Session.php
new file mode 100644
index 000000000..d72437279
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Session.php
@@ -0,0 +1,384 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/** We need the Auth library */
+require_once 'Horde/Auth.php';
+
+/**
+ * The Horde_Kolab_Session class holds additional user details for the current
+ * session.
+ *
+ * The core user credentials (login, pass) are kept within the Auth module and
+ * can be retrieved using Auth::getAuth() respectively
+ * Auth::getCredential('password'). Any additional Kolab user data
+ * relevant for the user session should be accessed via the Horde_Kolab_Session
+ * class.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Session.php,v 1.11 2009/02/07 14:03:32 wrobel Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Session {
+
+ /**
+ * User ID.
+ *
+ * @var string
+ */
+ var $user_id;
+
+ /**
+ * User UID.
+ *
+ * @var string
+ */
+ var $user_uid;
+
+ /**
+ * Primary user mail address.
+ *
+ * @var string
+ */
+ var $user_mail;
+
+ /**
+ * Full name.
+ *
+ * @var string
+ */
+ var $user_name = '';
+
+ /**
+ * True if the Kolab_Server login was successfull.
+ *
+ * @var boolean|PEAR_Error
+ */
+ var $auth;
+
+ /**
+ * The connection parameters for the IMAP server.
+ *
+ * @var array|PEAR_Error
+ */
+ var $_imap_params;
+
+ /**
+ * Our IMAP connection.
+ *
+ * @var Horde_Kolab_IMAP
+ */
+ var $_imap;
+
+ /**
+ * The free/busy server for the current user.
+ *
+ * @var array|PEAR_Error
+ */
+ var $freebusy_server;
+
+ /**
+ * Constructor.
+ *
+ * @param string $user The session will be setup for the user with
+ * this ID.
+ * @param array $credentials An array of login credentials. For Kolab,
+ * this must contain a "password" entry.
+ */
+ function Horde_Kolab_Session($user = null, $credentials = null)
+ {
+ global $conf;
+
+ if (empty($user)) {
+ $user = Auth::getAuth();
+ if (empty($user)) {
+ $user = 'anonymous';
+ } else if (!strpos($user, '@')) {
+ $user = $user . '@' . (!empty($_SERVER['SERVER_NAME']) ?
+ $_SERVER['SERVER_NAME'] : 'localhost');
+ }
+ }
+
+ $this->user_id = $user;
+ $this->_imap_params = array();
+
+ $user_object = null;
+
+ if ($user != 'anonymous') {
+ $server = $this->getServer($user, $credentials);
+ if (is_a($server, 'PEAR_Error')) {
+ $this->auth = $server;
+ } else {
+ $this->user_uid = $server->uid;
+ $user_object = $server->fetch();
+
+ if (is_a($user_object, 'PEAR_Error')) {
+ $this->auth = $user_object;
+ } else {
+ if (empty($conf['kolab']['imap']['allow_special_users'])
+ && !is_a($user_object, 'Horde_Kolab_Server_Object_user')) {
+ $this->auth = PEAR::raiseError(_('Access to special Kolab users is denied.'));
+ } else if (isset($conf['kolab']['server']['deny_group'])) {
+ $dn = $server->gidForMail($conf['kolab']['server']['deny_group']);
+ if (is_a($dn, 'PEAR_Error')) {
+ $this->auth = $dn;
+ } else if (empty($dn)) {
+ Horde::logMessage('The Kolab configuratin setting $conf[\'kolab\'][\'server\'][\'deny_group\'] holds a non-existing group!',
+ __FILE__, __LINE__, PEAR_LOG_WARNING);
+ $this->auth = true;
+ } else if (in_array($dn, $user_object->getGroups())) {
+ $this->auth = PEAR::raiseError(_('You are member of a group that may not login on this server.'));
+ } else {
+ $this->auth = true;
+ }
+ } else if (isset($conf['kolab']['server']['allow_group'])) {
+ $dn = $server->gidForMail($conf['kolab']['server']['allow_group']);
+ if (is_a($dn, 'PEAR_Error')) {
+ $this->auth = $dn;
+ } else if (empty($dn)) {
+ Horde::logMessage('The Kolab configuratin setting $conf[\'kolab\'][\'server\'][\'allow_group\'] holds a non-existing group!',
+ __FILE__, __LINE__, PEAR_LOG_WARNING);
+ $this->auth = true;
+ } else if (!in_array($dn, $user_object->getGroups())) {
+ $this->auth = PEAR::raiseError(_('You are no member of a group that may login on this server.'));
+ } else {
+ $this->auth = true;
+ }
+ } else {
+ /**
+ * At this point we can be certain the user is an
+ * authenticated Kolab user.
+ */
+ $this->auth = true;
+ }
+
+ if (empty($this->auth) || is_a($this->auth, 'PEAR_Error')) {
+ return;
+ }
+
+ $result = $user_object->get(KOLAB_ATTR_MAIL);
+ if (!empty($result) && !is_a($result, 'PEAR_Error')) {
+ $this->user_mail = $result;
+ }
+
+ $result = $user_object->get(KOLAB_ATTR_SID);
+ if (!empty($result) && !is_a($result, 'PEAR_Error')) {
+ $this->user_id = $result;
+ }
+
+ $result = $user_object->get(KOLAB_ATTR_FNLN);
+ if (!empty($result) && !is_a($result, 'PEAR_Error')) {
+ $this->user_name = $result;
+ }
+
+ $result = $user_object->getServer('imap');
+ if (!empty($result) && !is_a($result, 'PEAR_Error')) {
+ $server = explode(':', $result, 2);
+ if (!empty($server[0])) {
+ $this->_imap_params['hostspec'] = $server[0];
+ }
+ if (!empty($server[1])) {
+ $this->_imap_params['port'] = $server[1];
+ }
+ }
+
+ $result = $user_object->getServer('freebusy');
+ if (!empty($result) && !is_a($result, 'PEAR_Error')) {
+ $this->freebusy_server = $result;
+ }
+ }
+ }
+ }
+
+ if (empty($this->user_mail)) {
+ $this->user_mail = $user;
+ }
+
+ if (!isset($this->_imap_params['hostspec'])) {
+ if (isset($conf['kolab']['imap']['server'])) {
+ $this->_imap_params['hostspec'] = $conf['kolab']['imap']['server'];
+ } else {
+ $this->_imap_params['hostspec'] = 'localhost';
+ }
+ }
+
+ if (!isset($this->_imap_params['port'])) {
+ if (isset($conf['kolab']['imap']['port'])) {
+ $this->_imap_params['port'] = $conf['kolab']['imap']['port'];
+ } else {
+ $this->_imap_params['port'] = 143;
+ }
+ }
+
+ $this->_imap_params['protocol'] = 'imap/notls/novalidate-cert';
+ }
+
+ /**
+ * Returns the properties that need to be serialized.
+ *
+ * @return array List of serializable properties.
+ */
+ function __sleep()
+ {
+ $properties = get_object_vars($this);
+ unset($properties['_imap']);
+ $properties = array_keys($properties);
+ return $properties;
+ }
+
+ /**
+ * Get the Kolab Server connection.
+ *
+ * @param string $user The session will be setup for the user with
+ * this ID.
+ * @param array $credentials An array of login credentials. For Kolab,
+ * this must contain a "password" entry.
+ *
+ * @return Horde_Kolab_Server|PEAR_Error The Kolab Server connection.
+ */
+ function &getServer($user = null, $credentials = null)
+ {
+ /** We need the Kolab Server access. */
+ require_once 'Horde/Kolab/Server.php';
+
+ $params = array();
+ if ($this->user_uid) {
+ $params['uid'] = $this->user_uid;
+ $params['pass'] = Auth::getCredential('password');
+ } else if (isset($user)) {
+ $params['user'] = $user;
+ if (isset($credentials['password'])) {
+ $params['pass'] = $credentials['password'];
+ } else {
+ $params['pass'] = Auth::getCredential('password');
+ }
+ }
+ return Horde_Kolab_Server::singleton($params);
+ }
+
+ /**
+ * Get the IMAP connection parameters.
+ *
+ * @return array|PEAR_Error The IMAP connection parameters.
+ */
+ function &getImapParams()
+ {
+ return $this->_imap_params;
+ }
+
+ /**
+ * Create an IMAP connection.
+ *
+ * @return Horde_Kolab_IMAP|PEAR_Error The IMAP connection.
+ */
+ function &getImap()
+ {
+ if (!isset($this->_imap)) {
+
+ $params = $this->getImapParams();
+ if (is_a($params, 'PEAR_Error')) {
+ return $params;
+ }
+
+ /** We need the Kolab IMAP library now. */
+ require_once 'Horde/Kolab/IMAP.php';
+
+ $imap = &Horde_Kolab_IMAP::singleton($params['hostspec'],
+ $params['port'], true, false);
+ if (is_a($imap, 'PEAR_Error')) {
+ return $imap;
+ }
+
+ $result = $imap->connect(Auth::getAuth(),
+ Auth::getCredential('password'));
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ $this->_imap = &$imap;
+ }
+ return $this->_imap;
+ }
+
+ /**
+ * Attempts to return a reference to a concrete Horde_Kolab_Session instance.
+ *
+ * It will only create a new instance if no Horde_Kolab_Session instance
+ * currently exists or if a user ID has been specified that does not match the
+ * user ID/user mail of the current session.
+ *
+ * This method must be invoked as:
+ * $var = &Horde_Kolab_Session::singleton();
+ *
+ * @param string $user The session will be setup for the user with
+ * this ID.
+ * @param array $credentials An array of login credentials. For Kolab,
+ * this must contain a "password" entry.
+ *
+ * @static
+ *
+ * @return Horde_Kolab_Session The concrete Session reference.
+ */
+ function &singleton($user = null, $credentials = null, $destruct = false)
+ {
+ static $session;
+
+ if (!isset($session)) {
+ /**
+ * Horde_Kolab_Server currently has no caching so we mainly
+ * cache some user information here as reading this data
+ * may be expensive when running in a multi-host
+ * environment.
+ */
+ require_once 'Horde/SessionObjects.php';
+ $hs = &Horde_SessionObjects::singleton();
+ $session = $hs->query('kolab_session');
+ }
+
+ if (empty($user)) {
+ $user = Auth::getAuth();
+ }
+
+ if ($destruct || empty($session)
+ || ($user != $session->user_mail && $user != $session->user_id)) {
+ $session = new Horde_Kolab_Session($user, $credentials);
+ }
+
+ register_shutdown_function(array(&$session, 'shutdown'));
+
+ return $session;
+ }
+
+ /**
+ * Stores the object in the session cache.
+ *
+ * @return NULL
+ */
+ function shutdown()
+ {
+ require_once 'Horde/SessionObjects.php';
+ $session = &Horde_SessionObjects::singleton();
+ $session->overwrite('kolab_session', $this, false);
+ }
+
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Test/Server.php b/framework/Kolab_Server/lib/Horde/Kolab/Test/Server.php
new file mode 100644
index 000000000..d45ddd3db
--- /dev/null
+++ b/framework/Kolab_Server/lib/Horde/Kolab/Test/Server.php
@@ -0,0 +1,746 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Share
+ */
+
+/**
+ * We need the unit test framework
+ */
+require_once 'PHPUnit/Framework.php';
+require_once 'PHPUnit/Extensions/Story/TestCase.php';
+
+/**
+ * We need the classes to be tested
+ */
+require_once 'Horde.php';
+require_once 'Horde/Kolab/Server.php';
+
+/**
+ * Base for PHPUnit scenarios.
+ *
+ * $Horde: framework/Kolab_Server/lib/Horde/Kolab/Test/Server.php,v 1.3 2009/01/06 17:49:26 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Test
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Share
+ */
+class Horde_Kolab_Test_Server extends PHPUnit_Extensions_Story_TestCase
+{
+ /**
+ * Handle a "given" step.
+ *
+ * @param array &$world Joined "world" of variables.
+ * @param string $action The description of the step.
+ * @param array $arguments Additional arguments to the step.
+ *
+ * @return mixed The outcome of the step.
+ */
+ public function runGiven(&$world, $action, $arguments)
+ {
+ switch($action) {
+ case 'an empty Kolab server':
+ $world['server'] = &$this->prepareEmptyKolabServer();
+ break;
+ case 'a basic Kolab server':
+ $world['server'] = &$this->prepareBasicKolabServer();
+ break;
+ case 'the Kolab auth driver has been selected':
+ $world['auth'] = &$this->prepareKolabAuthDriver();
+ break;
+ default:
+ return $this->notImplemented($action);
+ }
+ }
+
+ /**
+ * Handle a "when" step.
+ *
+ * @param array &$world Joined "world" of variables.
+ * @param string $action The description of the step.
+ * @param array $arguments Additional arguments to the step.
+ *
+ * @return mixed The outcome of the step.
+ */
+ public function runWhen(&$world, $action, $arguments)
+ {
+ switch($action) {
+ case 'adding a Kolab server object':
+ $world['result']['add'] = $world['server']->add($arguments[0]);
+ break;
+ case 'logging in as a user with a password':
+ $world['login'] = $world['auth']->authenticate($arguments[0],
+ array('password' => $arguments[1]));
+ break;
+ case 'adding an object list':
+ foreach ($arguments[0] as $object) {
+ $result = $world['server']->add($object);
+ if (is_a($result, 'PEAR_Error')) {
+ $world['result']['add'] = $result;
+ return;
+ }
+ }
+ $world['result']['add'] = true;
+ break;
+ case 'adding a user without first name':
+ $world['result']['add'] = $world['server']->add($this->provideInvalidUserWithoutGivenName());
+ break;
+ case 'adding a user without last name':
+ $world['result']['add'] = $world['server']->add($this->provideInvalidUserWithoutLastName());
+ break;
+ case 'adding a user without password':
+ $world['result']['add'] = $world['server']->add($this->provideInvalidUserWithoutPassword());
+ break;
+ case 'adding a user without primary mail':
+ $world['result']['add'] = $world['server']->add($this->provideInvalidUserWithoutMail());
+ break;
+ case 'adding a distribution list':
+ $world['result']['add'] = $world['server']->add($this->provideDistributionList());
+ break;
+ case 'listing all users':
+ $world['list'] = $world['server']->listObjects(KOLAB_OBJECT_USER);
+ break;
+ case 'listing all groups':
+ $world['list'] = $world['server']->listObjects(KOLAB_OBJECT_GROUP);
+ break;
+ case 'listing all objects of type':
+ $world['list'] = $world['server']->listObjects($arguments[0]);
+ break;
+ case 'retrieving a hash list with all objects of type':
+ $world['list'] = $world['server']->listHash($arguments[0]);
+ break;
+ default:
+ return $this->notImplemented($action);
+ }
+ }
+
+ /**
+ * Handle a "then" step.
+ *
+ * @param array &$world Joined "world" of variables.
+ * @param string $action The description of the step.
+ * @param array $arguments Additional arguments to the step.
+ *
+ * @return mixed The outcome of the step.
+ */
+ public function runThen(&$world, $action, $arguments)
+ {
+ switch($action) {
+ case 'the result should be an object of type':
+ if (!isset($world['result'])) {
+ $this->fail('Did not receive a result!');
+ }
+ foreach ($world['result'] as $result) {
+ if ($result instanceOf PEAR_Error) {
+ $this->assertEquals('', $result->getMessage());
+ } else {
+ $this->assertEquals($arguments[0], get_class($result));
+ }
+ }
+ break;
+ case 'the result indicates success.':
+ if (!isset($world['result'])) {
+ $this->fail('Did not receive a result!');
+ }
+ foreach ($world['result'] as $result) {
+ if ($result instanceOf PEAR_Error) {
+ $this->assertEquals('', $result->getMessage());
+ } else {
+ $this->assertTrue($result);
+ }
+ }
+ break;
+ case 'the result should indicate an error with':
+ if (!isset($world['result'])) {
+ $this->fail('Did not receive a result!');
+ }
+ foreach ($world['result'] as $result) {
+ if ($result instanceOf PEAR_Error) {
+ $this->assertEquals($arguments[0], $result->getMessage());
+ } else {
+ $this->assertEquals($arguments[0], 'Action succeeded without an error.');
+ }
+ }
+ break;
+ case 'the list has a number of entries equal to':
+ if ($world['list'] instanceOf PEAR_Error) {
+ $this->assertEquals('', $world['list']->getMessage());
+ } else {
+ $this->assertEquals($arguments[0], count($world['list']));
+ }
+ break;
+ case 'the list is an empty array':
+ if ($world['list'] instanceOf PEAR_Error) {
+ $this->assertEquals('', $world['list']->getMessage());
+ } else {
+ $this->assertEquals(array(), $world['list']);
+ }
+ break;
+ case 'the list is an empty array':
+ if ($world['list'] instanceOf PEAR_Error) {
+ $this->assertEquals('', $world['list']->getMessage());
+ } else {
+ $this->assertEquals(array(), $world['list']);
+ }
+ break;
+ case 'the provided list and the result list match with regard to these attributes':
+ if ($world['list'] instanceOf PEAR_Error) {
+ $this->assertEquals('', $world['list']->getMessage());
+ } else {
+ $provided_vals = array();
+ foreach ($arguments[2] as $provided_element) {
+ if (isset($provided_element[$arguments[0]])) {
+ $provided_vals[] = $provided_element[$arguments[0]];
+ } else {
+ $this->fail(sprintf('The provided element %s does have no value for %s.',
+ print_r($provided_element, true),
+ print_r($arguments[0])));
+ }
+ }
+ $result_vals = array();
+ foreach ($world['list'] as $result_element) {
+ if (isset($result_element[$arguments[1]])) {
+ $result_vals[] = $result_element[$arguments[1]];
+ } else {
+ $this->fail(sprintf('The result element %s does have no value for %s.',
+ print_r($result_element, true),
+ print_r($arguments[1])));
+ }
+ }
+ $this->assertEquals(array(),
+ array_diff($provided_vals, $result_vals));
+ }
+ break;
+ case 'each element in the result list has an attribute':
+ if ($world['list'] instanceOf PEAR_Error) {
+ $this->assertEquals('', $world['list']->getMessage());
+ } else {
+ $result_vals = array();
+ foreach ($world['list'] as $result_element) {
+ if (!isset($result_element[$arguments[0]])) {
+ $this->fail(sprintf('The result element %s does have no value for %s.',
+ print_r($result_element, true),
+ print_r($arguments[0])));
+ }
+ }
+ }
+ break;
+ case 'each element in the result list has an attribute set to a given value':
+ if ($world['list'] instanceOf PEAR_Error) {
+ $this->assertEquals('', $world['list']->getMessage());
+ } else {
+ $result_vals = array();
+ foreach ($world['list'] as $result_element) {
+ if (!isset($result_element[$arguments[0]])) {
+ $this->fail(sprintf('The result element %s does have no value for %s.',
+ print_r($result_element, true),
+ print_r($arguments[0], true)));
+ }
+ if ($result_element[$arguments[0]] != $arguments[1]) {
+ $this->fail(sprintf('The result element %s has an unexpected value %s for %s.',
+ print_r($result_element, true),
+ print_r($result_element[$arguments[0]], true),
+ print_r($arguments[0], true)));
+ }
+ }
+ }
+ break;
+ case 'the login was successful':
+ $this->assertNoError($world['login']);
+ $this->assertTrue($world['login']);
+ break;
+ case 'the list contains a number of elements equal to':
+ $this->assertEquals($arguments[0], count($world['list']));
+ break;
+ default:
+ return $this->notImplemented($action);
+ }
+ }
+
+
+ /**
+ * Prepare an empty Kolab server.
+ *
+ * @return Horde_Kolab_Server The empty server.
+ */
+ public function &prepareEmptyKolabServer()
+ {
+ global $conf;
+
+ include_once 'Horde/Kolab/Server.php';
+
+ $GLOBALS['KOLAB_SERVER_TEST_DATA'] = array();
+
+ /** Prepare a Kolab test server */
+ $conf['kolab']['server']['driver'] = 'test';
+
+ $server = Horde_Kolab_Server::singleton();
+
+ /** Ensure we don't use a connection from older tests */
+ $server->unbind();
+
+ /** Set base DN */
+ $server->_base_dn = 'dc=example,dc=org';
+
+ /** Clean the server data */
+
+ return $server;
+ }
+
+ /**
+ * Prepare a Kolab server with some basic entries.
+ *
+ * @return Horde_Kolab_Server The empty server.
+ */
+ public function &prepareBasicServer()
+ {
+ $server = $this->prepareEmptyKolabServer();
+ $this->prepareUsers($server);
+ return $server;
+ }
+
+ /**
+ * Fill a Kolab Server with test users.
+ *
+ * @param Kolab_Server &$server The server to populate.
+ *
+ * @return Horde_Kolab_Server The empty server.
+ */
+ public function prepareUsers(&$server)
+ {
+ $result = $server->add($this->provideBasicUserOne());
+ $this->assertNoError($result);
+ $result = $server->add($this->provideBasicUserTwo());
+ $this->assertNoError($result);
+ $result = $server->add($this->provideBasicAddress());
+ $this->assertNoError($result);
+ $result = $server->add($this->provideBasicAdmin());
+ $this->assertNoError($result);
+ $result = $server->add($this->provideBasicDomainMaintainer());
+ $this->assertNoError($result);
+ $result = $server->add($this->provideBasicGroupOne());
+ $this->assertNoError($result);
+ $result = $server->add($this->provideBasicGroupTwo());
+ $this->assertNoError($result);
+ $result = $server->add($this->provideBasicMaintainer());
+ $this->assertNoError($result);
+ $result = $server->add($this->provideBasicSharedFolder());
+ $this->assertNoError($result);
+ }
+
+ /**
+ * Prepare a Kolab Auth Driver.
+ *
+ * @return Auth The auth driver.
+ */
+ public function &prepareKolabAuthDriver()
+ {
+ include_once 'Horde/Auth.php';
+
+ $auth = Auth::singleton('kolab');
+ return $auth;
+ }
+
+ /**
+ * Return a test user.
+ *
+ * @return array The test user.
+ */
+ public function provideBasicUserOne()
+ {
+ return array('givenName' => 'Gunnar',
+ 'sn' => 'Wrobel',
+ 'type' => KOLAB_OBJECT_USER,
+ 'mail' => 'wrobel@example.org',
+ 'uid' => 'wrobel',
+ 'userPassword' => 'none',
+ 'kolabHomeServer' => 'home.example.org',
+ 'kolabImapServer' => 'imap.example.org',
+ 'kolabFreeBusyServer' => 'https://fb.example.org/freebusy',
+ KOLAB_ATTR_IPOLICY => array('ACT_REJECT_IF_CONFLICTS'),
+ 'alias' => array('gunnar@example.org',
+ 'g.wrobel@example.org'),
+ );
+ }
+
+ /**
+ * Return a test user.
+ *
+ * @return array The test user.
+ */
+ public function provideBasicUserTwo()
+ {
+ return array('givenName' => 'Test',
+ 'sn' => 'Test',
+ 'type' => KOLAB_OBJECT_USER,
+ 'mail' => 'test@example.org',
+ 'uid' => 'test',
+ 'userPassword' => 'test',
+ 'kolabHomeServer' => 'home.example.org',
+ 'kolabImapServer' => 'home.example.org',
+ 'kolabFreeBusyServer' => 'https://fb.example.org/freebusy',
+ 'alias' => array('t.test@example.org'),
+ KOLAB_ATTR_KOLABDELEGATE => 'wrobel@example.org',);
+ }
+
+ /**
+ * Return a test address.
+ *
+ * @return array The test address.
+ */
+ public function provideBasicAddress()
+ {
+ return array('givenName' => 'Test',
+ 'sn' => 'Address',
+ 'type' => KOLAB_OBJECT_ADDRESS,
+ 'mail' => 'address@example.org');
+ }
+
+ /**
+ * Return a test administrator.
+ *
+ * @return array The test administrator.
+ */
+ public function provideBasicAdmin()
+ {
+ return array('sn' => 'Administrator',
+ 'givenName' => 'The',
+ 'uid' => 'admin',
+ 'type' => KOLAB_OBJECT_ADMINISTRATOR,
+ 'userPassword' => 'none');
+ }
+
+ /**
+ * Return a test maintainer.
+ *
+ * @return array The test maintainer.
+ */
+ public function provideBasicMaintainer()
+ {
+ return array('sn' => 'Tainer',
+ 'givenName' => 'Main',
+ 'uid' => 'maintainer',
+ 'type' => KOLAB_OBJECT_MAINTAINER,
+ 'userPassword' => 'none',
+ );
+ }
+
+ /**
+ * Return a test domain maintainer.
+ *
+ * @return array The test domain maintainer.
+ */
+ public function provideBasicDomainMaintainer()
+ {
+ return array('sn' => 'Maintainer',
+ 'givenName' => 'Domain',
+ 'uid' => 'domainmaintainer',
+ 'type' => KOLAB_OBJECT_DOMAINMAINTAINER,
+ 'userPassword' => 'none',
+ 'domain' => array('example.com'),
+ );
+ }
+
+ /**
+ * Return a test shared folder.
+ *
+ * @return array The test shared folder.
+ */
+ public function provideBasicSharedFolder()
+ {
+ return array('cn' => 'shared@example.org',
+ 'kolabHomeServer' => 'example.org',
+ 'type' => KOLAB_OBJECT_SHAREDFOLDER);
+ }
+
+ /**
+ * Return a test group.
+ *
+ * @return array The test group.
+ */
+ public function provideBasicGroupOne()
+ {
+ return array('mail' => 'empty.group@example.org',
+ 'type' => KOLAB_OBJECT_GROUP);
+ }
+
+ /**
+ * Return a test group.
+ *
+ * @return array The test group.
+ */
+ public function provideBasicGroupTwo()
+ {
+ return array('mail' => 'group@example.org',
+ 'type' => KOLAB_OBJECT_GROUP,
+ 'member' => array('cn=Test Test,dc=example,dc=org',
+ 'cn=Gunnar Wrobel,dc=example,dc=org'));
+ }
+
+ public function provideDistributionList()
+ {
+ return array('mail' => 'distlist@example.org',
+ 'type' => KOLAB_OBJECT_DISTLIST,
+ 'member' => array('cn=Test Test,dc=example,dc=org',
+ 'cn=Gunnar Wrobel,dc=example,dc=org'));
+ }
+
+ public function provideInvalidUserWithoutPassword()
+ {
+ return array('givenName' => 'Test',
+ 'sn' => 'Test',
+ 'type' => KOLAB_OBJECT_USER,
+ 'mail' => 'test@example.org');
+ }
+
+ public function provideInvalidUserWithoutGivenName()
+ {
+ return array('sn' => 'Test',
+ 'userPassword' => 'none',
+ 'type' => KOLAB_OBJECT_USER,
+ 'mail' => 'test@example.org');
+ }
+
+ public function provideInvalidUserWithoutLastName()
+ {
+ return array('givenName' => 'Test',
+ 'userPassword' => 'none',
+ 'type' => KOLAB_OBJECT_USER,
+ 'mail' => 'test@example.org');
+ }
+
+ public function provideInvalidUserWithoutMail()
+ {
+ return array('givenName' => 'Test',
+ 'sn' => 'Test',
+ 'userPassword' => 'none',
+ 'type' => KOLAB_OBJECT_USER);
+ }
+
+ public function provideInvalidUsers()
+ {
+ return array(
+ array(
+ $this->provideInvalidUserWithoutPassword(),
+ 'Adding object failed: The value for "userPassword" is missing!'
+ ),
+ array(
+ $this->provideInvalidUserWithoutGivenName(),
+ 'Adding object failed: Either the last name or the given name is missing!'
+ ),
+ array(
+ $this->provideInvalidUserWithoutLastName(),
+ 'Adding object failed: Either the last name or the given name is missing!'
+ ),
+ array(
+ $this->provideInvalidUserWithoutMail(),
+ 'Adding object failed: The value for "mail" is missing!'
+ ),
+ );
+ }
+
+ /** FIXME: Prefix the stuff bewlow with provide...() */
+
+ public function validUsers()
+ {
+ return array(
+ array(
+ $this->provideBasicUserOne(),
+ ),
+ array(
+ $this->provideBasicUserTwo(),
+ ),
+ );
+ }
+
+ public function validAddresses()
+ {
+ return array(
+ array(
+ $this->provideBasicAddress(),
+ ),
+ );
+ }
+
+ public function validAdmins()
+ {
+ return array(
+ array(
+ $this->provideBasicAdmin(),
+ ),
+ );
+ }
+
+ public function validMaintainers()
+ {
+ return array(
+ array(
+ $this->provideBasicMaintainer(),
+ )
+ );
+ }
+
+ public function validDomainMaintainers()
+ {
+ return array(
+ array(
+ $this->provideBasicDomainMaintainer(),
+ )
+ );
+ }
+
+ public function validGroups()
+ {
+ return array(
+ array(
+ $this->validGroupWithoutMembers(),
+ ),
+ array(
+ array('mail' => 'group@example.org',
+ 'type' => KOLAB_OBJECT_GROUP,
+ 'member' => array('cn=Test Test,dc=example,dc=org',
+ 'cn=Gunnar Wrobel,dc=example,dc=org')
+ ),
+ ),
+ array(
+ array('mail' => 'group2@example.org',
+ 'type' => KOLAB_OBJECT_GROUP,
+ 'member' => array('cn=Gunnar Wrobel,dc=example,dc=org')
+ ),
+ ),
+ );
+ }
+
+ public function validSharedFolders()
+ {
+ return array(
+ array('cn' => 'Shared',
+ 'type' => KOLAB_OBJECT_SHAREDFOLDER
+ ),
+ );
+ }
+
+ public function validGroupWithoutMembers()
+ {
+ return array('mail' => 'empty.group@example.org',
+ 'type' => KOLAB_OBJECT_GROUP,
+ );
+ }
+
+ public function userLists()
+ {
+ return array(
+ );
+ }
+
+ public function groupLists()
+ {
+ return array(
+ array(
+ array(
+ array('type' => KOLAB_OBJECT_GROUP,
+ 'mail' => 'empty.group@example.org',
+ ),
+ )
+ ),
+ array(
+ array(
+ array('mail' => 'empty.group@example.org',
+ 'type' => KOLAB_OBJECT_GROUP,
+ ),
+ ),
+ array(
+ array('mail' => 'group@example.org',
+ 'type' => KOLAB_OBJECT_GROUP,
+ 'member' => array('cn=Test Test,dc=example,dc=org',
+ 'cn=Gunnar Wrobel,dc=example,dc=org')
+ ),
+ ),
+ array(
+ array('mail' => 'group2@example.org',
+ 'type' => KOLAB_OBJECT_GROUP,
+ 'member' => array('cn=Gunnar Wrobel,dc=example,dc=org')
+ ),
+ ),
+ )
+ );
+ }
+
+ public function userListByLetter()
+ {
+ return array(
+ );
+ }
+
+ public function userListByAttribute()
+ {
+ return array(
+ );
+ }
+
+ public function userAdd()
+ {
+ return array(
+ );
+ }
+
+ public function invalidMails()
+ {
+ return array(
+ );
+ }
+
+ public function largeList()
+ {
+ return array(
+ );
+ }
+
+ /**
+ * Ensure that the variable contains no PEAR_Error and fail if it does.
+ *
+ * @param mixed $var The variable to check.
+ *
+ * @return NULL.
+ */
+ public function assertNoError($var)
+ {
+ if (is_a($var, 'PEAR_Error')) {
+ $this->assertEquals('', $var->getMessage());
+ }
+ }
+
+ /**
+ * Ensure that the variable contains a PEAR_Error and fail if it does
+ * not. Optionally compare the error message with the provided message and
+ * fail if both do not match.
+ *
+ * @param mixed $var The variable to check.
+ * @param string $msg The expected error message.
+ *
+ * @return NULL.
+ */
+ public function assertError($var, $msg = null)
+ {
+ $this->assertEquals('PEAR_Error', get_class($var));
+ if (isset($msg)) {
+ $this->assertEquals($msg, $var->getMessage());
+ }
+ }
+}
diff --git a/framework/Kolab_Server/package.xml b/framework/Kolab_Server/package.xml
new file mode 100644
index 000000000..4ce4ea70f
--- /dev/null
+++ b/framework/Kolab_Server/package.xml
@@ -0,0 +1,255 @@
+
+
+ Kolab_Server
+ pear.horde.org
+ A package for manipulating the Kolab user database.
+ This package allows to read/write entries in the Kolab user
+ database stored in LDAP.
+
+
+ Gunnar Wrobel
+ wrobel
+ p@rdus.de
+ yes
+
+
+ Thomas Jarosch
+ jarosch
+ thomas.jarosch@intra2net.com
+ yes
+
+
+ Chuck Hagenbuch
+ chuck
+ chuck@horde.org
+ yes
+
+
+ Jan Schneider
+ jan
+ jan@horde.org
+ yes
+
+ 2008-12-16
+
+ 0.4.0
+ 0.2.0
+
+
+ alpha
+ alpha
+
+ LGPL
+
+ * Fixed objectClass evaluation to respect case-insensitivity (Bug: #7694)
+ * kolab/issue2207 (Make it possible to enable and disable users to be able to
+ use the webclient)
+ * Added fullname attribute as support for kolab/issue2546 (Horde
+ should use name and email from ldap as defaults)
+ * Fixed initialization of parameters retrieved from LDAP.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 4.3.0
+
+
+ 1.4.0b1
+
+
+ Auth
+ pear.horde.org
+
+
+
+
+ Horde_LDAP
+ pear.horde.org
+
+
+ Horde_SessionObjects
+ pear.horde.org
+
+
+ PHPUnit
+ pear.phpunit.de
+
+
+ ldap
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2008-12-16
+
+ 0.3.0
+ 0.2.0
+
+
+ alpha
+ alpha
+
+ LGPL
+
+ * Fixed the fallback for a missing freebusy_server value.
+ * Fixed identification of external addresses.
+
+
+
+ 2008-10-29
+
+ 0.2.0
+ 0.2.0
+
+
+ alpha
+ alpha
+
+ LGPL
+
+ * Fixed retrieval of the users IMAP home server.
+ * Allowed retrieving a DN for an id matching either mail, uid or alias.
+ (Kolab issue 2587, https://www.intevation.de/roundup/kolab/issue2587)
+ * Moved Kolab session handler from Kolab_Storage to Kolab_Server.
+ * Enabled retrieval of the users free/busy server. (Enhancement: #6699)
+ * Added capability to list objects.
+ * Added write capabilities to the package.
+ * Moved the IMAP drivers from Kolab_Storage to Kolab_Server as the
+ IMAP connection must be handled by the Kolab session.
+ * Added a test class for simplified PHPUnit testing.
+
+
+
+
+ 0.1.1
+ 0.1.0
+
+
+ alpha
+ alpha
+
+ LGPL
+
+ * Renamed package to Kolab_Server.
+ * Removed an unnecessary translation.
+ * Added dnForMailOrAlias function to Horde_Kolab_Server.
+ * Fixed experimental KOLAB_ATTR_IMAPHOST attribute.
+
+
+
+
+ 0.1.0
+ 0.1.0
+
+
+ alpha
+ alpha
+
+ 2008-07-29
+ LGPL
+
+ * Initial release.
+
+
+
+
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/AddingObjectsTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/AddingObjectsTest.php
new file mode 100644
index 000000000..2a9f60b56
--- /dev/null
+++ b/framework/Kolab_Server/test/Horde/Kolab/Server/AddingObjectsTest.php
@@ -0,0 +1,73 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * We need the base class
+ */
+require_once 'Horde/Kolab/Test/Server.php';
+
+/**
+ * Adding objects to the server.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/AddingObjectsTest.php,v 1.3 2009/01/06 17:49:27 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_AddingObjectsTest extends Horde_Kolab_Test_Server {
+ /**
+ * Test adding valid users.
+ *
+ * @param array $user The user to add.
+ *
+ * @scenario
+ * @dataProvider validUsers
+ *
+ * @return NULL
+ */
+ public function addingValidUser($user)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a Kolab server object', $user)
+ ->then('the result should be an object of type', KOLAB_OBJECT_USER);
+ }
+
+ /**
+ * Test adding invalid users.
+ *
+ * @param array $user The user to add.
+ * @param string $error The error to expect.
+ *
+ * @scenario
+ * @dataProvider provideInvalidUsers
+ *
+ * @return NULL
+ */
+ public function addingInvalidUser($user, $error)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a Kolab server object', $user)
+ ->then('the result should indicate an error with', $error);
+ }
+
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/AdminTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/AdminTest.php
new file mode 100644
index 000000000..cf730f210
--- /dev/null
+++ b/framework/Kolab_Server/test/Horde/Kolab/Server/AdminTest.php
@@ -0,0 +1,138 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * We need the base class
+ */
+require_once 'Horde/Kolab/Test/Server.php';
+
+/**
+ * Test the admin object.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/AdminTest.php,v 1.3 2009/01/06 17:49:27 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_AdminTest extends Horde_Kolab_Test_Server {
+
+ /**
+ * Set up testing.
+ *
+ * @return NULL
+ */
+ protected function setUp()
+ {
+ $this->ldap = $this->prepareEmptyKolabServer();
+ }
+
+ /**
+ * Add an administrator object.
+ *
+ * @return NULL
+ */
+ private function _addValidAdmin()
+ {
+ $admin = $this->provideBasicAdmin();
+ $result = $this->ldap->add($admin);
+ $this->assertNoError($result);
+ }
+
+ /**
+ * Test ID generation for an admin.
+ *
+ * @return NULL
+ */
+ public function testGenerateId()
+ {
+ $admin = $this->provideBasicAdmin();
+ $this->assertNoError($admin);
+ $uid = $this->ldap->generateUid(KOLAB_OBJECT_ADMINISTRATOR, $admin);
+ $this->assertNoError($uid);
+ $this->assertEquals('cn=The Administrator,dc=example,dc=org', $uid);
+ }
+
+ /**
+ * Test fetching an admin.
+ *
+ * @return NULL
+ */
+ public function testFetchAdmin()
+ {
+ $this->_addValidAdmin();
+
+ $this->assertEquals(2, count($GLOBALS['KOLAB_SERVER_TEST_DATA']));
+ $this->assertContains('cn=admin,cn=internal,dc=example,dc=org',
+ array_keys($GLOBALS['KOLAB_SERVER_TEST_DATA']));
+
+ $administrators = $this->ldap->getGroups('cn=The Administrator,dc=example,dc=org');
+ $this->assertNoError($administrators);
+
+ $admin_group = $this->ldap->fetch('cn=admin,cn=internal,dc=example,dc=org');
+ $this->assertNoError($admin_group);
+ $this->assertTrue($admin_group->exists());
+
+ $admin = $this->ldap->fetch('cn=The Administrator,dc=example,dc=org');
+ $this->assertNoError($admin);
+ $this->assertEquals('Horde_Kolab_Server_Object_administrator',
+ get_class($admin));
+ }
+
+ /**
+ * Test listing the admins.
+ *
+ * @return NULL
+ */
+ public function testToHash()
+ {
+ $this->_addValidAdmin();
+
+ $admin = $this->ldap->fetch('cn=The Administrator,dc=example,dc=org');
+ $this->assertNoError($admin);
+
+ $hash = $admin->toHash();
+ $this->assertNoError($hash);
+ $this->assertContains('uid', array_keys($hash));
+ $this->assertContains('lnfn', array_keys($hash));
+ $this->assertEquals('admin', $hash['uid']);
+ }
+
+ /**
+ * Test listing admins.
+ *
+ * @return NULL
+ */
+ public function testListingGroups()
+ {
+ $this->_addValidAdmin();
+
+ $entries = $this->ldap->_search('(&(cn=*)(objectClass=inetOrgPerson)(!(uid=manager))(sn=*))');
+ $this->assertNoError($entries);
+ $this->assertEquals(1, count($entries));
+
+ $list = $this->ldap->listObjects(KOLAB_OBJECT_ADMINISTRATOR);
+ $this->assertNoError($list);
+ $this->assertEquals(1, count($list));
+ }
+
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/AllTests.php b/framework/Kolab_Server/test/Horde/Kolab/Server/AllTests.php
new file mode 100644
index 000000000..2463bbbd6
--- /dev/null
+++ b/framework/Kolab_Server/test/Horde/Kolab/Server/AllTests.php
@@ -0,0 +1,84 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Define the main method
+ */
+if (!defined('PHPUnit_MAIN_METHOD')) {
+ define('PHPUnit_MAIN_METHOD', 'Horde_Kolab_Server_AllTests::main');
+}
+
+require_once 'PHPUnit/Framework/TestSuite.php';
+require_once 'PHPUnit/TextUI/TestRunner.php';
+
+/**
+ * Combine the tests for this package.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/AllTests.php,v 1.5 2009/01/06 17:49:27 jan Exp $
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_AllTests {
+
+ /**
+ * Main entry point for running the suite.
+ *
+ * @return NULL
+ */
+ public static function main()
+ {
+ PHPUnit_TextUI_TestRunner::run(self::suite());
+ }
+
+ /**
+ * Collect the unit tests of this directory into a new suite.
+ *
+ * @return PHPUnit_Framework_TestSuite The test suite.
+ */
+ public static function suite()
+ {
+ $suite = new PHPUnit_Framework_TestSuite('Horde Framework - Kolab_Server');
+
+ $basedir = dirname(__FILE__);
+ $baseregexp = preg_quote($basedir . DIRECTORY_SEPARATOR, '/');
+
+ foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($basedir)) as $file) {
+ if ($file->isFile() && preg_match('/Test.php$/', $file->getFilename())) {
+ $pathname = $file->getPathname();
+ require $pathname;
+
+ $class = str_replace(DIRECTORY_SEPARATOR, '_',
+ preg_replace("/^$baseregexp(.*)\.php/", '\\1', $pathname));
+ $suite->addTestSuite('Horde_Kolab_Server_' . $class);
+ }
+ }
+
+ return $suite;
+ }
+
+}
+
+if (PHPUnit_MAIN_METHOD == 'Horde_Kolab_Server_AllTests::main') {
+ Horde_Kolab_Server_AllTests::main();
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/DistListHandlingTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/DistListHandlingTest.php
new file mode 100644
index 000000000..6f3611cb4
--- /dev/null
+++ b/framework/Kolab_Server/test/Horde/Kolab/Server/DistListHandlingTest.php
@@ -0,0 +1,56 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * We need the base class
+ */
+require_once 'Horde/Kolab/Test/Server.php';
+
+require_once 'Horde/Kolab/Server.php';
+
+/**
+ * Handling distribution lists.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/DistListHandlingTest.php,v 1.3 2009/01/06 17:49:27 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_DistListHandlingTest extends Horde_Kolab_Test_Server {
+
+ /**
+ * Test adding a distribution list.
+ *
+ * @scenario
+ *
+ * @return NULL
+ */
+ public function creatingDistributionList()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a distribution list')
+ ->then('the result should be an object of type',
+ KOLAB_OBJECT_DISTLIST);
+ }
+
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/GroupHandlingTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/GroupHandlingTest.php
new file mode 100644
index 000000000..ae959cb80
--- /dev/null
+++ b/framework/Kolab_Server/test/Horde/Kolab/Server/GroupHandlingTest.php
@@ -0,0 +1,456 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * We need the base class
+ */
+require_once 'Horde/Kolab/Test/Server.php';
+
+require_once 'Horde/Kolab/Server.php';
+
+/**
+ * Handling groups.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/GroupHandlingTest.php,v 1.3 2009/01/06 17:49:27 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_GroupHandlingTest extends Horde_Kolab_Test_Server {
+ /**
+ * Test listing groups if there are no groups.
+ *
+ * @scenario
+ *
+ * @return NULL
+ */
+ public function listingGroupsOnEmptyServer()
+ {
+ $this->given('an empty Kolab server')
+ ->when('retrieving a hash list with all objects of type',
+ KOLAB_OBJECT_GROUP)
+ ->then('the list is an empty array');
+ }
+
+ /**
+ * Test listing groups after adding some groups.
+ *
+ * @param array $group_list The groups to add.
+ *
+ * @scenario
+ * @dataProvider groupLists
+ *
+ * @return NULL
+ */
+ public function listingGroups($group_list)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding an object list', $group_list)
+ ->and('retrieving a hash list with all objects of type',
+ KOLAB_OBJECT_GROUP)
+ ->then('the result indicates success.')
+ ->and('the list has a number of entries equal to',
+ count($group_list));
+ }
+
+ /**
+ * Test the list of groups for the group id.
+ *
+ * @param array $group_list The groups to add.
+ *
+ * @scenario
+ * @dataProvider groupLists
+ *
+ * @return NULL
+ */
+ public function listingGroupsHasAttributeId($group_list)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding an object list', $group_list)
+ ->and('retrieving a hash list with all objects of type',
+ KOLAB_OBJECT_GROUP)
+ ->then('the provided list and the result list match with regard to these attributes',
+ 'mail', 'id', $group_list);
+ }
+
+ /**
+ * Test the list of groups for the group mail address.
+ *
+ * @param array $group_list The groups to add.
+ *
+ * @scenario
+ * @dataProvider groupLists
+ *
+ * @return NULL
+ */
+ public function listingGroupsHasAttributeMail($group_list)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding an object list', $group_list)
+ ->and('retrieving a hash list with all objects of type',
+ KOLAB_OBJECT_GROUP)
+ ->then('the provided list and the result list match with regard to these attributes',
+ 'mail', 'mail', $group_list);
+ }
+
+ /**
+ * Test the list of groups for the group visibility.
+ *
+ * @param array $group_list The groups to add.
+ *
+ * @scenario
+ * @dataProvider groupLists
+ *
+ * @return NULL
+ */
+ public function listingGroupsHasAttributeVisibility($group_list)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding an object list', $group_list)
+ ->and('retrieving a hash list with all objects of type',
+ KOLAB_OBJECT_GROUP)
+ ->then('each element in the result list has an attribute',
+ 'visible');
+ }
+
+ /**
+ * Test adding an invalid group.
+ *
+ * @scenario
+ *
+ * @return NULL
+ */
+ public function creatingGroupsWithoutMailAddressFails()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a group without a mail address')
+ ->then('the result should indicate an error with',
+ 'Adding object failed: The value for "mail" is missing!');
+ }
+
+ /**
+ * Test adding a group without setting the visibility.
+ *
+ * @scenario
+ *
+ * @return NULL
+ */
+ public function creatingGroupWithoutVisibilityCreatesVisibleGroup()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding an object', $this->validGroupWithoutMembers())
+ ->and('retrieving a hash list with all objects of type',
+ KOLAB_OBJECT_GROUP)
+ ->then('each element in the result list has an attribute set to a given value',
+ 'visible', true);
+ }
+
+ /**
+ * Test modifying a group mail address.
+ *
+ * @scenario
+ *
+ * @return NULL
+ */
+ public function modifyingGroupMailAddressIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a group with the mail address "test@example.org"')
+ ->and('modifying the mail address to "new@example.org"')
+ ->then('the result should indicate an error with',
+ 'The group cannot be modified: Changing the mail address from "test@example.org" to "new@example.org" is not allowed!');
+ }
+
+ /**
+ * Test modifying a group mail address.
+ *
+ * @scenario
+ */
+ public function conflictBetweenGroupMailAndUserMailIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a group with the mail address "test@example.org"')
+ ->and('adding a user "Test Test" with the mail address "test@example.org"')
+ ->then('the result should indicate an error with',
+ 'The user cannot be added: Mail address "test@example.org" is already the mail address for the group "test@example.org"!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function conflictBetweenUserMailAndGroupMailIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user "Test Test" with the mail address "test@example.org"')
+ ->and('adding a group with the mail address "test@example.org"')
+ ->then('the result should indicate an error with',
+ 'The group cannot be added: Mail address "test@example.org" is already the mail address of the user "Test Test"!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function conflictBetweenGroupMailAndUserAliasIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a group with the mail address "test@example.org"')
+ ->and('adding a user with the alias address "test@example.org"')
+ ->then('the result should indicate an error with',
+ 'The user cannot be added: Alias address "test@example.org" is already the mail address of the group "test@example.org"!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function conflictBetweenUserAliasAndGroupMailIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user "Test Test" with the alias address "test@example.org"')
+ ->and('adding a group with the mail address "test@example.org"')
+ ->then('the result should indicate an error with',
+ 'The group cannot be added: Mail address "test@example.org" is already the alias address of the user "Test Test"!');
+ }
+
+ /**
+ * kolab/issue890 (Assigning multiple Distribution Lists to user during creation and modification)
+ *
+ * @scenario
+ */
+ public function showGroupsWhenFetchingTheUser()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user "cn=Test Test" with the mail address "test@example.org"')
+ ->and('adding a group with the mail address "testgroup@example.org" and the member "cn=Test Test"')
+ ->and('fetching the user "test@example.org"')
+ ->and('listing the groups of this user')
+ ->then('the list should contain "testgroup@example.org"');
+ }
+
+ /**
+ * @scenario
+ */
+ public function allowAddingUserToGroup()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a group with the mail address "testgroup@example.org"')
+ ->and('adding a user "cn=Test Test" with the mail address "test@example.org"')
+ ->and('modifying group with the mail address "testgroup@example.org" to contain the member "cn=Test Test".')
+ ->and('fetching the groups "group@example.org"')
+ ->and('listing the members of this group')
+ ->then('the list should contain "test@example.org"');
+ }
+
+ /**
+ * @scenario
+ */
+ public function allowAddingUserToGroupWhenCreatingUser()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a group with the mail address "testgroup@example.org"')
+ ->and('adding a user "cn=Test Test" with the mail address "test@example.org" and member of "testgroup@example.org"')
+ ->and('fetching the groups "group@example.org"')
+ ->and('listing the members of this group')
+ ->then('the list should contain "test@example.org"');
+ }
+
+ /**
+ * @scenario
+ */
+ public function allowRemovingUserFromGroup()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user "cn=Test Test" with the mail address "test@example.org"')
+ ->and('adding a group with the mail address "testgroup@example.org" and the member "cn=Test Test"')
+ ->and('modifying group with the mail address "testgroup@example.org" to contain no members.')
+ ->and('fetching the groups "group@example.org"')
+ ->and('listing the members of this group')
+ ->then('the list is empty');
+ }
+
+ /**
+ * @scenario
+ */
+ public function deletingUserRemovesUserFromAllDistributionLists()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user "cn=Test Test" with the mail address "test@example.org"')
+ ->and('adding a group with the mail address "testgroup@example.org" and the member "cn=Test Test"')
+ ->and('adding a group with the mail address "testgroup2@example.org" and the member "cn=Test Test"')
+ ->and('deleting user "cn=Test Test"')
+ ->and('listing the members of group "testgroup@example.org"')
+ ->and('listing the members of group "testgroup2@example.org"')
+ ->then('the list of group "testgroup@example.org" is empty')
+ ->and('the list of group "testgroup2@example.org" is empty');
+ }
+
+ /**
+ * @scenario
+ */
+ public function modifyingUserIDDoesNotChangeGroupMembership()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user "cn=Test Test" with the mail address "test@example.org"')
+ ->and('adding a group with the mail address "testgroup@example.org" and the member "cn=Test Test"')
+ ->and('modifying user "cn=Test Test" to ID "cn=Test2 Test"')
+ ->and('listing the members of group "testgroup@example.org"')
+ ->then('the list of group "testgroup@example.org" contains "cn=Test2 Test"');
+ }
+
+ /**
+ * @scenario
+ */
+ public function addingGroupInUndefinedDomainIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->and('the only served mail domain is "example.org"')
+ ->when('adding a group with the mail address "test@doesnotexist.org"')
+ ->then('the result should indicate an error with', 'The group cannot be added: Domain "doesnotexist.org" is not being handled by this server!');
+ }
+
+ /**
+ * kolab/issue1368 (Webinterface allows to create email addresses with slash that cyrus cannot handle)
+ *
+ * @scenario
+ * @dataProvider invalidMails
+ */
+ public function disallowInvalidMailAddresses($address)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a group with an invalid mail address', $address)
+ ->then('the result should indicate an error with', "The group cannot be added: Address \"$address\" is not a valid mail address!");
+ }
+
+ /**
+ * @scenario
+ */
+ public function objectAttributeDescriptionsCanBeRetrieved()
+ {
+ $this->given('an empty Kolab server')
+ ->when('retrieving the supported attributes by the object type "group"')
+ ->then('the result is an array of Horde attribute descriptions')
+ ->and('contains the description of "members"');
+ }
+
+ /**
+ * @scenario
+ */
+ public function removingGroupFailsIfGroupDoesNotExist()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a group with the mail address "group@example.org"')
+ ->and('deleting the group with the mail address "group@example.org"')
+ ->then('the result should indicate an error with', 'The group cannot be deleted: Group "group@example.org" does not exist!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function removingGroupByMailSucceeds()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a group with the mail address "test@example.org"')
+ ->and('deleting the group with mail address "test@example.org"')
+ ->then('the result indicates success')
+ ->and('listing all groups returns an empty list');
+ }
+
+ /**
+ * kolab/issue1189 (IMAP login fails on some specific uids)
+ *
+ * @scenario
+ */
+ public function userUidsShouldNotResembleTheLocalPartOfMailAddresses()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a group with the mail address "test@example.org"')
+ ->and('adding a user with the uid "test"')
+ ->then('the result should indicate an error with', 'The user cannot be added: The uid "test" matches the local part of the mail address "test@example.org" assigned to group "test@example.org"!');
+ }
+
+ /**
+ * kolab/issue2207 (Make it possible to enable and disable users to be able to use the webclient.)
+ *
+ * @scenario
+ */
+ public function addedUserCanLoginIfInAllowedGroup()
+ {
+ $this->given('an empty Kolab server')
+ ->and('Horde uses the Kolab auth driver')
+ ->and('only members of group "testgroup@example.org" are allowed')
+ ->when('adding a user "cn=Test Test" with the mail address "test@example.org" and password "test"')
+ ->and('adding a group with the mail address "testgroup@example.org" and the member "cn=Test Test"')
+ ->and('trying to login to Horde with "test@example.org" and passowrd "test"')
+ ->then('the result indicates success')
+ ->and('the session shows "test@example.org" as the current user');
+ }
+
+ /**
+ * kolab/issue2207 (Make it possible to enable and disable users to be able to use the webclient.)
+ *
+ * @scenario
+ */
+ public function addedUserCannotLoginIfInNotInAllowedGroup()
+ {
+ $this->given('an empty Kolab server')
+ ->and('Horde uses the Kolab auth driver')
+ ->and('only members of group "testgroup@example.org" are allowed')
+ ->when('adding a user "cn=Test Test" with the mail address "test@example.org" and password "test"')
+ ->and('adding a group with the mail address "testgroup@example.org" and no members')
+ ->and('trying to login to Horde with "test@example.org" and passowrd "test"')
+ ->then('the user may not login');
+ }
+
+ /**
+ * kolab/issue2207 (Make it possible to enable and disable users to be able to use the webclient.)
+ *
+ * @scenario
+ */
+ public function addedUserCanLoginIfInNotInDisallowedGroup()
+ {
+ $this->given('an empty Kolab server')
+ ->and('Horde uses the Kolab auth driver')
+ ->and('members of group "testgroup@example.org" may not login')
+ ->when('adding a user "cn=Test Test" with the mail address "test@example.org" and password "test"')
+ ->and('adding a group with the mail address "testgroup@example.org" and no members')
+ ->and('trying to login to Horde with "test@example.org" and passowrd "test"')
+ ->then('the result indicates success')
+ ->and('the session shows "test@example.org" as the current user');
+ }
+
+ /**
+ * kolab/issue2207 (Make it possible to enable and disable users to be able to use the webclient.)
+ *
+ * @scenario
+ */
+ public function addedUserCannotLoginIfInDisallowedGroup()
+ {
+ $this->given('an empty Kolab server')
+ ->and('Horde uses the Kolab auth driver')
+ ->and('members of group "testgroup@example.org" may not login')
+ ->when('adding a user "cn=Test Test" with the mail address "test@example.org" and password "test"')
+ ->and('adding a group with the mail address "testgroup@example.org" and the member "cn=Test Test"')
+ ->and('trying to login to Horde with "test@example.org" and passowrd "test"')
+ ->then('the user may not login');
+ }
+
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/GroupTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/GroupTest.php
new file mode 100644
index 000000000..b62c6ac82
--- /dev/null
+++ b/framework/Kolab_Server/test/Horde/Kolab/Server/GroupTest.php
@@ -0,0 +1,137 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * We need the base class
+ */
+require_once 'Horde/Kolab/Test/Server.php';
+
+/**
+ * Test the group object.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/GroupTest.php,v 1.3 2009/01/06 17:49:27 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_GroupTest extends Horde_Kolab_Test_Server {
+
+ /**
+ * Set up testing.
+ *
+ * @return NULL
+ */
+ protected function setUp()
+ {
+ $this->ldap = $this->prepareEmptyKolabServer();
+ }
+
+ /**
+ * Add a group object.
+ *
+ * @return NULL
+ */
+ private function _addValidGroups()
+ {
+ $groups = $this->validGroups();
+ foreach ($groups as $group) {
+ $result = $this->ldap->add($group[0]);
+ $this->assertNoError($result);
+ }
+ }
+
+ /**
+ * Test ID generation for a group.
+ *
+ * @return NULL
+ */
+ public function testGenerateId()
+ {
+ $groups = $this->validGroups();
+ $this->assertEquals('cn=empty.group@example.org,dc=example,dc=org',
+ $this->ldap->generateUid(KOLAB_OBJECT_GROUP, $groups[0][0]));
+ }
+
+ /**
+ * Test fetching a group.
+ *
+ * @return NULL
+ */
+ public function testFetchGroup()
+ {
+ $this->_addValidGroups();
+
+ $group = $this->ldap->fetch('cn=empty.group@example.org,dc=example,dc=org');
+ $this->assertNoError($group);
+ $this->assertEquals('Horde_Kolab_Server_Object_group', get_class($group));
+ }
+
+ /**
+ * Test fetching a group.
+ *
+ * @return NULL
+ */
+ public function testToHash()
+ {
+ $this->_addValidGroups();
+
+ $group = $this->ldap->fetch('cn=empty.group@example.org,dc=example,dc=org');
+ $this->assertNoError($group);
+
+ $hash = $group->toHash();
+ $this->assertNoError($hash);
+ $this->assertContains('mail', array_keys($hash));
+ $this->assertContains('id', array_keys($hash));
+ $this->assertContains('visible', array_keys($hash));
+ $this->assertEquals('empty.group@example.org', $hash['mail']);
+ $this->assertEquals('empty.group@example.org', $hash['id']);
+ $this->assertTrue($hash['visible']);
+ }
+
+ /**
+ * Test listing groups.
+ *
+ * @return NULL
+ */
+ public function testListingGroups()
+ {
+ $this->assertEquals(0, count($GLOBALS['KOLAB_SERVER_TEST_DATA']));
+ $this->assertEquals(0,
+ count($this->ldap->_search('(&(!(cn=domains))(objectClass=kolabGroupOfNames))',
+ array(),
+ $this->ldap->_base_dn)));
+
+ $this->_addValidGroups();
+
+ $this->assertEquals(3, count($GLOBALS['KOLAB_SERVER_TEST_DATA']));
+ $this->assertEquals(3,
+ count($this->ldap->_search('(&(!(cn=domains))(objectClass=kolabGroupOfNames))',
+ array(),
+ $this->ldap->_base_dn)));
+
+ $list = $this->ldap->listObjects(KOLAB_OBJECT_GROUP);
+ $this->assertNoError($list);
+ $this->assertEquals(3, count($list));
+ }
+
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/ObjectTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/ObjectTest.php
new file mode 100644
index 000000000..e35a65d4f
--- /dev/null
+++ b/framework/Kolab_Server/test/Horde/Kolab/Server/ObjectTest.php
@@ -0,0 +1,167 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * We need the base class
+ */
+require_once 'Horde/Kolab/Test/Server.php';
+
+require_once 'PEAR.php';
+require_once 'Horde/Kolab/Server/Object.php';
+
+/**
+ * The the handling of objects.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/ObjectTest.php,v 1.7 2009/01/06 17:49:27 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_ObjectTest extends Horde_Kolab_Test_Server {
+
+ /**
+ * Set up a dummy db object that will not be used during the
+ * tests. We just need it so that PHP does not complain about the
+ * inability to refernce the storage class.
+ *
+ * @return NULL
+ */
+ protected function setUp()
+ {
+ $this->_dummydb = &new DummyDB();
+ }
+
+ /**
+ * Test construction of the class.
+ *
+ * @return NULL
+ */
+ public function testConstruct()
+ {
+ $ko = &new Horde_Kolab_Server_Object($this->_dummydb);
+ /** Not enough information provided */
+ $this->assertEquals('Specify either the UID or a search result!',
+ $ko->_cache->message);
+ $attr = $ko->get(KOLAB_ATTR_CN);
+ /** The base object supports nothing */
+ $this->assertError($attr, 'Attribute "cn" not supported!');
+ $ko2 = &new Horde_Kolab_Server_Object($this->_dummydb);
+ $ko = &new Horde_Kolab_Server_Object($ko2);
+ $this->assertNoError($ko);
+ $this->assertNoError($ko2);
+ /** Ensure that referencing works */
+ $this->assertSame($ko->_db, $ko2);
+ }
+
+ /**
+ * Provide test data for the ConstructDn test.
+ *
+ * @return array The test object data.
+ */
+ public static function provideConstructDn()
+ {
+ return array(
+ array('test', null, 'test'),
+ array(false, array('dn' => 'test'), 'test'),
+ array(false, array('dn' => array('test')), 'test'),
+ array('test2', array('dn' => array('test')), 'test2'),
+ );
+ }
+
+ /**
+ * Check a few DN values when constructing the object.
+ *
+ * @param string $dn The uid for the object.
+ * @param string $data Object data.
+ * @param string $expect Expect this uid.
+ *
+ * @dataProvider provideConstructDn
+ *
+ * @return NULL
+ */
+ public function testConstructDn($dn, $data, $expect)
+ {
+ $ko = &new Horde_Kolab_Server_Object($this->_dummydb, $dn, $data);
+ $ndn = $ko->get(KOLAB_ATTR_UID);
+ $this->assertNoError($ndn);
+ $this->assertEquals($expect, $ndn);
+ }
+
+ /**
+ * Provide test data for the GetFn test.
+ *
+ * @return array The test object data.
+ */
+ public static function provideGetFn()
+ {
+ return array(
+ array(
+ array(
+ 'dn' => 'test',
+ 'cn' => 'Frank Mustermann',
+ 'sn' => 'Mustermann'),
+ 'Frank'));
+ }
+
+ /**
+ * Check the generating of the "First Name" attribute.
+ *
+ * @param string $data Object data.
+ * @param string $expect Expect this full name.
+ *
+ * @dataProvider provideGetFn
+ *
+ * @return NULL
+ */
+ public function testGetFn($data, $expect)
+ {
+ $ko = &Horde_Kolab_Server_Object::factory(KOLAB_OBJECT_USER,
+ null, $this->_dummydb, $data);
+ $this->assertNoError($ko);
+ $ndn = $ko->get(KOLAB_ATTR_FN);
+ $this->assertNoError($ndn);
+ $this->assertEquals($expect, $ndn);
+ }
+
+}
+
+/**
+ * A dummy class for testing.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/ObjectTest.php,v 1.7 2009/01/06 17:49:27 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class DummyDB {
+
+
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/ServerTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/ServerTest.php
new file mode 100644
index 000000000..9959a9078
--- /dev/null
+++ b/framework/Kolab_Server/test/Horde/Kolab/Server/ServerTest.php
@@ -0,0 +1,72 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * We need the unit test framework
+ */
+require_once 'PHPUnit/Framework.php';
+
+require_once 'Horde/Kolab/Server.php';
+
+/**
+ * Tests for the main server class.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/ServerTest.php,v 1.6 2009/01/06 17:49:27 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_ServerTest extends PHPUnit_Framework_TestCase {
+ /**
+ * The base class provides no abilities for reading data. So it
+ * should mainly return error. But it should be capable of
+ * returning a dummy Kolab user object.
+ *
+ * @return NULL
+ */
+ public function testFetch()
+ {
+ $ks = &Horde_Kolab_Server::factory('none');
+ $user = $ks->fetch('test');
+ $this->assertEquals(KOLAB_OBJECT_USER, get_class($user));
+ $cn = $user->get(KOLAB_ATTR_CN);
+ $this->assertEquals('Not implemented!', $cn->message);
+ }
+
+ /**
+ * The base class returns no valid data. The DN request should
+ * just return false and the search for a mail address returns the
+ * provided argument.
+ *
+ * @return NULL
+ */
+ public function testDnFor()
+ {
+ $ks = &Horde_Kolab_Server::factory('none');
+ $dn = $ks->uidForIdOrMail('test');
+ $this->assertEquals(false, $dn);
+ $dn = $ks->uidForMailAddress('test');
+ $this->assertEquals('test', $dn);
+ }
+
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/SessionTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/SessionTest.php
new file mode 100644
index 000000000..16fbdecac
--- /dev/null
+++ b/framework/Kolab_Server/test/Horde/Kolab/Server/SessionTest.php
@@ -0,0 +1,209 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * We need the base class
+ */
+require_once 'Horde/Kolab/Test/Server.php';
+
+require_once 'Horde/Kolab/Session.php';
+
+/**
+ * Test the Kolab session handler.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/SessionTest.php,v 1.10 2009/01/14 21:46:54 wrobel Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_SessionTest extends Horde_Kolab_Test_Server {
+
+ /**
+ * Test class construction.
+ *
+ * @return NULL
+ */
+ public function testConstructEmpty()
+ {
+ global $conf;
+ $conf['kolab']['imap']['allow_special_users'] = true;
+
+ $session = &Horde_Kolab_Session::singleton();
+
+ $this->assertEquals('anonymous', $session->user_mail);
+
+ $params = $session->getImapParams();
+ $this->assertNoError($params);
+ $this->assertEquals('localhost', $params['hostspec']);
+ $this->assertEquals(143, $params['port']);
+ }
+
+ /**
+ * Test old style class construction.
+ *
+ * @return NULL
+ */
+ public function testConstructSimple()
+ {
+ global $conf;
+ $conf['kolab']['imap']['server'] = 'example.com';
+ $conf['kolab']['imap']['port'] = 200;
+ $conf['kolab']['freebusy']['server'] = 'fb.example.com';
+
+ $session = &new Horde_Kolab_Session();
+ $params = $session->getImapParams();
+ if (is_a($params, 'PEAR_Error')) {
+ $this->assertEquals('', $params->getMessage());
+ }
+ $this->assertEquals('example.com', $params['hostspec']);
+ $this->assertEquals(200, $params['port']);
+ }
+
+ /**
+ * Test IMAP server retrieval.
+ *
+ * @return NULL
+ */
+ public function testGetServer()
+ {
+ $server = &$this->prepareEmptyKolabServer();
+ $result = $server->add($this->provideBasicUserTwo());
+ $this->assertNoError($result);
+ $this->assertEquals(1, count($GLOBALS['KOLAB_SERVER_TEST_DATA']));
+
+ $session = &Horde_Kolab_Session::singleton('test',
+ array('password' => 'test'));
+
+ $this->assertNoError($session->auth);
+ $this->assertEquals('test@example.org', $session->user_mail);
+
+ $params = $session->getImapParams();
+ $this->assertNoError($params);
+ $this->assertEquals('home.example.org', $params['hostspec']);
+ $this->assertEquals(143, $params['port']);
+ $this->assertEquals('test@example.org', $session->user_mail);
+
+ $session->shutdown();
+
+ $hs = &Horde_SessionObjects::singleton();
+
+ $recovered_session = &$hs->query('kolab_session');
+ $params = $recovered_session->getImapParams();
+ $this->assertNoError($params);
+ $this->assertEquals('home.example.org', $params['hostspec']);
+ $this->assertEquals(143, $params['port']);
+ $this->assertEquals('test@example.org', $session->user_mail);
+
+ $this->assertEquals('https://fb.example.org/freebusy', $session->freebusy_server);
+ }
+
+ /**
+ * Test retrieving the FreeBusy server for the unauthenticated state.
+ *
+ * @return NULL
+ */
+ public function testGetFreeBusyServer()
+ {
+ $server = &$this->prepareEmptyKolabServer();
+ $result = $server->add($this->provideBasicUserTwo());
+ $this->assertNoError($result);
+ $session = &Horde_Kolab_Session::singleton();
+ $this->assertEquals('', $session->freebusy_server);
+ }
+
+ /**
+ * Test group based login allow implemention.
+ *
+ * @return NULL
+ */
+ public function testLoginAllow()
+ {
+ global $conf;
+ $conf['kolab']['server']['allow_group'] = 'group2@example.org';
+ $conf['kolab']['server']['deny_group'] = null;
+
+ $server = &$this->prepareEmptyKolabServer();
+ $result = $server->add($this->provideBasicUserOne());
+ $this->assertNoError($result);
+ $result = $server->add($this->provideBasicUserTwo());
+ $this->assertNoError($result);
+ $groups = $this->validGroups();
+ foreach ($groups as $group) {
+ $result = $server->add($group[0]);
+ $this->assertNoError($result);
+ }
+
+ $session = &Horde_Kolab_Session::singleton('wrobel',
+ array('password' => 'none'),
+ true);
+
+ $this->assertNoError($session->auth);
+ $this->assertEquals('wrobel@example.org', $session->user_mail);
+
+ $session = &Horde_Kolab_Session::singleton('test',
+ array('password' => 'test'),
+ true);
+
+ $this->assertError($session->auth, 'You are no member of a group that may login on this server.');
+ $this->assertTrue(empty($session->user_mail));
+ }
+
+ /**
+ * Test group based login deny implemention.
+ *
+ * @return NULL
+ */
+ public function testLoginDeny()
+ {
+ global $conf;
+ $conf['kolab']['server']['deny_group'] = 'group2@example.org';
+ unset($conf['kolab']['server']['allow_group']);
+
+ $server = &$this->prepareEmptyKolabServer();
+ $result = $server->add($this->provideBasicUserOne());
+ $this->assertNoError($result);
+ $result = $server->add($this->provideBasicUserTwo());
+ $this->assertNoError($result);
+ $groups = $this->validGroups();
+ foreach ($groups as $group) {
+ $result = $server->add($group[0]);
+ $this->assertNoError($result);
+ }
+
+ $session = &Horde_Kolab_Session::singleton('test',
+ array('password' => 'test'),
+ true);
+
+ $this->assertNoError($session->auth);
+ $this->assertEquals('test@example.org', $session->user_mail);
+
+ $session = &Horde_Kolab_Session::singleton('wrobel',
+ array('password' => 'none'),
+ true);
+
+ $this->assertError($session->auth, 'You are member of a group that may not login on this server.');
+ $this->assertTrue(empty($session->user_mail));
+
+ }
+
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/UserHandlingTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/UserHandlingTest.php
new file mode 100644
index 000000000..76c2cef8c
--- /dev/null
+++ b/framework/Kolab_Server/test/Horde/Kolab/Server/UserHandlingTest.php
@@ -0,0 +1,681 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * We need the base class
+ */
+require_once 'Horde/Kolab/Test/Server.php';
+
+/**
+ * Handling users.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/UserHandlingTest.php,v 1.3 2009/01/06 17:49:27 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_UserHandlingTest extends Horde_Kolab_Test_Server {
+
+ /**
+ * Test listing userss if there are no users.
+ *
+ * @scenario
+ *
+ * @return NULL
+ */
+ public function listingUsersOnEmptyServer()
+ {
+ $this->given('an empty Kolab server')
+ ->when('listing all users')
+ ->then('the list is an empty array');
+ }
+
+ /**
+ * Test listing users after adding some users.
+ *
+ * @param array $user_list The users to add.
+ *
+ * @scenario
+ * @dataProvider userLists
+ *
+ * @return NULL
+ */
+ public function listingUsersAfterAddingUsers($user_list)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding an object list', $user_list)
+ ->and('listing all users')
+ ->then('the list has a number of entries equal to', count($user_list));
+ }
+
+ /**
+ * Test listing users after adding some users.
+ *
+ * @param array $user_list The users to add.
+ *
+ * @scenario
+ * @dataProvider userLists
+ *
+ * @return NULL
+ */
+ public function listingUserCount($user_list)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding an object list', $user_list)
+ ->and('retriving the result count')
+ ->then('the count equals to', count($user_list));
+ }
+
+ /**
+ * Test the list of users for the user id.
+ *
+ * @param array $user_list The users to add.
+ *
+ * @scenario
+ * @dataProvider userLists
+ *
+ * @return NULL
+ */
+ public function listingUsersHasAttributeId($user_list)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user list', $user_list)
+ ->then('the user list contains the unique ID for each user');
+ }
+
+ /**
+ * Test the list of users for the user type.
+ *
+ * @param array $user_list The users to add.
+ *
+ * @scenario
+ * @dataProvider userLists
+ *
+ * @return NULL
+ */
+ public function listingUsersHasAttributeType($user_list)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user list', $user_list)
+ ->then('the user list contains the user type for each user');
+ }
+
+ /**
+ * Test the list of users for the user full name.
+ *
+ * @param array $user_list The users to add.
+ *
+ * @scenario
+ * @dataProvider userLists
+ *
+ * @return NULL
+ */
+ public function listingUsersHasAttributeFullName($user_list)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user list', $user_list)
+ ->then('the user list contains the full name for each user');
+ }
+
+ /**
+ * Test the list of users for the user mail.
+ *
+ * @param array $user_list The users to add.
+ *
+ * @scenario
+ * @dataProvider userLists
+ *
+ * @return NULL
+ */
+ public function listingUsersHasAttributeEmail($user_list)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user list', $user_list)
+ ->then('the user list contains the email for each user');
+ }
+
+ /**
+ * Test the list of users for the user uid.
+ *
+ * @param array $user_list The users to add.
+ *
+ * @scenario
+ * @dataProvider userLists
+ *
+ * @return NULL
+ */
+ public function listingUsersHasAttributeUid($user_list)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user list', $user_list)
+ ->then('the list contains the uid for each user');
+ }
+
+ /**
+ * @scenario
+ * @dataProvider userListByLetter
+ */
+ public function listingUsersCanBeRestrictedByStartLetterOfTheLastName($letter, $count)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding user list', $this->largeList())
+ ->and('retrieving the result count of a list restricted by the start letter of the last name', $letter)
+ ->then('the list contains a correct amount of results', $count);
+ }
+
+ /**
+ * @scenario
+ * @dataProvider userListByLetter
+ */
+ public function countingUsersCanBeRestrictedByStartLetterOfTheLastName($letter, $count)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding user list', $this->largeList())
+ ->and('retrieving the result count of a list restricted by the start letter of the last name', $letter)
+ ->then('the count contains a correct number', $count);
+ }
+
+ /**
+ * @scenario
+ * @dataProvider userListByAttribute
+ */
+ public function countingUsersCanBeRestrictedByContentsInAnAttribute($attribute, $content, $count)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding user list', $this->largeList())
+ ->and('retrieving the result count of a list restricted by content in an attribute', $attribute, $content)
+ ->then('the count contains a correct number', $count);
+ }
+
+ /**
+ * @scenario
+ */
+ public function creatingUserWithoutGivenName()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user without first name')
+ ->then('the result should indicate an error with', 'Adding object failed: Either the last name or the given name is missing!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function creatingUserWithoutLastName()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user without last name')
+ ->then('the result should indicate an error with', 'Adding object failed: Either the last name or the given name is missing!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function creatingUserWithoutPassword()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user without password')
+ ->then('the result should indicate an error with', 'Adding object failed: The value for "userPassword" is missing!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function creatingUserWithoutPrimaryMail()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user without primary mail')
+ ->then('the result should indicate an error with', 'Adding object failed: The value for "mail" is missing!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function creatingUserWithoutTypeCreatesStandardUser()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user without user type')
+ ->then('a standard user has been created');
+ }
+
+ /**
+ * @scenario
+ */
+ public function creatingUserWithoutInvitationPolicySetsManualPolicy()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user without an invitation policy')
+ ->then('the added user has a manual policy');
+ }
+
+ /**
+ * @scenario
+ */
+ public function creatingUserWithoutHomeServerFails()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user without a home server')
+ ->then('the result should indicate an error with', 'The user cannot be added: The home Kolab server (or network) has not been specified!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function creatingUserForDistributedKolabWithoutImapServerFails()
+ {
+ $this->given('an empty Kolab server')
+ ->and('distributed Kolab')
+ ->when('adding a user without an imap server')
+ ->then('the result should indicate an error with', 'The user cannot be added: The home imap server has not been specified!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function creatingUserWithImapServerFailsOnNonDistributedKolab()
+ {
+ $this->given('an empty Kolab server')
+ ->and('monolithic Kolab')
+ ->when('adding a user with an imap server')
+ ->then('the result should indicate an error with', 'The user cannot be added: A home imap server is only supported with a distributed Kolab setup!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function creatingUserWithFreeBusyServerFailsOnNonDistributedKolab()
+ {
+ $this->given('an empty Kolab server')
+ ->and('monolithic Kolab')
+ ->when('adding a user with a free/busy server')
+ ->then('the result should indicate an error with', 'The user cannot be added: A seperate free/busy server is only supported with a distributed Kolab setup!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function modifyingUserMailAddressIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user with the mail address "test@example.org"')
+ ->and('modifying the mail address to "new@example.org"')
+ ->then('the result should indicate an error with', 'The user cannot be modified: Changing the mail address from "test@example.org" to "new@example.org" is not allowed!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function modifyingUserHomeServerIsNotAllowd()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user with the home server "test.example.org"')
+ ->and('modifying the home server to "new.example.org"')
+ ->then('the result should indicate an error with', 'The user cannot be modified: Changing the home server from "test.example.org" to "new.example.org" is not allowed!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function modifyingUserImapServerIsNotAllowd()
+ {
+ $this->given('an empty Kolab server')
+ ->and('distributed Kolab')
+ ->when('adding a user with the imap server "test.example.org"')
+ ->and('modifying the imap server to "new.example.org"')
+ ->then('the result should indicate an error with', 'The user cannot be modified: Changing the imap server from "test.example.org" to "new.example.org" is not allowed!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function conflictBetweenMailAndMailIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user "Test Test" with the mail address "test@example.org"')
+ ->and('adding a user "Test2 Test2" with the mail address "test@example.org"')
+ ->then('the result should indicate an error with', 'The user cannot be added: Mail address "test@example.org" is already the mail address of user "Test Test"!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function conflictBetweenMailAndAliasIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user "Test Test" with the mail address "test@example.org"')
+ ->and('adding a user with the alias address "test@example.org"')
+ ->then('the result should indicate an error with', 'The user cannot be added: Alias address "test@example.org" is already the mail address of user "Test Test"!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function conflictBetweenAliasAndAliasIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user "Test Test" with the alias address "test@example.org"')
+ ->and('adding a user with the alias address "test@example.org"')
+ ->then('the result should indicate an error with', 'The user cannot be added: Alias address "test@example.org" is already the alias address of user "Test Test"!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function conflictBetweenMailAndUidIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user "Test Test" with the mail address "test@example.org"')
+ ->and('adding a user with the uid "test@example.org"')
+ ->then('the result should indicate an error with', 'The user cannot be added: Uid "test@example.org" is already the mail address of user "Test Test"!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function conflictBetweenUidAndUidIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user "Test Test" with the uid "test"')
+ ->and('adding a user with the uid "test"')
+ ->then('the result should indicate an error with', 'The user cannot be added: Uid "test" is already the uid of user "Test Test"!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function nonExistingDelegateIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user with the delegate address "test@example.org"')
+ ->then('the result should indicate an error with', 'The user cannot be added: Delegate address "test@example.org" does not exist!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function addingUserInUndefinedDomainIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->and('the only served mail domain is "example.org"')
+ ->when('adding a user with the mail address "test@doesnotexist.org"')
+ ->then('the result should indicate an error with', 'The user cannot be added: Domain "doesnotexist.org" is not being handled by this server!');
+ }
+
+ /**
+ * kolab/issue444 (a kolab user may delegate to an external user which should not be possible)
+ *
+ * @scenario
+ */
+ public function addingUserWithDelegateInUndefinedDomainIsNotAllowed()
+ {
+ $this->given('an empty Kolab server')
+ ->and('the only served mail domain is "example.org"')
+ ->when('adding a user with the delegate mail address "test@doesnotexist.org"')
+ ->then('the result should indicate an error with', 'The user cannot be added: Domain "doesnotexist.org" is not being handled by this server!');
+ }
+
+ /**
+ * kolab/issue1368 (Webinterface allows to create email addresses with slash that cyrus cannot handle)
+ *
+ * @scenario
+ * @dataProvider invalidMails
+ */
+ public function disallowInvalidMailAddresses($address)
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user with an invalid mail address', $address)
+ ->then('the result should indicate an error with', "The user cannot be added: Address \"$address\" is not a valid mail address!");
+ }
+
+ /**
+ * @scenario
+ */
+ public function addingUserOnUndefinedHomeServer()
+ {
+ $this->given('an empty Kolab server')
+ ->and('the only home server in the network is "example.org"')
+ ->when('adding a user with the home server "doesnotexist.org"')
+ ->then('the result should indicate an error with', 'The user cannot be added: Host "doesnotexist.org" is not part of the Kolab network!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function addingUserOnUndefinedImapServer()
+ {
+ $this->given('an empty Kolab server')
+ ->and('distributed Kolab')
+ ->and('the only imap server in the network is "example.org"')
+ ->when('adding a user with the imap server "doesnotexist.org"')
+ ->then('the result should indicate an error with', 'The user cannot be added: Imap server "doesnotexist.org" is not part of the Kolab network!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function userAttributesCanBeExtended()
+ {
+ $this->given('an empty Kolab server')
+ ->and('an extended attribute "test" has been defined')
+ ->when('adding a user with the attribute "test" set to "FIND ME"')
+ ->then('the result indicates success')
+ ->and('the user can be found using the "test" attribute with the value "FIND ME"');
+ }
+
+ /**
+ * @scenario
+ */
+ public function extendedObjectAttributeDescriptionsCanBeRetrieved()
+ {
+ $this->given('an empty Kolab server')
+ ->and('an extended attribute "test" has been defined')
+ ->when('retrieving the supported attributes by the object type "user"')
+ ->then('the result is an array of Horde attribute descriptions')
+ ->and('contains the description of "test"');
+ }
+
+ /**
+ * @scenario
+ */
+ public function removingUserFailsIfUserDoesNotExist()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user with the ID "cn=Test Test"')
+ ->and('deleting the user with the ID "cn=Dummy Dummy"')
+ ->then('the result should indicate an error with', 'The user cannot be deleted: User "cn=Dummy Dummy" does not exist!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function removingUserByMailSucceeds()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user with the mail address "test@example.org"')
+ ->and('deleting the user with mail address "test@example.org"')
+ ->then('the result indicates success')
+ ->and('listing all users returns an empty list');
+ }
+
+ /**
+ * @scenario
+ */
+ public function removingUserByIdSucceeds()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user with the ID "cn=Test Test"')
+ ->and('deleting the user with the ID "cn=Test Test"')
+ ->then('the result indicates success')
+ ->and('listing all users returns an empty list');
+ }
+
+ /**
+ * @scenario
+ */
+ public function addedUserCanLogin()
+ {
+ $this->given('an empty Kolab server')
+ ->and('Horde uses the Kolab auth driver')
+ ->when('adding a user with the mail address "test@example.org" and password "test"')
+ ->and('trying to login to Horde with "test@example.org" and passowrd "test"')
+ ->then('the result indicates success')
+ ->and('the session shows "test@example.org" as the current user');
+ }
+
+ /**
+ * @scenario
+ */
+ public function allowUserWithExtendedObjectClasses()
+ {
+ $this->given('an empty Kolab server')
+ ->and('an extended set of objectclasses')
+ ->when('adding a user with the mail address "test@example.org"')
+ ->and('fetching user "test@example.org"')
+ ->then('has the additional object classes set');
+ }
+
+ /**
+ * @scenario
+ */
+ public function allowToCheckUserPasswords()
+ {
+ $this->given('an empty Kolab server')
+ ->and('password check enabled')
+ ->when('adding a user with the mail address "test@example.org" and password "tosimple"')
+ ->then('the result should indicate an error with', 'The user cannot be added: The chosen password is not complex enough!');
+ }
+
+ /**
+ * @scenario
+ */
+ public function allowToSetAttributeDefaults()
+ {
+ $this->given('an empty Kolab server')
+ ->and('an extended attribute "test" with the default value "test" has been defined')
+ ->when('adding a user with the mail address "test@example.org" and an empty attribute "test"')
+ ->and('fetching user "test@example.org"')
+ ->then('the user object has the attribute "test" set to "test"');
+ }
+
+ /**
+ * kolab/issue2742 (Have a default quota value when creating new users via the web interface)
+ *
+ * @scenario
+ */
+ public function allowToSetDomainSpecificAttributeDefaults()
+ {
+ $this->given('an empty Kolab server')
+ ->and('domain "example.org" is served by the Kolab server')
+ ->and('domain "example2.org" is served by the Kolab server')
+ ->and('an extended attribute "test" with the default value "test" has been defined')
+ ->and('an extended attribute "test" with the default value "test2" has been defined for domain example2.org')
+ ->when('adding a user with the mail address "test@example.org" and an empty attribute "test"')
+ ->and('adding a user with the mail address "test@example2.org" and an empty attribute "test"')
+ ->and('fetching user "test@example.org" and "test@example2.org"')
+ ->then('the user "test@example.org" has the attribute "test" set to "test"')
+ ->and('the user "test@example2.org" has the attribute "test" set to "test2"');
+ }
+
+ /**
+ * kolab/issue3035 (Initialise internal Horde parameters when creating a user)
+ *
+ * @scenario
+ * @dataProvider userAdd
+ */
+ public function addedUserHasPreferencesInitialized()
+ {
+ $this->given('an empty Kolab server')
+ ->and('Horde uses the Kolab auth driver')
+ ->when('adding a user', $user)
+ ->and('trying to login to Horde with "test@example.org" and passowrd "test"')
+ ->then('the preferences are automatically set to the user information', $user);
+ }
+
+ /**
+ * kolab/issue1189 (IMAP login fails on some specific uids)
+ *
+ * @scenario
+ */
+ public function userUidsShouldNotResembleTheLocalPartOfMailAddresses()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user "cn=Test Test" with the mail address "test@example.org"')
+ ->and('adding a user with the uid "test"')
+ ->then('the result should indicate an error with', 'The user cannot be added: The uid "test" matches the local part of the mail address "test@example.org" assigned to user "cn=Test Test"!');
+ }
+
+ /**
+ * kolab/issue606 (It is not possible to register people with middlename correctly)
+ *
+ * @scenario
+ */
+ public function allowToSetTheMiddleName()
+ {
+ $this->given('an empty Kolab server')
+ ->and('an extended attribute "middleName" has been defined')
+ ->when('adding a user with the mail address "test@example.org" and the middle name "Middle"')
+ ->and('fetching user "test@example.org"')
+ ->then('the user object has the attribute "middleName" set to "Middle"');
+ }
+
+ /**
+ * kolab/issue1880 (Poor handling of apostrophes in ldap and admin webpages)
+ *
+ * @scenario
+ */
+ public function correctlyEscapeApostrophesInNames()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user with the mail address "test@example.org" and the last name "O\'Donnell"')
+ ->and('fetching user "test@example.org"')
+ ->then('the user name has the attribute "sn" set to "O\'Donnell"');
+ }
+
+ /**
+ * kolab/issue1677 (Allow a user to use an external address as sender)
+ *
+ * @scenario
+ */
+ public function allowUserToUseExternalAddressAsSender()
+ {
+ $this->given('an empty Kolab server')
+ ->when('adding a user with the mail address "test@example.org" and the external address "other@doesnotexist.org"')
+ ->and('fetching user "test@example.org"')
+ ->then('the user has the attribute external address "other@doesnotexist.org"');
+ }
+
+ /**
+ * kolab/issue3036 (cn = "givenName sn" ?)
+ *
+ * @scenario
+ */
+ public function allowCustomFullnameHandling()
+ {
+ $this->given('an empty Kolab server')
+ ->and('an extended attribute "middleName" has been defined')
+ ->and('custom full name handling has been set to "lastname, firstname middlename"')
+ ->when('adding a user with the mail address "test@example.org", the last name "Test", the first name "Test", and the middle name "Middle"')
+ ->and('fetching user "test@example.org"')
+ ->then('the user has the attribute full name "Test, Test Middle"');
+ }
+
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/UserTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/UserTest.php
new file mode 100644
index 000000000..cd918239b
--- /dev/null
+++ b/framework/Kolab_Server/test/Horde/Kolab/Server/UserTest.php
@@ -0,0 +1,125 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * We need the base class
+ */
+require_once 'Horde/Kolab/Test/Server.php';
+
+/**
+ * Test the user object.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/UserTest.php,v 1.5 2009/01/06 17:49:27 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_UserTest extends Horde_Kolab_Test_Server {
+
+ /**
+ * Set up testing.
+ *
+ * @return NULL
+ */
+ protected function setUp()
+ {
+ $this->server = $this->prepareEmptyKolabServer();
+ $users = $this->validUsers();
+ foreach ($users as $user) {
+ $result = $this->server->add($user[0]);
+ $this->assertNoError($result);
+ }
+ }
+
+ /**
+ * Test ID generation for a user.
+ *
+ * @return NULL
+ */
+ public function testGenerateId()
+ {
+ $users = $this->validUsers();
+ $this->assertEquals('Gunnar Wrobel',
+ Horde_Kolab_Server_Object_user::generateId($users[0][0]));
+
+ $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org',
+ $this->server->generateUid(KOLAB_OBJECT_USER,
+ $users[0][0]));
+ }
+
+ /**
+ * Test adding invalid user.
+ *
+ * @return NULL
+ */
+ public function testAddInvalidUser()
+ {
+ $user = $this->provideInvalidUserWithoutGivenName();
+
+ $result = $this->server->add($user);
+
+ $this->assertError($result,
+ 'Adding object failed: Either the last name or the given name is missing!');
+ }
+
+ /**
+ * Test fetching a user.
+ *
+ * @return NULL
+ */
+ public function testFetchUser()
+ {
+ $user = $this->server->fetch('cn=Gunnar Wrobel,dc=example,dc=org');
+ $this->assertNoError($user);
+ $this->assertEquals('Horde_Kolab_Server_Object_user', get_class($user));
+ }
+
+ /**
+ * Test fetching server information.
+ *
+ * @return NULL
+ */
+ public function testGetServer()
+ {
+ $user = $this->server->fetch('cn=Gunnar Wrobel,dc=example,dc=org');
+ $this->assertNoError($user);
+ $imap = $user->getServer('imap');
+ $this->assertEquals('imap.example.org', $imap);
+
+ $user = $this->server->fetch('cn=Test Test,dc=example,dc=org');
+ $imap = $user->getServer('imap');
+ $this->assertEquals('home.example.org', $imap);
+
+ $user = $this->server->fetch('cn=Gunnar Wrobel,dc=example,dc=org');
+ $attr = $user->get(KOLAB_ATTR_FREEBUSYHOST);
+ if (is_a($attr, 'PEAR_Error')) {
+ $this->assertEquals('', $attr->getMessage());
+ }
+ $this->assertEquals('https://fb.example.org/freebusy', $attr);
+
+ $imap = $user->getServer('freebusy');
+ $this->assertEquals('https://fb.example.org/freebusy', $imap);
+
+ }
+
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/ldapTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/ldapTest.php
new file mode 100644
index 000000000..3aebcb879
--- /dev/null
+++ b/framework/Kolab_Server/test/Horde/Kolab/Server/ldapTest.php
@@ -0,0 +1,232 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * We need the unit test framework
+ */
+require_once 'PHPUnit/Framework.php';
+
+require_once 'Horde/Kolab/Server.php';
+require_once 'Horde/Kolab/Server/ldap.php';
+
+/**
+ * Test the LDAP backend.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/ldapTest.php,v 1.7 2009/01/06 17:49:27 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_ldapTest extends PHPUnit_Framework_TestCase {
+
+ /**
+ * Test handling of object classes.
+ *
+ * @return NULL
+ */
+ public function testGetObjectClasses()
+ {
+ $ldap = $this->getMock('Horde_Kolab_Server_ldap', array('_read'));
+ $ldap->expects($this->any())
+ ->method('_read')
+ ->will($this->returnValue(array (
+ 'objectClass' =>
+ array (
+ 'count' => 4,
+ 0 => 'top',
+ 1 => 'inetOrgPerson',
+ 2 => 'kolabInetOrgPerson',
+ 3 => 'hordePerson',
+ ),
+ 0 => 'objectClass',
+ 'count' => 1)));
+
+ $classes = $ldap->_getObjectClasses('cn=Gunnar Wrobel,dc=example,dc=org');
+ $this->assertContains('top', $classes);
+ $this->assertContains('kolabinetorgperson', $classes);
+ $this->assertContains('hordeperson', $classes);
+
+ $ldap = $this->getMock('Horde_Kolab_Server_ldap', array('_read'));
+ $ldap->expects($this->any())
+ ->method('_read')
+ ->will($this->returnValue(PEAR::raiseError('LDAP Error: No such object: cn=DOES NOT EXIST,dc=example,dc=org: No such object')));
+
+ $classes = $ldap->_getObjectClasses('cn=DOES NOT EXIST,dc=example,dc=org');
+ $this->assertEquals('LDAP Error: No such object: cn=DOES NOT EXIST,dc=example,dc=org: No such object',
+ $classes->message);
+ }
+
+ /**
+ * Test retrieving a primary mail for a mail or uid.
+ *
+ * @return NULL
+ */
+ public function testMailForUidOrMail()
+ {
+ $ldap = $this->getMock('Horde_Kolab_Server_ldap', array('_getAttributes',
+ '_search', '_count',
+ '_firstEntry'));
+ $ldap->expects($this->any())
+ ->method('_getAttributes')
+ ->will($this->returnValue(array (
+ 'mail' =>
+ array (
+ 'count' => 1,
+ 0 => 'wrobel@example.org',
+ ),
+ 0 => 'mail',
+ 'count' => 1)));
+ $ldap->expects($this->any())
+ ->method('_search')
+ ->will($this->returnValue('cn=Gunnar Wrobel,dc=example,dc=org'));
+ $ldap->expects($this->any())
+ ->method('_count')
+ ->will($this->returnValue(1));
+ $ldap->expects($this->any())
+ ->method('_firstEntry')
+ ->will($this->returnValue(1));
+
+ $mail = $ldap->mailForIdOrMail('wrobel');
+ $this->assertEquals('wrobel@example.org', $mail);
+
+ $ldap = $this->getMock('Horde_Kolab_Server_ldap', array('_getAttributes',
+ '_search',
+ '_count',
+ '_firstEntry',
+ '_errno',
+ '_error'));
+ $ldap->expects($this->any())
+ ->method('_getAttributes')
+ ->will($this->returnValue(false));
+ $ldap->expects($this->any())
+ ->method('_search')
+ ->will($this->returnValue('cn=Gunnar Wrobel,dc=example,dc=org'));
+ $ldap->expects($this->any())
+ ->method('_count')
+ ->will($this->returnValue(1));
+ $ldap->expects($this->any())
+ ->method('_firstEntry')
+ ->will($this->returnValue(1));
+ $ldap->expects($this->any())
+ ->method('_errno')
+ ->will($this->returnValue(1));
+ $ldap->expects($this->any())
+ ->method('_error')
+ ->will($this->returnValue('cn=DOES NOT EXIST,dc=example,dc=org: No such object'));
+
+ $mail = $ldap->mailForIdOrMail('wrobel');
+ $this->assertEquals('Retrieving attributes failed. Error was: cn=DOES NOT EXIST,dc=example,dc=org: No such object',
+ $mail->message);
+
+ $ldap = $this->getMock('Horde_Kolab_Server_ldap', array('_getAttributes',
+ '_search',
+ '_count'));
+ $ldap->expects($this->any())
+ ->method('_getAttributes')
+ ->will($this->returnValue(false));
+ $ldap->expects($this->any())
+ ->method('_search')
+ ->will($this->returnValue('cn=Gunnar Wrobel,dc=example,dc=org'));
+ $ldap->expects($this->any())
+ ->method('_count')
+ ->will($this->returnValue(4));
+
+ $mail = $ldap->mailForIdOrMail('wrobel');
+ $this->assertEquals('Found 4 results when expecting only one!',
+ $mail->message);
+ }
+
+ /**
+ * Test retrieving a DN for a mail or uid.
+ *
+ * @return NULL
+ */
+ public function testDnForUidOrMail()
+ {
+ $ldap = $this->getMock('Horde_Kolab_Server_ldap', array('_getDn',
+ '_search', '_count',
+ '_firstEntry'));
+ $ldap->expects($this->any())
+ ->method('_getDn')
+ ->will($this->returnValue('cn=Gunnar Wrobel,dc=example,dc=org'));
+ $ldap->expects($this->any())
+ ->method('_search')
+ ->will($this->returnValue('cn=Gunnar Wrobel,dc=example,dc=org'));
+ $ldap->expects($this->any())
+ ->method('_count')
+ ->will($this->returnValue(1));
+ $ldap->expects($this->any())
+ ->method('_firstEntry')
+ ->will($this->returnValue(1));
+
+ $dn = $ldap->uidForIdOrMail('wrobel');
+ $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $dn);
+
+ $ldap = $this->getMock('Horde_Kolab_Server_ldap', array('_getDn',
+ '_search',
+ '_count',
+ '_firstEntry',
+ '_errno',
+ '_error'));
+ $ldap->expects($this->any())
+ ->method('_getDn')
+ ->will($this->returnValue(false));
+ $ldap->expects($this->any())
+ ->method('_search')
+ ->will($this->returnValue('cn=Gunnar Wrobel,dc=example,dc=org'));
+ $ldap->expects($this->any())
+ ->method('_count')
+ ->will($this->returnValue(1));
+ $ldap->expects($this->any())
+ ->method('_firstEntry')
+ ->will($this->returnValue(1));
+ $ldap->expects($this->any())
+ ->method('_errno')
+ ->will($this->returnValue(1));
+ $ldap->expects($this->any())
+ ->method('_error')
+ ->will($this->returnValue('cn=DOES NOT EXIST,dc=example,dc=org: No such object'));
+
+ $dn = $ldap->uidForIdOrMail('wrobel');
+ $this->assertEquals('Retrieving DN failed. Error was: cn=DOES NOT EXIST,dc=example,dc=org: No such object',
+ $dn->message);
+
+ $ldap = $this->getMock('Horde_Kolab_Server_ldap', array('_getDn',
+ '_search',
+ '_count'));
+ $ldap->expects($this->any())
+ ->method('_getDn')
+ ->will($this->returnValue(false));
+ $ldap->expects($this->any())
+ ->method('_search')
+ ->will($this->returnValue('cn=Gunnar Wrobel,dc=example,dc=org'));
+ $ldap->expects($this->any())
+ ->method('_count')
+ ->will($this->returnValue(4));
+
+ $dn = $ldap->uidForIdOrMail('wrobel');
+ $this->assertEquals('Found 4 results when expecting only one!',
+ $dn->message);
+ }
+
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/testTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/testTest.php
new file mode 100644
index 000000000..992fe282b
--- /dev/null
+++ b/framework/Kolab_Server/test/Horde/Kolab/Server/testTest.php
@@ -0,0 +1,604 @@
+
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * We need the base class
+ */
+require_once 'Horde/Kolab/Test/Server.php';
+
+/**
+ * Test the test backend.
+ *
+ * $Horde: framework/Kolab_Server/test/Horde/Kolab/Server/testTest.php,v 1.12 2009/01/06 17:49:27 jan Exp $
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_testTest extends Horde_Kolab_Test_Server {
+
+ /**
+ * Set up testing.
+ *
+ * @return NULL
+ */
+ protected function setUp()
+ {
+ $this->ldap = &$this->prepareBasicServer();
+ }
+
+ /**
+ * Test search base.
+ *
+ * @return NULL
+ */
+ public function testSearchBase()
+ {
+ $result = $this->ldap->_search('(objectClass=top)', array('objectClass'));
+ $this->assertNoError($result);
+ $this->assertEquals(12, count($result));
+
+ $result = $this->ldap->_search('(objectClass=top)', array('objectClass'),
+ 'cn=internal,dc=example,dc=org');
+ $this->assertNoError($result);
+ $this->assertEquals(3, count($result));
+ }
+
+ /**
+ * Test sorting.
+ *
+ * @return NULL
+ */
+ public function testSorting()
+ {
+ $result = $this->ldap->_search('(mail=*)', array('mail'));
+ $this->assertNoError($result);
+ $this->assertEquals(5, count($result));
+ $this->ldap->_sort($result, 'mail');
+ $this->assertEquals('address@example.org', $result[0]['data']['mail'][0]);
+ $this->assertEquals('wrobel@example.org',
+ $result[count($result) - 1]['data']['mail'][0]);
+ }
+
+ /**
+ * Test listing objects.
+ *
+ * @return NULL
+ */
+ public function testListObjects()
+ {
+ $filter = '(&(objectClass=kolabInetOrgPerson)(uid=*)(mail=*)(sn=*))';
+ $attributes = array(
+ KOLAB_ATTR_SN,
+ KOLAB_ATTR_CN,
+ KOLAB_ATTR_UID,
+ KOLAB_ATTR_MAIL,
+ KOLAB_ATTR_DELETED,
+ );
+
+ $sort = KOLAB_ATTR_SN;
+ $result = $this->ldap->_search($filter);
+ $this->assertNoError($result);
+ $this->assertEquals(2, count($result));
+
+ $result = $this->ldap->listObjects(KOLAB_OBJECT_USER);
+ $this->assertNoError($result);
+ $this->assertEquals(2, count($result));
+ $this->assertEquals(KOLAB_OBJECT_USER, get_class($result[0]));
+
+ $result = $this->ldap->listObjects(KOLAB_OBJECT_SHAREDFOLDER);
+ $this->assertNoError($result);
+ $this->assertEquals(1, count($result));
+ $this->assertEquals(KOLAB_OBJECT_SHAREDFOLDER, get_class($result[0]));
+ }
+
+ /**
+ * Test handling of object classes.
+ *
+ * @return NULL
+ */
+ public function testGetObjectClasses()
+ {
+ $classes = $this->ldap->_getObjectClasses('cn=Gunnar Wrobel,dc=example,dc=org');
+ $this->assertNoError($classes);
+ $this->assertContains('top', $classes);
+ $this->assertContains('kolabinetorgperson', $classes);
+ $this->assertContains('hordeperson', $classes);
+
+ $classes = $this->ldap->_getObjectClasses('cn=DOES NOT EXIST,dc=example,dc=org');
+ $this->assertError($classes,
+ 'LDAP Error: No such object: cn=DOES NOT EXIST,dc=example,dc=org: No such object');
+
+ $classes = $this->ldap->_getObjectClasses('cn=The Administrator,dc=example,dc=org');
+ $this->assertNoError($classes);
+ $this->assertContains('kolabinetorgperson', $classes);
+ }
+
+ /**
+ * Test handling of object types.
+ *
+ * @return NULL
+ */
+ public function testDetermineType()
+ {
+ $type = $this->ldap->_determineType('cn=empty.group@example.org,dc=example,dc=org');
+ $this->assertNoError($type);
+ $this->assertEquals(KOLAB_OBJECT_GROUP, $type);
+
+ $type = $this->ldap->_determineType('cn=shared@example.org,dc=example,dc=org');
+ $this->assertNoError($type);
+ $this->assertEquals(KOLAB_OBJECT_SHAREDFOLDER, $type);
+
+ $type = $this->ldap->_determineType('cn=The Administrator,dc=example,dc=org');
+ $this->assertNoError($type);
+ $this->assertEquals(KOLAB_OBJECT_ADMINISTRATOR, $type);
+
+ $type = $this->ldap->_determineType('cn=Main Tainer,dc=example,dc=org');
+ $this->assertNoError($type);
+ $this->assertEquals(KOLAB_OBJECT_MAINTAINER, $type);
+
+ $type = $this->ldap->_determineType('cn=Domain Maintainer,dc=example,dc=org');
+ $this->assertNoError($type);
+ $this->assertEquals(KOLAB_OBJECT_DOMAINMAINTAINER, $type);
+
+ $type = $this->ldap->_determineType('cn=Test Address,cn=external,dc=example,dc=org');
+ $this->assertNoError($type);
+ $this->assertEquals(KOLAB_OBJECT_ADDRESS, $type);
+
+ $type = $this->ldap->_determineType('cn=Gunnar Wrobel,dc=example,dc=org');
+ $this->assertNoError($type);
+ $this->assertEquals(KOLAB_OBJECT_USER, $type);
+ }
+
+ /**
+ * Test retrieving a primary mail for a mail or id.
+ *
+ * @return NULL
+ */
+ public function testMailForIdOrMail()
+ {
+ $mail = $this->ldap->mailForIdOrMail('wrobel');
+ $this->assertNoError($mail);
+ $this->assertEquals('wrobel@example.org', $mail);
+
+ $mail = $this->ldap->mailForIdOrMail('wrobel@example.org');
+ $this->assertNoError($mail);
+ $this->assertEquals('wrobel@example.org', $mail);
+
+ $mail = $this->ldap->mailForIdOrMail('DOES NOT EXIST');
+ $this->assertNoError($mail);
+ $this->assertSame(null, $mail);
+ }
+
+ /**
+ * Test retrieving a UID for a mail or id.
+ *
+ * @return NULL
+ */
+ public function testUidForIdOrMail()
+ {
+ $uid = $this->ldap->uidForIdOrMail('wrobel');
+ $this->assertNoError($uid);
+ $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+ $uid = $this->ldap->uidForIdOrMail('wrobel@example.org');
+ $this->assertNoError($uid);
+ $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+ $uid = $this->ldap->uidForIdOrMail('DOES NOT EXIST');
+ $this->assertNoError($uid);
+ $this->assertSame(false, $uid);
+ }
+
+ /**
+ * Test retrieving a UID for a mail or id.
+ *
+ * @return NULL
+ */
+ public function testUidForMailOrIdOrAlias()
+ {
+ $uid = $this->ldap->uidForMailOrIdOrAlias('g.wrobel@example.org');
+ $this->assertNoError($uid);
+ $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+ $uid = $this->ldap->uidForMailOrIdOrAlias('wrobel@example.org');
+ $this->assertNoError($uid);
+ $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+ $uid = $this->ldap->uidForMailOrIdOrAlias('wrobel');
+ $this->assertNoError($uid);
+ $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+ $uid = $this->ldap->uidForMailOrIdOrAlias('DOES NOT EXIST');
+ $this->assertNoError($uid);
+ $this->assertSame(false, $uid);
+ }
+
+ /**
+ * Test retrieving all addresses for a mail or id.
+ *
+ * @return NULL
+ */
+ public function testAddrsForIdOrMail()
+ {
+ $addrs = $this->ldap->addrsForIdOrMail('wrobel');
+
+ $testuser = $this->ldap->fetch('cn=Test Test,dc=example,dc=org');
+ $this->assertNoError($testuser);
+ $this->assertContains('wrobel@example.org',
+ $testuser->get(KOLAB_ATTR_KOLABDELEGATE, false));
+
+ $this->assertNoError($addrs);
+ $this->assertContains('wrobel@example.org', $addrs);
+ $this->assertContains('test@example.org', $addrs);
+ $this->assertContains('t.test@example.org', $addrs);
+ $this->assertContains('g.wrobel@example.org', $addrs);
+ $this->assertContains('gunnar@example.org', $addrs);
+
+ $addrs = $this->ldap->addrsForIdOrMail('test@example.org');
+ $this->assertNoError($addrs);
+ $this->assertContains('test@example.org', $addrs);
+ $this->assertContains('t.test@example.org', $addrs);
+ }
+
+ /**
+ * Test retrieving a UID for a primary mail.
+ *
+ * @return NULL
+ */
+ public function testUidForMailAddress()
+ {
+ $uid = $this->ldap->uidForMailAddress('wrobel@example.org');
+ $this->assertNoError($uid);
+ $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+ $uid = $this->ldap->uidForMailAddress('test@example.org');
+ $this->assertNoError($uid);
+ $this->assertEquals('cn=Test Test,dc=example,dc=org', $uid);
+
+ $uid = $this->ldap->uidForMailAddress('gunnar@example.org');
+ $this->assertNoError($uid);
+ $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+ $uid = $this->ldap->uidForMailAddress('wrobel');
+ $this->assertNoError($uid);
+ $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+ }
+
+ /**
+ * Test retrieving a UID for an attribute.
+ *
+ * @return NULL
+ */
+ public function testUidForAttr()
+ {
+ $uid = $this->ldap->uidForAttr('alias', 'g.wrobel@example.org');
+ $this->assertNoError($uid);
+ $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+ }
+
+ /**
+ * Test group membership testing.
+ *
+ * @return NULL
+ */
+ public function testMemberOfGroupAddress()
+ {
+ $uid = $this->ldap->uidForMailAddress('g.wrobel@example.org');
+ $this->assertNoError($uid);
+ $member = $this->ldap->memberOfGroupAddress($uid, 'group@example.org');
+ $this->assertNoError($member);
+ $this->assertTrue($member);
+
+ $member = $this->ldap->memberOfGroupAddress(
+ $this->ldap->uidForMailAddress('test@example.org'),
+ 'group@example.org');
+ $this->assertNoError($member);
+ $this->assertTrue($member);
+
+ $member = $this->ldap->memberOfGroupAddress(
+ $this->ldap->uidForMailAddress('somebody@example.org'),
+ 'group@example.org');
+ $this->assertNoError($member);
+ $this->assertFalse($member);
+ }
+
+ /**
+ * Test group fetching.
+ *
+ * @return NULL
+ */
+ public function testGetGroups()
+ {
+ $filter = '(&(objectClass=kolabGroupOfNames)(member='
+ . Horde_LDAP::quote('cn=The Administrator,dc=example,dc=org') . '))';
+ $result = $this->ldap->_search($filter, array());
+ $this->assertNoError($result);
+ $this->assertTrue(!empty($result));
+
+ $entry = $this->ldap->_firstEntry($result);
+ $this->assertNoError($entry);
+ $this->assertTrue(!empty($entry));
+
+ $uid = $this->ldap->_getDn($entry);
+ $this->assertNoError($uid);
+ $this->assertTrue(!empty($uid));
+
+ $entry = $this->ldap->_nextEntry($entry);
+ $this->assertNoError($entry);
+ $this->assertTrue(empty($entry));
+
+ $entries = $this->ldap->_getDns($result);
+ $this->assertNoError($entries);
+ $this->assertTrue(!empty($entries));
+
+ $groups = $this->ldap->getGroups('cn=The Administrator,dc=example,dc=org');
+ $this->assertNoError($groups);
+ $this->assertTrue(!empty($groups));
+
+ $groups = $this->ldap->getGroups($this->ldap->uidForMailAddress('g.wrobel@example.org'));
+ $this->assertNoError($groups);
+ $this->assertContains('cn=group@example.org,dc=example,dc=org', $groups);
+
+ $groups = $this->ldap->getGroups($this->ldap->uidForMailAddress('test@example.org'));
+ $this->assertNoError($groups);
+ $this->assertContains('cn=group@example.org,dc=example,dc=org', $groups);
+
+ $groups = $this->ldap->getGroups('nobody');
+ $this->assertNoError($groups);
+ $this->assertTrue(empty($groups));
+
+ }
+
+ /**
+ * Test parsing of LDAP filters.
+ *
+ * @return NULL
+ */
+ public function testFilterParse()
+ {
+ $db = &Horde_Kolab_Server::factory('test', array());
+
+ $a = $db->_parse('(a=b)');
+ $this->assertNoError($a);
+ $this->assertEquals(array('att' => 'a', 'log' => '=', 'val' => 'b'),
+ $a);
+
+ $a = $db->_parse('(&(a=b)(c=d))');
+ $this->assertNoError($a);
+ $this->assertEquals(array('op' => '&', 'sub' => array(
+ array('att' => 'a', 'log' => '=', 'val' => 'b'),
+ array('att' => 'c', 'log' => '=', 'val' => 'd'),
+ )), $a);
+
+ $a = $db->_parse('(&(a=1)(|(b=2)(c=3)))');
+ $this->assertNoError($a);
+ $this->assertEquals(array('op' => '&', 'sub' => array(
+ array('att' => 'a', 'log' => '=', 'val' => '1'),
+ array('op' => '|', 'sub' =>
+ array(
+ array('att' => 'b', 'log' => '=', 'val' => '2'),
+ array('att' => 'c', 'log' => '=', 'val' => '3'),
+ )))), $a);
+
+ $a = $db->_parseSub('(!(x=2))(b=1)');
+ $this->assertNoError($a);
+ $this->assertEquals(array(array('op' => '!', 'sub' =>
+ array(
+ array('att' => 'x', 'log' => '=', 'val' => '2'),
+ )
+ ),
+ array('att' => 'b', 'log' => '=', 'val' => '1'),
+ ), $a);
+
+ $a = $db->_parse('(&(!(x=2))(b=1))');
+ $this->assertNoError($a);
+ $this->assertEquals(array('op' => '&', 'sub' => array(
+ array('op' => '!', 'sub' =>
+ array(
+ array('att' => 'x', 'log' => '=', 'val' => '2'),
+ )
+ ),
+ array('att' => 'b', 'log' => '=', 'val' => '1'),
+ )), $a);
+
+ }
+
+ /**
+ * Test searching in the simulated LDAP data.
+ *
+ * @return NULL
+ */
+ public function testSearch()
+ {
+ $db = &Horde_Kolab_Server::factory('test',
+ array('data' =>
+ array(
+ 'cn=a' => array(
+ 'dn' => 'cn=a',
+ 'data' => array(
+ 'a' => '1',
+ 'b' => '1',
+ 'c' => '1',
+ )
+ ),
+ 'cn=b' => array(
+ 'dn' => 'cn=b',
+ 'data' => array(
+ 'a' => '1',
+ 'b' => '2',
+ 'c' => '2',
+ )
+ ),
+ 'cn=c' => array(
+ 'dn' => 'cn=c',
+ 'data' => array(
+ 'a' => '1',
+ 'b' => '2',
+ 'c' => '3',
+ )
+ ),
+ 'cn=d' => array(
+ 'dn' => 'cn=d',
+ 'data' => array(
+ 'a' => '2',
+ 'b' => '2',
+ 'c' => '1',
+ )
+ ),
+ )
+ )
+ );
+
+ $a = $db->_search('(c=1)');
+ $this->assertNoError($a);
+ $this->assertEquals(
+ array(
+ array(
+ 'dn' => 'cn=a',
+ 'data' => array(
+ 'a' => '1',
+ 'b' => '1',
+ 'c' => '1',
+ )
+ ),
+ array(
+ 'dn' => 'cn=d',
+ 'data' => array(
+ 'a' => '2',
+ 'b' => '2',
+ 'c' => '1',
+ )
+ ),
+ ),
+ $a
+ );
+
+ $a = $db->_search('(c=3)');
+ $this->assertNoError($a);
+ $this->assertEquals(
+ array(
+ array(
+ 'dn' => 'cn=c',
+ 'data' => array(
+ 'a' => '1',
+ 'b' => '2',
+ 'c' => '3',
+ )
+ ),
+ ),
+ $a
+ );
+
+ $a = $db->_search('(c=3)', array('a'));
+ $this->assertNoError($a);
+ $this->assertEquals(
+ array(
+ array(
+ 'dn' => 'cn=c',
+ 'data' => array(
+ 'a' => '1',
+ )
+ ),
+ ),
+ $a
+ );
+
+ $a = $db->_search('(&(a=1)(b=2))', array('a', 'b'));
+ $this->assertNoError($a);
+ $this->assertEquals(
+ array(
+ array(
+ 'dn' => 'cn=b',
+ 'data' => array(
+ 'a' => '1',
+ 'b' => '2',
+ )
+ ),
+ array(
+ 'dn' => 'cn=c',
+ 'data' => array(
+ 'a' => '1',
+ 'b' => '2',
+ )
+ ),
+ ),
+ $a
+ );
+
+ $a = $db->_search('(&(b=2))', array('b'));
+ $this->assertNoError($a);
+ $this->assertEquals(
+ array(
+ array(
+ 'dn' => 'cn=b',
+ 'data' => array(
+ 'b' => '2',
+ )
+ ),
+ array(
+ 'dn' => 'cn=c',
+ 'data' => array(
+ 'b' => '2',
+ )
+ ),
+ array(
+ 'dn' => 'cn=d',
+ 'data' => array(
+ 'b' => '2',
+ )
+ ),
+ ),
+ $a
+ );
+
+ $a = $db->_search('(!(b=2))', array('a', 'b'));
+ $this->assertNoError($a);
+ $this->assertEquals(
+ array(
+ array(
+ 'dn' => 'cn=a',
+ 'data' => array(
+ 'a' => '1',
+ 'b' => '1',
+ )
+ ),
+ ),
+ $a
+ );
+
+ $a = $db->_search('(&(!(x=2))(b=1))', array('b'));
+ $this->assertNoError($a);
+ $this->assertEquals(
+ array(
+ array(
+ 'dn' => 'cn=a',
+ 'data' => array(
+ 'b' => '1',
+ )
+ ),
+ ),
+ $a
+ );
+ }
+
+}