Intermediate step in refactoring Kolab_Server.
authorGunnar Wrobel <p@rdus.de>
Sat, 10 Oct 2009 21:12:06 +0000 (23:12 +0200)
committerGunnar Wrobel <p@rdus.de>
Sat, 10 Oct 2009 21:12:06 +0000 (23:12 +0200)
71 files changed:
framework/Kolab_Server/lib/Horde/Kolab/Server.php
framework/Kolab_Server/lib/Horde/Kolab/Server/Base.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Connection.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Connection/Simpleldap.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Connection/Splittedldap.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Exception.php
framework/Kolab_Server/lib/Horde/Kolab/Server/Factory.php
framework/Kolab_Server/lib/Horde/Kolab/Server/File.php
framework/Kolab_Server/lib/Horde/Kolab/Server/Filtered.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Ldap.php
framework/Kolab_Server/lib/Horde/Kolab/Server/List.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/List/Base.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Logged.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Mapped.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Object.php
framework/Kolab_Server/lib/Horde/Kolab/Server/Object/Kolabgroupofnames.php
framework/Kolab_Server/lib/Horde/Kolab/Server/Objects.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Objects/Base.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/And.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Approx.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Begins.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Contains.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Ends.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Equals.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Greater.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Group.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Less.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Mapped.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Not.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Or.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Single.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Ldap.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Result.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Result/Ldap.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Schema.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Schema/Base.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Search.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Search/Base.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Standard.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Structure.php
framework/Kolab_Server/lib/Horde/Kolab/Server/Structure/Base.php [new file with mode: 0644]
framework/Kolab_Server/lib/Horde/Kolab/Server/Structure/Ldap.php
framework/Kolab_Server/lib/Horde/Kolab/Server/Test.php
framework/Kolab_Server/package.xml
framework/Kolab_Server/test/Horde/Kolab/Server/AdminTest.php
framework/Kolab_Server/test/Horde/Kolab/Server/Autoload.php
framework/Kolab_Server/test/Horde/Kolab/Server/Connection/SimpleldapTest.php [new file with mode: 0644]
framework/Kolab_Server/test/Horde/Kolab/Server/Connection/SplittedldapTest.php [new file with mode: 0644]
framework/Kolab_Server/test/Horde/Kolab/Server/GroupTest.php
framework/Kolab_Server/test/Horde/Kolab/Server/InetorgpersonTest.php
framework/Kolab_Server/test/Horde/Kolab/Server/KolabgermanbankarrangementTest.php
framework/Kolab_Server/test/Horde/Kolab/Server/KolabinetorgpersonTest.php
framework/Kolab_Server/test/Horde/Kolab/Server/Kolabpop3accountTest.php
framework/Kolab_Server/test/Horde/Kolab/Server/LdapBase.php [new file with mode: 0644]
framework/Kolab_Server/test/Horde/Kolab/Server/ObjectTest.php
framework/Kolab_Server/test/Horde/Kolab/Server/OrgPersonTest.php
framework/Kolab_Server/test/Horde/Kolab/Server/PersonTest.php
framework/Kolab_Server/test/Horde/Kolab/Server/Query/ElementTest.php [new file with mode: 0644]
framework/Kolab_Server/test/Horde/Kolab/Server/Query/LdapTest.php [new file with mode: 0644]
framework/Kolab_Server/test/Horde/Kolab/Server/Result/LdapTest.php [new file with mode: 0644]
framework/Kolab_Server/test/Horde/Kolab/Server/Scenario.php
framework/Kolab_Server/test/Horde/Kolab/Server/Server/FactoryTest.php [new file with mode: 0644]
framework/Kolab_Server/test/Horde/Kolab/Server/Server/InterfaceTest.php [new file with mode: 0644]
framework/Kolab_Server/test/Horde/Kolab/Server/Server/LdapTest.php [new file with mode: 0644]
framework/Kolab_Server/test/Horde/Kolab/Server/Server/LoggedTest.php [new file with mode: 0644]
framework/Kolab_Server/test/Horde/Kolab/Server/Server/SearchTest.php [new file with mode: 0644]
framework/Kolab_Server/test/Horde/Kolab/Server/Server/ServerTest.php [new file with mode: 0644]
framework/Kolab_Server/test/Horde/Kolab/Server/Server/TestTest.php [new file with mode: 0644]
framework/Kolab_Server/test/Horde/Kolab/Server/UserTest.php

index 47f327d..facd7fe 100644 (file)
  */
 
 /**
- * The Autoloader allows us to omit "require/include" statements.
- */
-require_once 'Horde/Autoloader.php';
-
-/**
- * We need Log.php for the Log constants
- */
-require_once 'Log.php';
-
-/**
- * This class provides methods to deal with Kolab objects stored in
- * the Kolab object db.
+ * This class defines the interface of a generic Kolab user database.
  *
  * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
  *
@@ -36,715 +25,142 @@ require_once 'Log.php';
  * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
  * @link     http://pear.horde.org/index.php?package=Kolab_Server
  */
-abstract class Horde_Kolab_Server
+interface Horde_Kolab_Server
 {
-    /** Maximum accepted level for the object class hierarchy */
-    const MAX_HIERARCHY = 100;
-
-    /**
-     * Server parameters.
-     *
-     * @var array
-     */
-    public $params = array();
-
-    /**
-     * The UID of the current user.
-     *
-     * @var string
-     */
-    public $uid;
-
-    /**
-     * The search methods offered by the object defined for this server.
-     *
-     * @var array
-     */
-    protected $searches;
-
-    /**
-     * The structure handler for this server.
-     *
-     * @var Horde_Kolab_Server_Structure
-     */
-    public $structure;
-
-    /**
-     * A cache for object attribute definitions.
-     *
-     * @var array
-     */
-    protected $attributes;
-
-    /**
-     * The data cache.
-     *
-     * @var Horde_Cache
-     */
-    protected $cache;
-
-    /**
-     * The log handler.
-     *
-     * @var Horde_Log_Logger
-     */
-    protected $logger;
-
-    /**
-     * Construct a new Horde_Kolab_Server object.
-     *
-     * @param array $params Parameter array.
-     */
-    public function __construct(Horde_Kolab_Server_Structure $structure,
-                                $params = array())
-    {
-        $structure->setServer($this);
-        $this->structure = $structure;
-        $this->params    = $params;
-
-        if (!isset($this->params['cache_lifetime'])) {
-            $this->params['cache_lifetime'] = 300;
-        }
-
-        if (isset($params['uid'])) {
-            $this->uid = $params['uid'];
-        }
-
-        /** Initialize the search operations supported by this server. */
-        $this->searches = $this->getSearchOperations();
-    }
-
-    /**
-     * Set the optional log handler.
-     *
-     * @param Horde_Log_Logger $logger The log handler.
-     *
-     * @return NULL
-     */
-    public function setLogger(Horde_Log_Logger $logger)
-    {
-        $this->logger = $logger;
-    }
-
-    /**
-     * Set the optional cache handler.
-     *
-     * @param Horde_Cache $cache The cache handler.
-     *
-     * @return NULL
-     */
-    public function setCache(Horde_Cache $cache)
-    {
-        $this->cache = $cache;
-    }
-
-    /**
-     * 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 The concrete Horde_Kolab_Server reference.
-     *
-     * @throws Horde_Kolab_Server_Exception If the driver configuration is
-     *                                      missing or the given user could not
-     *                                      be identified.
-     */
-    static public function &singleton($params = array())
-    {
-        global $conf;
-
-        static $instances = array();
-
-        $provider = new stdClass;
-
-        $sparam         = $params;
-        $sparam['pass'] = isset($sparam['pass'])
-            ? hash('sha256', $sparam['pass']) : '';
-        ksort($sparam);
-        $signature = serialize($sparam);
-
-        if (empty($instances[$signature])) {
-
-            $user_pass = '';
-
-            if (!empty($params['driver'])) {
-                $provider->kolab_server_driver = $params['driver'];
-                unset($params['driver']);
-            } else if (isset($conf['kolab']['server']['driver'])) {
-                $provider->kolab_server_driver = $conf['kolab']['server']['driver'];
-                if (isset($conf['kolab']['server']['params'])) {
-                    if (!is_array($params)) {
-                        $params = $conf['kolab']['server']['params'];
-                    } else {
-                        if (isset($params['user']) && isset($params['pass'])) {
-                            $user_pass = $params['pass'];
-                            unset($params['pass']);
-                        }
-                        $params = array_merge($conf['kolab']['server']['params'],
-                                              $params);
-                    }
-                }
-            } else {
-                throw new Horde_Kolab_Server_Exception(
-                    'The configuration for the Kolab server driver is missing!');
-            }
-
-            /* Provide caching */
-            if (!empty($params['cache']['driver']) && class_exists('Horde_Cache')) {
-                $provider->cache  = Horde_Cache::singleton($params['cache']['driver'],
-                                                           isset($params['cache']['params'])
-                                                           ? $params['cache']['params'] : null);
-            }
-
-            /* Provide the structure */
-            $structure        = isset($params['structure']['driver'])
-                ? $params['structure']['driver'] : 'kolab';
-            $structure_params = isset($params['structure']['params'])
-                ? $params['structure']['params'] : array();
-
-            $provider->kolab_server_structure = &Horde_Kolab_Server_Structure::factory($structure,
-                                                                                       $structure_params);
-
-            if (isset($params['user'])) {
-
-                $provider->kolab_server_params = $params;
-
-                $tmp_server = &Horde_Kolab_Server_Factory::getServer($provider);
-
-                try {
-                    $uid = $tmp_server->uidForIdOrMail($params['user']);
-                } catch (Horde_Kolab_Server_Exception $e) {
-                    throw new Horde_Kolab_Server_Exception(
-                        sprintf(_("Failed identifying the UID of the Kolab user %s. Error was: %s"),
-                                $params['user'],
-                                $e->getMessage()));
-                }
-                if ($uid === false) {
-                    throw new Horde_Kolab_Server_MissingObjectException(
-                        sprintf(_("Failed identifying the UID of the Kolab user %s. No such user."),
-                                $params['user']));
-                }
-                $params['uid'] = $uid;
-                unset($params['user']);
-                $params['pass'] = $user_pass;
-            }
-
-            if (!empty($params['write']) && isset($params['host_master'])) {
-                $params['host'] = $params['host_master'];
-            }
-
-            $provider->kolab_server_params = $params;
-
-            $instances[$signature] = &Horde_Kolab_Server_Factory::getServer($provider);
-        }
-
-        return $instances[$signature];
-    }
-
-    /**
-     * Get the connection to the master server for write access.
-     *
-     * @return Horde_Kolab_Server The Horde_Kolab_Server reference to the master
-     *                            server.
-     */
-    function &getMaster()
-    {
-        if (!isset($this->params['host_master'])
-            || !empty($this->params['write'])
-            || $this->params['host_master'] == $this->params['host']) {
-            return $this;
-        }
-        $params          = $this->params;
-        $params['write'] = true;
-        return Horde_Kolab_Server::singleton($params);
-    }
-
     /**
-     * Stores the attribute definitions in the cache.
+     * Connect to the server.
      *
-     * @return Horde_Kolab_Server The concrete Horde_Kolab_Server reference.
-     */
-    function shutdown()
-    {
-        if (isset($this->attributes)) {
-            if (isset($this->cache)) {
-                foreach ($this->attributes as $key => $value) {
-                    $this->cache->set('attributes_' . $key, @serialize($value));
-                }
-            }
-        }
-    }
-
-    /**
-     * Fetch a Kolab object.
-     *
-     * This method will not retrieve any data from the server
-     * immediately. Instead it will simply generate a new instance for the
-     * desired object.
-     *
-     * The server data will only be accessed once you start reading the object
-     * data.
-     *
-     * This method can also be used in order to fetch non-existing objects that
-     * will be saved later. This is however not recommended and you should
-     * rather use the add($info) method for that.
+     * @param string $guid The global unique id of the user.
+     * @param string $pass The password.
      *
-     * If you do not provide the object type the server will try to determine it
-     * automatically based on the uid. As this requires reading data from the
-     * server it is recommended to specify the object type whenever it is known.
-     *
-     * If you do not specify a uid the object corresponding to the user bound to
-     * the server will be returned.
-     *
-     * @param string $uid  The UID of the object to fetch.
-     * @param string $type The type of the object to fetch.
-     *
-     * @return Kolab_Object The corresponding Kolab object.
+     * @return NULL.
      *
-     * @throws Horde_Kolab_Server_Exception
+     * @throws Horde_Kolab_Server_Exception If the connection failed.
      */
-    public function &fetch($uid = null, $type = null)
-    {
-        if (!isset($uid)) {
-            $uid = $this->uid;
-        }
-        if (empty($type)) {
-            $type = $this->determineType($uid);
-        }
-
-        $object = &Horde_Kolab_Server_Object::factory($type, $uid, $this);
-        return $object;
-    }
+    public function connectGuid($guid = null, $pass = null);
 
     /**
-     * Add a Kolab object.
-     *
-     * @param array $info The object to store.
+     * Low level access to reading object data.
      *
-     * @return Kolab_Object The newly created Kolab object.
+     * This function provides direct access to the Server data.
      *
-     * @throws Horde_Kolab_Server_Exception If the type of the object to add has
-     *                                      been left undefined or the object
-     *                                      already exists.
-     */
-    public function &add($info)
-    {
-        if (!isset($info['type'])) {
-            throw new Horde_Kolab_Server_Exception(
-                'The type of a new object must be specified!');
-        }
-
-        $type = $info['type'];
-        unset($info['type']);
-        $object = &Horde_Kolab_Server_Object::factory($type, null, $this, $info);
-        if ($object->exists()) {
-            throw new Horde_Kolab_Server_Exception(
-                sprintf(_("The object with the uid \"%s\" does already exist!"),
-                        $object->get(Horde_Kolab_Server_Object::ATTRIBUTE_UID)));
-        }
-        $object->save();
-        return $object;
-    }
-
-    /**
-     * Generate a hash representation for a list of objects.
+     * Usually you should use
      *
-     * The approach taken here is somewhat slow as the server data gets fetched
-     * into objects first which are then converted to hashes again. Since a
-     * server search will usually deliver the result as a hash the intermediate
-     * object conversion is inefficient.
+     * <code>
+     *   $object = $server->fetch('a server uid');
+     *   $variable = $object['attribute']
+     * </code>
      *
-     * But as the object classes are able to treat the attributes returned from
-     * the server with custom parsing, this is currently the preferred
-     * method. Especially for large result sets it would be better if this
-     * method would call a static object class function that operate on the
-     * result array returned from the server without using objects.
+     * to access object attributes. This is slower but takes special object
+     * handling into account (e.g. custom attribute parsing).
      *
-     * @param string $type   The type of the objects to be listed
-     * @param array  $params Additional parameters.
+     * @param string $guid The object to retrieve.
      *
-     * @return array An array of Kolab objects.
+     * @return array An array of attributes.
      *
      * @throws Horde_Kolab_Server_Exception
-     *
-     * @todo The LDAP driver needs a more efficient version of this call as it
-     *       is not required to generate objects before returning data as a
-     *       hash. It can be derived directly from the LDAP result.
      */
-    public function listHash($type, $params = null)
-    {
-        $list = $this->listObjects($type, $params);
-
-        if (isset($params['attributes'])) {
-            $attributes = $params['attributes'];
-        } else {
-            $attributes = null;
-        }
-
-        $hash = array();
-        foreach ($list as $uid => $entry) {
-            $hash[$uid] = $entry->toHash($attributes);
-        }
-
-        return $hash;
-    }
-
-    /**
-     * Returns the set of objects supported by this server.
-     *
-     * @return array An array of supported objects.
-     */
-    public function getSupportedObjects()
-    {
-        return $this->structure->getSupportedObjects();
-    }
-
-    /**
-     * Determine the type of an object by its tree position and other
-     * parameters.
-     *
-     * @param string $uid The UID of the object to examine.
-     *
-     * @return string The class name of the corresponding object type.
-     *
-     * @throws Horde_Kolab_Server_Exception If the object type is unknown.
-     */
-    public function determineType($uid)
-    {
-        return $this->structure->determineType($uid);
-    }
+    public function read($guid);
 
     /**
-     * Generates a UID for the given information.
+     * Low level access to reading some object attributes.
      *
-     * @param string $type The class name of the object to create.
-     * @param string $id   The id of the object.
-     * @param array  $info Any additional information about the object to create.
+     * @param string $guid  The object to retrieve.
+     * @param string $attrs Restrict to these attributes.
      *
-     * @return string The UID.
+     * @return array An array of attributes.
      *
      * @throws Horde_Kolab_Server_Exception
-     */
-    public function generateServerUid($type, $id, $info)
-    {
-        return $this->structure->generateServerUid($type, $id, $info);
-    }
-
-    /**
-     * Return the attributes supported by the given object class.
-     *
-     * @param string $class Determine the attributes for this class.
-     *
-     * @return array The supported attributes.
-     *
-     * @throws Horde_Kolab_Server_Exception If the schema analysis fails.
-     */
-    public function &getAttributes($class)
-    {
-        if (!isset($this->attributes)) {
-            if (isset($this->cache)) {
-                register_shutdown_function(array($this, 'shutdown'));
-            }
-        }
-        if (empty($this->attributes[$class])) {
-
-            if (isset($this->cache)) {
-                $this->attributes[$class] = @unserialize($cache->get('attributes_' . $class,
-                                                                     $this->params['cache_lifetime']));
-            }
-
-            if (empty($this->attributes[$class])) {
-
-                $childclass = $class;
-                $classes    = array();
-                $level      = 0;
-                while ($childclass != 'Horde_Kolab_Server_Object'
-                       && $level < self::MAX_HIERARCHY) {
-                    $classes[]  = $childclass;
-                    $childclass = get_parent_class($childclass);
-                    $level++;
-                }
-
-                /** Finally add the basic object class */
-                $classes[] = $childclass;
-
-                if ($level == self::MAX_HIERARCHY) {
-                    if (isset($this->logger)) {
-                        $logger->err(sprintf('The maximal level of the object hierarchy has been exceeded for class \"%s\"!',
-                                             $class));
-                    }
-                }
-
-                /**
-                 * Collect attributes from bottom to top.
-                 */
-                $classes = array_reverse($classes);
-
-                $types = array('defined', 'required', 'derived', 'collapsed',
-                               'defaults', 'locked', 'object_classes');
-                foreach ($types as $type) {
-                    $$type = array();
-                }
-
-                foreach ($classes as $childclass) {
-                    $vars = get_class_vars($childclass);
-                    if (isset($vars['init_attributes'])) {
-                        foreach ($types as $type) {
-                            /**
-                             * If the user wishes to adhere to the schema
-                             * information from the server we will skip the
-                             * attributes defined within the object class here.
-                             */
-                            if (!empty($this->params['schema_override'])
-                                && in_array($type, 'defined', 'required')) {
-                                continue;
-                            }
-                            if (isset($vars['init_attributes'][$type])) {
-                                $$type = array_merge($$type,
-                                                     $vars['init_attributes'][$type]);
-                            }
-                        }
-                    }
-                }
-
-                $attrs = array();
-
-                foreach ($object_classes as $object_class) {
-                    $info = $this->getObjectclassSchema($object_class);
-                    if (isset($info['may'])) {
-                        $defined = array_merge($defined, $info['may']);
-                    }
-                    if (isset($info['must'])) {
-                        $defined  = array_merge($defined, $info['must']);
-                        $required = array_merge($required, $info['must']);
-                    }
-                    foreach ($defined as $attribute) {
-                        try {
-                            $attrs[$attribute] = $this->getAttributeSchema($attribute);
-                        } catch (Horde_Kolab_Server_Exception $e) {
-                            /**
-                             * If the server considers the attribute to be
-                             * invalid we mark it.
-                             */
-                            $attrs[$attribute] = array('invalid' => true);
-                        }
-                    }
-                    foreach ($required as $attribute) {
-                        $attrs[$attribute]['required'] = true;
-                    }
-                    foreach ($locked as $attribute) {
-                        $attrs[$attribute]['locked'] = true;
-                    }
-                    foreach ($defaults as $attribute => $default) {
-                        $attrs[$attribute]['default'] = $default;
-                    }
-                    $attrs[Horde_Kolab_Server_Object::ATTRIBUTE_OC]['default'] = $object_classes;
-                }
-                foreach ($derived as $key => $attributes) {
-                    $supported = true;
-                    if (isset($attributes['base'])) {
-                        foreach ($attributes['base'] as $attribute) {
-                            /**
-                             * Usually derived attribute are determined on basis
-                             * of one or more attributes. If any of these is not
-                             * supported the derived attribute should not be
-                             * included into the set of supported attributes.
-                             */
-                            if (!isset($attrs[$attribute])) {
-                                unset($derived[$attribute]);
-                                $supported = false;
-                                break;
-                            }
-                        }
-                    }
-                    if ($supported) {
-                        $attrs[$key] = $attributes;
-                    }
-                }
-                $check_collapsed = $collapsed;
-                foreach ($check_collapsed as $key => $attributes) {
-                    if (isset($attributes['base'])) {
-                        foreach ($attributes['base'] as $attribute) {
-                            /**
-                             * Usually collapsed attribute are determined on basis
-                             * of one or more attributes. If any of these is not
-                             * supported the collapsed attribute should not be
-                             * included into the set of supported attributes.
-                             */
-                            if (!isset($attrs[$attribute])) {
-                                unset($collapsed[$attribute]);
-                            }
-                        }
-                    }
-                }
-                $this->attributes[$class] = array($attrs,
-                                                  array(
-                                                      'derived'   => array_keys($derived),
-                                                      'collapsed' => $collapsed,
-                                                      'locked'    => $locked,
-                                                      'required'  => $required));
-            }
-        }
-        return $this->attributes[$class];
-    }
-
-    /**
-     * Return the schema for the given objectClass.
-     *
-     * @param string $objectclass Fetch the schema for this objectClass.
-     *
-     * @return array The schema for the given objectClass.
      *
-     * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
+     * @see Horde_Kolab_Server::read
      */
-    protected function getObjectclassSchema($objectclass)
-    {
-        return array();
-    }
+    public function readAttributes($guid, array $attrs);
 
     /**
-     * Return the schema for the given attribute.
-     *
-     * @param string $attribute Fetch the schema for this attribute.
+     * Finds object data matching a given set of criteria.
      *
-     * @return array The schema for the given attribute.
+     * @param Horde_Kolab_Server_Query $query  The criteria for the search.
+     * @param array                    $params Additional search parameters.
      *
-     * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
-     */
-    protected function getAttributeSchema($attribute)
-    {
-        return array();
-    }
-
-    /**
-     * Returns the set of search operations supported by this server type.
-     *
-     * @return array An array of supported search operations.
-     */
-    public function getSearchOperations()
-    {
-        $server_searches = array();
-        foreach ($this->getSupportedObjects() as $sobj) {
-            if (in_array('getSearchOperations', get_class_methods($sobj))) {
-                $searches = call_user_func(array($sobj, 'getSearchOperations'));
-                foreach ($searches as $search) {
-                    $server_searches[$search] = array('class' => $sobj);
-                }
-            }
-        }
-        return $server_searches;
-    }
-
-    /**
-     * Capture undefined calls.
-     *
-     * @param string $method The name of the called method.
-     * @param array  $args   Arguments of the call.
-     *
-     * @return NULL.
+     * @return Horde_Kolab_Server_Result The result object.
      *
      * @throws Horde_Kolab_Server_Exception
      */
-    public function __call($method, $args)
-    {
-        if (in_array($method, array_keys($this->searches))) {
-            array_unshift($args, $this);
-            if (isset($this->searches[$method])) {
-                return call_user_func_array(array($this->searches[$method]['class'],
-                                                  $method), $args);
-            }
-        }
-        throw new Horde_Kolab_Server_Exception(
-            sprintf("The server type \"%s\" does not support method \"%s\"!",
-                    get_class($this), $method));
-    }
+    public function find(
+        Horde_Kolab_Server_Query $query,
+        array $params = array()
+    );
 
     /**
-     * Stub for reading object data.
+     * Finds all object data below a parent matching a given set of criteria.
      *
-     * @param string $uid   The object to retrieve.
-     * @param string $attrs Restrict to these attributes.
+     * @param Horde_Kolab_Server_Query $query  The criteria for the search.
+     * @param string                   $parent The parent to search below.
+     * @param array                    $params Additional search parameters.
      *
-     * @return array An array of attributes.
+     * @return Horde_Kolab_Server_Result The result object.
      *
      * @throws Horde_Kolab_Server_Exception
      */
-    abstract public function read($uid, $attrs = null);
+    public function findBelow(
+        Horde_Kolab_Server_Query $query,
+        $parent,
+        array $params = array()
+    );
 
     /**
-     * Find object data matching a given set of criteria.
+     * Modify existing object data.
      *
-     * @param array  $criteria The criteria for the search.
-     * @param string $params   Additional search parameters.
+     * @param string $guid The GUID of the object to be added.
+     * @param array  $data The attributes of the object to be stored.
      *
-     * @return array The result array.
+     * @return NULL
      *
      * @throws Horde_Kolab_Server_Exception
      */
-    abstract public function find($criteria, $params = array());
+    public function save($guid, array $data);
 
     /**
-     * Stub for saving object data.
+     * Add new object data.
      *
-     * @param string  $uid    The UID of the object to be added.
-     * @param array   $data   The attributes of the object to be added.
-     * @param boolean $exists Does the object already exist on the server?
+     * @param string $guid The GUID of the object to be added.
+     * @param array  $data The attributes of the object to be added.
      *
-     * @return boolean  True if saving succeeded.
+     * @return NULL
      *
      * @throws Horde_Kolab_Server_Exception
      */
-    abstract public function save($uid, $data, $exists = false);
+    public function add($guid, array $data);
 
     /**
-     * Stub for deleting an object.
+     * Delete an object.
      *
-     * @param string $uid The UID of the object to be deleted.
+     * @param string $guid The GUID of the object to be deleted.
      *
-     * @return boolean True if deleting the object succeeded.
+     * @return NULL
      *
      * @throws Horde_Kolab_Server_Exception
      */
-    abstract public function delete($uid);
+    public function delete($guid);
 
     /**
-     * Stub for renaming an object.
+     * Rename an object.
      *
-     * @param string $uid The UID of the object to be renamed.
-     * @param string $new The new UID of the object.
+     * @param string $guid The GUID of the object to be renamed.
+     * @param string $new  The new GUID of the object.
      *
-     * @return boolean True if renaming succeeded.
+     * @return NULL
      *
      * @throws Horde_Kolab_Server_Exception
      */
-    abstract public function rename($uid, $new);
+    public function rename($guid, $new);
 
     /**
-     * List all objects of a specific type
-     *
-     * @param string $type   The type of the objects to be listed
-     * @param array  $params Additional parameters.
+     * Return the database schema description.
      *
-     * @return array An array of Kolab objects.
-     *
-     * @throws Horde_Kolab_Server_Exception
-     */
-    abstract public function listObjects($type, $params = null);
-
-    /**
-     * Return the root of the UID values on this server.
+     * @return array The schema.
      *
-     * @return string The base UID on this server (base DN on ldap).
+     * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
      */
-    abstract public function getBaseUid();
-
-}
+    public function getSchema();
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Base.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Base.php
new file mode 100644 (file)
index 0000000..6f244ce
--- /dev/null
@@ -0,0 +1,327 @@
+<?php
+/**
+ * A library for accessing the Kolab user database.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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 objects stored in
+ * the Kolab object db.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+abstract class Horde_Kolab_Server_Base implements Horde_Kolab_Server,
+    Horde_Kolab_Server_Objects,
+    Horde_Kolab_Server_Schema,
+    Horde_Kolab_Server_Search,
+    Horde_Kolab_Server_Structure
+{
+    /**
+     * Server parameters.
+     *
+     * @var array
+     */
+    protected $params = array();
+
+    /**
+     * The user name of the current user.
+     *
+     * @var string
+     */
+    private $_user = null;
+
+    /**
+     * The structure handler for this server.
+     *
+     * @var Horde_Kolab_Server_Structure
+     */
+    protected $structure;
+
+    /**
+     * The search handler for this server.
+     *
+     * @var Horde_Kolab_Server_Search
+     */
+    protected $search;
+
+    /**
+     * The object handler for this server.
+     *
+     * @var Horde_Kolab_Server_Objects
+     */
+    protected $objects;
+
+    /**
+     * The data cache.
+     *
+     * @var mixed
+     */
+    protected $cache = null;
+
+    /**
+     * Construct a new Horde_Kolab_Server object.
+     *
+     * @param array $params Parameter array.
+     */
+    public function __construct(
+        Horde_Kolab_Server_Objects $objects,
+        Horde_Kolab_Server_Structure $structure,
+        Horde_Kolab_Server_Search $search,
+        Horde_Kolab_Server_Schema $schema
+    ) {
+        $objects->setServer($this);
+        $structure->setServer($this);
+        $search->setServer($this);
+        $schema->setServer($this);
+
+        $this->objects   = $objects;
+        $this->structure = $structure;
+        $this->search    = $search;
+        $this->schema    = $schema;
+    }
+
+    /**
+     * Set configuration parameters.
+     *
+     * @param array $params The parameters.
+     *
+     * @return NULL
+     */
+    public function setParams(array $params)
+    {
+        $this->params = array_merge($this->params, $params);
+
+        if (isset($this->params['uid'])) {
+            $this->uid = $this->params['uid'];
+        }
+    }
+
+    /**
+     * Set the cache handler.
+     *
+     * @param mixed $cache The cache handler.
+     *
+     * @return NULL
+     */
+    public function setCache($cache)
+    {
+        $this->cache = $cache;
+    }
+
+    /**
+     * Connect to the server.
+     *
+     * @param string $user The user name.
+     * @param string $pass The password.
+     *
+     * @return NULL.
+     *
+     * @throws Horde_Kolab_Server_Exception If the connection failed.
+     */
+    public function connect($user = null, $pass = null)
+    {
+        /** Do we need to switch the user? */
+        if ($user !== $this->_current_user) {
+            $this->user = $this->_connect($user, $pass);
+        }
+    }
+
+    /**
+     * Connect to the server.
+     *
+     * @param string $uid  The unique id of the user.
+     * @param string $pass The password.
+     *
+     * @return NULL.
+     *
+     * @throws Horde_Kolab_Server_Exception If the connection failed.
+     */
+    public function connectUid($uid = null, $pass = null)
+    {
+    }
+
+    /**
+     * Add a Kolab object.
+     *
+     * @param array $info The object to store.
+     *
+     * @return Kolab_Object The newly created Kolab object.
+     *
+     * @throws Horde_Kolab_Server_Exception If the type of the object to add has
+     *                                      been left undefined or the object
+     *                                      already exists.
+     */
+    public function add(array $info)
+    {
+        return $this->objects->add($info);
+    }
+
+    /**
+     * 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 The corresponding Kolab object.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function fetch($uid = null, $type = null)
+    {
+        return $this->objects->fetch($uid = null, $type = null);
+    }
+
+    /**
+     * 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 An array of Kolab objects.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function listObjects($type, $params = null)
+    {
+        return $this->objects->listObjects($type, $params = null);
+    }
+
+    /**
+     * 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 An array of Kolab objects.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function listHash($type, $params = null)
+    {
+        return $this->objects->listHash($type, $params = null);
+    }
+
+    /**
+     * Returns the set of objects supported by this server.
+     *
+     * @return array An array of supported objects.
+     */
+    public function getSupportedObjects()
+    {
+        return $this->structure->getSupportedObjects();
+    }
+
+    /**
+     * Determine the type of an object by its tree position and other
+     * parameters.
+     *
+     * @param string $uid The UID of the object to examine.
+     *
+     * @return string The class name of the corresponding object type.
+     *
+     * @throws Horde_Kolab_Server_Exception If the object type is unknown.
+     */
+    public function determineType($uid)
+    {
+        return $this->structure->determineType($uid);
+    }
+
+    /**
+     * Generates a UID for the given information.
+     *
+     * @param string $type The class name 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 The UID.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function generateServerUid($type, $id, $info)
+    {
+        return $this->structure->generateServerUid($type, $id, $info);
+    }
+
+    /**
+     * Return the schema for the given objectClass.
+     *
+     * @param string $objectclass Fetch the schema for this objectClass.
+     *
+     * @return array The schema for the given objectClass.
+     *
+     * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
+     */
+    public function getObjectclassSchema($objectclass)
+    {
+        return $this->schema->getObjectclassSchema($objectclass);
+    }
+
+    /**
+     * Return the schema for the given attribute.
+     *
+     * @param string $attribute Fetch the schema for this attribute.
+     *
+     * @return array The schema for the given attribute.
+     *
+     * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
+     */
+    public function getAttributeSchema($attribute)
+    {
+        return $this->schema->getAttributeSchema($attribute);
+    }
+
+    /**
+     * Return the attributes supported by the given object class.
+     *
+     * @param string $class Determine the attributes for this class.
+     *
+     * @return array The supported attributes.
+     *
+     * @throws Horde_Kolab_Server_Exception If the schema analysis fails.
+     */
+    public function &getAttributes($class)
+    {
+        return $this->schema->getAttributes($class);
+    }
+
+    /**
+     * Returns the set of search operations supported by this server type.
+     *
+     * @return array An array of supported search operations.
+     */
+    public function getSearchOperations()
+    {
+        return $this->search->getSearchOperations();
+    }
+
+    /**
+     * Capture undefined calls and assume they refer to a search operation.
+     *
+     * @param string $method The name of the called method.
+     * @param array  $args   Arguments of the call.
+     *
+     * @return NULL.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function __call($method, $args)
+    {
+        return $this->search->__call($method, $args);
+    }
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Connection.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Connection.php
new file mode 100644 (file)
index 0000000..2da34a2
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Interface for connection handling.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Interface for connection handling.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+interface Horde_Kolab_Server_Connection
+{
+    /**
+     * Get the server read connection.
+     *
+     * @return mixed The connection for reading data.
+     */
+    public function getRead();
+
+    /**
+     * Get the server write connection.
+     *
+     * @return mixed The connection for writing data.
+     */
+    public function getWrite();
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Connection/Simpleldap.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Connection/Simpleldap.php
new file mode 100644 (file)
index 0000000..73c903f
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/**
+ * A simple LDAP setup without read-only slaves.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * A simple LDAP setup without read-only slaves.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Connection_Simpleldap
+implements Horde_Kolab_Server_Connection
+{
+    /**
+     * The LDAP connection handle.
+     *
+     * @var Net_LDAP2
+     */
+    private $_ldap;
+
+    /**
+     * Constructor
+     *
+     * @param Net_LDAP2 $ldap The ldap connection.
+     */
+    public function __construct(Net_LDAP2 $ldap)
+    {
+        $this->_ldap = $ldap;
+    }
+
+    /**
+     * Get the server read connection.
+     *
+     * @return mixed The connection for reading data.
+     */
+    public function getRead()
+    {
+        return $this->_ldap;
+    }
+
+    /**
+     * Get the server write connection.
+     *
+     * @return mixed The connection for writing data.
+     */
+    public function getWrite()
+    {
+        return $this->_ldap;
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Connection/Splittedldap.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Connection/Splittedldap.php
new file mode 100644 (file)
index 0000000..b5781a7
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+/**
+ * A connection to a LDAP master/slave setup.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * A connection to a LDAP master/slave setup.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Connection_Splittedldap
+implements Horde_Kolab_Server_Connection
+{
+    /**
+     * LDAP read connection handle.
+     *
+     * @var Net_LDAP2
+     */
+    private $_ldap_read;
+
+    /**
+     * LDAP write connection handle.
+     *
+     * @var Net_LDAP2
+     */
+    private $_ldap_write;
+
+    /**
+     * Constructor
+     *
+     * @param Net_LDAP2 $ldap_read  The ldap_read connection.
+     * @param Net_LDAP2 $ldap_write The ldap_write connection.
+     */
+    public function __construct(
+        Net_LDAP2 $ldap_read,
+        Net_LDAP2 $ldap_write
+    ) {
+        $this->_ldap_read = $ldap_read;
+        $this->_ldap_write = $ldap_write;
+    }
+
+    /**
+     * Get the server read connection.
+     *
+     * @return mixed The connection for reading data.
+     */
+    public function getRead()
+    {
+        return $this->_ldap_read;
+    }
+
+    /**
+     * Get the server write connection.
+     *
+     * @return mixed The connection for writing data.
+     */
+    public function getWrite()
+    {
+        return $this->_ldap_write;
+    }
+}
\ No newline at end of file
index dfc1cfe..44260dd 100644 (file)
@@ -30,45 +30,20 @@ class Horde_Kolab_Server_Exception extends Horde_Exception
     /**
      * Constants to define the error type.
      */
-    const SYSTEM              = 1;
-    const EMPTY_RESULT        = 2;
-    const INVALID_INFORMATION = 3;
 
-    /**
-     * The array of available error messages. These are connected to the error
-     * codes used above and might be used to differentiate between what we show
-     * the user in the frontend and what we actually log in the backend.
-     *
-     * @var array
-     */
-    protected $messages;
+    /** Unknown error type */
+    const SYSTEM                    = 1;
 
-    /**
-     * Exception constructor
-     *
-     * @param mixed $message The exception message, a PEAR_Error object, or an
-     *                       Exception object.
-     * @param mixed $code    A numeric error code, or
-     *                       an array from error_get_last().
-     */
-    public function __construct($message = null, $code = null)
-    {
-        $this->setMessages();
+    /** The LDAP extension is missing */
+    const MISSING_LDAP_EXTENSION    = 2;
 
-        parent::__construct($message, $code);
-    }
+    /** Binding to the LDAP server failed */
+    const BIND_FAILED               = 3;
+
+    const EMPTY_RESULT              = 4;
+
+    const INVALID_INFORMATION       = 5;
+
+    const INVALID_QUERY             = 6;
 
-    /**
-     * Initialize the messages handled by this exception.
-     *
-     * @return NULL
-     */
-    protected function setMessages()
-    {
-        $this->messages = array(
-            self::SYSTEM              => _("An internal error occured."),
-            self::EMPTY_RESULT        => _("No result was found."),
-            self::INVALID_INFORMATION => _("The information provided is invalid."),
-        );
-    }
 }
index 06d8e58..a9938cc 100644 (file)
  */
 
 /**
- * The Autoloader allows us to omit "require/include" statements.
- */
-require_once 'Horde/Autoloader.php';
-
-/**
  * A factory for Kolab server objects.
  *
  * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
@@ -33,90 +28,297 @@ require_once 'Horde/Autoloader.php';
 class Horde_Kolab_Server_Factory
 {
     /**
-     * Attempts to return a concrete Horde_Kolab_Server instance.
+     * Singleton instances.
      *
-     * @param Horde_Injector $injector The object providing our dependencies.
+     * @var array
+     */
+    static private $_instances = array();
+
+    /**
+     * Setup the machinery to create Horde_Kolab_Server objects.
      *
-     * @return Horde_Kolab_Server The newly created concrete Horde_Kolab_Server
-     *                            instance.
+     * @param array          $configuration The parameters required to create
+     *                                      the desired Horde_Kolab_Server object.
+     * @param Horde_Injector $injector      The object providing our dependencies.
      *
-     * @throws Horde_Kolab_Server_Exception If the requested Horde_Kolab_Server
-     *                                      subclass could not be found.
+     * @return NULL
      */
-    static public function &getServer(Horde_Injector $injector)
+    static public function setup(array $configuration, Horde_Injector $injector)
     {
-        $driver = 'Horde_Kolab_Server_Ldap';
-        $params = array();
+        self::setupObjects($injector);
+        self::setupSearch($injector);
+        self::setupSchema($injector);
 
-        try {
-            $config = $injector->getInstance('Horde_Kolab_Server_Config');
+        self::setupStructure(
+            isset($configuration['structure'])
+            ? $configuration['structure'] : array(),
+            $injector
+        );
+        unset($configuration['structure']);
 
-            if (isset($config->driver)) {
-                $driver = $config->driver;
-            }
-            if (isset($config->params)) {
-                $params = $config->params;
-            }
-        } catch (ReflectionException $e) {
-        }
+        self::setupCache(
+            $injector,
+            isset($configuration['cache'])
+            ? $configuration['cache'] : null
+        );
+        unset($configuration['cache']);
 
-        $class = 'Horde_Kolab_Server_' . ucfirst(basename($driver));
-        if (!class_exists($class)) {
-            throw new Horde_Kolab_Server_Exception('Server type definition "' . $class . '" missing.');
-        }
+        self::setupLogger(
+            $injector,
+            isset($configuration['logger'])
+            ? $configuration['logger'] : null
+        );
+        unset($configuration['logger']);
 
-        $server = new $class($injector->getInstance('Horde_Kolab_Server_Structure'),
-                             $params);
+        self::setupServer($configuration, $injector);
+    }
+
+    /**
+     * Setup the machinery to create a Horde_Kolab_Server_Objects handler.
+     *
+     * @param Horde_Injector $injector The object providing our dependencies.
+     *
+     * @return NULL
+     */
+    static protected function setupObjects(Horde_Injector $injector)
+    {
+        $injector->bindImplementation(
+            'Horde_Kolab_Server_Objects',
+            'Horde_Kolab_Server_Objects_Base'
+        );
+    }
 
-        try {
-            $server->setCache($injector->getInstance('Horde_Kolab_Server_Cache'));
-        } catch (ReflectionException $e) {
+    /**
+     * Setup the machinery to create a Horde_Kolab_Server_Structure handler.
+     *
+     * @param array          $configuration The parameters required to create
+     *                                      the desired
+     *                                      Horde_Kolab_Server_Structure handler.
+     * @param Horde_Injector $injector      The object providing our dependencies.
+     *
+     * @return NULL
+     */
+    static protected function setupStructure(
+        array $configuration,
+        Horde_Injector $injector
+    ) {
+        if (!isset($configuration['driver'])) {
+            $configuration['driver'] = 'Horde_Kolab_Server_Structure_Kolab';
         }
 
-        try {
-            $server->setLogger($injector->getInstance('Horde_Kolab_Server_Logger'));
-        } catch (ReflectionException $e) {
+        switch (ucfirst(strtolower($configuration['driver']))) {
+        case 'Ldap':
+        case 'Kolab':
+            $driver = 'Horde_Kolab_Server_Structure_'
+                . ucfirst(strtolower($configuration['driver']));
+            break;
+        default:
+            $driver = $configuration['driver'];
+            break;
         }
 
-        return $server;
+        $injector->bindImplementation('Horde_Kolab_Server_Structure', $driver);
     }
 
     /**
-     * Attempts to return a concrete Horde_Kolab_Server_Structure instance.
+     * Setup the machinery to create a Horde_Kolab_Server_Search handler.
      *
      * @param Horde_Injector $injector The object providing our dependencies.
      *
-     * @return Horde_Kolab_Server_Structure The newly created concrete
-     *                                      Horde_Kolab_Server_Structure
-     *                                      instance.
+     * @return NULL
+     */
+    static protected function setupSearch(Horde_Injector $injector)
+    {
+        $injector->bindImplementation(
+            'Horde_Kolab_Server_Search',
+            'Horde_Kolab_Server_Search_Base'
+        );
+    }
+
+    /**
+     * Setup the machinery to create a Horde_Kolab_Server_Schema handler.
+     *
+     * @param Horde_Injector $injector The object providing our dependencies.
      *
-     * @throws Horde_Kolab_Server_Exception If the requested
-     *                                      Horde_Kolab_Server_Structure
-     *                                      subclass could not be found.
+     * @return NULL
      */
-    static public function &getStructure(Horde_Injector $injector)
+    static protected function setupSchema(Horde_Injector $injector)
     {
-        $driver = 'Horde_Kolab_Server_Structure_Kolab';
-        $params = array();
+        $injector->bindImplementation(
+            'Horde_Kolab_Server_Schema',
+            'Horde_Kolab_Server_Schema_Base'
+        );
+    }
 
-        try {
-            $config = $injector->getInstance('Horde_Kolab_Server_Structure_Config');
+    /**
+     * Provide a cache handler for Horde_Kolab_Server.
+     *
+     * @param Horde_Injector $injector The object providing our dependencies.
+     * @param mixed          $instance The cache handler or empty if it
+     *                                 should be created.
+     *
+     * @return NULL
+     */
+    static protected function setupCache(
+        Horde_Injector $injector,
+        $instance = null
+    ) {
+        if (empty($instance)) {
+            $instance = new Horde_Cache_Null();
+        }
+        $injector->setInstance('Horde_Kolab_Server_Cache', $instance);
+    }
 
-            if (isset($config->driver)) {
-                $driver = $config->driver;
-            }
-            if (isset($config->params)) {
-                $params = $config->params;
+    /**
+     * Provide a log handler for Horde_Kolab_Server.
+     *
+     * @param Horde_Injector $injector The object providing our dependencies.
+     * @param mixed          $instance The log handler or empty if it
+     *                                 should be created.
+     *
+     * @return NULL
+     */
+    static protected function setupLogger(
+        Horde_Injector $injector,
+        $instance = null
+    ) {
+        if (empty($instance)) {
+            $instance = new Horde_Log_Logger(new Horde_Log_Handler_Null());
+        }
+        $injector->setInstance('Horde_Kolab_Server_Logger', $instance);
+    }
+
+        if (empty($this->_ldap_read)) {
+            $this->handleError(
+                Net_LDAP2::checkLDAPExtension(),
+                Horde_Kolab_Server_Exception::MISSING_LDAP_EXTENSION
+            );
+
+            $this->_ldap_read = new Net_LDAP2($this->params);
+
+            if (isset($this->params['host_master'])
+                && $this->params['host_master'] == $this->params['host']
+            ) {
+
+                $params         = $this->params;
+                $params['host'] = $this->params['host_master'];
+
+                $this->_ldap_write = new Net_LDAP2($params);
+            } else {
+                $this->_ldap_write = $this->_ldap_read;
             }
-        } catch (ReflectionException $e) {
         }
 
-        //@todo: either we use driver names or real class names.
-        //$class = 'Horde_Kolab_Server_Structure_' . ucfirst(basename($driver));
-        if (!class_exists($driver)) {
-            throw new Horde_Kolab_Server_Exception('Structure type definition "' . $driver . '" missing.');
+        if ($write) {
+            return $this->_ldap_write;
+        } else {
+            return $this->_ldap_read;
+        }
+
+    /**
+     * Setup the machinery to create a Horde_Kolab_Server.
+     *
+     * @param array          $configuration The parameters required to create
+     *                                      the desired Horde_Kolab_Server.
+     * @param Horde_Injector $injector      The object providing our dependencies.
+     *
+     * @return NULL
+     */
+    static protected function setupServer(
+        array $configuration,
+        Horde_Injector $injector
+    ) {
+        if (empty($configuration['driver'])) {
+            $configuration['driver'] = 'Horde_Kolab_Server_Ldap';
+        }
+         
+        $config = new stdClass;
+
+        switch (ucfirst(strtolower($configuration['driver']))) {
+        case 'Ldap':
+        case 'Test':
+        case 'File':
+            $config->driver = 'Horde_Kolab_Server_'
+                . ucfirst(strtolower($configuration['driver']));
+            break;
+        default:
+            $config->driver = $configuration['driver'];
+            break;
+        }
+
+        $config->params = isset($configuration['params'])
+            ? $configuration['params'] : array();
+
+        $injector->setInstance('Horde_Kolab_Server_Config', $config);
+
+        $injector->bindFactory(
+            'Horde_Kolab_Server',
+            'Horde_Kolab_Server_Factory',
+            'getServer'
+        );
+    }
+
+    /**
+     * Attempts to return a concrete Horde_Kolab_Server instance.
+     *
+     * @param Horde_Injector $injector The object providing our dependencies.
+     *
+     * @return Horde_Kolab_Server The newly created concrete Horde_Kolab_Server
+     *                            instance.
+     */
+    static public function &getServer(Horde_Injector $injector)
+    {
+        $config = $injector->getInstance('Horde_Kolab_Server_Config');
+        $driver = $config->driver;
+        $server = new $driver(
+            $injector->getInstance('Horde_Kolab_Server_Objects'),
+            $injector->getInstance('Horde_Kolab_Server_Structure'),
+            $injector->getInstance('Horde_Kolab_Server_Search'),
+            $injector->getInstance('Horde_Kolab_Server_Schema')
+        );
+        $server->setParams($config->params);
+        $server->setCache($injector->getInstance('Horde_Kolab_Server_Cache'));
+        $server->setLogger($injector->getInstance('Horde_Kolab_Server_Logger'));
+
+        return $server;
+    }
+
+    /**
+     * 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:
+     * <code>
+     *   $var = &Horde_Kolab_Server::singleton()
+     * </code>
+     *
+     * @param array $params An array of parameters.
+     *
+     * @return Horde_Kolab_Server The concrete Horde_Kolab_Server reference.
+     */
+    static public function &singleton($params = array())
+    {
+        $signature = hash('md5', serialize(ksort($params)));
+
+        if (!isset(self::$_instances[$signature])) {
+            $params['cache'] = Horde_Cache::singleton(
+                $GLOBALS['conf']['cache']['driver'],
+               //@todo: Can we omit Horde:: here?
+                Horde::getDriverConfig(
+                    'cache',
+                    $GLOBALS['conf']['cache']['driver']
+                )
+            );
+            $params['logger'] = Horde::getLogger();
+            $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+            self::setup($params, $injector);
+            self::$_instances[$signature] = $injector->getInstance(
+                'Horde_Kolab_Server'
+            );
         }
-        $structure = new $driver($params);
-        return $structure;
+
+        return self::$_instances[$signature];
     }
 }
\ No newline at end of file
index 5cb63b7..ba69a17 100644 (file)
@@ -37,21 +37,33 @@ class Horde_Kolab_Server_File extends Horde_Kolab_Server_Test
     private $_file;
 
     /**
-     * Construct a new Horde_Kolab_Server object.
+     * Set configuration parameters.
      *
-     * @param array $params Parameter array.
+     * @param array $params The parameters.
+     *
+     * @return NULL
      */
-    public function __construct(Horde_Kolab_Server_Structure $structure,
-                                $params = array())
+    public function setParams(array $params)
     {
         if (isset($params['file'])) {
             $this->_file = $params['file'];
-        } else {
-            throw new Horde_Kolab_Server_Exception('The file based driver requires a \'file\' parameter.');
         }
-        parent::__construct($structure, $params);
+
+        parent::setParams($params);
     }
 
+    /**
+     * Get the file parameter.
+     *
+     * @return NULL
+     */
+    private function _getFile()
+    {
+        if (empty($this->_file)) {
+            throw new Horde_Kolab_Server_Exception('The file based driver requires a \'file\' parameter.');
+        }
+        return $this->_file;
+    }
     
     /**
      * Load the current state of the database.
@@ -60,7 +72,7 @@ class Horde_Kolab_Server_File extends Horde_Kolab_Server_Test
      */
     protected function load()
     {
-        $raw_data = file_get_contents($this->_file);
+        $raw_data = file_get_contents($this->_getFile());
         if (!$raw_data === false) {
             $data = @unserialize($raw_data);
             if ($data !== false) {
@@ -69,7 +81,7 @@ class Horde_Kolab_Server_File extends Horde_Kolab_Server_Test
                 $error = error_get_last();
                 if (isset($this->logger)) {
                     $this->logger->warn(sprintf('Horde_Kolab_Server_file failed to read the database from %s. Error was: %s',
-                                                $this->_file, $error['message']));
+                                                $this->_getFile(), $error['message']));
                 }
                 $this->data = array();
             }
@@ -84,12 +96,12 @@ class Horde_Kolab_Server_File extends Horde_Kolab_Server_Test
     protected function store()
     {
         $raw_data = serialize($this->data);
-        $result = @file_put_contents($this->_file, $raw_data);
+        $result = @file_put_contents($this->_getFile(), $raw_data);
         if ($result === false) {
             $error = error_get_last();
             if (isset($this->logger)) {
                 $this->logger->warn(sprintf('Horde_Kolab_Server_file failed to store the database in %s. Error was: %s',
-                                            $this->_file,  $error['message']));
+                                            $this->_getFile(),  $error['message']));
             }
         }
     }
@@ -101,7 +113,7 @@ class Horde_Kolab_Server_File extends Horde_Kolab_Server_Test
      */
     public function clean()
     {
-        unlink($this->_file);
+        unlink($this->_getFile());
         $this->data = array();
         $this->store();
     }
@@ -111,8 +123,8 @@ class Horde_Kolab_Server_File extends Horde_Kolab_Server_Test
      *
      * @return string The path to the database.
      */
-    public function getStoragePAth()
+    public function getStoragePath()
     {
-        return $this->_file;
+        return $this->_getFile();
     }
 }
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Filtered.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Filtered.php
new file mode 100644 (file)
index 0000000..ab77cbe
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+/**
+ * The driver for accessing objects stored in a filtered LDAP.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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 objects stored in
+ * a filtered LDAP db.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Filtered extends Horde_Kolab_Server_Ldap
+{
+    /**
+     * A global filter to add to each query.
+     *
+     * @var string
+     */
+    private $_filter;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Kolab_Server_Connection $connection The LDAP connection.
+     * @param string                        $base_dn    The LDAP server base DN.
+     * @param string                        $filter     A global filter to add
+     *                                                  to all queries.
+     */
+    public function __construct(
+        Horde_Kolab_Server_Connection $connection,
+        $base_dn,
+        $filter = null
+    ) {
+        parent::__construct($connection, $base_dn);
+        $this->_filter  = $filter;
+    }
+
+    /**
+     * Finds all object data below a parent matching a given set of criteria.
+     *
+     * @param array  $criteria The criteria for the search.
+     * @param string $parent   The parent to search below.
+     * @param array  $params   Additional search parameters.
+     *
+     * @return Horde_Kolab_Server_Result The result object.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function findBelow(array $criteria, $parent, array $params = array())
+    {
+        $query = new Horde_Kolab_Server_Query_Ldap($criteria);
+        $query_string = (string) $query;
+        if (!empty($this->_filter)) {
+            $query_string = '(&(' . $this->_filter . ')' . $query_string . ')';
+        }
+        return $this->_search($query_string, $params, $parent);
+    }
+}
index 61aad37..8294e2e 100644 (file)
  * @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
+abstract class Horde_Kolab_Server_Ldap implements Horde_Kolab_Server
 {
     /**
-     * LDAP connection handle.
+     * The GUID of the current user.
      *
-     * @var Net_LDAP2
+     * @var string
      */
-    private $_ldap;
+    private $_guid;
 
     /**
-     * The configuration for connection to the LDAP server.
+     * LDAP connection handle.
      *
-     * @var array
+     * @var Horde_Kolab_Server_Connection
      */
-    private $_config;
+    private $_conn;
 
     /**
      * Base DN of the LDAP server.
@@ -50,449 +50,242 @@ class Horde_Kolab_Server_Ldap extends Horde_Kolab_Server
     private $_base_dn;
 
     /**
-     * The LDAP schemas.
-     *
-     * @var Net_LDAP2_Schema
-     */
-    private $_schema;
-
-    /**
-     * The last search result. This can be used to retrieve additional
-     * information about the LDAP search result (e.g. if the size
-     * limit for the search has been exceeded).
-     *
-     * @var Net_LDAP2_Search
-     */
-    public $lastSearch;
-
-    /**
-     * Construct a new Horde_Kolab_Server_ldap object.
+     * Constructor.
      *
-     * @param array $params Parameter array.
+     * @param Horde_Kolab_Server_Connection $connection The LDAP connection.
+     * @param string                        $base_dn    The LDAP server base DN.
+     * @param string                        $filter     A global filter to add
+     *                                                  to all queries.
      */
-    public function __construct(Horde_Kolab_Server_Structure $structure,
-                                $params = array())
-    {
-        if (!isset($params['charset'])) {
-            $params['charset'] = 'UTF-8';
-        }
-
-        $base_config = array('host'           => 'localhost',
-                             'port'           => 389,
-                             'version'        => 3,
-                             'starttls'       => false,
-                             'uid'            => '',
-                             'pass'           => '',
-                             'basedn'         => '',
-                             'charset'        => '',
-                             'options'        => array(),
-                             'auto_reconnect' => true);
-
-        $config = array_merge($base_config, $params);
-
-        $this->_base_dn = $config['basedn'];
-
-        $config['binddn'] = $config['uid'];
-        $config['bindpw'] = $config['pass'];
-
-        $this->_config = $config;
-
-        $this->connect();
-
-        parent::__construct($structure, $params);
+    public function __construct(
+        Horde_Kolab_Server_Connection $connection,
+        $base_dn,
+        $filter = null
+    ) {
+        $this->_conn    = $connection;
+        $this->_base_dn = $base_dn;
     }
 
-    
     /**
-     * Connect to the LDAP server.
+     * Connect to the server.
+     *
+     * @param string $guid The global unique id of the user.
+     * @param string $pass The password.
      *
      * @return NULL.
      *
      * @throws Horde_Kolab_Server_Exception If the connection failed.
      */
-    protected function connect()
+    public function connectGuid($guid = null, $pass = null)
     {
-        $this->_ldap = Net_LDAP2::connect($this->_config);
-        if (is_a($this->_ldap, 'PEAR_Error')) {
-            throw new Horde_Kolab_Server_Exception($this->_ldap,
-                                                   Horde_Kolab_Server_Exception::SYSTEM);
+        /** Do we need to switch the user? */
+        if ((empty($guid) && empty($this->_guid))
+            || $guid !== $this->_guid
+        ) {
+            $this->_handleError(
+                $this->_conn->getRead()->bind($guid, $pass),
+                Horde_Kolab_Server_Exception::BIND_FAILED
+            );
+            $this->_guid = $guid;
         }
     }
 
     /**
-     * Map attributes defined within this library their their real world
-     * counterparts.
-     *
-     * @param array $data The data that has been read and needs to be mapped.
-     *
-     * @return NULL
-     */
-    protected function unmapAttributes(&$data)
-    {
-        if (!empty($this->params['map'])) {
-            foreach ($this->params['map'] as $attribute => $map) {
-                if (isset($data[$map])) {
-                    $data[$attribute] = $data[$map];
-                    unset($data[$map]);
-                }
-            }
-        }
-    }
-
-    /**
-     * Map attributes defined within this library into their real world
-     * counterparts.
-     *
-     * @param array $data The data to be written.
+     * Low level access to reading object data.
      *
-     * @return NULL
-     */
-    protected function mapAttributes(&$data)
-    {
-        if (!empty($this->params['map'])) {
-            foreach ($this->params['map'] as $attribute => $map) {
-                if (isset($data[$attribute])) {
-                    $data[$map] = $data[$attribute];
-                    unset($data[$attribute]);
-                }
-            }
-        }
-    }
-
-    /**
-     * Map attribute keys defined within this library into their real world
-     * counterparts.
+     * @param string $guid The object to retrieve.
      *
-     * @param array $keys The attribute keys.
+     * @return array An array of attributes.
      *
-     * @return NULL
+     * @throws Horde_Kolab_Server_Exception If the search operation hit an error
+     *                                      or returned no result.
      */
-    protected function mapKeys(&$keys)
+    public function read($guid)
     {
-        if (!empty($this->params['map'])) {
-            foreach ($this->params['map'] as $attribute => $map) {
-                $key = array_search($attribute, $keys);
-                if ($key !== false) {
-                    $keys[$key] = $map;
-                }
-            }
-        }
-    }
-
-    /**
-     * Map a single attribute key defined within this library into its real
-     * world counterpart.
-     *
-     * @param array $field The attribute name.
-     *
-     * @return The real name of this attribute on the server we connect to.
-     */
-    protected function mapField($field)
-    {
-        if (!empty($this->params['map'])
-            && isset($this->params['map'][$field])) {
-            return $this->params['map'][$field];
-        }
-        return $field;
+        $params = array('scope' => 'base');
+        $data = $this->_search(null, $params, $guid)->asArray();
+        if ($data->count() == 0) {
+            throw new Horde_Kolab_Server_Exception(
+                'Empty result!',
+                Horde_Kolab_Server_Exception::EMPTY_RESULT
+            );
+        }            
+        return array_pop($data->asArray());
     }
 
     /**
-     * Low level access to reading object data.
-     *
-     * This function provides fast access to the Server data.
-     *
-     * Usually you should use
-     *
-     * $object = $server->fetch('a server uid');
-     * $variable = $object['attribute']
-     *
-     * to access object attributes. This is slower but takes special object
-     * handling into account (e.g. custom attribute parsing).
+     * Low level access to reading some object attributes.
      *
-     * @param string $uid   The object to retrieve.
+     * @param string $guid  The object to retrieve.
      * @param string $attrs Restrict to these attributes.
      *
-     * @return array|boolean An array of attributes or false if the specified
-     *                       object was not found.
+     * @return array An array of attributes.
      *
-     * @throws Horde_Kolab_Server_Exception If the search operation retrieved a
-     *                                      problematic result.
+     * @throws Horde_Kolab_Server_Exception If the search operation hit an error
+     *                                      or returned no result.
      */
-    public function read($uid, $attrs = null)
+    public function readAttributes($guid, array $attrs)
     {
-        $params = array('scope' => 'base');
-        if (!empty($attrs)) {
-            $params['attributes'] = $attrs;
-        }
-
-        $data = $this->search(null, $params, $uid);
-        if (empty($data)) {
-            throw new Horde_Kolab_Server_Exception(_("Empty result!"),
-                                                   Horde_Kolab_Server_Exception::EMPTY_RESULT);
+        $params = array(
+            'scope' => 'base',
+            'attributes' => $attrs
+        );
+        $data = $this->_search(null, $params, $guid);
+        if ($data->count() == 0) {
+            throw new Horde_Kolab_Server_Exception(
+                'Empty result!',
+                Horde_Kolab_Server_Exception::EMPTY_RESULT
+            );
         }            
-
-        return array_pop($data);
+        return array_pop($data->asArray());
     }
 
     /**
-     * Save an object.
+     * Finds object data matching a given set of criteria.
+     *
+     * @param Horde_Kolab_Server_Query $query  The criteria for the search.
+     * @param array                    $params Additional search parameters.
      *
-     * @param string  $uid     The UID of the object to be added.
-     * @param array   $data    The attributes of the object to be changed.
-     * @param boolean $exists  Does the object already exist on the server?
+     * @return Horde_Kolab_Server_Result The result object.
      *
-     * @return boolean  True if saving succeeded.
+     * @throws Horde_Kolab_Server_Exception
      */
-    public function save($uid, $data, $exists = false)
-    {
-        $this->mapAttributes($data);
-
-        if ($exists === false) {
-            $entry  = Net_LDAP2_Entry::createFresh($uid, $data['add']);
-            $result = $this->_ldap->add($entry);
-            if ($result instanceOf PEAR_Error) {
-                throw new Horde_Kolab_Server_Exception($result,
-                                                       Horde_Kolab_Server_Exception::SYSTEM);
-            }
-        } else {
-            $entry  = $this->_ldap->getEntry($uid, $data['attributes']);
-            $result = $this->_ldap->modify($entry, $data);
-            if ($result instanceOf PEAR_Error) {
-                throw new Horde_Kolab_Server_Exception($result,
-                                                       Horde_Kolab_Server_Exception::SYSTEM);
-            }
-        }
-        if (isset($this->logger)) {
-            $this->logger->debug(sprintf('The object \"%s\" has been successfully saved!',
-                                         $uid));
-        }
-        return true;
+    public function find(
+        Horde_Kolab_Server_Query $query,
+        array $params = array()
+    ) {
+        $this->findBelow($query, $this->_base_dn, $params);
     }
 
     /**
-     * Delete an object.
+     * Finds all object data below a parent matching a given set of criteria.
      *
-     * @param string $uid The UID of the object to be deleted.
+     * @param Horde_Kolab_Server_Query $query  The criteria for the search.
+     * @param string                   $parent The parent to search below.
+     * @param array                    $params Additional search parameters.
      *
-     * @return boolean True if saving succeeded.
+     * @return Horde_Kolab_Server_Result The result object.
      *
      * @throws Horde_Kolab_Server_Exception
      */
-    public function delete($uid)
-    {
-        $result = $this->_ldap->delete($uid);
-        if ($result instanceOf PEAR_Error) {
-            throw new Horde_Kolab_Server_Exception($result,
-                                                   Horde_Kolab_Server_Exception::SYSTEM);
-        }
-        if (isset($this->logger)) {
-            $this->logger(sprintf('The object \"%s\" has been successfully deleted!',
-                                  $uid));
-        }
-        return true;
-    }
+    abstract public function findBelow(
+        Horde_Kolab_Server_Query $query,
+        $parent,
+        array $params = array()
+    );
 
     /**
-     * Rename an object.
+     * Modify existing object data.
      *
-     * @param string $uid The UID of the object to be renamed.
-     * @param string $new The new UID of the object.
+     * @param string $guid The GUID of the object to be added.
+     * @param array  $data The attributes of the object to be stored.
      *
-     * @return boolean True if renaming succeeded.
+     * @return NULL
      *
      * @throws Horde_Kolab_Server_Exception
      */
-    public function rename($uid, $new)
+    public function save($guid, array $data)
     {
-        /* Net_LDAP modifies the variable */
-        $old = $uid;
-        $result = $this->_ldap->move($old, $new);
-        if ($result instanceOf PEAR_Error) {
-            throw new Horde_Kolab_Server_Exception($result,
-                                                   Horde_Kolab_Server_Exception::SYSTEM);
-        }
-        if (isset($this->logger)) {
-            $this->logger->debug(sprintf('The object \"%s\" has been successfully renamed to \"%s\"!',
-                                         $uid, $new));
-        }
-        return true;
+        $entry  = $this->_conn->getWrite()->getEntry($guid, $data['attributes']);
+        $this->_handleError(
+            $this->_conn->getWrite()->modify($entry, $data),
+            Horde_Kolab_Server_Exception::SYSTEM
+        );
     }
 
     /**
-     * List all objects of a specific type.
+     * Add new object data.
      *
-     * @param string $type   The type of the objects to be listed
-     * @param array  $params Additional parameters.
+     * @param string $guid The GUID of the object to be added.
+     * @param array  $data The attributes of the object to be added.
      *
-     * @return array An array of Kolab objects.
+     * @return NULL
      *
      * @throws Horde_Kolab_Server_Exception
-     *
-     * @todo Sorting
      */
-    public function listObjects($type, $params = null)
+    public function add($guid, array $data)
     {
-        if (empty($params['base_dn'])) {
-            $base = $this->_base_dn;
-        } else {
-            $base = $params['base_dn'];
-        }
-
-        $result   = Horde_Kolab_Server_Object::loadClass($type);
-        $vars     = get_class_vars($type);
-        $criteria = call_user_func(array($type, 'getFilter'));
-        $filter   = $this->searchQuery($criteria);
-        $sort     = $vars['sort_by'];
-
-        if (isset($params['sort'])) {
-            $sort = $params['sort'];
-        }
-
-        $options = array('scope' => 'sub');
-        if (isset($params['attributes'])) {
-            $options['attributes'] = $params['attributes'];
-        } else {
-            $options['attributes'] = $this->getAttributes($type);
-        }
-
-        $data = $this->search($filter, $options, $base);
-        if (empty($data)) {
-            return array();
-        }
-
-        if ($sort) {
-            /* FIXME */
-            /* $data = $result->as_sorted_struct(); */
-            /*$this->sort($result, $sort); */
-        }
-
-        if (isset($params['from'])) {
-            $from = $params['from'];
-        } else {
-            $from = -1;
-        }
-
-        if (isset($params['to'])) {
-            $sort = $params['to'];
-        } else {
-            $to = -1;
-        }
-
-        if (!empty($vars['required_group'])) {
-            $required_group = new Horde_Kolab_Server_Object_Kolabgroupofnames($this,
-                                                                              null,
-                                                                              $vars['required_group']);
-        }
-
-        $objects = array();
-        foreach ($data as $uid => $entry) {
-            if (!empty($vars['required_group'])) {
-                if (!$required_group->exists() || !$required_group->isMember($uid)) {
-                    continue;
-                }
-            }
-            $objects[$uid] = &Horde_Kolab_Server_Object::factory($type, $uid,
-                                                                 $this, $entry);
-        }
-        return $objects;
+        $entry  = Net_LDAP2_Entry::createFresh($guid, $data);
+        $this->_handleError($entry, Horde_Kolab_Server_Exception::SYSTEM);
+        $this->_handleError(
+            $this->_conn->getWrite()->add($entry),
+            Horde_Kolab_Server_Exception::SYSTEM
+        );
     }
 
     /**
-     * Return the root of the UID values on this server.
+     * Delete an object.
      *
-     * @return string The base UID on this server (base DN on ldap).
-     */
-    public function getBaseUid()
-    {
-        return $this->_base_dn;
-    }
-
-    /**
-     * Return the ldap schema.
+     * @param string $guid The UID of the object to be deleted.
      *
-     * @return Net_LDAP2_Schema The LDAP schema.
+     * @return NULL
      *
-     * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
+     * @throws Horde_Kolab_Server_Exception
      */
-    private function _getSchema()
+    public function delete($guid)
     {
-        if (!isset($this->_schema)) {
-            $result = $this->_ldap->schema();
-            if ($result instanceOf PEAR_Error) {
-                throw new Horde_Kolab_Server_Exception($result,
-                                                       Horde_Kolab_Server_Exception::SYSTEM);
-            }
-            $this->_schema = &$result;
-        }
-        return $this->_schema;
+        $this->_handleError(
+            $this->_conn->getWrite()->delete($guid),
+            Horde_Kolab_Server_Exception::SYSTEM
+        );
     }
 
     /**
-     * Return the schema for the given objectClass.
+     * Rename an object.
      *
-     * @param string $objectclass Fetch the schema for this objectClass.
+     * @param string $guid The UID of the object to be renamed.
+     * @param string $new  The new UID of the object.
      *
-     * @return array The schema for the given objectClass.
+     * @return NULL
      *
-     * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
+     * @throws Horde_Kolab_Server_Exception
      */
-    protected function getObjectclassSchema($objectclass)
+    public function rename($guid, $new)
     {
-        if (!empty($this->_config['schema_support'])) {
-            $schema = $this->_getSchema();
-            $info = $schema->get('objectclass', $objectclass);
-            if ($info instanceOf PEAR_Error) {
-                throw new Horde_Kolab_Server_Exception($info,
-                                                       Horde_Kolab_Server_Exception::SYSTEM);
-            }
-            return $info;
-        }
-        return parent::getObjectclassSchema($objectclass);
+        $this->_handleError(
+            $this->_conn->getWrite()->move($old, $new),
+            Horde_Kolab_Server_Exception::SYSTEM
+        );
     }
 
     /**
-     * Return the schema for the given attribute.
-     *
-     * @param string $attribute Fetch the schema for this attribute.
+     * Return the ldap schema.
      *
-     * @return array The schema for the given attribute.
+     * @return Net_LDAP2_Schema The LDAP schema.
      *
      * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
      */
-    protected function getAttributeSchema($attribute)
+    public function getSchema()
     {
-        if (!empty($this->_config['schema_support'])) {
-            $schema = $this->_getSchema();
-            $info = $schema->get('attribute', $attribute);
-            if ($info instanceOf PEAR_Error) {
-                throw new Horde_Kolab_Server_Exception($info,
-                                                       Horde_Kolab_Server_Exception::SYSTEM);
-            }
-            return $info;
-        }
-        return parent::getAttributeSchema($attribute);
+        $result = $this->_conn->getRead()->schema();
+        $this->_handleError($result, Horde_Kolab_Server_Exception::SYSTEM);
+        return $result;
     }
 
     /**
-     * Find object data matching a given set of criteria.
+     * Check for a PEAR Error and convert it to an exception if necessary.
      *
-     * @param array  $criteria The criteria for the search.
-     * @param string $params   Additional search parameters.
+     * @param mixed $result The result to be tested.
+     * @param code  $code   The error code to use in case the result is an error.
      *
-     * @return array The result array.
+     * @return NULL.
      *
-     * @throws Horde_Kolab_Server_Exception
+     * @throws Horde_Kolab_Server_Exception If the connection failed.
      */
-    public function find($criteria, $params = array())
-    {
-        return $this->search($this->searchQuery($criteria), $params);
+    private function _handleError(
+        $result,
+        $code = Horde_Kolab_Server_Exception::SYSTEM
+    ) {
+        if (is_a($result, 'PEAR_Error')) {
+            throw new Horde_Kolab_Server_Exception($result, $code);
+        }
     }
 
     /**
      * Search for object data.
      *
      * @param string $filter The LDAP search filter.
-     * @param string $params Additional search parameters.
+     * @param array  $params Additional search parameters.
      * @param string $base   The search base
      *
      * @return array The result array.
@@ -500,153 +293,14 @@ class Horde_Kolab_Server_Ldap extends Horde_Kolab_Server
      * @throws Horde_Kolab_Server_Exception If the search operation encountered
      *                                      a problem.
      */
-    public function search($filter = null, $params = array(), $base = null)
-    {
-        if (isset($params['attributes'])) {
-            $this->mapKeys($params['attributes']);
-        }
-
-        if (!isset($base)) {
-            $base = $this->_base_dn;
-        }
-        $this->lastSearch = &$this->_ldap->search($base, $filter, $params);
-        if (is_a($this->lastSearch, 'PEAR_Error')) {
-            throw new Horde_Kolab_Server_Exception($this->lastSearch,
-                                                   Horde_Kolab_Server_Exception::SYSTEM);
-        }
-        $data = $this->lastSearch->as_struct();
-        if (is_a($data, 'PEAR_Error')) {
-            throw new Horde_Kolab_Server_Exception($data,
-                                                   Horde_Kolab_Server_Exception::SYSTEM);
-        }
-        $this->unmapAttributes($data);
-        return $data;
-    }
-
-    /**
-     * Get the LDAP object classes for the given DN.
-     *
-     * @param string $uid DN of the object.
-     *
-     * @return array An array of object classes.
-     *
-     * @throws Horde_Kolab_Server_Exception If the object has no
-     *                                      object classes.
-     */
-    public function getObjectClasses($uid)
-    {
-        $object = $this->read($uid, array(Horde_Kolab_Server_Object::ATTRIBUTE_OC));
-        if (!isset($object[Horde_Kolab_Server_Object::ATTRIBUTE_OC])) {
-            throw new Horde_Kolab_Server_Exception(sprintf("The object %s has no %s attribute!",
-                                                           $uid, Horde_Kolab_Server_Object::ATTRIBUTE_OC),
-                                                   Horde_Kolab_Server_Exception::SYSTEM);
-        }
-        $result = array_map('strtolower',
-                            $object[Horde_Kolab_Server_Object::ATTRIBUTE_OC]);
-        return $result;
-    }
-
-    /**
-     * Build a search query.
-     *
-     * Taken from the Turba LDAP driver.
-     *
-     * @param array $criteria The array of criteria.
-     *
-     * @return string  An LDAP query filter.
-     */
-    public function searchQuery($criteria)
-    {
-        /* Accept everything. */
-        $filter = '(' . strtolower(Horde_Kolab_Server_Object::ATTRIBUTE_OC) . '=*)';
-
-        /* Build the LDAP filter. */
-        if (count($criteria)) {
-            $f = $this->buildSearchQuery($criteria);
-            if ($f instanceOf Net_LDAP2_Filter) {
-                $filter = $f->asString();
-            }
-        }
-
-        /* Add source-wide filters, which are _always_ AND-ed. */
-        if (!empty($this->params['filter'])) {
-            $filter = '(&' . '(' . $this->params['filter'] . ')' . $filter . ')';
-        }
-        return $filter;
-    }
-
-    /**
-     * Build a piece of a search query.
-     *
-     * Taken from the Turba LDAP driver.
-     *
-     * @param array $criteria The array of criteria.
-     *
-     * @return string  An LDAP query fragment.
-     */
-    protected function &buildSearchQuery($criteria)
+    private function _search($filter, array $params, $base)
     {
-        if (!is_array($criteria)) {
-            throw new Horde_Kolab_Server_Exception(sprintf("Invalid search criteria \"%s\"!",
-                                                           $criteria),
-                                                   Horde_Kolab_Server_Exception::SYSTEM);
-        }
-        if (isset($criteria['field'])) {
-            $rhs     = isset($criteria['test']) ? $criteria['test'] : '';
-            /* Keep this in for reference as we did not really test servers with different encoding yet */
-            // require_once 'Horde/Nls.php';
-            //$rhs     = Horde_String::convertCharset($criteria['test'], Horde_Nls::getCharset(), $this->params['charset']);
-            switch ($criteria['op']) {
-            case '=':
-                $op = 'equals';
-                break;
-            default:
-                $op = $criteria['op'];
-            }
-            return Net_LDAP2_Filter::create($this->mapField($criteria['field']),
-                                            $op, $rhs);
-        }
-        foreach ($criteria as $key => $vals) {
-            if (!empty($vals['OR'])
-                || !empty($vals['AND'])
-                || !empty($vals['NOT'])) {
-                $parts = $this->buildSearchQuery($vals);
-                if (count($parts) > 1) {
-                    if (!empty($vals['OR'])) {
-                        $operator = '|';
-                    } else if (!empty($vals['NOT'])) {
-                        $operator = '!';
-                    } else {
-                        $operator = '&';
-                    }
-                    return Net_LDAP2_Filter::combine($operator, $parts);
-                } else {
-                    return $parts[0];
-                }
-            } else {
-                $parts = array();
-                foreach ($vals as $test) {
-                    $parts[] = &$this->buildSearchQuery($test);
-                }
-                switch ($key) {
-                case 'OR':
-                    $operator = '|';
-                    break;
-                case 'AND':
-                    $operator = '&';
-                    break;
-                case 'NOT':
-                    $operator = '!';
-                    break;
-                }
-                if (count($parts) > 1) {
-                    return Net_LDAP2_Filter::combine($operator, $parts);
-                } else if ($operator == '!') {
-                    return Net_LDAP2_Filter::combine($operator, $parts[0]);
-                } else {
-                    return $parts[0];
-                }
-            }
-        }
+        $this->_lastSearch = &$this->_conn->getRead()->search(
+            $base, $filter, $params
+        );
+        $this->_handleError(
+            $this->_lastSearch, Horde_Kolab_Server_Exception::SYSTEM
+        );
+        return new Horde_Kolab_Server_Result_Ldap($this->_lastSearch);
     }
 }
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/List.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/List.php
new file mode 100644 (file)
index 0000000..7a9ad87
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Interface for server lists.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Interface for server lists.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+interface Horde_Kolab_Server_List
+{
+    /**
+     * 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 An array of Kolab objects.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     *
+     * @todo Sorting
+     */
+    public function listObjects($type, $params = null);
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/List/Base.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/List/Base.php
new file mode 100644 (file)
index 0000000..2c57856
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+ * A server list implementation.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * A server list implementation.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_List_Base implements Horde_Kolab_Server_List
+{
+    /**
+     * 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 An array of Kolab objects.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     *
+     * @todo Sorting
+     */
+    public function listObjects($type, $params = null)
+    {
+        $result   = Horde_Kolab_Server_Object::loadClass($type);
+        $vars     = get_class_vars($type);
+        $criteria = call_user_func(array($type, 'getFilter'));
+        $filter   = $this->searchQuery($criteria);
+        $sort     = $vars['sort_by'];
+
+        if (isset($params['sort'])) {
+            $sort = $params['sort'];
+        }
+
+        $options = array('scope' => 'sub');
+        if (isset($params['attributes'])) {
+            $options['attributes'] = $params['attributes'];
+        } else {
+            $options['attributes'] = $this->getAttributes($type);
+        }
+
+        $data = $this->search($filter, $options, $base);
+        if (empty($data)) {
+            return array();
+        }
+
+        if ($sort) {
+            /* FIXME */
+            /* $data = $result->as_sorted_struct(); */
+            /*$this->sort($result, $sort); */
+        }
+
+        if (isset($params['from'])) {
+            $from = $params['from'];
+        } else {
+            $from = -1;
+        }
+
+        if (isset($params['to'])) {
+            $sort = $params['to'];
+        } else {
+            $to = -1;
+        }
+
+        if (!empty($vars['required_group'])) {
+            $required_group = new Horde_Kolab_Server_Object_Kolabgroupofnames(
+                $this,
+                null,
+                $vars['required_group']
+            );
+        }
+
+        $objects = array();
+        foreach ($data as $uid => $entry) {
+            if (!empty($vars['required_group'])) {
+                if (!$required_group->exists() || !$required_group->isMember($uid)) {
+                    continue;
+                }
+            }
+            $objects[$uid] = &Horde_Kolab_Server_Object::factory(
+                $type, $uid, $this, $entry
+            );
+        }
+        return $objects;
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Logged.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Logged.php
new file mode 100644 (file)
index 0000000..072642b
--- /dev/null
@@ -0,0 +1,227 @@
+<?php
+/**
+ * A server delegation that logs server access via Horde_Log_Logger.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * A server delegation that logs server access via Horde_Log_Logger.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Logged implements Horde_Kolab_Server
+{
+    /**
+     * The server we delegate to.
+     *
+     * @var Horde_Kolab_Server
+     */
+    private $_server;
+
+    /**
+     * The log handler.
+     *
+     * @var Horde_Log_Logger
+     */
+    private $_logger;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Kolab_Server $server The base server connection.
+     * @param Horde_Log_Logger   $logger THe log handler.
+     */
+    public function __construct(
+        Horde_Kolab_Server $server,
+        Horde_Log_Logger $logger
+    ) {
+        $this->_server = $server;
+        $this->_logger = $logger;
+    }
+
+    /**
+     * Connect to the server.
+     *
+     * @param string $guid The global unique id of the user.
+     * @param string $pass The password.
+     *
+     * @return NULL.
+     *
+     * @throws Horde_Kolab_Server_Exception If the connection failed.
+     */
+    public function connectGuid($guid = null, $pass = null)
+    {
+         $this->_server->connectGuid($guid, $pass);
+    }
+
+    /**
+     * Low level access to reading object data.
+     *
+     * @param string $guid  The object to retrieve.
+     * @param array  $attrs Restrict to these attributes.
+     *
+     * @return array An array of attributes.
+     *
+     * @throws Horde_Kolab_Server_Exception If the search operation hit an error
+     *                                      or returned no result.
+     */
+    public function read($guid, array $attrs = array())
+    {
+        return $this->_server->read($guid);
+    }
+
+    /**
+     * Low level access to reading some object attributes.
+     *
+     * @param string $guid  The object to retrieve.
+     * @param string $attrs Restrict to these attributes.
+     *
+     * @return array An array of attributes.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     *
+     * @see Horde_Kolab_Server::read
+     */
+    public function readAttributes($guid, array $attrs)
+    {
+        return $this->_server->readAttributes($guid, $attrs);
+    }
+
+    /**
+     * Finds object data matching a given set of criteria.
+     *
+     * @param Horde_Kolab_Server_Query $query  The criteria for the search.
+     * @param array                    $params Additional search parameters.
+     *
+     * @return Horde_Kolab_Server_Result The result object.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function find(
+        Horde_Kolab_Server_Query $query,
+        array $params = array()
+    ) {
+        return $this->_server->find($query, $params);
+    }
+
+    /**
+     * Finds all object data below a parent matching a given set of criteria.
+     *
+     * @param Horde_Kolab_Server_Query $query  The criteria for the search.
+     * @param string                   $parent The parent to search below.
+     * @param array                    $params Additional search parameters.
+     *
+     * @return Horde_Kolab_Server_Result The result object.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function findBelow(
+        Horde_Kolab_Server_Query $query,
+        $parent,
+        array $params = array()
+    ) {
+        return $this->_server->findBelow($query, $parent, $params);
+    }
+
+    /**
+     * Modify existing object data.
+     *
+     * @param string $guid The GUID of the object to be added.
+     * @param array  $data The attributes of the object to be added.
+     *
+     * @return NULL
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function save($guid, array $data)
+    {
+        $this->_server->save($guid, $data);
+        $this->_logger->info(
+            sprintf("The object \"%s\" has been successfully saved!", $guid)
+        );
+    }
+
+    /**
+     * Add new object data.
+     *
+     * @param string $guid The GUID of the object to be added.
+     * @param array  $data The attributes of the object to be added.
+     *
+     * @return NULL
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function add($guid, array $data)
+    {
+        $this->_server->add($guid, $data);
+        $this->_logger->info(
+            sprintf("The object \"%s\" has been successfully added!", $guid)
+        );
+    }
+
+    /**
+     * Delete an object.
+     *
+     * @param string $guid The GUID of the object to be deleted.
+     *
+     * @return NULL
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function delete($guid)
+    {
+        $this->_server->delete($guid);
+        $this->_logger->info(
+            sprintf("The object \"%s\" has been successfully deleted!", $guid)
+        );
+    }
+
+    /**
+     * Rename an object.
+     *
+     * @param string $guid The GUID of the object to be renamed.
+     * @param string $new  The new GUID of the object.
+     *
+     * @return NULL
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function rename($guid, $new)
+    {
+        $this->_server->rename($guid, $new);
+        $this->_logger->info(
+            sprintf(
+                "The object \"%s\" has been successfully renamed to \"%s\"!",
+                $guid, $new
+            )
+        );
+    }
+
+    /**
+     * Return the ldap schema.
+     *
+     * @return Net_LDAP2_Schema The LDAP schema.
+     *
+     * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
+     */
+    public function getSchema()
+    {
+        return $this->_server->getSchema();
+    }
+}
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Mapped.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Mapped.php
new file mode 100644 (file)
index 0000000..5287b59
--- /dev/null
@@ -0,0 +1,309 @@
+<?php
+/**
+ * A server delegation that maps object attributes.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * A server delegation that maps object attributes.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Mapped implements Horde_Kolab_Server
+{
+    /**
+     * The server we delegate to.
+     *
+     * @var Horde_Kolab_Server
+     */
+    private $_server;
+
+    /**
+     * The attribute mapping.
+     *
+     * @var array
+     */
+    private $_mapping;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Kolab_Server $server  The base server connection.
+     * @param array              $mapping The attribute mapping.
+     */
+    public function __construct(Horde_Kolab_Server $server, array $mapping)
+    {
+        $this->_server  = $server;
+        $this->_mapping = $mapping;
+    }
+
+    /**
+     * Connect to the server. Use this method if the user name you can provide
+     * does not match a GUID. In this case it will be required to map this user
+     * name first.
+     *
+     * @param string $user The user name.
+     * @param string $pass The password.
+     *
+     * @return NULL.
+     *
+     * @throws Horde_Kolab_Server_Exception If the connection failed.
+     */
+    public function connect($user = null, $pass = null)
+    {
+        $this->_server->connect($user, $pass);
+    }
+
+    /**
+     * Connect to the server.
+     *
+     * @param string $guid The global unique id of the user.
+     * @param string $pass The password.
+     *
+     * @return NULL.
+     *
+     * @throws Horde_Kolab_Server_Exception If the connection failed.
+     */
+    public function connectGuid($guid = null, $pass = null)
+    {
+         $this->_server->connectGuid($guid, $pass);
+    }
+
+    /**
+     * Low level access to reading object data.
+     *
+     * @param string $guid  The object to retrieve.
+     * @param array  $attrs Restrict to these attributes.
+     *
+     * @return array An array of attributes.
+     *
+     * @throws Horde_Kolab_Server_Exception If the search operation hit an error
+     *                                      or returned no result.
+     */
+    public function read($guid, array $attrs = array())
+    {
+        $data = $this->_server->read($guid);
+        $this->unmapAttributes($data);
+        return $data;
+    }
+
+    /**
+     * Low level access to reading some object attributes.
+     *
+     * @param string $guid  The object to retrieve.
+     * @param string $attrs Restrict to these attributes.
+     *
+     * @return array An array of attributes.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     *
+     * @see Horde_Kolab_Server::read
+     */
+    public function readAttributes($guid, array $attrs)
+    {
+        $this->mapKeys($attrs);
+        $data = $this->_server->readAttributes($guid, $attrs);
+        $this->unmapAttributes($data);
+        return $data;
+    }
+
+    /**
+     * Finds object data matching a given set of criteria.
+     *
+     * @param array $criteria The criteria for the search.
+     * @param array $params   Additional search parameters.
+     *
+     * @return array The result array.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function find(array $criteria, array $params = array())
+    {
+        $criteria = new Horde_Kolab_Server_Query_Element_Mapped($criteria, $this);
+        $data = $this->_server->find($criteria, $params);
+        $this->unmapAttributes($data);
+        return $data;
+    }
+
+    /**
+     * Finds all object data below a parent matching a given set of criteria.
+     *
+     * @param array  $criteria The criteria for the search.
+     * @param string $parent   The parent to search below.
+     * @param array  $params   Additional search parameters.
+     *
+     * @return array The result array.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function findBelow(array $criteria, $parent, array $params = array())
+    {
+        $criteria = new Horde_Kolab_Server_Query_Element_Mapped($criteria, $this);
+        $data = $this->_server->findBelow($criteria, $parent, $params);
+        $this->unmapAttributes($data);
+        return $data;
+    }
+
+
+    /**
+     * Modify existing object data.
+     *
+     * @param string $guid The GUID of the object to be added.
+     * @param array  $data The attributes of the object to be added.
+     *
+     * @return NULL
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function save($guid, array $data)
+    {
+        $this->mapAttributes($data);
+        $this->_server->save($guid, $data);
+    }
+
+    /**
+     * Add new object data.
+     *
+     * @param string $guid The GUID of the object to be added.
+     * @param array  $data The attributes of the object to be added.
+     *
+     * @return NULL
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function add($guid, array $data)
+    {
+        $this->mapAttributes($data);
+        $this->_server->add($guid, $data);
+    }
+
+    /**
+     * Delete an object.
+     *
+     * @param string $guid The GUID of the object to be deleted.
+     *
+     * @return NULL
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function delete($guid)
+    {
+        $this->_server->delete($guid);
+    }
+
+    /**
+     * Rename an object.
+     *
+     * @param string $guid The GUID of the object to be renamed.
+     * @param string $new  The new GUID of the object.
+     *
+     * @return NULL
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function rename($guid, $new)
+    {
+         $this->_server->rename($guid, $new);
+    }
+
+    /**
+     * Return the ldap schema.
+     *
+     * @return Net_LDAP2_Schema The LDAP schema.
+     *
+     * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
+     */
+    public function getSchema()
+    {
+        return $this->_server->getSchema();
+    }
+
+
+    /**
+     * Map attributes defined within this library to their real world
+     * counterparts.
+     *
+     * @param array &$data The data that has been read and needs to be mapped.
+     *
+     * @return NULL
+     */
+    protected function unmapAttributes(&$data)
+    {
+        foreach ($data as &$element) {
+            foreach ($this->mapping as $attribute => $map) {
+                if (isset($element[$map])) {
+                    $element[$attribute] = $element[$map];
+                    unset($element[$map]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Map attributes defined within this library into their real world
+     * counterparts.
+     *
+     * @param array &$data The data to be written.
+     *
+     * @return NULL
+     */
+    protected function mapAttributes(&$data)
+    {
+        foreach ($this->mapping as $attribute => $map) {
+            if (isset($data[$attribute])) {
+                $data[$map] = $data[$attribute];
+                unset($data[$attribute]);
+            }
+        }
+    }
+
+    /**
+     * Map attribute keys defined within this library into their real world
+     * counterparts.
+     *
+     * @param array &$keys The attribute keys.
+     *
+     * @return NULL
+     */
+    protected function mapKeys(&$keys)
+    {
+        foreach ($this->mapping as $attribute => $map) {
+            $key = array_search($attribute, $keys);
+            if ($key !== false) {
+                $keys[$key] = $map;
+            }
+        }
+    }
+
+    /**
+     * Map a single attribute key defined within this library into its real
+     * world counterpart.
+     *
+     * @param array $field The attribute name.
+     *
+     * @return The real name of this attribute on the server we connect to.
+     */
+    public function mapField($field)
+    {
+        if (isset($this->mapping[$field])) {
+            return $this->mapping[$field];
+        }
+        return $field;
+    }
+}
index c2a5a71..193c4ca 100644 (file)
@@ -916,6 +916,8 @@ class Horde_Kolab_Server_Object
      */
     public function prepareChanges($info)
     {
+        $changes = array();
+
         if (!empty($this->attributes)) {
             foreach ($info as $key => $value) {
                 if (!in_array($key, array_keys($this->attributes))) {
@@ -1020,7 +1022,7 @@ class Horde_Kolab_Server_Object
      *                                      meet the expectations.
      */
     static protected function uidFromResult($result,
-                                            $restrict = Horde_Kolab_Server::RESULT_SINGLE)
+                                            $restrict = Horde_Kolab_Server_Object::RESULT_SINGLE)
     {
         if (empty($result)) {
             return false;
@@ -1053,7 +1055,7 @@ class Horde_Kolab_Server_Object
      *                                      meet the expectations.
      */
     static protected function attrsFromResult($result, $attrs,
-                                              $restrict = Horde_Kolab_Server::RESULT_SINGLE)
+                                              $restrict = Horde_Kolab_Server_Object::RESULT_SINGLE)
     {
         switch ($restrict) {
         case self::RESULT_STRICT:
@@ -1101,7 +1103,7 @@ class Horde_Kolab_Server_Object
      * @throws Horde_Kolab_Server_Exception
      */
     static public function basicUidForSearch($server, $criteria,
-                                             $restrict = Horde_Kolab_Server::RESULT_SINGLE)
+                                             $restrict = Horde_Kolab_Server_Object::RESULT_SINGLE)
     {
         $params = array('attributes' => self::ATTRIBUTE_UID);
         $filter = $server->searchQuery($criteria);
@@ -1123,7 +1125,7 @@ class Horde_Kolab_Server_Object
      * @throws Horde_Kolab_Server_Exception
      */
     static public function attrsForSearch($server, $criteria, $attrs,
-                                          $restrict = Horde_Kolab_Server::RESULT_SINGLE)
+                                          $restrict = Horde_Kolab_Server_Object::RESULT_SINGLE)
     {
         $params = array('attributes' => $attrs);
         $filter = $server->searchQuery($criteria);
index 1fd13d4..8604e89 100644 (file)
@@ -89,7 +89,7 @@ class Horde_Kolab_Server_Object_Kolabgroupofnames extends Horde_Kolab_Server_Obj
     {
         switch ($attr) {
         case self::ATTRIBUTE_VISIBILITY:
-            //FIXME: This needs structural knowledge and should be in a
+            //@todo This needs structural knowledge and should be in a
             //structural class.
             return strpos($this->uid, 'cn=internal') === false;
         default:
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Objects.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Objects.php
new file mode 100644 (file)
index 0000000..92aa7ce
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+/**
+ * A library for accessing the Kolab user database.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Interface for a server object list.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+interface Horde_Kolab_Server_Objects
+{
+    /**
+     * Add a Kolab object.
+     *
+     * @param array $info The object to store.
+     *
+     * @return Kolab_Object The newly created Kolab object.
+     *
+     * @throws Horde_Kolab_Server_Exception If the type of the object to add has
+     *                                      been left undefined or the object
+     *                                      already exists.
+     */
+    public function add(array $info);
+
+    /**
+     * Fetch a Kolab object.
+     *
+     * This method will not retrieve any data from the server
+     * immediately. Instead it will simply generate a new instance for the
+     * desired object.
+     *
+     * The server data will only be accessed once you start reading the object
+     * data.
+     *
+     * This method can also be used in order to fetch non-existing objects that
+     * will be saved later. This is however not recommended and you should
+     * rather use the add($info) method for that.
+     *
+     * If you do not provide the object type the server will try to determine it
+     * automatically based on the uid. As this requires reading data from the
+     * server it is recommended to specify the object type whenever it is known.
+     *
+     * If you do not specify a uid the object corresponding to the user bound to
+     * the server will be returned.
+     *
+     * @param string $uid  The UID of the object to fetch.
+     * @param string $type The type of the object to fetch.
+     *
+     * @return Kolab_Object The corresponding Kolab object.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function fetch($uid = null, $type = null);
+
+    /**
+     * 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 An array of Kolab objects.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function listObjects($type, $params = null);
+
+    /**
+     * Generate a hash representation for a list of objects.
+     *
+     * The approach taken here is somewhat slow as the server data gets fetched
+     * into objects first which are then converted to hashes again. Since a
+     * server search will usually deliver the result as a hash the intermediate
+     * object conversion is inefficient.
+     *
+     * But as the object classes are able to treat the attributes returned from
+     * the server with custom parsing, this is currently the preferred
+     * method. Especially for large result sets it would be better if this
+     * method would call a static object class function that operate on the
+     * result array returned from the server without using objects.
+     *
+     * @param string $type   The type of the objects to be listed
+     * @param array  $params Additional parameters.
+     *
+     * @return array An array of Kolab objects.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     *
+     * @todo The LDAP driver needs a more efficient version of this call as it
+     *       is not required to generate objects before returning data as a
+     *       hash. It can be derived directly from the LDAP result.
+     */
+    public function listHash($type, $params = null);
+
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Objects/Base.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Objects/Base.php
new file mode 100644 (file)
index 0000000..63636e5
--- /dev/null
@@ -0,0 +1,243 @@
+<?php
+/**
+ * A library for accessing the Kolab user database.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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 objects stored in
+ * the Kolab object db.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Objects_Base implements Horde_Kolab_Server_Objects
+{
+    /**
+     * A link to the server handler.
+     *
+     * @var Horde_Kolab_Server
+     */
+    protected $server;
+
+    /**
+     * Set the server reference for this object.
+     *
+     * @param Horde_Kolab_Server &$server A link to the server handler.
+     */
+    public function setServer($server)
+    {
+        $this->server = $server;
+    }
+
+    /**
+     * Fetch a Kolab object.
+     *
+     * This method will not retrieve any data from the server
+     * immediately. Instead it will simply generate a new instance for the
+     * desired object.
+     *
+     * The server data will only be accessed once you start reading the object
+     * data.
+     *
+     * This method can also be used in order to fetch non-existing objects that
+     * will be saved later. This is however not recommended and you should
+     * rather use the add($info) method for that.
+     *
+     * If you do not provide the object type the server will try to determine it
+     * automatically based on the uid. As this requires reading data from the
+     * server it is recommended to specify the object type whenever it is known.
+     *
+     * If you do not specify a uid the object corresponding to the user bound to
+     * the server will be returned.
+     *
+     * @param string $uid  The UID of the object to fetch.
+     * @param string $type The type of the object to fetch.
+     *
+     * @return Kolab_Object The corresponding Kolab object.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function fetch($uid = null, $type = null)
+    {
+        if (!isset($uid)) {
+            $uid = $this->uid;
+        }
+        if (empty($type)) {
+            $type = $this->determineType($uid);
+        }
+
+        $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 The newly created Kolab object.
+     *
+     * @throws Horde_Kolab_Server_Exception If the type of the object to add has
+     *                                      been left undefined or the object
+     *                                      already exists.
+     */
+    public function add(array $info)
+    {
+        if (!isset($info['type'])) {
+            throw new Horde_Kolab_Server_Exception(
+                'The type of a new object must be specified!');
+        }
+
+        $type = $info['type'];
+        unset($info['type']);
+        $object = &Horde_Kolab_Server_Object::factory($type, null, $this, $info);
+        if ($object->exists()) {
+            throw new Horde_Kolab_Server_Exception(
+                sprintf(_("The object with the uid \"%s\" does already exist!"),
+                        $object->get(Horde_Kolab_Server_Object::ATTRIBUTE_UID)));
+        }
+        $object->save();
+        return $object;
+    }
+
+    /**
+     * Generate a hash representation for a list of objects.
+     *
+     * The approach taken here is somewhat slow as the server data gets fetched
+     * into objects first which are then converted to hashes again. Since a
+     * server search will usually deliver the result as a hash the intermediate
+     * object conversion is inefficient.
+     *
+     * But as the object classes are able to treat the attributes returned from
+     * the server with custom parsing, this is currently the preferred
+     * method. Especially for large result sets it would be better if this
+     * method would call a static object class function that operate on the
+     * result array returned from the server without using objects.
+     *
+     * @param string $type   The type of the objects to be listed
+     * @param array  $params Additional parameters.
+     *
+     * @return array An array of Kolab objects.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     *
+     * @todo The LDAP driver needs a more efficient version of this call as it
+     *       is not required to generate objects before returning data as a
+     *       hash. It can be derived directly from the LDAP result.
+     */
+    public function listHash($type, $params = null)
+    {
+        $list = $this->listObjects($type, $params);
+
+        if (isset($params['attributes'])) {
+            $attributes = $params['attributes'];
+        } else {
+            $attributes = null;
+        }
+
+        $hash = array();
+        foreach ($list as $uid => $entry) {
+            $hash[$uid] = $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 An array of Kolab objects.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     *
+     * @todo Sorting
+     * @todo Is this LDAP specific?
+     */
+    public 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);
+        $vars     = get_class_vars($type);
+        $criteria = call_user_func(array($type, 'getFilter'));
+        $filter   = $this->searchQuery($criteria);
+        $sort     = $vars['sort_by'];
+
+        if (isset($params['sort'])) {
+            $sort = $params['sort'];
+        }
+
+        $options = array('scope' => 'sub');
+        if (isset($params['attributes'])) {
+            $options['attributes'] = $params['attributes'];
+        } else {
+            $options['attributes'] = $this->getAttributes($type);
+        }
+
+        $data = $this->search($filter, $options, $base);
+        if (empty($data)) {
+            return array();
+        }
+
+        if ($sort) {
+            /* FIXME */
+            /* $data = $result->as_sorted_struct(); */
+            /*$this->sort($result, $sort); */
+        }
+
+        if (isset($params['from'])) {
+            $from = $params['from'];
+        } else {
+            $from = -1;
+        }
+
+        if (isset($params['to'])) {
+            $sort = $params['to'];
+        } else {
+            $to = -1;
+        }
+
+        if (!empty($vars['required_group'])) {
+            $required_group = new Horde_Kolab_Server_Object_Kolabgroupofnames($this,
+                                                                              null,
+                                                                              $vars['required_group']);
+        }
+
+        $objects = array();
+        foreach ($data as $uid => $entry) {
+            if (!empty($vars['required_group'])) {
+                if (!$required_group->exists() || !$required_group->isMember($uid)) {
+                    continue;
+                }
+            }
+            $objects[$uid] = &Horde_Kolab_Server_Object::factory($type, $uid,
+                                                                 $this, $entry);
+        }
+        return $objects;
+    }
+
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query.php
new file mode 100644 (file)
index 0000000..706e2eb
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+/**
+ * Interface for server queries.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Interface for server queries.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+interface Horde_Kolab_Server_Query
+{
+    /**
+     * Return the query as a string.
+     *
+     * @return string The query in string format.
+     */
+    public function __toString();
+
+    /**
+     * Convert the equals element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Single $single The element to convert.
+     *
+     * @return mixed The query element in query format.
+     */
+    public function convertEquals(Horde_Kolab_Server_Query_Element_Equals $equals);
+
+    /**
+     * Convert the begins element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Single $single The element to convert.
+     *
+     * @return mixed The query element in query format.
+     */
+    public function convertBegins(Horde_Kolab_Server_Query_Element_Begins $begins);
+
+    /**
+     * Convert the ends element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Single $single The element to convert.
+     *
+     * @return mixed The query element in query format.
+     */
+    public function convertEnds(Horde_Kolab_Server_Query_Element_Ends $ends);
+
+    /**
+     * Convert the contains element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Single $single The element to convert.
+     *
+     * @return mixed The query element in query format.
+     */
+    public function convertContains(Horde_Kolab_Server_Query_Element_Contains $contains);
+
+    /**
+     * Convert the less element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Single $single The element to convert.
+     *
+     * @return mixed The query element in query format.
+     */
+    public function convertLess(Horde_Kolab_Server_Query_Element_Less $less);
+
+    /**
+     * Convert the greater element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Single $single The element to convert.
+     *
+     * @return mixed The query element in query format.
+     */
+    public function convertGreater(Horde_Kolab_Server_Query_Element_Greater $greater);
+
+    /**
+     * Convert the approx element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Single $single The element to convert.
+     *
+     * @return mixed The query element in query format.
+     */
+    public function convertApprox(Horde_Kolab_Server_Query_Element_Approx $approx);
+
+    /**
+     * Convert the not element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Group $group The element to convert.
+     *
+     * @return mixed The query element in query format.
+     */
+    public function convertNot(Horde_Kolab_Server_Query_Element_Not $not);
+
+    /**
+     * Convert the and element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Group $group The element to convert.
+     *
+     * @return mixed The query element in query format.
+     */
+    public function convertAnd(Horde_Kolab_Server_Query_Element_And $and);
+
+    /**
+     * Convert the or element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Group $group The element to convert.
+     *
+     * @return mixed The query element in query format.
+     */
+    public function convertOr(Horde_Kolab_Server_Query_Element_Group $or);
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element.php
new file mode 100644 (file)
index 0000000..3071c6a
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Interface for server query elements.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Interface for server query elements.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+interface Horde_Kolab_Server_Query_Element
+{
+    /**
+     * Return the query element name.
+     *
+     * @return string The name of the query element.
+     */
+    public function getName();
+
+    /**
+     * Return the value of this element.
+     *
+     * @return mixed The query value.
+     */
+    public function getValue();
+
+    /**
+     * Return the elements of this group.
+     *
+     * @return mixed The group elements.
+     */
+    public function getElements();
+
+    /**
+     * Convert this element to a query element.
+     *
+     * @return mixedd The element as query.
+     */
+    public function convert(Horde_Kolab_Server_Query $writer);
+
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/And.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/And.php
new file mode 100644 (file)
index 0000000..abe52ba
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * An element grouping by and.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * An element grouping by and.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_Element_And
+extends Horde_Kolab_Server_Query_Element_Group
+{
+    /**
+     * Convert this element to a query element.
+     *
+     * @return mixed The element as query.
+     */
+    public function convert(Horde_Kolab_Server_Query $writer)
+    {
+        return $writer->convertAnd($this);
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Approx.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Approx.php
new file mode 100644 (file)
index 0000000..486ce56
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * An element expressing an 'approx' match.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * An element expressing an 'approx' match.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_Element_Approx
+extends Horde_Kolab_Server_Query_Element_Single
+{
+    /**
+     * Convert this element to a query element.
+     *
+     * @return mixed The element as query.
+     */
+    public function convert(Horde_Kolab_Server_Query $writer)
+    {
+        return $writer->convertApprox($this);
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Begins.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Begins.php
new file mode 100644 (file)
index 0000000..3686bc3
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * An element expressing an 'begins' match.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * An element expressing an 'begins' match.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_Element_Begins
+extends Horde_Kolab_Server_Query_Element_Single
+{
+    /**
+     * Convert this element to a query element.
+     *
+     * @return mixed The element as query.
+     */
+    public function convert(Horde_Kolab_Server_Query $writer)
+    {
+        return $writer->convertBegins($this);
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Contains.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Contains.php
new file mode 100644 (file)
index 0000000..224d187
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * An element expressing an 'contains' match.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * An element expressing an 'contains' match.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_Element_Contains
+extends Horde_Kolab_Server_Query_Element_Single
+{
+    /**
+     * Convert this element to a query element.
+     *
+     * @return mixed The element as query.
+     */
+    public function convert(Horde_Kolab_Server_Query $writer)
+    {
+        return $writer->convertContains($this);
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Ends.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Ends.php
new file mode 100644 (file)
index 0000000..742f9ba
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * An element expressing an 'ends' match.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * An element expressing an 'ends' match.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_Element_Ends
+extends Horde_Kolab_Server_Query_Element_Single
+{
+    /**
+     * Convert this element to a query element.
+     *
+     * @return mixed The element as query.
+     */
+    public function convert(Horde_Kolab_Server_Query $writer)
+    {
+        return $writer->convertEnds($this);
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Equals.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Equals.php
new file mode 100644 (file)
index 0000000..c8164ae
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * An element expressing an 'equals' match.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * An element expressing an 'equals' match.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_Element_Equals
+extends Horde_Kolab_Server_Query_Element_Single
+{
+    /**
+     * Convert this element to a query element.
+     *
+     * @return mixed The element as query.
+     */
+    public function convert(Horde_Kolab_Server_Query $writer)
+    {
+        return $writer->convertEquals($this);
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Greater.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Greater.php
new file mode 100644 (file)
index 0000000..a7f843c
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * An element expressing an 'greater' match.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * An element expressing an 'greater' match.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_Element_Greater
+extends Horde_Kolab_Server_Query_Element_Single
+{
+    /**
+     * Convert this element to a query element.
+     *
+     * @return mixed The element as query.
+     */
+    public function convert(Horde_Kolab_Server_Query $writer)
+    {
+        return $writer->convertGreater($this);
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Group.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Group.php
new file mode 100644 (file)
index 0000000..d861d1a
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+/**
+ * A grouped query element.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * A grouped query element.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+abstract class Horde_Kolab_Server_Query_Element_Group
+implements Horde_Kolab_Server_Query_Element
+{
+    /**
+     * The group elements.
+     *
+     * @var array
+     */
+    protected $_elements;
+
+    /**
+     * Constructor.
+     *
+     * @param array $elements The group elements.
+     */
+    public function __construct(array $elements)
+    {
+        $this->_elements = $elements;
+    }
+
+    /**
+     * Return the query element name.
+     *
+     * This should never be called for group elements.
+     *
+     * @return string The name of the query element.
+     */
+    public function getName()
+    {
+        throw new Exception('Not supported!');
+    }
+
+    /**
+     * Return the value of this element.
+     *
+     * This should never be called for group elements.
+     *
+     * @return mixed The query value.
+     */
+    public function getValue()
+    {
+        throw new Exception('Not supported!');
+    }
+
+    /**
+     * Return the elements of this group.
+     *
+     * @return mixed The group elements.
+     */
+    public function getElements()
+    {
+        return $this->_elements;
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Less.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Less.php
new file mode 100644 (file)
index 0000000..00df41f
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * An element expressing an 'less' match.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * An element expressing an 'less' match.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_Element_Less
+extends Horde_Kolab_Server_Query_Element_Single
+{
+    /**
+     * Convert this element to a query element.
+     *
+     * @return mixed The element as query.
+     */
+    public function convert(Horde_Kolab_Server_Query $writer)
+    {
+        return $writer->convertLess($this);
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Mapped.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Mapped.php
new file mode 100644 (file)
index 0000000..2cd6123
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+/**
+ * A mapped query element.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * A mapped query element.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_Element_Mapped
+implements Horde_Kolab_Server_Query_Element
+{
+    /**
+     * Delegated element.
+     *
+     * @var Horde_Kolab_Server_Query_Element
+     */
+    private $_element;
+
+    /**
+     * Name mapper.
+     *
+     * @var Horde_Kolab_Server_Mapped
+     */
+    private $_mapper;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Kolab_Server_Query_Element $element The mapped element.
+     * @param Horde_Kolab_Server_Mapped        $mapper  The mapping handler.
+     */
+    public function __construct(
+        Horde_Kolab_Server_Query_Element $element,
+        Horde_Kolab_Server_Mapped $mapper
+    ) {
+        $this->_element = $element;
+        $this->_mapper  = $mapper;
+    }
+
+    /**
+     * Return the query element name.
+     *
+     * @return string The name of the query element.
+     */
+    public function getName()
+    {
+        return $this->_mapper->mapField($this->_element->getName());
+    }
+
+    /**
+     * Return the value of this element.
+     *
+     * @return mixed The query value.
+     */
+    public function getValue()
+    {
+        return $this->_element->getValue();
+    }
+
+    /**
+     * Return the elements of this group.
+     *
+     * @return mixed The group elements.
+     */
+    public function getElements()
+    {
+        $elements = array();
+        foreach ($this->_element->getElements() as $element) {
+            $elements[] = new Horde_Kolab_Server_Query_Element_Mapped(
+                $element, $this->_mapper
+            );
+        }
+        return $elements;
+    }
+
+    /**
+     * Convert this element to a string.
+     *
+     * @return string The query string of the element.
+     */
+    public function convert(Horde_Kolab_Server_Query $writer)
+    {
+        return $this->_element->convert($writer);
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Not.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Not.php
new file mode 100644 (file)
index 0000000..2f7340d
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+/**
+ * A negating element.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * A negating element.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_Element_Not
+extends Horde_Kolab_Server_Query_Element_Group
+{
+    /**
+     * Constructor.
+     *
+     * @param array  $elements The group elements.
+     */
+    public function __construct(Horde_Kolab_Server_Query_Element $element) {
+        parent::__construct(array($element));
+    }
+
+    /**
+     * Convert this element to a query element.
+     *
+     * @return mixed The element as query.
+     */
+    public function convert(Horde_Kolab_Server_Query $writer)
+    {
+        return $writer->convertNot($this);
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Or.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Or.php
new file mode 100644 (file)
index 0000000..6e89b28
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * An element grouping by or.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * An element grouping by or.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_Element_Or
+extends Horde_Kolab_Server_Query_Element_Group
+{
+    /**
+     * Convert this element to a query element.
+     *
+     * @return mixed The element as query.
+     */
+    public function convert(Horde_Kolab_Server_Query $writer)
+    {
+        return $writer->convertOr($this);
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Single.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Element/Single.php
new file mode 100644 (file)
index 0000000..59b6cc5
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * A single query element.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * A single query element.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+abstract class Horde_Kolab_Server_Query_Element_Single
+implements Horde_Kolab_Server_Query_Element
+{
+    /**
+     * The element name.
+     *
+     * @var string
+     */
+    protected $_name;
+
+    /**
+     * The comparison value.
+     *
+     * @var mixed
+     */
+    protected $_value;
+
+    /**
+     * Constructor.
+     *
+     * @param string $name  The element name.
+     * @param mixed  $value The comparison value.
+     */
+    public function __construct($name, $value)
+    {
+        $this->_name  = $name;
+        $this->_value = $value;
+    }
+
+    /**
+     * Return the query element name.
+     *
+     * @return string The name of the query element.
+     */
+    public function getName()
+    {
+        return $this->_name;
+    }
+
+    /**
+     * Return the value of this element.
+     *
+     * @return mixed The query value.
+     */
+    public function getValue()
+    {
+        return $this->_value;
+    }
+
+    /**
+     * Return the elements of this group.
+     *
+     * This should never be called for single elements.
+     *
+     * @return mixed The group elements.
+     */
+    public function getElements()
+    {
+        throw new Exception('Not supported!');
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Ldap.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Query/Ldap.php
new file mode 100644 (file)
index 0000000..e90f995
--- /dev/null
@@ -0,0 +1,268 @@
+<?php
+/**
+ * Handler for LDAP server queries.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Handler for LDAP server queries.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_Ldap implements Horde_Kolab_Server_Query
+{
+    /**
+     * The query criteria.
+     *
+     * @var Horde_Kolab_Server_Query_Element
+     */
+    private $_criteria;
+
+    /**
+     * Constructor.
+     *
+     * @param array $criteria The query criteria.
+     */
+    public function __construct(Horde_Kolab_Server_Query_Element $criteria)
+    {
+        $this->_criteria = $criteria;
+    }
+
+    /**
+     * Return the query as a string.
+     *
+     * @return string The query in string format.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    public function __toString()
+    {
+        $filter = $this->_criteria->convert($this);
+        return $filter->asString();
+    }
+
+    /**
+     * Convert the equals element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Equals $equals The element to convert.
+     *
+     * @return mixed The query element in query format.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    public function convertEquals(Horde_Kolab_Server_Query_Element_Equals $equals)
+    {
+        return $this->_convertSingle($equals, 'equals');
+    }
+
+    /**
+     * Convert the begins element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Begins $begins The element to convert.
+     *
+     * @return mixed The query element in query format.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    public function convertBegins(Horde_Kolab_Server_Query_Element_Begins $begins)
+    {
+        return $this->_convertSingle($begins, 'begins');
+    }
+
+    /**
+     * Convert the ends element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Ends $ends The element to convert.
+     *
+     * @return mixed The query element in query format.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    public function convertEnds(Horde_Kolab_Server_Query_Element_Ends $ends)
+    {
+        return $this->_convertSingle($ends, 'ends');
+    }
+
+    /**
+     * Convert the contains element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Contains $contains The element to convert.
+     *
+     * @return mixed The query element in query format.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    public function convertContains(Horde_Kolab_Server_Query_Element_Contains $contains)
+    {
+        return $this->_convertSingle($contains, 'contains');
+    }
+
+    /**
+     * Convert the less element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Less $less The element to convert.
+     *
+     * @return mixed The query element in query format.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    public function convertLess(Horde_Kolab_Server_Query_Element_Less $less)
+    {
+        return $this->_convertSingle($less, 'less');
+    }
+
+    /**
+     * Convert the greater element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Grater $grater The element to convert.
+     *
+     * @return mixed The query element in query format.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    public function convertGreater(Horde_Kolab_Server_Query_Element_Greater $greater)
+    {
+        return $this->_convertSingle($greater, 'greater');
+    }
+
+    /**
+     * Convert the approx element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Approx $approx The element to convert.
+     *
+     * @return mixed The query element in query format.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    public function convertApprox(Horde_Kolab_Server_Query_Element_Approx $approx)
+    {
+        return $this->_convertSingle($approx, 'approx');
+    }
+
+    /**
+     * Convert the single element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Single $single   The element to convert.
+     * @param string                                  $operator The element operation.
+     *
+     * @return mixed The query element in query format.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    private function _convertSingle(
+        Horde_Kolab_Server_Query_Element_Single $single,
+        $operator
+    ) {
+        $result = Net_LDAP2_Filter::create(
+            $single->getName(), $operator, $single->getValue()
+        );
+        $this->_handleError($result);
+        return $result;
+    }
+
+    /**
+     * Convert the not element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Group $group The element to convert.
+     *
+     * @return mixed The query element in query format.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    public function convertNot(Horde_Kolab_Server_Query_Element_Not $not)
+    {
+        $elements = $not->getElements();
+        $result = Net_LDAP2_Filter::combine('!', $elements[0]->convert($this));
+        $this->_handleError($result);
+        return $result;
+    }
+
+    /**
+     * Convert the and element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Group $group The element to convert.
+     *
+     * @return mixed The query element in query format.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    public function convertAnd(Horde_Kolab_Server_Query_Element_And $and)
+    {
+        return $this->_convertGroup($and, '&');
+    }
+
+    /**
+     * Convert the or element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Group $group The element to convert.
+     *
+     * @return mixed The query element in query format.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    public function convertOr(Horde_Kolab_Server_Query_Element_Group $or)
+    {
+        return $this->_convertGroup($or, '|');
+    }
+
+    /**
+     * Convert the group element to query format.
+     *
+     * @param Horde_Kolab_Server_Query_Element_Group $group    The element to convert.
+     * @param string                                 $operator The element operation.
+     *
+     * @return mixed The query element in query format.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    public function _convertGroup(
+        Horde_Kolab_Server_Query_Element_Group $group,
+        $operator
+    ) {
+        $filters = array();
+        foreach ($group->getElements() as $element) {
+            $filters[] = $element->convert($this);
+        }
+        $result = Net_LDAP2_Filter::combine($operator, $filters);
+        $this->_handleError($result);
+        return $result;
+    }
+
+    /**
+     * Check for a PEAR Error and convert it to an exception if necessary.
+     *
+     * @param mixed $result The result to be tested.
+     * @param code  $code   The error code to use in case the result is an error.
+     *
+     * @return NULL.
+     *
+     * @throws Horde_Kolab_Server_Exception If the connection failed.
+     *
+     * @throws Horde_Kolab_Server_Exception If the query is malformed.
+     */
+    private function _handleError(
+        $result,
+        $code = Horde_Kolab_Server_Exception::INVALID_QUERY
+    ) {
+        if (is_a($result, 'PEAR_Error')) {
+            throw new Horde_Kolab_Server_Exception($result, $code);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Result.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Result.php
new file mode 100644 (file)
index 0000000..bf365fd
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Interface for query results.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Interface for query results.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+interface Horde_Kolab_Server_Result
+{
+    /**
+     * The number of result entries.
+     *
+     * @return int The number of elements.
+     */
+    public function count();
+
+    /**
+     * Test if the last search exceeded the size limit.
+     *
+     * @return boolean True if the last search exceeded the size limit.
+     */
+    public function sizeLimitExceeded();
+
+    /**
+     * Return the result as an array.
+     *
+     * @return array The resulting array.
+     */
+    public function asArray();
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Result/Ldap.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Result/Ldap.php
new file mode 100644 (file)
index 0000000..273a0c0
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Handler for LDAP query results.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Handler for LDAP query results.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Result_Ldap implements  Horde_Kolab_Server_Result
+{
+    /**
+     * The search result.
+     *
+     * @var Net_LDAP2_Search
+     */
+    private $_search;
+
+    /**
+     * Constructor.
+     *
+     * @param Net_LDAP2_Search $search The LDAP search result.
+     */
+    public function __construct(Net_LDAP2_Search $search)
+    {
+        $this->_search = $search;
+    }
+
+    /**
+     * The number of result entries.
+     *
+     * @return int The number of elements.
+     */
+    public function count()
+    {
+        return $this->_search->count();
+    }
+
+    /**
+     * Test if the last search exceeded the size limit.
+     *
+     * @return boolean True if the last search exceeded the size limit.
+     */
+    public function sizeLimitExceeded()
+    {
+        return $this->_search->sizeLimitExceeded();
+    }
+
+    /**
+     * Return the result as an array.
+     *
+     * @return array The resulting array.
+     */
+    public function asArray()
+    {
+        return $this->_search->as_struct();
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Schema.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Schema.php
new file mode 100644 (file)
index 0000000..07792a2
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+/**
+ * A library for accessing the Kolab user database.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Interface for the structural handler of a Kolab database.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+interface Horde_Kolab_Server_Schema
+{
+    /**
+     * Return the schema for the given objectClass.
+     *
+     * @param string $objectclass Fetch the schema for this objectClass.
+     *
+     * @return array The schema for the given objectClass.
+     *
+     * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
+     */
+    public function getObjectclassSchema($objectclass);
+
+    /**
+     * Return the schema for the given attribute.
+     *
+     * @param string $attribute Fetch the schema for this attribute.
+     *
+     * @return array The schema for the given attribute.
+     *
+     * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
+     */
+    public function getAttributeSchema($attribute);
+
+    /**
+     * Return the attributes supported by the given object class.
+     *
+     * @param string $class Determine the attributes for this class.
+     *
+     * @return array The supported attributes.
+     *
+     * @throws Horde_Kolab_Server_Exception If the schema analysis fails.
+     */
+    public function &getAttributes($class);
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Schema/Base.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Schema/Base.php
new file mode 100644 (file)
index 0000000..9ed27a7
--- /dev/null
@@ -0,0 +1,273 @@
+<?php
+/**
+ * A library for accessing the Kolab user database.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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 objects stored in
+ * the Kolab object db.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Schema_Base implements Horde_Kolab_Server_Schema
+{
+    /** Maximum accepted level for the object class hierarchy */
+    const MAX_HIERARCHY = 100;
+
+    /**
+     * A cache for object attribute definitions.
+     *
+     * @var array
+     */
+    protected $attributes;
+
+    /**
+     * A link to the server handler.
+     *
+     * @var Horde_Kolab_Server
+     */
+    protected $server;
+
+    /**
+     * Set the server reference for this object.
+     *
+     * @param Horde_Kolab_Server &$server A link to the server handler.
+     */
+    public function setServer($server)
+    {
+        $this->server = $server;
+    }
+
+    /**
+     * Return the schema for the given objectClass.
+     *
+     * @param string $objectclass Fetch the schema for this objectClass.
+     *
+     * @return array The schema for the given objectClass.
+     *
+     * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
+     */
+    public function getObjectclassSchema($objectclass)
+    {
+        if (!empty($this->_config['schema_support'])) {
+            $schema = $this->_getSchema();
+            $info = $schema->get('objectclass', $objectclass);
+            $this->_handleError($info, Horde_Kolab_Server_Exception::SYSTEM);
+            return $info;
+        }
+        return parent::getObjectclassSchema($objectclass);
+    }
+
+    /**
+     * Return the schema for the given attribute.
+     *
+     * @param string $attribute Fetch the schema for this attribute.
+     *
+     * @return array The schema for the given attribute.
+     *
+     * @throws Horde_Kolab_Server_Exception If retrieval of the schema failed.
+     */
+    public function getAttributeSchema($attribute)
+    {
+        if (!empty($this->_config['schema_support'])) {
+            $schema = $this->_getSchema();
+            $info = $schema->get('attribute', $attribute);
+            $this->_handleError($info, Horde_Kolab_Server_Exception::SYSTEM);
+            return $info;
+        }
+        return parent::getAttributeSchema($attribute);
+    }
+
+    /**
+     * Return the attributes supported by the given object class.
+     *
+     * @param string $class Determine the attributes for this class.
+     *
+     * @return array The supported attributes.
+     *
+     * @throws Horde_Kolab_Server_Exception If the schema analysis fails.
+     */
+    public function &getAttributes($class)
+    {
+        if (!isset($this->attributes)) {
+            if (isset($this->cache)) {
+                register_shutdown_function(array($this, 'shutdown'));
+            }
+        }
+        if (empty($this->attributes[$class])) {
+
+            if (isset($this->cache)) {
+                $this->attributes[$class] = @unserialize($cache->get('attributes_' . $class,
+                                                                     $this->params['cache_lifetime']));
+            }
+
+            if (empty($this->attributes[$class])) {
+
+                $childclass = $class;
+                $classes    = array();
+                $level      = 0;
+                while ($childclass != 'Horde_Kolab_Server_Object'
+                       && $level < self::MAX_HIERARCHY) {
+                    $classes[]  = $childclass;
+                    $childclass = get_parent_class($childclass);
+                    $level++;
+                }
+
+                /** Finally add the basic object class */
+                $classes[] = $childclass;
+
+                if ($level == self::MAX_HIERARCHY) {
+                    if (isset($this->logger)) {
+                        $logger->err(sprintf('The maximal level of the object hierarchy has been exceeded for class \"%s\"!',
+                                             $class));
+                    }
+                }
+
+                /**
+                 * Collect attributes from bottom to top.
+                 */
+                $classes = array_reverse($classes);
+
+                $types = array('defined', 'required', 'derived', 'collapsed',
+                               'defaults', 'locked', 'object_classes');
+                foreach ($types as $type) {
+                    $$type = array();
+                }
+
+                foreach ($classes as $childclass) {
+                    $vars = get_class_vars($childclass);
+                    if (isset($vars['init_attributes'])) {
+                        foreach ($types as $type) {
+                            /**
+                             * If the user wishes to adhere to the schema
+                             * information from the server we will skip the
+                             * attributes defined within the object class here.
+                             */
+                            if (!empty($this->params['schema_override'])
+                                && in_array($type, 'defined', 'required')) {
+                                continue;
+                            }
+                            if (isset($vars['init_attributes'][$type])) {
+                                $$type = array_merge($$type,
+                                                     $vars['init_attributes'][$type]);
+                            }
+                        }
+                    }
+                }
+
+                $attrs = array();
+
+                foreach ($object_classes as $object_class) {
+                    $info = $this->getObjectclassSchema($object_class);
+                    if (isset($info['may'])) {
+                        $defined = array_merge($defined, $info['may']);
+                    }
+                    if (isset($info['must'])) {
+                        $defined  = array_merge($defined, $info['must']);
+                        $required = array_merge($required, $info['must']);
+                    }
+                    foreach ($defined as $attribute) {
+                        try {
+                            $attrs[$attribute] = $this->getAttributeSchema($attribute);
+                        } catch (Horde_Kolab_Server_Exception $e) {
+                            /**
+                             * If the server considers the attribute to be
+                             * invalid we mark it.
+                             */
+                            $attrs[$attribute] = array('invalid' => true);
+                        }
+                    }
+                    foreach ($required as $attribute) {
+                        $attrs[$attribute]['required'] = true;
+                    }
+                    foreach ($locked as $attribute) {
+                        $attrs[$attribute]['locked'] = true;
+                    }
+                    foreach ($defaults as $attribute => $default) {
+                        $attrs[$attribute]['default'] = $default;
+                    }
+                    $attrs[Horde_Kolab_Server_Object::ATTRIBUTE_OC]['default'] = $object_classes;
+                }
+                foreach ($derived as $key => $attributes) {
+                    $supported = true;
+                    if (isset($attributes['base'])) {
+                        foreach ($attributes['base'] as $attribute) {
+                            /**
+                             * Usually derived attribute are determined on basis
+                             * of one or more attributes. If any of these is not
+                             * supported the derived attribute should not be
+                             * included into the set of supported attributes.
+                             */
+                            if (!isset($attrs[$attribute])) {
+                                unset($derived[$attribute]);
+                                $supported = false;
+                                break;
+                            }
+                        }
+                    }
+                    if ($supported) {
+                        $attrs[$key] = $attributes;
+                    }
+                }
+                $check_collapsed = $collapsed;
+                foreach ($check_collapsed as $key => $attributes) {
+                    if (isset($attributes['base'])) {
+                        foreach ($attributes['base'] as $attribute) {
+                            /**
+                             * Usually collapsed attribute are determined on basis
+                             * of one or more attributes. If any of these is not
+                             * supported the collapsed attribute should not be
+                             * included into the set of supported attributes.
+                             */
+                            if (!isset($attrs[$attribute])) {
+                                unset($collapsed[$attribute]);
+                            }
+                        }
+                    }
+                }
+                $this->attributes[$class] = array($attrs,
+                                                  array(
+                                                      'derived'   => array_keys($derived),
+                                                      'collapsed' => $collapsed,
+                                                      'locked'    => $locked,
+                                                      'required'  => $required));
+            }
+        }
+        return $this->attributes[$class];
+    }
+
+    /**
+     * Stores the attribute definitions in the cache.
+     *
+     * @return Horde_Kolab_Server The concrete Horde_Kolab_Server reference.
+     */
+    public function shutdown()
+    {
+        if (isset($this->attributes)) {
+            if (isset($this->cache)) {
+                foreach ($this->attributes as $key => $value) {
+                    $this->cache->set('attributes_' . $key, @serialize($value));
+                }
+            }
+        }
+    }
+
+
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Search.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Search.php
new file mode 100644 (file)
index 0000000..6f72f7b
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * A library for accessing the Kolab user database.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Defines the interface of the search handler for a Kolab Server.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+interface Horde_Kolab_Server_Search
+{
+    /**
+     * Returns the set of search operations supported by this server type.
+     *
+     * @return array An array of supported search operations.
+     */
+    public function getSearchOperations();
+
+    /**
+     * Capture undefined calls.
+     *
+     * @param string $method The name of the called method.
+     * @param array  $args   Arguments of the call.
+     *
+     * @return NULL.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function __call($method, $args);
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Search/Base.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Search/Base.php
new file mode 100644 (file)
index 0000000..caf111f
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+/**
+ * A library for accessing the Kolab user database.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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 objects stored in
+ * the Kolab object db.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Search_Base implements Horde_Kolab_Server_Search
+{
+    /**
+     * A link to the server handler.
+     *
+     * @var Horde_Kolab_Server
+     */
+    protected $server;
+
+    /**
+     * Set the server reference for this object.
+     *
+     * @param Horde_Kolab_Server &$server A link to the server handler.
+     */
+    public function setServer($server)
+    {
+        $this->server = $server;
+    }
+
+    /**
+     * The search methods offered by the object defined for this server.
+     *
+     * @var array
+     */
+    protected $searches;
+
+    /*__construct
+        /** Initialize the search operations supported by this server. *
+        $this->searches = $this->getSearchOperations();
+        */
+
+    /**
+     * Returns the set of search operations supported by this server type.
+     *
+     * @return array An array of supported search operations.
+     */
+    public function getSearchOperations()
+    {
+        $server_searches = array();
+        foreach ($this->getSupportedObjects() as $sobj) {
+            if (in_array('getSearchOperations', get_class_methods($sobj))) {
+                $searches = call_user_func(array($sobj, 'getSearchOperations'));
+                foreach ($searches as $search) {
+                    $server_searches[$search] = array('class' => $sobj);
+                }
+            }
+        }
+        return $server_searches;
+    }
+
+    /**
+     * Capture undefined calls and assume they refer to a search operation.
+     *
+     * @param string $method The name of the called method.
+     * @param array  $args   Arguments of the call.
+     *
+     * @return NULL.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function __call($method, $args)
+    {
+        if (in_array($method, array_keys($this->searches))) {
+            array_unshift($args, $this);
+            if (isset($this->searches[$method])) {
+                return call_user_func_array(array($this->searches[$method]['class'],
+                                                  $method), $args);
+            }
+        }
+        throw new Horde_Kolab_Server_Exception(
+            sprintf("The server type \"%s\" does not support method \"%s\"!",
+                    get_class($this), $method));
+    }
+
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Standard.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Standard.php
new file mode 100644 (file)
index 0000000..d4e9437
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+/**
+ * The driver for accessing objects stored in standard LDAP.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @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 objects stored in
+ * a standard LDAP db.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Standard extends Horde_Kolab_Server_Ldap
+{
+    /**
+     * Finds all object data below a parent matching a given set of criteria.
+     *
+     * @param array  $criteria The criteria for the search.
+     * @param string $parent   The parent to search below.
+     * @param array  $params   Additional search parameters.
+     *
+     * @return Horde_Kolab_Server_Result The result object.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function findBelow(array $criteria, $parent, array $params = array())
+    {
+        $query = new Horde_Kolab_Server_Query_Ldap($criteria);
+        return $this->_search((string) $query, $params, $parent);
+    }
+}
index 17c6fb9..68f8540 100644 (file)
@@ -12,7 +12,8 @@
  */
 
 /**
- * An abstract class definiing methods to deal with an object tree structure.
+ * The interface definition for the handlers dealing with the Kolab Server
+ * object tree structure.
  *
  * Copyright 2009 The Horde Project (http://www.horde.org/)
  *
  * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
  * @link     http://pear.horde.org/index.php?package=Kolab_Server
  */
-abstract class Horde_Kolab_Server_Structure
+interface Horde_Kolab_Server_Structure
 {
     /**
-     * A link to the server handler.
-     *
-     * @var Horde_Kolab_Server
-     */
-    protected $server;
-
-    /**
-     * Structure parameters.
-     *
-     * @var array
-     */
-    protected $params = array();
-
-    /**
-     * Construct a new Horde_Kolab_Server_Structure object.
-     *
-     * @param array              $params  Parameter array.
-     */
-    public function __construct($params = array())
-    {
-        $this->params = $params;
-    }
-
-    /**
-     * Set the server reference for this object.
-     *
-     * @param Horde_Kolab_Server &$server A link to the server handler.
-     */
-    public function setServer($server)
-    {
-        $this->server = $server;
-    }
-
-    /**
      * Returns the set of objects supported by this structure.
      *
      * @return array An array of supported objects.
      */
-    abstract public function getSupportedObjects();
+    public function getSupportedObjects();
 
     /**
      * Determine the type of an object by its tree position and other
@@ -78,7 +45,7 @@ abstract class Horde_Kolab_Server_Structure
      *
      * @throws Horde_Kolab_Server_Exception If the object type is unknown.
      */
-    abstract public function determineType($uid);
+    public function determineType($uid);
 
     /**
      * Generates a UID for the given information.
@@ -91,23 +58,5 @@ abstract class Horde_Kolab_Server_Structure
      *
      * @throws Horde_Kolab_Server_Exception If the given type is unknown.
      */
-    abstract public function generateServerUid($type, $id, $info);
-
-    /**
-     * Quote an UID part.
-     *
-     * @param string $id   The UID part.
-     *
-     * @return string The quoted part.
-     */
-    abstract public function quoteForUid($id);
-
-    /**
-     * Quote an filter part.
-     *
-     * @param string $part   The filter part.
-     *
-     * @return string The quoted part.
-     */
-    abstract public function quoteForFilter($part);
+    public function generateServerUid($type, $id, $info);
 }
diff --git a/framework/Kolab_Server/lib/Horde/Kolab/Server/Structure/Base.php b/framework/Kolab_Server/lib/Horde/Kolab/Server/Structure/Base.php
new file mode 100644 (file)
index 0000000..bf58f74
--- /dev/null
@@ -0,0 +1,139 @@
+<?php
+/**
+ * A simple structural handler for a tree of objects.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * An abstract class definiing methods to deal with an object tree structure.
+ *
+ * Copyright 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+abstract class Horde_Kolab_Server_Structure_Base implements Horde_Kolab_Server_Structure
+{
+    /**
+     * A link to the server handler.
+     *
+     * @var Horde_Kolab_Server
+     */
+    protected $server;
+
+    /**
+     * Set the server reference for this object.
+     *
+     * @param Horde_Kolab_Server &$server A link to the server handler.
+     */
+    public function setServer($server)
+    {
+        $this->server = $server;
+    }
+
+    /**
+     * Returns the set of objects supported by this structure.
+     *
+     * @return array An array of supported objects.
+     */
+    public function getSupportedObjects()
+    {
+    }
+
+    /**
+     * Determine the type of an object by its tree position and other
+     * parameters.
+     *
+     * @param string $uid The UID of the object to examine.
+     *
+     * @return string The class name of the corresponding object type.
+     *
+     * @throws Horde_Kolab_Server_Exception If the object type is unknown.
+     */
+    public function determineType($uid)
+    {
+    }
+
+    /**
+     * Generates a UID for the given information.
+     *
+     * @param string $type The class name 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 The UID.
+     *
+     * @throws Horde_Kolab_Server_Exception If the given type is unknown.
+     */
+    public function generateServerUid($type, $id, $info)
+    {
+    }
+
+    /**
+     * Get the LDAP object classes for the given DN.
+     *
+     * This is meant to be a shortcut for the structure handler. It should be
+     * used when determining the object type.
+     *
+     * @param string $uid DN of the object.
+     *
+     * @return array An array of object classes.
+     *
+     * @throws Horde_Kolab_Server_Exception If the object has no
+     *                                      object classes.
+     */
+    public function getObjectClasses($uid)
+    {
+        $object = $this->read($uid, array(Horde_Kolab_Server_Object::ATTRIBUTE_OC));
+        if (!isset($object[Horde_Kolab_Server_Object::ATTRIBUTE_OC])) {
+            throw new Horde_Kolab_Server_Exception(
+                sprintf(
+                    "The object %s has no %s attribute!",
+                    $uid, Horde_Kolab_Server_Object::ATTRIBUTE_OC
+                ),
+                Horde_Kolab_Server_Exception::SYSTEM
+            );
+        }
+        $result = array_map(
+            'strtolower',
+            $object[Horde_Kolab_Server_Object::ATTRIBUTE_OC]
+        );
+        return $result;
+    }
+
+    /**
+     * Connect to the server. Use this method if the user name you can provide
+     * does not match a DN. In this case it will be required to map this user
+     * name first.
+     *
+     * @param string $user The user name.
+     * @param string $pass The password.
+     *
+     * @return NULL.
+     *
+     * @throws Horde_Kolab_Server_Exception If the connection failed.
+     */
+    protected function _connect($user = null, $pass = null)
+    {
+        /** Bind anonymously first. */
+        $this->connectUid();
+        $guid = $this->structure->getGuidForUser($user);
+        $this->connectUid($guid, $pass);
+        return $this->structure->getUserForUser($user);
+    }
+
+
+}
index 7c0d6c3..382815f 100644 (file)
@@ -25,7 +25,7 @@
  * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
  * @link     http://pear.horde.org/index.php?package=Kolab_Server
  */
-class Horde_Kolab_Server_Structure_Ldap extends Horde_Kolab_Server_Structure
+class Horde_Kolab_Server_Structure_Ldap extends Horde_Kolab_Server_Structure_Base
 {
     /**
      * Returns the set of objects supported by this structure.
@@ -90,29 +90,4 @@ class Horde_Kolab_Server_Structure_Ldap extends Horde_Kolab_Server_Structure
         return sprintf('%s,%s', $id, $this->server->getBaseUid());
     }
 
-    /**
-     * Quote an UID part.
-     *
-     * @param string $id   The UID part.
-     *
-     * @return string The UID part.
-     */
-    public function quoteForUid($id)
-    {
-        $id = Net_LDAP2_Util::escape_dn_value($id);
-        return $id[0];
-    }
-
-    /**
-     * Quote an filter part.
-     *
-     * @param string $part   The filter part.
-     *
-     * @return string The quoted part.
-     */
-    public function quoteForFilter($part)
-    {
-        $part = Net_LDAP2_Util::escape_filter_value($part);
-        return $part[0];
-    }
 }
index c04e437..461ccc1 100644 (file)
@@ -85,14 +85,17 @@ class Horde_Kolab_Server_Test extends Horde_Kolab_Server_Ldap
     private $_current_index;
 
     /**
-     * Construct a new Horde_Kolab_Server object.
+     * Set configuration parameters.
      *
-     * @param array $params Parameter array.
+     * @param array $params The parameters.
+     *
+     * @return NULL
      */
-    public function __construct(Horde_Kolab_Server_Structure $structure,
-                                $params = array())
+    public function setParams(array $params)
     {
-        $this->load();
+        //@todo Load when connecting
+        //$this->load();
+
         if (isset($params['data'])) {
             $this->data = $params['data'];
         } else {
@@ -101,8 +104,6 @@ class Horde_Kolab_Server_Test extends Horde_Kolab_Server_Ldap
             }
         }
 
-        parent::__construct($structure, $params);
-
         if (isset($this->params['admin'])
             && isset($this->params['admin']['type'])) {
             $type = $this->params['admin']['type'];
@@ -114,18 +115,25 @@ class Horde_Kolab_Server_Test extends Horde_Kolab_Server_Ldap
             }
         }
 
-        $this->store();
+        //@todo Load when connecting
+        //$this->store();
+
+        parent::setParams($params);
     }
 
     /**
-     * Connect to the server.
+     * Connect to the LDAP server.
+     *
+     * @param string $uid  The unique id of the user.
+     * @param string $pass The password.
      *
      * @return NULL.
      *
      * @throws Horde_Kolab_Server_Exception If the connection failed.
      */
-    protected function connect()
+    protected function _connectUid($uid = null, $pass = null)
     {
+        //@todo
     }
 
     /**
@@ -135,6 +143,7 @@ class Horde_Kolab_Server_Test extends Horde_Kolab_Server_Ldap
      */
     protected function load()
     {
+        //@todo: remove the global
         if (isset($GLOBALS['KOLAB_SERVER_TEST_DATA'])) {
             $this->data = $GLOBALS['KOLAB_SERVER_TEST_DATA'];
         } else {
@@ -483,7 +492,7 @@ class Horde_Kolab_Server_Test extends Horde_Kolab_Server_Ldap
      *
      * @throws Horde_Kolab_Server_Exception If the object does not exist.
      */
-    public function read($dn, $attrs = null)
+    public function read($uid, array $attrs = array())
     {
         if (!$this->bound) {
             $result = $this->bind();
@@ -522,9 +531,9 @@ class Horde_Kolab_Server_Test extends Horde_Kolab_Server_Ldap
      * @param array   $data    The attributes of the object to be added/replaced.
      * @param boolean $exists  Does the object already exist on the server?
      *
-     * @return boolean  True if saving succeeded.
+     * @return NULL
      */
-    public function save($uid, $data, $exists = false)
+    public function save($uid, array $data, $exists = false)
     {
         if (!$this->bound) {
             $result = $this->bind();
@@ -597,8 +606,8 @@ class Horde_Kolab_Server_Test extends Horde_Kolab_Server_Ldap
         $this->store();
 
         if (isset($this->logger)) {
-            $logger->debug(sprintf('The object \"%s\" has been successfully saved!',
-                                   $uid));
+            $this->logger->debug(sprintf('The object \"%s\" has been successfully saved!',
+                                         $uid));
         }
     }
 
@@ -628,7 +637,7 @@ class Horde_Kolab_Server_Test extends Horde_Kolab_Server_Ldap
      *
      * @param string $uid The UID of the object to be deleted.
      *
-     * @return boolean True if saving succeeded.
+     * @return NULL
      *
      * @throws Horde_Kolab_Server_Exception
      */
@@ -642,10 +651,9 @@ class Horde_Kolab_Server_Test extends Horde_Kolab_Server_Ldap
         }
         $this->store();
         if (isset($this->logger)) {
-            $logger->debug(sprintf('The object \"%s\" has been successfully deleted!',
-                                   $uid));
+            $this->logger->debug(sprintf('The object \"%s\" has been successfully deleted!',
+                                         $uid));
         }
-        return true;
     }
 
     /**
@@ -654,7 +662,7 @@ class Horde_Kolab_Server_Test extends Horde_Kolab_Server_Ldap
      * @param string $uid The UID of the object to be renamed.
      * @param string $new The new UID of the object.
      *
-     * @return boolean True if renaming succeeded.
+     * @return NULL
      *
      * @throws Horde_Kolab_Server_Exception
      */
@@ -666,10 +674,9 @@ class Horde_Kolab_Server_Test extends Horde_Kolab_Server_Ldap
         }
         $this->store();
         if (isset($this->logger)) {
-            $logger->debug(sprintf('The object \"%s\" has been successfully renamed to \"%s\"!',
-                                   $uid, $new));
+            $this->logger->debug(sprintf('The object \"%s\" has been successfully renamed to \"%s\"!',
+                                         $uid, $new));
         }
-        return true;
     }
 
     /**
index 848b24d..7f25d3b 100644 (file)
@@ -61,11 +61,17 @@ http://pear.php.net/dtd/package-2.0.xsd">
      <dir name="Kolab">
       <file name="Server.php" role="php" />
       <dir name="Server">
+       <file name="Base.php" role="php" />
+       <file name="Connection.php" role="php" />
+       <dir name="Connection">
+        <file name="Simpleldap.php" role="php" />
+        <file name="Splittedldap.php" role="php" />
+       </dir> <!-- /lib/Horde/Kolab/Server/Connection -->
        <file name="Exception.php" role="php" />
        <file name="Factory.php" role="php" />
        <file name="File.php" role="php" />
        <file name="Ldap.php" role="php" />
-       <file name="MissingObjectException.php" role="php" />
+       <file name="Logged.php" role="php" />
        <file name="Object.php" role="php" />
        <dir name="Object">
         <file name="Groupofnames.php" role="php" />
@@ -88,8 +94,45 @@ http://pear.php.net/dtd/package-2.0.xsd">
          <file name="User.php" role="php" />
         </dir> <!-- /lib/Horde/Kolab/Server/Object/Kolab -->
        </dir> <!-- /lib/Horde/Kolab/Server/Object -->
+       <file name="Objects.php" role="php" />
+       <dir name="Objects">
+        <file name="Base.php" role="php" />
+       </dir> <!-- /lib/Horde/Kolab/Server/Objects -->
+       <file name="Query.php" role="php" />
+       <dir name="Query">
+        <file name="Element.php" role="php" />
+        <dir name="Element">
+         <file name="And.php" role="php" />
+         <file name="Approx.php" role="php" />
+         <file name="Begins.php" role="php" />
+         <file name="Contains.php" role="php" />
+         <file name="Ends.php" role="php" />
+         <file name="Equals.php" role="php" />
+         <file name="Greater.php" role="php" />
+         <file name="Group.php" role="php" />
+         <file name="Less.php" role="php" />
+         <file name="Mapped.php" role="php" />
+         <file name="Not.php" role="php" />
+         <file name="Or.php" role="php" />
+         <file name="Single.php" role="php" />
+        </dir> <!-- /lib/Horde/Kolab/Server/Element -->
+        <file name="Ldap.php" role="php" />
+       </dir> <!-- /lib/Horde/Kolab/Server/Query -->
+       <file name="Result.php" role="php" />
+       <dir name="Result">
+        <file name="Ldap.php" role="php" />
+       </dir> <!-- /lib/Horde/Kolab/Server/Result -->
+       <file name="Schema.php" role="php" />
+       <dir name="Schema">
+        <file name="Base.php" role="php" />
+       </dir> <!-- /lib/Horde/Kolab/Server/Schema -->
+       <file name="Search.php" role="php" />
+       <dir name="Search">
+        <file name="Base.php" role="php" />
+       </dir> <!-- /lib/Horde/Kolab/Server/Search -->
        <file name="Structure.php" role="php" />
        <dir name="Structure">
+        <file name="Base.php" role="php" />
         <file name="Kolab.php" role="php" />
         <file name="Ldap.php" role="php" />
        </dir> <!-- /lib/Horde/Kolab/Server/Structure -->
@@ -104,7 +147,13 @@ http://pear.php.net/dtd/package-2.0.xsd">
        <file name="AddingObjectsTest.php" role="test" />
        <file name="AdminTest.php" role="test" />
        <file name="AllTests.php" role="test" />
-       <file name="Autoload.php" role="test" />
+       <file name="Autoload.php" role="test">
+        <replace from="@test_dir@" to="test_dir" type="pear-config"/>
+       </file>
+       <dir name="Connection">
+        <file name="SimpleldapTest.php" role="php" />
+        <file name="SplittedldapTest.php" role="php" />
+       </dir> <!-- /test/Horde/Kolab/Server/Connection -->
        <file name="DistListHandlingTest.php" role="test" />
        <file name="GroupHandlingTest.php" role="test" />
        <file name="GroupTest.php" role="test" />
@@ -118,6 +167,9 @@ http://pear.php.net/dtd/package-2.0.xsd">
        <file name="PersonTest.php" role="test" />
        <file name="Scenario.php" role="test" />
        <file name="ServerTest.php" role="test" />
+       <dir name="Server">
+        <file name="FactoryTest.php" role="test" />
+       </dir> <!-- /test/Horde/Kolab/Server/Server -->
        <file name="TestTest.php" role="test" />
        <file name="UserHandlingTest.php" role="test" />
        <file name="UserTest.php" role="test" />
@@ -136,34 +188,18 @@ http://pear.php.net/dtd/package-2.0.xsd">
     <min>1.4.0b1</min>
    </pearinstaller>
    <package>
-    <name>Net_LDAP2</name>
-    <channel>pear.php.net</channel>
-   </package>
-   <package>
-    <name>Autoloader</name>
-    <channel>pear.horde.org</channel>
-   </package>
-   <package>
-    <name>Horde_Framework</name>
+    <name>Exception</name>
     <channel>pear.horde.org</channel>
    </package>
   </required>
   <optional>
    <package>
-    <name>Horde_LDAP</name>
-    <channel>pear.horde.org</channel>
-   </package>
-   <package>
-    <name>Horde_Cache</name>
-    <channel>pear.horde.org</channel>
-   </package>
-   <package>
     <name>Date</name>
     <channel>pear.horde.org</channel>
    </package>
    <package>
-    <name>PHPUnit</name>
-    <channel>pear.phpunit.de</channel>
+    <name>Net_LDAP2</name>
+    <channel>pear.php.net</channel>
    </package>
    <extension>
     <name>ldap</name>
@@ -173,11 +209,15 @@ http://pear.php.net/dtd/package-2.0.xsd">
  <phprelease>
   <filelist>
    <install name="lib/Horde/Kolab/Server.php" as="Horde/Kolab/Server.php" />
+   <install name="lib/Horde/Kolab/Server/Base.php" as="Horde/Kolab/Server/Base.php" />
+   <install name="lib/Horde/Kolab/Server/Connection.php" as="Horde/Kolab/Server/Connection.php" />
+   <install name="lib/Horde/Kolab/Server/Connection/Simpleldap.php" as="Horde/Kolab/Server/Connection/Simpleldap.php" />
+   <install name="lib/Horde/Kolab/Server/Connection/Splittedldap.php" as="Horde/Kolab/Server/Connection/Splittedldap.php" />
    <install name="lib/Horde/Kolab/Server/Exception.php" as="Horde/Kolab/Server/Exception.php" />
    <install name="lib/Horde/Kolab/Server/Factory.php" as="Horde/Kolab/Server/Factory.php" />
    <install name="lib/Horde/Kolab/Server/File.php" as="Horde/Kolab/Server/File.php" />
    <install name="lib/Horde/Kolab/Server/Ldap.php" as="Horde/Kolab/Server/Ldap.php" />
-   <install name="lib/Horde/Kolab/Server/MissingObjectException.php" as="Horde/Kolab/Server/MissingObjectException.php" />
+   <install name="lib/Horde/Kolab/Server/Logged.php" as="Horde/Kolab/Server/Logged.php" />
    <install name="lib/Horde/Kolab/Server/Object.php" as="Horde/Kolab/Server/Object.php" />
    <install name="lib/Horde/Kolab/Server/Object/Groupofnames.php" as="Horde/Kolab/Server/Object/Groupofnames.php" />
    <install name="lib/Horde/Kolab/Server/Object/Inetorgperson.php" as="Horde/Kolab/Server/Object/Inetorgperson.php" />
@@ -196,7 +236,32 @@ http://pear.php.net/dtd/package-2.0.xsd">
    <install name="lib/Horde/Kolab/Server/Object/Kolab/User.php" as="Horde/Kolab/Server/Object/Kolab/User.php" />
    <install name="lib/Horde/Kolab/Server/Object/Organizationalperson.php" as="Horde/Kolab/Server/Object/Organizationalperson.php" />
    <install name="lib/Horde/Kolab/Server/Object/Person.php" as="Horde/Kolab/Server/Object/Person.php" />
+   <install name="lib/Horde/Kolab/Server/Objects.php" as="Horde/Kolab/Server/Objects.php" />
+   <install name="lib/Horde/Kolab/Server/Objects/Base.php" as="Horde/Kolab/Server/Objects/Base.php" />
+   <install name="lib/Horde/Kolab/Server/Query.php" as="Horde/Kolab/Server/Query.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element.php" as="Horde/Kolab/Server/Query/Element.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element/And.php" as="Horde/Kolab/Server/Query/Element/And.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element/Approx.php" as="Horde/Kolab/Server/Query/Element/Approx.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element/Begins.php" as="Horde/Kolab/Server/Query/Element/Begins.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element/Contains.php" as="Horde/Kolab/Server/Query/Element/Contains.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element/Ends.php" as="Horde/Kolab/Server/Query/Element/Ends.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element/Equals.php" as="Horde/Kolab/Server/Query/Element/Equals.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element/Greater.php" as="Horde/Kolab/Server/Query/Element/Greater.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element/Group.php" as="Horde/Kolab/Server/Query/Element/Group.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element/Less.php" as="Horde/Kolab/Server/Query/Element/Less.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element/Mapped.php" as="Horde/Kolab/Server/Query/Element/Mapped.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element/Not.php" as="Horde/Kolab/Server/Query/Element/Not.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element/Or.php" as="Horde/Kolab/Server/Query/Element/Or.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Element/Single.php" as="Horde/Kolab/Server/Query/Element/Single.php" />
+   <install name="lib/Horde/Kolab/Server/Query/Ldap.php" as="Horde/Kolab/Server/Query/Ldap.php" />
+   <install name="lib/Horde/Kolab/Server/Result.php" as="Horde/Kolab/Server/Result.php" />
+   <install name="lib/Horde/Kolab/Server/Result/Ldap.php" as="Horde/Kolab/Server/Result/Ldap.php" />
+   <install name="lib/Horde/Kolab/Server/Schema.php" as="Horde/Kolab/Server/Schema.php" />
+   <install name="lib/Horde/Kolab/Server/Schema/Base.php" as="Horde/Kolab/Server/Schema/Base.php" />
+   <install name="lib/Horde/Kolab/Server/Search.php" as="Horde/Kolab/Server/Search.php" />
+   <install name="lib/Horde/Kolab/Server/Search/Base.php" as="Horde/Kolab/Server/Search/Base.php" />
    <install name="lib/Horde/Kolab/Server/Structure.php" as="Horde/Kolab/Server/Structure.php" />
+   <install name="lib/Horde/Kolab/Server/Structure/Base.php" as="Horde/Kolab/Server/Structure/Base.php" />
    <install name="lib/Horde/Kolab/Server/Structure/Kolab.php" as="Horde/Kolab/Server/Structure/Kolab.php" />
    <install name="lib/Horde/Kolab/Server/Structure/Ldap.php" as="Horde/Kolab/Server/Structure/Ldap.php" />
    <install name="lib/Horde/Kolab/Server/Test.php" as="Horde/Kolab/Server/Test.php" />
@@ -204,6 +269,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
    <install name="test/Horde/Kolab/Server/AdminTest.php" as="Horde/Kolab/Server/AdminTest.php" />
    <install name="test/Horde/Kolab/Server/AllTests.php" as="Horde/Kolab/Server/AllTests.php" />
    <install name="test/Horde/Kolab/Server/Autoload.php" as="Horde/Kolab/Server/Autoload.php" />
+   <install name="test/Horde/Kolab/Server/Connection/SimpleldapTest.php" as="Horde/Kolab/Server/Connection/SimpleldapTest.php" />
+   <install name="test/Horde/Kolab/Server/Connection/SplittedldapTest.php" as="Horde/Kolab/Server/Connection/SplittedldapTest.php" />
    <install name="test/Horde/Kolab/Server/DistListHandlingTest.php" as="Horde/Kolab/Server/DistListHandlingTest.php" />
    <install name="test/Horde/Kolab/Server/GroupHandlingTest.php" as="Horde/Kolab/Server/GroupHandlingTest.php" />
    <install name="test/Horde/Kolab/Server/GroupTest.php" as="Horde/Kolab/Server/GroupTest.php" />
@@ -217,6 +284,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
    <install name="test/Horde/Kolab/Server/PersonTest.php" as="Horde/Kolab/Server/PersonTest.php" />
    <install name="test/Horde/Kolab/Server/Scenario.php" as="Horde/Kolab/Server/Scenario.php" />
    <install name="test/Horde/Kolab/Server/ServerTest.php" as="Horde/Kolab/Server/ServerTest.php" />
+   <install name="test/Horde/Kolab/Server/Server/FactoryTest.php" as="Horde/Kolab/Server/Server/FactoryTest.php" />
    <install name="test/Horde/Kolab/Server/TestTest.php" as="Horde/Kolab/Server/TestTest.php" />
    <install name="test/Horde/Kolab/Server/UserHandlingTest.php" as="Horde/Kolab/Server/UserHandlingTest.php" />
    <install name="test/Horde/Kolab/Server/UserTest.php" as="Horde/Kolab/Server/UserTest.php" />
index c22c420..e77ef4e 100644 (file)
@@ -40,6 +40,8 @@ class Horde_Kolab_Server_AdminTest extends Horde_Kolab_Server_Scenario
      */
     protected function setUp()
     {
+        parent::setUp();
+
         $this->server = $this->getKolabMockServer();
     }
 
index 3b29921..edb1ce8 100644 (file)
  * @link     http://pear.horde.org/index.php?package=Kolab_Server
  */
 
-if (!defined('HORE_KOLAB_SERVER_TESTS')) {
-    /**
-     * The Autoloader allows us to omit "require/include" statements.
-     */
-    require_once 'Horde/Autoloader.php';
+/**
+ * The Autoloader allows us to omit "require/include" statements.
+ */
+require_once 'Horde/Autoloader.php';
+
+if (!defined('HORDE_KOLAB_SERVER_TESTS')) {
 
     $test_dir = '@test_dir@/Kolab_Server';
 
-    if (substr($test_dir, 0, 1) == '@') {
+    if (strpos($test_dir, '@test_dir') === 0) {
         /**
          * Assume we are working in development mode and this package resides in
          * 'framework'.
          */
-        define('HORE_KOLAB_SERVER_TESTS', dirname(__FILE__) . '/../../..');
+        define('HORDE_KOLAB_SERVER_TESTS', dirname(__FILE__) . '/../../..');
     } else {
-        define('HORE_KOLAB_SERVER_TESTS', $test_dir);
+        define('HORDE_KOLAB_SERVER_TESTS', $test_dir);
     }
 
-    Horde_Autoloader::addClassPath(HORE_KOLAB_SERVER_TESTS);
+    Horde_Autoloader::addClassPath(HORDE_KOLAB_SERVER_TESTS);
 }
\ No newline at end of file
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/Connection/SimpleldapTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/Connection/SimpleldapTest.php
new file mode 100644 (file)
index 0000000..cff61dc
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+/**
+ * Test the handler for a simple LDAP setup without read-only slaves.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Prepare the test setup.
+ */
+require_once dirname(__FILE__) . '/../Autoload.php';
+
+/**
+ * Test the handler for a simple LDAP setup without read-only slaves.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Connection_SimpleldapTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        if (!extension_loaded('ldap') && !@dl('ldap.' . PHP_SHLIB_SUFFIX)) {
+            $this->markTestSuiteSkipped('Ldap extension is missing!');
+        };
+
+        if (!class_exists('Net_LDAP2')) {
+            $this->markTestSuiteSkipped('PEAR package Net_LDAP2 is not installed!');
+        }
+    }
+
+    public function testMethodConstructHasParameterNetldap2Connection()
+    {
+        $ldap = $this->getMock('Net_LDAP2');
+        $conn = new Horde_Kolab_Server_Connection_Simpleldap($ldap);
+    }
+
+    public function testMethodConstructHasPostconditionThatTheGivenServerWasStored()
+    {
+        $ldap = $this->getMock('Net_LDAP2');
+        $conn = new Horde_Kolab_Server_Connection_Simpleldap($ldap);
+        $this->assertSame($ldap, $conn->getRead());
+    }
+
+    public function testMethodGetreadHasResultNetldap2TheHandledConnection()
+    {
+        $ldap = $this->getMock('Net_LDAP2');
+        $conn = new Horde_Kolab_Server_Connection_Simpleldap($ldap);
+        $this->assertType('Net_LDAP2', $conn->getRead());
+    }
+
+    public function testMethodGetwriteHasResultNetldap2TheHandledConnection()
+    {
+        $ldap = $this->getMock('Net_LDAP2');
+        $conn = new Horde_Kolab_Server_Connection_Simpleldap($ldap);
+        $this->assertSame($conn->getWrite(), $conn->getRead());
+    }
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/Connection/SplittedldapTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/Connection/SplittedldapTest.php
new file mode 100644 (file)
index 0000000..e590131
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+/**
+ * Test the handler for a LDAP master/slave setup.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Prepare the test setup.
+ */
+require_once dirname(__FILE__) . '/../Autoload.php';
+
+/**
+ * Test the handler for a LDAP master/slave setup.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Connection_SplittedTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        if (!extension_loaded('ldap') && !@dl('ldap.' . PHP_SHLIB_SUFFIX)) {
+            $this->markTestSuiteSkipped('Ldap extension is missing!');
+        };
+
+        if (!class_exists('Net_LDAP2')) {
+            $this->markTestSuiteSkipped('PEAR package Net_LDAP2 is not installed!');
+        }
+    }
+
+    public function testMethodConstructHasParameterNetldap2ReadConnectionAndParameterNetldap2WriteConnection()
+    {
+        $ldap_read = $this->getMock('Net_LDAP2');
+        $ldap_write = $this->getMock('Net_LDAP2');
+        $conn = new Horde_Kolab_Server_Connection_Splittedldap($ldap_read, $ldap_write);
+    }
+
+    public function testMethodConstructHasPostconditionThatTheGivenServersWereStored()
+    {
+        $ldap_read = $this->getMock('Net_LDAP2');
+        $ldap_write = $this->getMock('Net_LDAP2');
+        $conn = new Horde_Kolab_Server_Connection_Splittedldap($ldap_read, $ldap_write);
+        $this->assertSame($ldap_read, $conn->getRead());
+        $this->assertSame($ldap_write, $conn->getWrite());
+    }
+
+    public function testMethodGetreadHasResultNetldap2TheHandledConnection()
+    {
+        $ldap_read = $this->getMock('Net_LDAP2');
+        $ldap_write = $this->getMock('Net_LDAP2');
+        $conn = new Horde_Kolab_Server_Connection_Splittedldap($ldap_read, $ldap_write);
+        $this->assertType('Net_LDAP2', $conn->getRead());
+        $this->assertType('Net_LDAP2', $conn->getWrite());
+    }
+
+    public function testMethodGetwriteHasResultNetldap2TheHandledConnection()
+    {
+        $ldap_read = $this->getMock('Net_LDAP2');
+        $ldap_write = $this->getMock('Net_LDAP2');
+        $conn = new Horde_Kolab_Server_Connection_Splittedldap($ldap_read, $ldap_write);
+        $this->assertFalse($conn->getWrite() === $conn->getRead());
+    }
+}
index c69a8f5..36af340 100644 (file)
@@ -40,6 +40,8 @@ class Horde_Kolab_Server_GroupTest extends Horde_Kolab_Server_Scenario
      */
     protected function setUp()
     {
+        parent::setUp();
+
         $this->ldap = $this->getKolabMockServer();
     }
 
@@ -121,8 +123,6 @@ class Horde_Kolab_Server_GroupTest extends Horde_Kolab_Server_Scenario
      */
     public function testListingGroups()
     {
-        $this->assertEquals(0, count($GLOBALS['KOLAB_SERVER_TEST_DATA']));
-
         $result = $this->ldap->search(
             '(&(!(cn=domains))(objectClass=kolabGroupOfNames))',
             array(),
index d543677..7ff3deb 100644 (file)
@@ -68,6 +68,8 @@ class Horde_Kolab_Server_InetorgpersonTest extends Horde_Kolab_Server_Scenario
      */
     protected function setUp()
     {
+        parent::setUp();
+
         $this->initializeEnvironments();
         $this->servers = $this->getKolabServers();
     }
index 7eff469..57bf376 100644 (file)
@@ -60,6 +60,8 @@ class Horde_Kolab_Server_KolabgermanbankarrangementTest extends Horde_Kolab_Serv
      */
     protected function setUp()
     {
+        parent::setUp();
+
         $this->initializeEnvironments();
         $this->servers = $this->getKolabServers();
     }
index 75410d9..3b616d7 100644 (file)
@@ -60,6 +60,8 @@ class Horde_Kolab_Server_KolabinetorgpersonTest extends Horde_Kolab_Server_Scena
      */
     protected function setUp()
     {
+        parent::setUp();
+
         $this->initializeEnvironments();
         $this->servers = $this->getKolabServers();
     }
index 157abd0..646f363 100644 (file)
@@ -62,6 +62,8 @@ class Horde_Kolab_Server_Kolabpop3accountTest extends Horde_Kolab_Server_Scenari
      */
     protected function setUp()
     {
+        parent::setUp();
+
         $this->initializeEnvironments();
         $this->servers = $this->getKolabServers();
     }
@@ -183,27 +185,23 @@ class Horde_Kolab_Server_Kolabpop3accountTest extends Horde_Kolab_Server_Scenari
             $account_data = $this->objects[1];
             $account_data[Horde_Kolab_Server_Object_Kolabpop3account::ATTRIBUTE_OWNERUID] = $person->getUid();
             $account = $server->add($account_data);
-            $this->assertNoError($account);
-
             $account = $server->fetch($account->getUid());
-            $this->assertNoError($account);
 
             $this->assertEquals($this->objects[1][Horde_Kolab_Server_Object_Kolabpop3account::ATTRIBUTE_SERVER],
                                 $account->get(Horde_Kolab_Server_Object_Kolabpop3account::ATTRIBUTE_SERVER));
 
             $result = $account->save(array(Horde_Kolab_Server_Object_Kolabpop3account::ATTRIBUTE_SERVER => 'pop3s.example.com'));
-            $this->assertNoError($result);
 
             $account = $server->fetch($account->getUid());
-            $this->assertNoError($account);
 
-            $this->assertEquals($account->get(Horde_Kolab_Server_Object_Kolabpop3account::ATTRIBUTE_SERVER),
-                                'pop3s.example.com');
+            $this->assertContains(
+                'pop3s.example.com',
+                $account->get(Horde_Kolab_Server_Object_Kolabpop3account::ATTRIBUTE_SERVER, false)
+            );
 
             $this->assertContains('frank@example.com', $account->getUid());
 
             $result = $server->delete($account->getUid());
-            $this->assertNoError($result);
         }
     }
 }
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/LdapBase.php b/framework/Kolab_Server/test/Horde/Kolab/Server/LdapBase.php
new file mode 100644 (file)
index 0000000..1af1984
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Skip LDAP based tests if we don't have ldap or Net_LDAP2.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Prepare the test setup.
+ */
+require_once dirname(__FILE__) . '/Autoload.php';
+
+/**
+ * Skip LDAP based tests if we don't have ldap or Net_LDAP2.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_LdapBase extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        if (!extension_loaded('ldap') && !@dl('ldap.' . PHP_SHLIB_SUFFIX)) {
+            $this->markTestSuiteSkipped('Ldap extension is missing!');
+        };
+
+        if (!class_exists('Net_LDAP2')) {
+            $this->markTestSuiteSkipped('PEAR package Net_LDAP2 is not installed!');
+        }
+    }
+}
\ No newline at end of file
index 0b830e9..4ac9bce 100644 (file)
@@ -42,6 +42,8 @@ class Horde_Kolab_Server_ObjectTest extends Horde_Kolab_Server_Scenario
      */
     protected function setUp()
     {
+        parent::setUp();
+
         $this->_dummydb = new DummyDB();
     }
 
index 667ccb1..c720ce0 100644 (file)
@@ -60,6 +60,8 @@ class Horde_Kolab_Server_OrgPersonTest extends Horde_Kolab_Server_Scenario
      */
     protected function setUp()
     {
+        parent::setUp();
+
         $this->initializeEnvironments();
         $this->servers = $this->getKolabServers();
     }
index 90016e3..de1b0b6 100644 (file)
@@ -114,6 +114,8 @@ class Horde_Kolab_Server_PersonTest extends Horde_Kolab_Server_Scenario
      */
     protected function setUp()
     {
+        parent::setUp();
+
         $this->initializeEnvironments();
         $this->servers = $this->getKolabServers();
     }
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/Query/ElementTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/Query/ElementTest.php
new file mode 100644 (file)
index 0000000..01f4578
--- /dev/null
@@ -0,0 +1,212 @@
+<?php
+/**
+ * Test the LDAP query elements.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Require our basic test case definition
+ */
+require_once dirname(__FILE__) . '/../Autoload.php';
+
+/**
+ * Test the LDAP query elements.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_ElementTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        $this->writer = $this->getMock(
+            'Horde_Kolab_Server_Query_Ldap', array(), array(), '', false
+        );
+    }
+
+    public function testClassAndMethodConvertHasResultMixedTheConvertedElement()
+    {
+        $this->writer->expects($this->exactly(1))
+            ->method('convertAnd')
+            ->will($this->returnValue('converted'));
+        $and = new Horde_Kolab_Server_Query_Element_And(array());
+        $this->assertEquals('converted', $and->convert($this->writer));
+    }
+
+    public function testClassApproxMethodConvertHasResultMixedTheConvertedElement()
+    {
+        $this->writer->expects($this->exactly(1))
+            ->method('convertApprox')
+            ->will($this->returnValue('converted'));
+        $approx = new Horde_Kolab_Server_Query_Element_Approx('', '');
+        $this->assertEquals('converted', $approx->convert($this->writer));
+    }
+
+    public function testClassBeginsMethodConvertHasResultMixedTheConvertedElement()
+    {
+        $this->writer->expects($this->exactly(1))
+            ->method('convertBegins')
+            ->will($this->returnValue('converted'));
+        $begins = new Horde_Kolab_Server_Query_Element_Begins('', '');
+        $this->assertEquals('converted', $begins->convert($this->writer));
+    }
+
+    public function testClassContainsMethodConvertHasResultMixedTheConvertedElement()
+    {
+        $this->writer->expects($this->exactly(1))
+            ->method('convertContains')
+            ->will($this->returnValue('converted'));
+        $contains = new Horde_Kolab_Server_Query_Element_Contains('', '');
+        $this->assertEquals('converted', $contains->convert($this->writer));
+    }
+
+    public function testClassEndsMethodConvertHasResultMixedTheConvertedElement()
+    {
+        $this->writer->expects($this->exactly(1))
+            ->method('convertEnds')
+            ->will($this->returnValue('converted'));
+        $ends = new Horde_Kolab_Server_Query_Element_Ends('', '');
+        $this->assertEquals('converted', $ends->convert($this->writer));
+    }
+
+    public function testClassEqualsMethodConvertHasResultMixedTheConvertedElement()
+    {
+        $this->writer->expects($this->exactly(1))
+            ->method('convertEquals')
+            ->will($this->returnValue('converted'));
+        $Equals = new Horde_Kolab_Server_Query_Element_Equals('', '');
+        $this->assertEquals('converted', $Equals->convert($this->writer));
+    }
+
+    public function testClassGreaterMethodConvertHasResultMixedTheConvertedElement()
+    {
+        $this->writer->expects($this->exactly(1))
+            ->method('convertGreater')
+            ->will($this->returnValue('converted'));
+        $Greater = new Horde_Kolab_Server_Query_Element_Greater('', '');
+        $this->assertEquals('converted', $Greater->convert($this->writer));
+    }
+
+    public function testClassLessMethodConvertHasResultMixedTheConvertedElement()
+    {
+        $this->writer->expects($this->exactly(1))
+            ->method('convertLess')
+            ->will($this->returnValue('converted'));
+        $less = new Horde_Kolab_Server_Query_Element_Less('', '');
+        $this->assertEquals('converted', $less->convert($this->writer));
+    }
+
+    public function testClassNotMethodConstructHasPostconditionThatTheElementWasSavedAsArray()
+    {
+        $less = new Horde_Kolab_Server_Query_Element_Less('', '');
+        $not = new Horde_Kolab_Server_Query_Element_Not($less);
+        $this->assertType('array', $not->getElements());
+    }
+
+    public function testClassNotMethodConvertHasResultMixedTheConvertedElement()
+    {
+        $this->writer->expects($this->exactly(1))
+            ->method('convertNot')
+            ->will($this->returnValue('converted'));
+        $less = new Horde_Kolab_Server_Query_Element_Less('', '');
+        $not = new Horde_Kolab_Server_Query_Element_Not($less);
+        $this->assertEquals('converted', $not->convert($this->writer));
+    }
+
+    public function testClassOrMethodConvertHasResultMixedTheConvertedElement()
+    {
+        $this->writer->expects($this->exactly(1))
+            ->method('convertOr')
+            ->will($this->returnValue('converted'));
+        $or = new Horde_Kolab_Server_Query_Element_Or(array());
+        $this->assertEquals('converted', $or->convert($this->writer));
+    }
+
+    public function testClassGroupMethodConstructHasParameterArrayElements()
+    {
+        $or = new Horde_Kolab_Server_Query_Element_Or(array());
+    }
+
+    public function testClassGroupMethodConstructHasPostconditionThatTheElementsWereSaved()
+    {
+        $or = new Horde_Kolab_Server_Query_Element_Or(array());
+        $this->assertEquals(array(), $or->getElements());
+    }
+
+    /**
+     * @expectedException Exception
+     */
+    public function testClassGroupMethodGetnameThrowsException()
+    {
+        $or = new Horde_Kolab_Server_Query_Element_Or(array());
+        $or->getName();
+    }
+
+    /**
+     * @expectedException Exception
+     */
+    public function testClassGroupMethodGetvalueThrowsException()
+    {
+        $or = new Horde_Kolab_Server_Query_Element_Or(array());
+        $or->getValue();
+    }
+
+    public function testClassGroupMethodGetelementsHasResultArrayTheGroupElements()
+    {
+        $or = new Horde_Kolab_Server_Query_Element_Or(array());
+        $this->assertEquals(array(), $or->getElements());
+    }
+
+    public function testClassSingleMethodConstructHasParameterStringName()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('name', '');
+    }
+
+    public function testClassSingleMethodConstructHasParameterStringValue()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('', 'value');
+    }
+
+    public function testClassSingleMethodConstructHasPostconditionThatNameAndValueWereSaved()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('name', 'value');
+        $this->assertEquals('name', $equals->getName());
+        $this->assertEquals('value', $equals->getValue());
+    }
+
+    public function testClassSingleMethodGetnameHasResultStringTheName()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('name', '');
+        $this->assertEquals('name', $equals->getName());
+    }
+
+    public function testClassSingleMethodGetvalueHasResultStringTheValue()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('', 'value');
+        $this->assertEquals('value', $equals->getValue());
+    }
+
+    /**
+     * @expectedException Exception
+     */
+    public function testClassSingleMethodGetelementsThrowsException()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('', '');
+        $equals->getElements();
+    }
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/Query/LdapTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/Query/LdapTest.php
new file mode 100644 (file)
index 0000000..85e44ba
--- /dev/null
@@ -0,0 +1,181 @@
+<?php
+/**
+ * Test the LDAP query handler.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Require our basic test case definition
+ */
+require_once dirname(__FILE__) . '/../LdapBase.php';
+
+/**
+ * Test the LDAP query handler.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Query_LdapTest extends Horde_Kolab_Server_LdapBase
+{
+
+    public function testMethodConstructHasParameterQueryelementTheQueryCriteria()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('equals', 'equals');
+        $query = new Horde_Kolab_Server_Query_Ldap($equals);
+    }
+
+    public function testMethodConstructHasPostconditionThatTheQueryCriteriaWereSaved()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('equals', 'equals');
+        $query = new Horde_Kolab_Server_Query_Ldap($equals);
+        $this->assertEquals(
+            '(equals=equals)',
+            (string) $query
+        );
+    }
+
+    public function testMethodTostringHasResultStringTheQuery()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('equals', 'equals');
+        $contains = new Horde_Kolab_Server_Query_Element_Equals('contains', 'contains');
+        $or = new Horde_Kolab_Server_Query_Element_Or(array($equals, $contains));
+        $query = new Horde_Kolab_Server_Query_Ldap($or);
+        $this->assertEquals(
+            '(|(equals=equals)(contains=contains))',
+            (string) $query
+        );
+    }
+
+    public function testMethodConvertequealsHasResultNetldapfilter()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('equals', 'equals');
+        $query = new Horde_Kolab_Server_Query_Ldap($equals);
+        $this->assertEquals(
+            '(equals=equals)',
+            $query->convertEquals($equals)->asString()
+        );
+    }
+
+    public function testMethodConvertbeginsHasResultNetldapfilter()
+    {
+        $begins = new Horde_Kolab_Server_Query_Element_Begins('begins', 'begins');
+        $query = new Horde_Kolab_Server_Query_Ldap($begins);
+        $this->assertEquals(
+            '(begins=begins*)',
+            $query->convertBegins($begins)->asString()
+        );
+    }
+
+    public function testMethodConvertendsHasResultNetldapfilter()
+    {
+        $ends = new Horde_Kolab_Server_Query_Element_Ends('ends', 'ends');
+        $query = new Horde_Kolab_Server_Query_Ldap($ends);
+        $this->assertEquals(
+            '(ends=*ends)',
+            $query->convertEnds($ends)->asString()
+        );
+    }
+
+    public function testMethodConvertcontainsHasResultNetldapfilter()
+    {
+        $contains = new Horde_Kolab_Server_Query_Element_Contains('contains', 'contains');
+        $query = new Horde_Kolab_Server_Query_Ldap($contains);
+        $this->assertEquals(
+            '(contains=*contains*)',
+            $query->convertContains($contains)->asString()
+        );
+    }
+
+    public function testMethodConvertlessHasResultNetldapfilter()
+    {
+        $less = new Horde_Kolab_Server_Query_Element_Less('less', 'less');
+        $query = new Horde_Kolab_Server_Query_Ldap($less);
+        $this->assertEquals(
+            '(less<less)',
+            $query->convertLess($less)->asString()
+        );
+    }
+
+    public function testMethodConvertgreaterHasResultNetldapfilter()
+    {
+        $greater = new Horde_Kolab_Server_Query_Element_Greater('greater', 'greater');
+        $query = new Horde_Kolab_Server_Query_Ldap($greater);
+        $this->assertEquals(
+            '(greater>greater)',
+            $query->convertGreater($greater)->asString()
+        );
+    }
+
+    public function testMethodConvertapproxHasResultNetldapfilter()
+    {
+        $approx = new Horde_Kolab_Server_Query_Element_Approx('approx', 'approx');
+        $query = new Horde_Kolab_Server_Query_Ldap($approx);
+        $this->assertEquals(
+            '(approx~=approx)',
+            $query->convertApprox($approx)->asString()
+        );
+    }
+
+    public function testMethodConvertnotHasResultNetldapfilter()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('equals', 'equals');
+        $not = new Horde_Kolab_Server_Query_Element_Not($equals);
+        $query = new Horde_Kolab_Server_Query_Ldap($not);
+        $this->assertEquals(
+            '(!(equals=equals))',
+            $query->convertNot($not)->asString()
+        );
+    }
+
+    public function testMethodConvertandHasResultNetldapfilter()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('equals', 'equals');
+        $contains = new Horde_Kolab_Server_Query_Element_Equals('contains', 'contains');
+        $and = new Horde_Kolab_Server_Query_Element_And(array($equals, $contains));
+        $query = new Horde_Kolab_Server_Query_Ldap($and);
+        $this->assertEquals(
+            '(&(equals=equals)(contains=contains))',
+            $query->convertAnd($and)->asString()
+        );
+    }
+
+    public function testMethodConvertorHasResultNetldapfilter()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('equals', 'equals');
+        $contains = new Horde_Kolab_Server_Query_Element_Equals('contains', 'contains');
+        $or = new Horde_Kolab_Server_Query_Element_Or(array($equals, $contains));
+        $query = new Horde_Kolab_Server_Query_Ldap($or);
+        $this->assertEquals(
+            '(|(equals=equals)(contains=contains))',
+            $query->convertOr($or)->asString()
+        );
+    }
+
+    public function testMethodConvertorThrowsExceptionIfLessThanTwoElementsWereProvided()
+    {
+        $equals = new Horde_Kolab_Server_Query_Element_Equals('equals', 'equals');
+        $or = new Horde_Kolab_Server_Query_Element_Or(array($equals));
+        $query = new Horde_Kolab_Server_Query_Ldap($or);
+        try {
+            $query->convertOr($or)->asString();
+            $this->fail('No exception!');
+        } catch (Horde_Kolab_Server_Exception $e) {
+            $this->assertEquals(Horde_Kolab_Server_Exception::INVALID_QUERY, $e->getCode());
+        }
+    }
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/Result/LdapTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/Result/LdapTest.php
new file mode 100644 (file)
index 0000000..4da773a
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+/**
+ * Test the LDAP result handler.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Prepare the test setup.
+ */
+require_once dirname(__FILE__) . '/../Autoload.php';
+
+/**
+ * Test the LDAP result handler.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Result_LdapTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        if (!extension_loaded('ldap') && !@dl('ldap.' . PHP_SHLIB_SUFFIX)) {
+            $this->markTestSuiteSkipped('Ldap extension is missing!');
+        };
+
+        if (!class_exists('Net_LDAP2')) {
+            $this->markTestSuiteSkipped('PEAR package Net_LDAP2 is not installed!');
+        }
+    }
+
+    public function testMethodConstructHasParameterNetldap2searchSearchResult()
+    {
+        $search = $this->getMock(
+            'Net_LDAP2_Search', array(), array(), '', false
+        );
+        $result = new Horde_Kolab_Server_Result_Ldap($search);
+    }
+
+
+    public function testMethodCountHasResultIntTheNumberOfElementsFound()
+    {
+        $search = $this->getMock(
+            'Net_LDAP2_Search', array('count'), array(), '', false
+        );
+        $search->expects($this->exactly(1))
+            ->method('count')
+            ->will($this->returnValue(1));
+        $result = new Horde_Kolab_Server_Result_Ldap($search);
+        $this->assertEquals(1, $result->count());
+    }
+
+    public function testMethodSizelimitexceededHasResultBooleanIndicatingIfTheSearchSizeLimitWasHit()
+    {
+        $search = $this->getMock(
+            'Net_LDAP2_Search', array('sizeLimitExceeded'), array(), '', false
+        );
+        $search->expects($this->exactly(1))
+            ->method('sizeLimitExceeded')
+            ->will($this->returnValue(true));
+        $result = new Horde_Kolab_Server_Result_Ldap($search);
+        $this->assertTrue($result->sizeLimitExceeded());
+    }
+
+    public function testMethodAsarrayHasResultArrayWithTheSearchResults()
+    {
+        $search = $this->getMock(
+            'Net_LDAP2_Search', array('as_struct'), array(), '', false
+        );
+        $search->expects($this->exactly(1))
+            ->method('as_struct')
+            ->will($this->returnValue(array('a' => 'a')));
+        $result = new Horde_Kolab_Server_Result_Ldap($search);
+        $this->assertEquals(array('a' => 'a'), $result->asArray());
+    }
+}
index f2f62b0..b8c8df1 100644 (file)
@@ -65,12 +65,6 @@ class Horde_Kolab_Server_Scenario extends PHPUnit_Extensions_Story_TestCase
             }
             break;
         case 'several Kolab servers':
-            foreach ($this->getEnvironments() as $environment) {
-                $this->prepareInjector($environment);
-                $this->prepareKolabServerConfiguration($environment);
-                $this->prepareKolabServer($environment);
-            }
-            break;
         case 'the test environments':
             $this->initializeEnvironments();
             break;
@@ -338,6 +332,23 @@ class Horde_Kolab_Server_Scenario extends PHPUnit_Extensions_Story_TestCase
     }
 
     /**
+     * Prepare the log handler for the given environment.
+     *
+     * @param string $environment The name of the environment.
+     *
+     * @return NULL
+     */
+    public function prepareLogger($environment)
+    {
+        $logger  = new Horde_Log_Logger();
+        $handler = new Horde_Log_Handler_Mock();
+        $logger->addHandler($handler);
+
+        $this->world['injector'][$environment]->setInstance('Horde_Log_Logger',
+                                                            $logger);
+    }
+
+    /**
      * Prepare the server configuration for the given environment.
      *
      * @param string $environment The name of the environment.
@@ -401,6 +412,7 @@ class Horde_Kolab_Server_Scenario extends PHPUnit_Extensions_Story_TestCase
     public function initializeEnvironment($environment)
     {
         $this->prepareInjector($environment);
+        $this->prepareLogger($environment);
         $this->prepareKolabServerConfiguration($environment);
         $this->prepareKolabServer($environment);
     }
@@ -1045,11 +1057,21 @@ class Horde_Kolab_Server_Scenario extends PHPUnit_Extensions_Story_TestCase
     }
 
     /**
+     * Setup function.
+     *
+     * @return NULL.
+     */
+    protected function setUp()
+    {
+        $this->added = array();
+    }
+
+    /**
      * Cleanup function.
      *
      * @return NULL.
      */
-    public function tearDown()
+    protected function tearDown()
     {
         if (isset($this->added)) {
             $added = array_reverse($this->added);
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/Server/FactoryTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/Server/FactoryTest.php
new file mode 100644 (file)
index 0000000..4289a9e
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+/**
+ * Test the server factory interface.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Prepare the test setup.
+ */
+require_once dirname(__FILE__) . '/../Autoload.php';
+
+/**
+ * Test the server factory interface.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Server_FactoryTest extends Horde_Kolab_Server_Scenario
+{
+    public function testMethodSetupHasPostconditionThatAObjectHandlerOfTypeBaseIsBoundToObjects()
+    {
+        $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+        Horde_Kolab_Server_Factory::setup(array(), $injector);
+        $this->assertType(
+            'Horde_Kolab_Server_Objects_Base',
+            $injector->getInstance('Horde_Kolab_Server_Objects')
+        );
+    }
+
+    public function testMethodSetupHasPostconditionThatASchemaHandlerOfTypeBaseIsBoundToSchema()
+    {
+        $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+        Horde_Kolab_Server_Factory::setup(array(), $injector);
+        $this->assertType(
+            'Horde_Kolab_Server_Schema_Base',
+            $injector->getInstance('Horde_Kolab_Server_Schema')
+        );
+    }
+
+    public function testMethodSetupHasPostconditionThatASearchHandlerOfTypeBaseIsBoundToSearch()
+    {
+        $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+        Horde_Kolab_Server_Factory::setup(array(), $injector);
+        $this->assertType(
+            'Horde_Kolab_Server_Search_Base',
+            $injector->getInstance('Horde_Kolab_Server_Search')
+        );
+    }
+
+    public function testMethodSetupHasPostconditionThatAStructureOfTypeBaseIsBoundToStructure()
+    {
+        $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+        Horde_Kolab_Server_Factory::setup(array(), $injector);
+        $this->assertType(
+            'Horde_Kolab_Server_Structure_Kolab',
+            $injector->getInstance('Horde_Kolab_Server_Structure')
+        );
+    }
+
+    public function testMethodSetupHasPostconditionThatAStructureHandlerOfTypeLdapIsBoundToStructureIfConfiguredThatWay()
+    {
+        $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+        Horde_Kolab_Server_Factory::setup(
+            array('structure' => array('driver' => 'ldap')),
+            $injector
+        );
+        $this->assertType(
+            'Horde_Kolab_Server_Structure_Ldap',
+            $injector->getInstance('Horde_Kolab_Server_Structure')
+        );
+    }
+
+    public function testMethodSetupHasPostconditionThatAServerOfTypeLdapIsBoundToServer()
+    {
+        $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+        Horde_Kolab_Server_Factory::setup(array(), $injector);
+        $this->assertType(
+            'Horde_Kolab_Server_Ldap',
+            $injector->getInstance('Horde_Kolab_Server')
+        );
+    }
+
+    public function testMethodSetupHasPostconditionThatAServerOfTypeLdapIsBoundToServerIfConfiguredThatWay()
+    {
+        $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+        Horde_Kolab_Server_Factory::setup(
+            array('driver' => 'file', 'params' => array('file' => '/tmp/nix')),
+            $injector
+        );
+        $this->assertType(
+            'Horde_Kolab_Server_File',
+            $injector->getInstance('Horde_Kolab_Server')
+        );
+    }
+
+    public function testMethodSingletonReturnsTheSameInstanceWithTheSameParameters()
+    {
+        Horde_Kolab_Server_Factory::singleton();
+    }
+
+    public function testMethodSingletonReturnsDifferentInstancesWithDifferentParameters()
+    {
+        Horde_Kolab_Server_Factory::singleton();
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/Server/InterfaceTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/Server/InterfaceTest.php
new file mode 100644 (file)
index 0000000..c8fe164
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Test the server interface.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Prepare the test setup.
+ */
+require_once dirname(__FILE__) . '/../Autoload.php';
+
+/**
+ * Test the server interface.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Server_InterfaceTest extends Horde_Kolab_Server_Scenario
+{
+
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/Server/LdapTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/Server/LdapTest.php
new file mode 100644 (file)
index 0000000..c4e070c
--- /dev/null
@@ -0,0 +1,363 @@
+<?php
+/**
+ * Test the LDAP driver.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Require our basic test case definition
+ */
+require_once dirname(__FILE__) . '/../LdapBase.php';
+
+/**
+ * Test the LDAP backend.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Server_LdapTest extends Horde_Kolab_Server_LdapBase
+{
+    public function setUp()
+    {
+        parent::setUp();
+
+        $this->logger = new Horde_Log_Handler_Mock();
+
+        $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+        Horde_Kolab_Server_Factory::setup(
+            array('logger' => new Horde_Log_Logger($this->logger)), $injector
+        );
+        $this->server = $injector->getInstance('Horde_Kolab_Server');
+    }
+
+    public function testMethodConnectuidHasPostconditionThatTheUidIsSetIfTheConnectionWasSuccessful()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('bind'));
+        $ldap->expects($this->exactly(1))
+            ->method('bind')
+            ->will($this->returnValue(true));
+        $this->server->setLdap($ldap);
+        $this->server->connectUid('test', 'test');
+        $this->assertEquals('test', $this->server->uid);
+    }
+
+    public function testMethodConnectuidThrowsExceptionIfTheConnectionFailed()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('bind'));
+        $ldap->expects($this->exactly(1))
+            ->method('bind')
+            ->will($this->returnValue(PEAR::raiseError('Bind failed!')));
+        $this->server->setLdap($ldap);
+        try {
+            $this->server->connectUid('test', 'test');
+            $this->fail('No exception!');
+        } catch (Exception $e) {
+            $this->assertEquals('Bind failed!', $e->getMessage());
+            $this->assertEquals(Horde_Kolab_Server_Exception::BIND_FAILED, $e->getCode());
+        }
+    }
+
+    public function testMethodSearchHasPostconditionThatItIsPossibleToTestTheLastResultForAnExceededSearchSizeLimit()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('search'));
+        $ldap->expects($this->exactly(1))
+            ->method('search')
+            ->will($this->returnValue(new Search_Mock(array(array('dn' => 'test')), true)));
+        $this->server->setLdap($ldap);
+        $this->server->search('filter');
+        $this->assertTrue($this->server->sizeLimitExceeded());
+    }
+
+    public function testMethodSearchReturnsArraySearchResult()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('search'));
+        $ldap->expects($this->exactly(1))
+            ->method('search')
+            ->will($this->returnValue(new Search_Mock(array(array('dn' => 'test')))));
+        $this->server->setLdap($ldap);
+        $this->assertEquals(array(array('dn' => 'test')), $this->server->search('filter'));
+    }
+
+    public function testMethodSearchReturnsArrayMappedSearchResultIfMappingIsActivated()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('search'));
+        $ldap->expects($this->exactly(1))
+            ->method('search')
+            ->will($this->returnValue(new Search_Mock(array(array('dn2' => 'test')))));
+        $this->server->setLdap($ldap);
+        $this->server->setParams(array('map' => array('dn' => 'dn2')));
+        $this->assertEquals(
+            array(array('dn' => 'test')),
+            $this->server->search(
+                'filter',
+                array('attributes' => array('dn'))
+            )
+        );
+    }
+
+    public function testMethodSearchThrowsExceptionIfTheSearchFailed()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('search'));
+        $ldap->expects($this->exactly(1))
+            ->method('search')
+            ->will($this->returnValue(PEAR::raiseError('Search failed!')));
+        $this->server->setLdap($ldap);
+        try {
+            $this->assertEquals(array('dn' => 'test'), $this->server->search('filter'));
+            $this->fail('No exception!');
+        } catch (Exception $e) {
+            $this->assertEquals('Search failed!', $e->getMessage());
+            $this->assertEquals(Horde_Kolab_Server_Exception::SYSTEM, $e->getCode());
+        }
+    }
+
+    public function testMethodReadReturnsArrayReadResult()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('search'));
+        $ldap->expects($this->exactly(1))
+            ->method('search')
+            ->will($this->returnValue(new Search_Mock(array(array('dn' => 'test')))));
+        $this->server->setLdap($ldap);
+        $this->assertEquals(array('dn' => 'test'), $this->server->read('test'));
+    }
+
+    public function testMethodReadThrowsExceptionIfTheObjectWasNotFound()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('search'));
+        $ldap->expects($this->exactly(1))
+            ->method('search')
+            ->will($this->returnValue(new Search_Mock(array())));
+        $this->server->setLdap($ldap);
+        try {
+            $this->assertEquals(array(), $this->server->read('test', array('dn')));
+            $this->fail('No exception!');
+        } catch (Exception $e) {
+            $this->assertEquals('Empty result!', $e->getMessage());
+            $this->assertEquals(Horde_Kolab_Server_Exception::EMPTY_RESULT, $e->getCode());
+        }
+    }
+
+    public function testMethodSaveHasPostconditionThatTheEntryWasSaved()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('add'));
+        $ldap->expects($this->exactly(1))
+            ->method('add')
+            ->with(new PHPUnit_Framework_Constraint_IsInstanceOf('Net_LDAP2_Entry'));
+        $this->server->setLdap($ldap);
+        $this->server->save('test', array('add' => array('dn' => 'test')));
+    }
+
+    public function testMethodSaveThrowsExceptionIfDataToSaveIsNoArray()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('add'));
+        $this->server->setLdap($ldap);
+        try {
+            $this->server->save('test', array('add' => 'hello'));
+            $this->fail('No exception!');
+        } catch (Exception $e) {
+            $this->assertEquals('Unable to create fresh entry: Parameter $attrs needs to be an array!', $e->getMessage());
+            $this->assertEquals(Horde_Kolab_Server_Exception::SYSTEM, $e->getCode());
+        }
+
+    }
+
+    public function testMethodSaveThrowsExceptionIfSavingDataFailed()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('add'));
+        $ldap->expects($this->exactly(1))
+            ->method('add')
+            ->will($this->returnValue(PEAR::raiseError('Saving failed!')));
+        $this->server->setLdap($ldap);
+        try {
+            $this->server->save('test', array('add' => array('dn' => 'test')));
+            $this->fail('No exception!');
+        } catch (Exception $e) {
+            $this->assertEquals('Saving failed!', $e->getMessage());
+            $this->assertEquals(Horde_Kolab_Server_Exception::SYSTEM, $e->getCode());
+        }
+    }
+
+    public function testMethodDeleteHasPostconditionThatTheEntryWasDeleted()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('delete'));
+        $ldap->expects($this->exactly(1))
+            ->method('delete')
+            ->with('test');
+        $this->server->setLdap($ldap);
+        $this->server->delete('test');
+    }
+
+    public function testMethodDeleteThrowsExceptionIfDeletingDataFailed()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('delete'));
+        $ldap->expects($this->exactly(1))
+            ->method('delete')
+            ->will($this->returnValue(PEAR::raiseError('Deleting failed!')));
+        $this->server->setLdap($ldap);
+        try {
+            $this->server->delete('test');
+            $this->fail('No exception!');
+        } catch (Exception $e) {
+            $this->assertEquals('Deleting failed!', $e->getMessage());
+            $this->assertEquals(Horde_Kolab_Server_Exception::SYSTEM, $e->getCode());
+        }
+    }
+
+    public function testMethodRenameHasPostconditionThatTheEntryWasRenamed()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('move'));
+        $ldap->expects($this->exactly(1))
+            ->method('move')
+            ->with('test', 'new');
+        $this->server->setLdap($ldap);
+        $this->server->rename('test', 'new');
+    }
+
+    public function testMethodRenameThrowsExceptionIfRenamingDataFailed()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('move'));
+        $ldap->expects($this->exactly(1))
+            ->method('move')
+            ->will($this->returnValue(PEAR::raiseError('Renaming failed!')));
+        $this->server->setLdap($ldap);
+        try {
+            $this->server->rename('test', 'new');
+            $this->fail('No exception!');
+        } catch (Exception $e) {
+            $this->assertEquals('Renaming failed!', $e->getMessage());
+            $this->assertEquals(Horde_Kolab_Server_Exception::SYSTEM, $e->getCode());
+        }
+    }
+
+    public function testMethodGetobjectclassesHasResultArrayWithLowerCaseObjectclassNames()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('search'));
+        $ldap->expects($this->exactly(1))
+            ->method('search')
+            ->will($this->returnValue(new Search_Mock(array(array(Horde_Kolab_Server_Object::ATTRIBUTE_OC => array('test', 'PERSON', 'Last'))))));
+        $this->server->setLdap($ldap);
+        $this->assertEquals(
+            array('test', 'person', 'last'),
+            $this->server->getObjectClasses('test')
+        );
+    }
+
+    public function testMethodGetobjectclassesThrowsExceptionIfTheObjectHasNoAttributeObjectclass()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('search'));
+        $ldap->expects($this->exactly(1))
+            ->method('search')
+            ->will($this->returnValue(new Search_Mock(array(array('dummy' => array('test', 'PERSON', 'Last'))))));
+        $this->server->setLdap($ldap);
+        try {
+            $this->server->getObjectClasses('test');
+            $this->fail('No exception!');
+        } catch (Exception $e) {
+            $this->assertEquals('The object test has no objectClass attribute!', $e->getMessage());
+            $this->assertEquals(Horde_Kolab_Server_Exception::SYSTEM, $e->getCode());
+        }
+    }
+
+    public function testMethodGetschemaReturnsArrayWithADescriptionOfAllObjectClasses()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('schema'));
+        $ldap->expects($this->exactly(1))
+            ->method('schema')
+            ->will($this->returnValue(array('schema' => 'dummy')));
+        $this->server->setLdap($ldap);
+        $this->assertEquals(
+            array('schema' => 'dummy'),
+            $this->server->getSchema()
+        );
+    }
+
+    public function testMethodGetschemaThrowsExceptionIfTheSchemaRetrievalFailed()
+    {
+        $ldap = $this->getMock('Net_LDAP2', array('schema'));
+        $ldap->expects($this->exactly(1))
+            ->method('schema')
+            ->will($this->returnValue(PEAR::raiseError('Schema failed!')));
+        $this->server->setLdap($ldap);
+        try {
+            $this->server->getSchema();
+            $this->fail('No exception!');
+        } catch (Exception $e) {
+            $this->assertEquals('Schema failed!', $e->getMessage());
+            $this->assertEquals(Horde_Kolab_Server_Exception::SYSTEM, $e->getCode());
+        }
+    }
+
+    /**
+     * 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'); */
+/*         if ($classes instanceOf PEAR_Error) { */
+/*             $this->assertEquals('', $classes->getMessage()); */
+/*         } */
+/*         $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); */
+/*     } */
+
+}
+
+
+class Search_Mock
+{
+    public function __construct($result, $limit = false)
+    {
+        $this->result = $result;
+        $this->limit  = $limit;
+    }
+    public function as_struct()
+    {
+        return $this->result;
+    }
+    public function sizeLimitExceeded()
+    {
+        return $this->limit;
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/Server/LoggedTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/Server/LoggedTest.php
new file mode 100644 (file)
index 0000000..1957bb2
--- /dev/null
@@ -0,0 +1,167 @@
+<?php
+/**
+ * Test the LDAP driver.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Require our basic test case definition
+ */
+require_once dirname(__FILE__) . '/../LdapBase.php';
+
+/**
+ * Test the LDAP backend.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Server_LoggedTest extends Horde_Kolab_Server_LdapBase
+{
+    public function setUp()
+    {
+        parent::setUp();
+
+        $this->logger = new Horde_Log_Handler_Mock();
+        $this->server = $this->getMock('Horde_Kolab_Server');
+        $this->logged = new Horde_Kolab_Server_Logged(
+            $this->server,
+            new Horde_Log_Logger($this->logger)
+        );
+    }
+
+    public function testMethodConnectguidDelegatesToServer()
+    {
+        $this->server->expects($this->exactly(1))
+            ->method('connectGuid')
+            ->with('user', 'pass');
+        $this->logged->connectGuid('user', 'pass');
+    }
+
+    public function testMethodReadDelegatesToServer()
+    {
+        $this->server->expects($this->exactly(1))
+            ->method('read')
+            ->with('guid');
+        $this->logged->read('guid');
+    }
+
+    public function testMethodReadattributesDelegatesToServer()
+    {
+        $this->server->expects($this->exactly(1))
+            ->method('readAttributes')
+            ->with('guid', array('a'));
+        $this->logged->readAttributes('guid', array('a'));
+    }
+
+    public function testMethodFindDelegatesToServer()
+    {
+        $query = $this->getMock(
+            'Horde_Kolab_Server_Query', array(), array(), '', false
+        );
+        $this->server->expects($this->exactly(1))
+            ->method('find')
+            ->with($query);
+        $this->logged->find($query);
+    }
+
+    public function testMethodFindbelowDelegatesToServer()
+    {
+        $query = $this->getMock(
+            'Horde_Kolab_Server_Query', array(), array(), '', false
+        );
+        $this->server->expects($this->exactly(1))
+            ->method('findBelow')
+            ->with($query, 'none');
+        $this->logged->findBelow($query, 'none');
+    }
+
+    public function testMethodSaveDelegatesToServer()
+    {
+        $this->server->expects($this->exactly(1))
+            ->method('save')
+            ->with('a', array('a' => 'a'));
+        $this->logged->save('a', array('a' => 'a'));
+    }
+
+    public function testMethodAddDelegatesToServer()
+    {
+        $this->server->expects($this->exactly(1))
+            ->method('add')
+            ->with('a', array('a' => 'a'));
+        $this->logged->add('a', array('a' => 'a'));
+    }
+
+    public function testMethodDeleteDelegatesToServer()
+    {
+        $this->server->expects($this->exactly(1))
+            ->method('delete')
+            ->with('a');
+        $this->logged->delete('a');
+    }
+
+    public function testMethodRenameDelegatesToServer()
+    {
+        $this->server->expects($this->exactly(1))
+            ->method('rename')
+            ->with('a', 'b');
+        $this->logged->rename('a', 'b');
+    }
+
+    public function testMethodGetschemaDelegatesToServer()
+    {
+        $this->server->expects($this->exactly(1))
+            ->method('getSchema');
+        $this->logged->getSchema();
+    }
+
+    public function testMethodSaveHasPostconditionThatTheEventWasLogged()
+    {
+        $this->logged->save('a', array('a' => 'a'));
+        $this->assertEquals(
+            $this->logger->events[0]['message'],
+            'The object "a" has been successfully saved!'
+        );
+    }
+
+    public function testMethodAddHasPostconditionThatTheEventWasLogged()
+    {
+        $this->logged->add('a', array('a' => 'a'));
+        $this->assertEquals(
+            $this->logger->events[0]['message'],
+            'The object "a" has been successfully added!'
+        );
+    }
+
+    public function testMethodDeleteHasPostconditionThatTheEventWasLogged()
+    {
+        $this->logged->delete('a');
+        $this->assertEquals(
+            $this->logger->events[0]['message'],
+            'The object "a" has been successfully deleted!'
+        );
+    }
+
+    public function testMethodRenameHasPostconditionThatTheEventWasLogged()
+    {
+        $this->logged->rename('a', 'b');
+        $this->assertEquals(
+            $this->logger->events[0]['message'],
+            'The object "a" has been successfully renamed to "b"!'
+        );
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/Server/SearchTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/Server/SearchTest.php
new file mode 100644 (file)
index 0000000..3980296
--- /dev/null
@@ -0,0 +1,221 @@
+<?php
+/**
+ * Test the search handler driver.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Prepare the test setup.
+ */
+require_once dirname(__FILE__) . '/../Autoload.php';
+
+/**
+ * Test the search handler.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Server_SearchTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        if (!extension_loaded('ldap') && !@dl('ldap.' . PHP_SHLIB_SUFFIX)) {
+            $this->markTestSuiteSkipped('Ldap extension is missing!');
+        };
+
+        if (!class_exists('Net_LDAP2')) {
+            $this->markTestSuiteSkipped('PEAR package Net_LDAP2 is not installed!');
+        }
+
+        $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+        Horde_Kolab_Server_Factory::setup(array(), $injector);
+        $this->server = $injector->getInstance('Horde_Kolab_Server');
+    }
+
+    /**
+     * 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); */
+/*     } */
+
+}
+
+
+class Search_Mock
+{
+    public function __construct($result, $limit = false)
+    {
+        $this->result = $result;
+        $this->limit  = $limit;
+    }
+    public function as_struct()
+    {
+        return $this->result;
+    }
+    public function sizeLimitExceeded()
+    {
+        return $this->limit;
+    }
+}
\ No newline at end of file
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/Server/ServerTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/Server/ServerTest.php
new file mode 100644 (file)
index 0000000..b879a14
--- /dev/null
@@ -0,0 +1,329 @@
+
+<?php
+/**
+ * Test the server class.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Prepare the test setup.
+ */
+require_once dirname(__FILE__) . '/../Autoload.php';
+
+/**
+ * Tests for the main server class.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Server_ServerTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * Provide a mock server.
+     *
+     * @return Horde_Kolab_Server The mock server.
+     */
+    protected function getMockServer()
+    {
+        $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+        $config = new stdClass;
+        $config->driver = 'none';
+        $injector->setInstance('Horde_Kolab_Server_Config', $config);
+        $injector->bindFactory('Horde_Kolab_Server_Structure',
+                               'Horde_Kolab_Server_Factory',
+                               'getStructure');
+        $injector->bindFactory('Horde_Kolab_Server',
+                               'Horde_Kolab_Server_Factory',
+                               'getServer');
+        return $injector->getInstance('Horde_Kolab_Server');
+    }
+
+    /**
+     * The generating a uid for an object.
+     *
+     * @return NULL
+     */
+    public function testGenerateUid()
+    {
+        $ks   = $this->getMockServer();
+        $user = new Horde_Kolab_Server_Object($ks, null, null);
+        $this->assertEquals(preg_replace('/[0-9a-f]*/', '', $user->get(Horde_Kolab_Server_Object::ATTRIBUTE_UID)), '');
+    }
+
+    /**
+     * Test creating the server object.
+     *
+     * @return NULL
+     */
+    public function testCreation()
+    {
+        try {
+            $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+            $config = new stdClass;
+            $config->driver = 'dummy';
+            $injector->setInstance('Horde_Kolab_Server_Config', $config);
+            $injector->bindFactory('Horde_Kolab_Server_Structure',
+                                   'Horde_Kolab_Server_Factory',
+                                   'getStructure');
+            $injector->bindFactory('Horde_Kolab_Server',
+                                   'Horde_Kolab_Server_Factory',
+                                   'getServer');
+            Horde_Kolab_Server_Factory::getServer($injector);
+            $this->assertFail('No error!');
+        } catch (Horde_Kolab_Server_Exception $e) {
+            $this->assertEquals('Server type definition "Horde_Kolab_Server_Dummy" missing.',
+                                $e->getMessage());
+        }
+    }
+
+    /**
+     * 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   = $this->getMockServer();
+        $user = $ks->fetch('test');
+        $this->assertEquals('Horde_Kolab_Server_Object_Kolab_User', get_class($user));
+
+        $ks   = $this->getMockServer();
+        $user = $ks->fetch();
+        $this->assertEquals('Horde_Kolab_Server_Object_Kolab_User', get_class($user));
+    }
+
+    /**
+     * Test listing objects.
+     *
+     * @return NULL
+     */
+    public function testList()
+    {
+        $ks   = $this->getMockServer();
+        $hash = $ks->listHash('Horde_Kolab_Server_Object');
+        $this->assertEquals($hash, array());
+
+        $ks   = $this->getMockServer();
+        $hash = $ks->listHash('Horde_Kolab_Server_Object');
+        $this->assertEquals($hash, array());
+    }
+
+}
+
+/**
+ * A dummy class to test the original abstract class.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_None extends Horde_Kolab_Server
+{
+    /**
+     * 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.
+     */
+    public function read($uid, $attrs = null)
+    {
+        return false;
+    }
+
+    /**
+     * Stub for saving object data.
+     *
+     * @param string $uid   The object to retrieve.
+     * @param string $attrs Restrict to these attributes.
+     *
+     * @return array|PEAR_Error An array of attributes.
+     */
+    public function save($uid, $data, $exists = false)
+    {
+        throw new Horde_Kolab_Server_Exception('Not implemented!');
+    }
+
+    /**
+     * Stub for deleting an object.
+     *
+     * @param string $uid The UID of the object to be deleted.
+     *
+     * @return boolean True if saving succeeded.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function delete($uid)
+    {
+        throw new Horde_Kolab_Server_Exception('Not implemented!');
+    }
+
+    /**
+     * Stub for renaming an object.
+     *
+     * @param string $uid The UID of the object to be renamed.
+     * @param string $new The new UID of the object.
+     *
+     * @return boolean True if renaming succeeded.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function rename($uid, $new)
+    {
+        throw new Horde_Kolab_Server_Exception('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.
+     */
+    public function determineType($uid)
+    {
+        return 'Horde_Kolab_Server_Object_Kolab_User';
+    }
+
+    /**
+     * 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.
+     */
+    public function listObjects($type, $params = null)
+    {
+        return array();
+    }
+
+    /**
+     * 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.
+     */
+    public function generateServerUid($type, $id, $info)
+    {
+        return $id;
+    }
+
+    /**
+     * Return the root of the UID values on this server.
+     *
+     * @return string The base UID on this server (base DN on ldap).
+     */
+    public function getBaseUid()
+    {
+        return '';
+    }
+
+    /**
+     * Identify the UID for the first object found using the specified
+     * search criteria.
+     *
+     * @param array $criteria The search parameters as array.
+     * @param int   $restrict A Horde_Kolab_Server::RESULT_* result restriction.
+     *
+     * @return boolean|string|array The UID(s) or false if there was no result.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function uidForSearch($criteria,
+                                 $restrict = Horde_Kolab_Server::RESULT_SINGLE)
+    {
+        /* In the default class we just return false */
+        return false;
+    }
+
+    /**
+     * Identify the GID for the first group found using the specified
+     * search criteria
+     *
+     * @param array $criteria The search parameters as array.
+     * @param int   $restrict A Horde_Kolab_Server::RESULT_* result restriction.
+     *
+     * @return boolean|string|array The GID(s) or false if there was no result.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function gidForSearch($criteria,
+                                 $restrict = Horde_Kolab_Server::RESULT_SINGLE)
+    {
+        /* In the default class we just return false */
+        return false;
+    }
+
+    /**
+     * Identify attributes for the objects found using a filter.
+     *
+     * @param array $criteria The search parameters as array.
+     * @param array $attrs    The attributes to retrieve.
+     * @param int   $restrict A Horde_Kolab_Server::RESULT_* result restriction.
+     *
+     * @return array The results.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function attrsForSearch($criteria, $attrs,
+                                   $restrict = Horde_Kolab_Server::RESULT_SINGLE)
+    {
+        /* In the default class we just return an empty array */
+        return array();
+    }
+
+    /**
+     * Find object data matching a given set of criteria.
+     *
+     * @param array  $criteria The criteria for the search.
+     * @param string $params   Additional search parameters.
+     *
+     * @return array The result array.
+     *
+     * @throws Horde_Kolab_Server_Exception
+     */
+    public function find($criteria, $params = array())
+    {
+        /* In the default class we just return an empty array */
+        return array();
+    }
+
+    /**
+     * Returns the set of objects supported by this server.
+     *
+     * @return array An array of supported objects.
+     */
+    public function getSupportedObjects()
+    {
+        return array('Horde_Kolab_Server_Object');
+    }
+}
diff --git a/framework/Kolab_Server/test/Horde/Kolab/Server/Server/TestTest.php b/framework/Kolab_Server/test/Horde/Kolab/Server/Server/TestTest.php
new file mode 100644 (file)
index 0000000..3123526
--- /dev/null
@@ -0,0 +1,626 @@
+<?php
+/**
+ * Test the test driver.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Server
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Prepare the test setup.
+ */
+require_once dirname(__FILE__) . '/../Autoload.php';
+
+/**
+ * Test the test backend.
+ *
+ * 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 <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Kolab_Server_Server_TestTest extends Horde_Kolab_Server_Scenario
+{
+
+    /** The file based mock environment */
+    const ENVIRONMENT_FILE = 'file';
+
+    /**
+     * The environments we provide to the test.
+     *
+     * @var array
+     */
+    protected $_environments = array(
+        self::ENVIRONMENT_MOCK,
+        self::ENVIRONMENT_FILE
+    );
+
+    /**
+     * Prepare the server configuration for the given environment.
+     *
+     * @param string $environment The name of the environment.
+     *
+     * @return NULL
+     */
+    public function prepareKolabServerConfiguration($environment)
+    {
+        switch ($environment) {
+        case self::ENVIRONMENT_FILE:
+            /** Prepare a Kolab test server */
+            $config = new stdClass;
+            $config->driver = 'file';
+            $config->params = array(
+                'file'     => Horde::getTempFile('fileTest'),
+                'basedn'   => 'dc=example,dc=org',
+                'hashtype' => 'plain'
+            );
+            $this->world['injector'][$environment]->setInstance('Horde_Kolab_Server_Config', $config);
+            break;
+        default:
+            return parent::prepareKolabServerConfiguration($environment);
+        }
+    }
+
+    /**
+     * Set up testing.
+     *
+     * @return NULL
+     */
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $this->initializeEnvironments();
+        $this->servers = $this->getKolabServers();
+        foreach ($this->servers as $server) {
+            $this->addBasicUsersToServer($server);
+        }
+    }
+
+    /**
+     * Test search base.
+     *
+     * @return NULL
+     */
+    public function testSearchBase()
+    {
+        foreach ($this->servers as $server) {
+            $result = $server->search(
+                '(' . Horde_Kolab_Server_Object::ATTRIBUTE_OC
+                . '=' . Horde_Kolab_Server_Object::OBJECTCLASS_TOP . ')',
+                array(Horde_Kolab_Server_Object::ATTRIBUTE_OC));
+            $this->assertEquals(13, count($result));
+      
+            $result = $server->search(
+                '(' . Horde_Kolab_Server_Object::ATTRIBUTE_OC
+                . '=' . Horde_Kolab_Server_Object::OBJECTCLASS_TOP . ')',
+                array(Horde_Kolab_Server_Object::ATTRIBUTE_OC),
+                'cn=internal,dc=example,dc=org');
+            $this->assertEquals(4, count($result));
+        }
+    }
+
+    /**
+     * Test sorting.
+     *
+     * @return NULL
+     */
+    public function testSorting()
+    {
+        foreach ($this->servers as $server) {
+
+/*         $result = $server->search('(mail=*)', array('mail')); */
+/*         $this->assertEquals(5, count($result)); */
+/*         $server->sort($result, 'mail'); */
+/*         foreach ($result as $object) { */
+/*             if (isset($object['data']['dn'])) { */
+/*                 switch ($object['data']['dn']) { */
+/*                 case 'cn=Test Address,cn=external,dc=example,dc=org': */
+/*                     $this->assertContains('address@example.org', $object['data']['mail']); */
+/*                     break; */
+/*                 case '': */
+/*                     $this->assertContains('address@example.org', $object['data']['mail']); */
+/*                     break; */
+/*                 } */
+/*             } */
+/*         } */
+        }
+    }
+
+    /**
+     * Test listing objects.
+     *
+     * @return NULL
+     */
+    public function testListObjects()
+    {
+        foreach ($this->servers as $server) {
+            $filter     = '(&(objectClass=kolabInetOrgPerson)(uid=*)(mail=*)(sn=*))';
+            $attributes = array(
+                Horde_Kolab_Server_Object_Kolabinetorgperson::ATTRIBUTE_SN,
+                Horde_Kolab_Server_Object_Kolabinetorgperson::ATTRIBUTE_CN,
+                Horde_Kolab_Server_Object_Kolabinetorgperson::ATTRIBUTE_UID,
+                Horde_Kolab_Server_Object_Kolabinetorgperson::ATTRIBUTE_MAIL,
+                Horde_Kolab_Server_Object_Kolabinetorgperson::ATTRIBUTE_DELETED,
+            );
+
+            $sort   = Horde_Kolab_Server_Object_Kolabinetorgperson::ATTRIBUTE_SN;
+            $result = $server->search($filter);
+            $this->assertEquals(2, count($result));
+
+            $result = $server->listObjects('Horde_Kolab_Server_Object_Kolab_User');
+            $this->assertEquals('Horde_Kolab_Server_Object_Kolab_User', get_class(array_shift($result)));
+
+            $result = $server->listObjects('Horde_Kolab_Server_Object_Kolabsharedfolder');
+            $this->assertEquals(1, count($result));
+            $this->assertEquals('Horde_Kolab_Server_Object_Kolabsharedfolder', get_class(array_shift($result)));
+        }
+    }
+
+    /**
+     * Test handling of object classes.
+     *
+     * @return NULL
+     */
+    public function testGetObjectClasses()
+    {
+        foreach ($this->servers as $server) {
+            $classes = $server->getObjectClasses('cn=Gunnar Wrobel,dc=example,dc=org');
+            $this->assertContains('top', $classes);
+            $this->assertContains('kolabinetorgperson', $classes);
+
+            try {
+                $classes = $server->getObjectClasses('cn=DOES NOT EXIST,dc=example,dc=org');
+            } catch (Horde_Kolab_Server_Exception $classes) {
+            }
+            $this->assertError($classes,
+                               'No such object: cn=DOES NOT EXIST,dc=example,dc=org');
+
+            $classes = $server->getObjectClasses('cn=The Administrator,dc=example,dc=org');
+            $this->assertContains('kolabinetorgperson', $classes);
+        }
+    }
+
+    /**
+     * Test handling of object types.
+     *
+     * @return NULL
+     */
+    public function testDetermineType()
+    {
+        foreach ($this->servers as $server) {
+            $type = $server->determineType('cn=empty.group@example.org,dc=example,dc=org');
+            $this->assertEquals('Horde_Kolab_Server_Object_Kolabgroupofnames', $type);
+
+            $type = $server->determineType('cn=shared@example.org,dc=example,dc=org');
+            $this->assertEquals('Horde_Kolab_Server_Object_Kolabsharedfolder', $type);
+
+            $type = $server->determineType('cn=The Administrator,dc=example,dc=org');
+            $this->assertEquals('Horde_Kolab_Server_Object_Kolab_Administrator', $type);
+
+            $type = $server->determineType('cn=Main Tainer,dc=example,dc=org');
+            $this->assertEquals('Horde_Kolab_Server_Object_Kolab_Maintainer', $type);
+
+            $type = $server->determineType('cn=Domain Maintainer,dc=example,dc=org');
+            $this->assertEquals('Horde_Kolab_Server_Object_Kolab_Domainmaintainer', $type);
+
+            $type = $server->determineType('cn=Test Address,cn=external,dc=example,dc=org');
+            $this->assertEquals('Horde_Kolab_Server_Object_Kolab_Address', $type);
+
+            $type = $server->determineType('cn=Gunnar Wrobel,dc=example,dc=org');
+            $this->assertEquals('Horde_Kolab_Server_Object_Kolab_User', $type);
+        }
+    }
+
+    /**
+     * Test retrieving a primary mail for a mail or id.
+     *
+     * @return NULL
+     */
+    public function testMailForIdOrMail()
+    {
+        foreach ($this->servers as $server) {
+            $mail = $server->mailForIdOrMail('wrobel');
+            $this->assertEquals('wrobel@example.org', $mail);
+
+            $mail = $server->mailForIdOrMail('wrobel@example.org');
+            $this->assertEquals('wrobel@example.org', $mail);
+
+            $mail = $server->mailForIdOrMail('DOES NOT EXIST');
+            $this->assertSame(false, $mail);
+        }
+    }
+
+    /**
+     * Test retrieving a UID for a mail or id.
+     *
+     * @return NULL
+     */
+    public function testUidForIdOrMail()
+    {
+        foreach ($this->servers as $server) {
+            $uid = $server->uidForIdOrMail('wrobel');
+            $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+            $uid = $server->uidForIdOrMail('wrobel@example.org');
+            $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+            $uid = $server->uidForIdOrMail('DOES NOT EXIST');
+            $this->assertSame(false, $uid);
+        }
+    }
+
+    /**
+     * Test retrieving a UID for a mail or id.
+     *
+     * @return NULL
+     */
+    public function testUidForMailOrIdOrAlias()
+    {
+        foreach ($this->servers as $server) {
+            $uid = $server->uidForIdOrMailOrAlias('g.wrobel@example.org');
+            $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+            $uid = $server->uidForIdOrMailOrAlias('wrobel@example.org');
+            $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+            $uid = $server->uidForIdOrMailOrAlias('wrobel');
+            $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+            $uid = $server->uidForIdOrMailOrAlias('DOES NOT EXIST');
+            $this->assertSame(false, $uid);
+        }
+    }
+
+    /**
+     * Test retrieving all addresses for a mail or id.
+     *
+     * @return NULL
+     */
+    public function testAddrsForIdOrMail()
+    {
+        foreach ($this->servers as $server) {
+            $addrs = $server->addrsForIdOrMail('wrobel');
+
+            $testuser = $server->fetch('cn=Test Test,dc=example,dc=org');
+            $this->assertContains('wrobel@example.org',
+                                  $testuser->get(Horde_Kolab_Server_Object_Kolabinetorgperson::ATTRIBUTE_DELEGATE, false));
+
+            $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 = $server->addrsForIdOrMail('test@example.org');
+            $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()
+    {
+        foreach ($this->servers as $server) {
+            $uid = $server->uidForIdOrMailOrAlias('wrobel@example.org');
+            $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+            $uid = $server->uidForIdOrMailOrAlias('test@example.org');
+            $this->assertEquals('cn=Test Test,dc=example,dc=org', $uid);
+
+            $uid = $server->uidForIdOrMailOrAlias('gunnar@example.org');
+            $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+            $uid = $server->uidForIdOrMailOrAlias('wrobel');
+            $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+        }
+    }
+
+    /**
+     * Test retrieving a UID for an attribute.
+     *
+     * @return NULL
+     */
+    public function testUidForAttr()
+    {
+        foreach ($this->servers as $server) {
+            $uid = $server->uidForSearch(array('AND' => array(array('field' => 'alias',
+                                                                    'op' => '=',
+                                                                    'test' => 'g.wrobel@example.org'))));
+            $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+        }
+    }
+
+    /**
+     * Test group membership testing.
+     *
+     * @return NULL
+     */
+    public function testMemberOfGroupAddress()
+    {
+        foreach ($this->servers as $server) {
+            $uid = $server->uidForIdOrMailOrAlias('g.wrobel@example.org');
+            $member = $server->memberOfGroupAddress($uid, 'group@example.org');
+            $this->assertTrue($member);
+
+            $member = $server->memberOfGroupAddress(
+                $server->uidForIdOrMailOrAlias('test@example.org'),
+                'group@example.org');
+            $this->assertTrue($member);
+
+            $member = $server->memberOfGroupAddress(
+                $server->uidForIdOrMailOrAlias('somebody@example.org'),
+                'group@example.org');
+            $this->assertFalse($member);
+        }
+    }
+
+    /**
+     * Test group fetching.
+     *
+     * @return NULL
+     */
+    public function testGetGroups()
+    {
+        foreach ($this->servers as $server) {
+            $filter = '(&(objectClass=kolabGroupOfNames)(member='
+                . Net_LDAP2_Util::escape_filter_value('cn=The Administrator,dc=example,dc=org') . '))';
+            $result = $server->search($filter, array());
+            $this->assertTrue(!empty($result));
+
+            /*         $entry = $server->_firstEntry($result); */
+            /*         $this->assertTrue(!empty($entry)); */
+
+            /*         $uid = $server->_getDn($entry); */
+            /*         $this->assertTrue(!empty($uid)); */
+
+            /*         $entry = $server->_nextEntry($entry); */
+            /*         $this->assertTrue(empty($entry)); */
+
+            /*         $entries = $server->_getDns($result); */
+            /*         $this->assertTrue(!empty($entries)); */
+
+            $groups = $server->getGroups('cn=The Administrator,dc=example,dc=org');
+            $this->assertTrue(!empty($groups));
+
+            $groups = $server->getGroups($server->uidForIdOrMailOrAlias('g.wrobel@example.org'));
+            $this->assertContains('cn=group@example.org,dc=example,dc=org', $groups);
+
+            $groups = $server->getGroupAddresses($server->uidForIdOrMailOrAlias('g.wrobel@example.org'));
+            $this->assertContains('group@example.org', $groups);
+
+            $groups = $server->getGroups($server->uidForIdOrMailOrAlias('test@example.org'));
+            $this->assertContains('cn=group@example.org,dc=example,dc=org', $groups);
+
+            $groups = $server->getGroupAddresses($server->uidForIdOrMailOrAlias('test@example.org'));
+            $this->assertContains('group@example.org', $groups);
+
+            $groups = $server->getGroups('nobody');
+            $this->assertTrue(empty($groups));
+        }
+    }
+
+    /**
+     * Test parsing of LDAP filters.
+     *
+     * @return NULL
+     */
+    public function testFilterParse()
+    {
+        $db = $this->getKolabMockServer();
+
+        $a = $db->parse('(a=b)');
+        $this->assertEquals(array('att' => 'a', 'log' => '=', 'val' => 'b'),
+                            $a);
+
+        $a = $db->parse('(&(a=b)(c=d))');
+        $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->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->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->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()
+    {
+        $injector = new Horde_Injector(new Horde_Injector_TopLevel());
+        $config = new stdClass;
+        $config->driver = 'test';
+        $config->params = 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',
+                    )
+                ),
+            )
+        );
+        $injector->setInstance('Horde_Kolab_Server_Config', $config);
+        $injector->bindFactory('Horde_Kolab_Server_Structure',
+                               'Horde_Kolab_Server_Factory',
+                               'getStructure');
+        $injector->bindFactory('Horde_Kolab_Server',
+                               'Horde_Kolab_Server_Factory',
+                               'getServer');
+        $db = $injector->getInstance('Horde_Kolab_Server');
+
+        $a = $db->search('(c=1)');
+        $this->assertEquals(
+            array(
+                'cn=a' => array(
+                    'a' => '1',
+                    'b' => '1',
+                    'c' => '1',
+                    'dn' => 'cn=a',
+                ),
+                'cn=d' => array(
+                    'a' => '2',
+                    'b' => '2',
+                    'c' => '1',
+                    'dn' => 'cn=d',
+                ),
+            ),
+            $a
+        );
+
+        $a = $db->search('(c=3)');
+        $this->assertEquals(
+            array(
+                'cn=c' => array(
+                    'a' => '1',
+                    'b' => '2',
+                    'c' => '3',
+                    'dn' => 'cn=c',
+                ),
+            ),
+            $a
+        );
+
+        $a = $db->search('(c=3)', array('attributes' => array('a')));
+        $this->assertEquals(
+            array(
+                'cn=c' => array(
+                    'a' => '1',
+                    'dn' => 'cn=c',
+                ),
+            ),
+            $a
+        );
+
+        $a = $db->search('(&(a=1)(b=2))', array('attributes' => array('a', 'b')));
+        $this->assertEquals(
+            array(
+                'cn=b' => array(
+                    'a' => '1',
+                    'b' => '2',
+                    'dn' => 'cn=b',
+                ),
+                'cn=c' => array(
+                    'a' => '1',
+                    'b' => '2',
+                    'dn' => 'cn=c',
+                ),
+            ),
+            $a
+        );
+
+        $a = $db->search('(&(b=2))', array('attributes' => array('b')));
+        $this->assertEquals(
+            array(
+                'cn=b' => array(
+                    'b' => '2',
+                    'dn' => 'cn=b',
+                ),
+                'cn=c' => array(
+                    'b' => '2',
+                    'dn' => 'cn=c',
+                ),
+                'cn=d' => array(
+                    'b' => '2',
+                    'dn' => 'cn=d',
+                ),
+            ),
+            $a
+        );
+
+        $a = $db->search('(!(b=2))', array('attributes' => array('a', 'b')));
+        $this->assertEquals(
+            array(
+                'cn=a' => array(
+                    'a' => '1',
+                    'b' => '1',
+                    'dn' => 'cn=a',
+                ),
+            ),
+            $a
+        );
+
+        $a = $db->search('(&(!(x=2))(b=1))', array('attributes' => array('b')));
+        $this->assertEquals(
+            array(
+                'cn=a' => array(
+                    'b' => '1',
+                    'dn' => 'cn=a',
+                ),
+            ),
+            $a
+        );
+    }
+
+}
index d23c7fd..cd0e747 100644 (file)
@@ -40,6 +40,8 @@ class Horde_Kolab_Server_UserTest extends Horde_Kolab_Server_Scenario
      */
     protected function setUp()
     {
+        parent::setUp();
+
         $this->server = $this->getKolabMockServer();
         $users        = $this->validUsers();
         foreach ($users as $user) {