Index: trunk/library/Zend/Ldap/Exception.php
===================================================================
--- trunk/library/Zend/Ldap/Exception.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Exception.php	(revision 5146)
@@ -0,0 +1,172 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 22996 2010-09-22 17:01:46Z sgehrig $
+ */
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @uses       Zend_Exception
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Exception extends Zend_Exception
+{
+    const LDAP_SUCCESS                        = 0x00;
+    const LDAP_OPERATIONS_ERROR               = 0x01;
+    const LDAP_PROTOCOL_ERROR                 = 0x02;
+    const LDAP_TIMELIMIT_EXCEEDED             = 0x03;
+    const LDAP_SIZELIMIT_EXCEEDED             = 0x04;
+    const LDAP_COMPARE_FALSE                  = 0x05;
+    const LDAP_COMPARE_TRUE                   = 0x06;
+    const LDAP_AUTH_METHOD_NOT_SUPPORTED      = 0x07;
+    const LDAP_STRONG_AUTH_REQUIRED           = 0x08;
+    const LDAP_PARTIAL_RESULTS                = 0x09;
+    const LDAP_REFERRAL                       = 0x0a;
+    const LDAP_ADMINLIMIT_EXCEEDED            = 0x0b;
+    const LDAP_UNAVAILABLE_CRITICAL_EXTENSION = 0x0c;
+    const LDAP_CONFIDENTIALITY_REQUIRED       = 0x0d;
+    const LDAP_SASL_BIND_IN_PROGRESS          = 0x0e;
+    const LDAP_NO_SUCH_ATTRIBUTE              = 0x10;
+    const LDAP_UNDEFINED_TYPE                 = 0x11;
+    const LDAP_INAPPROPRIATE_MATCHING         = 0x12;
+    const LDAP_CONSTRAINT_VIOLATION           = 0x13;
+    const LDAP_TYPE_OR_VALUE_EXISTS           = 0x14;
+    const LDAP_INVALID_SYNTAX                 = 0x15;
+    const LDAP_NO_SUCH_OBJECT                 = 0x20;
+    const LDAP_ALIAS_PROBLEM                  = 0x21;
+    const LDAP_INVALID_DN_SYNTAX              = 0x22;
+    const LDAP_IS_LEAF                        = 0x23;
+    const LDAP_ALIAS_DEREF_PROBLEM            = 0x24;
+    const LDAP_PROXY_AUTHZ_FAILURE            = 0x2F;
+    const LDAP_INAPPROPRIATE_AUTH             = 0x30;
+    const LDAP_INVALID_CREDENTIALS            = 0x31;
+    const LDAP_INSUFFICIENT_ACCESS            = 0x32;
+    const LDAP_BUSY                           = 0x33;
+    const LDAP_UNAVAILABLE                    = 0x34;
+    const LDAP_UNWILLING_TO_PERFORM           = 0x35;
+    const LDAP_LOOP_DETECT                    = 0x36;
+    const LDAP_NAMING_VIOLATION               = 0x40;
+    const LDAP_OBJECT_CLASS_VIOLATION         = 0x41;
+    const LDAP_NOT_ALLOWED_ON_NONLEAF         = 0x42;
+    const LDAP_NOT_ALLOWED_ON_RDN             = 0x43;
+    const LDAP_ALREADY_EXISTS                 = 0x44;
+    const LDAP_NO_OBJECT_CLASS_MODS           = 0x45;
+    const LDAP_RESULTS_TOO_LARGE              = 0x46;
+    const LDAP_AFFECTS_MULTIPLE_DSAS          = 0x47;
+    const LDAP_OTHER                          = 0x50;
+    const LDAP_SERVER_DOWN                    = 0x51;
+    const LDAP_LOCAL_ERROR                    = 0x52;
+    const LDAP_ENCODING_ERROR                 = 0x53;
+    const LDAP_DECODING_ERROR                 = 0x54;
+    const LDAP_TIMEOUT                        = 0x55;
+    const LDAP_AUTH_UNKNOWN                   = 0x56;
+    const LDAP_FILTER_ERROR                   = 0x57;
+    const LDAP_USER_CANCELLED                 = 0x58;
+    const LDAP_PARAM_ERROR                    = 0x59;
+    const LDAP_NO_MEMORY                      = 0x5a;
+    const LDAP_CONNECT_ERROR                  = 0x5b;
+    const LDAP_NOT_SUPPORTED                  = 0x5c;
+    const LDAP_CONTROL_NOT_FOUND              = 0x5d;
+    const LDAP_NO_RESULTS_RETURNED            = 0x5e;
+    const LDAP_MORE_RESULTS_TO_RETURN         = 0x5f;
+    const LDAP_CLIENT_LOOP                    = 0x60;
+    const LDAP_REFERRAL_LIMIT_EXCEEDED        = 0x61;
+    const LDAP_CUP_RESOURCES_EXHAUSTED        = 0x71;
+    const LDAP_CUP_SECURITY_VIOLATION         = 0x72;
+    const LDAP_CUP_INVALID_DATA               = 0x73;
+    const LDAP_CUP_UNSUPPORTED_SCHEME         = 0x74;
+    const LDAP_CUP_RELOAD_REQUIRED            = 0x75;
+    const LDAP_CANCELLED                      = 0x76;
+    const LDAP_NO_SUCH_OPERATION              = 0x77;
+    const LDAP_TOO_LATE                       = 0x78;
+    const LDAP_CANNOT_CANCEL                  = 0x79;
+    const LDAP_ASSERTION_FAILED               = 0x7A;
+    const LDAP_SYNC_REFRESH_REQUIRED          = 0x1000;
+    const LDAP_X_SYNC_REFRESH_REQUIRED        = 0x4100;
+    const LDAP_X_NO_OPERATION                 = 0x410e;
+    const LDAP_X_ASSERTION_FAILED             = 0x410f;
+    const LDAP_X_NO_REFERRALS_FOUND           = 0x4110;
+    const LDAP_X_CANNOT_CHAIN                 = 0x4111;
+
+    /* internal error code constants */
+
+    const LDAP_X_DOMAIN_MISMATCH              = 0x7001;
+    const LDAP_X_EXTENSION_NOT_LOADED         = 0x7002;
+
+    /**
+     * @param Zend_Ldap $ldap A Zend_Ldap object
+     * @param string    $str  An informtive exception message
+     * @param int       $code An LDAP error code
+     */
+    public function __construct(Zend_Ldap $ldap = null, $str = null, $code = 0)
+    {
+        $errorMessages = array();
+        $message = '';
+        if ($ldap !== null) {
+            $oldCode = $code;
+            $message = $ldap->getLastError($code, $errorMessages) . ': ';
+            if ($code === 0) {
+                $message = '';
+                $code = $oldCode;
+            }
+        }
+        if (empty($message)) {
+            if ($code > 0) {
+                $message = '0x' . dechex($code) . ': ';
+            }
+        }
+
+        if (!empty($str)) {
+            $message .= $str;
+        } else {
+            $message .= 'no exception message';
+        }
+
+        parent::__construct($message, $code);
+    }
+
+
+    /**
+     * @deprecated not necessary any more - will be removed
+     * @param Zend_Ldap $ldap A Zend_Ldap object
+     * @return int The current error code for the resource
+     */
+    public static function getLdapCode(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            return $ldap->getLastErrorCode();
+        }
+        return 0;
+    }
+
+    /**
+     * @deprecated will be removed
+     * @return int The current error code for this exception
+     */
+    public function getErrorCode()
+    {
+        return $this->getCode();
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/Logical.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/Logical.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/Logical.php	(revision 5146)
@@ -0,0 +1,107 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Logical.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_Abstract
+ */
+require_once 'Zend/Ldap/Filter/Abstract.php';
+/**
+ * @see Zend_Ldap_Filter_String
+ */
+require_once 'Zend/Ldap/Filter/String.php';
+
+/**
+ * Zend_Ldap_Filter_Logical provides a base implementation for a grouping filter.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Ldap_Filter_Logical extends Zend_Ldap_Filter_Abstract
+{
+    const TYPE_AND = '&';
+    const TYPE_OR  = '|';
+
+    /**
+     * All the sub-filters for this grouping filter.
+     *
+     * @var array
+     */
+    private $_subfilters;
+
+    /**
+     * The grouping symbol.
+     *
+     * @var string
+     */
+    private $_symbol;
+
+    /**
+     * Creates a new grouping filter.
+     *
+     * @param array  $subfilters
+     * @param string $symbol
+     */
+    protected function __construct(array $subfilters, $symbol)
+    {
+        foreach ($subfilters as $key => $s) {
+            if (is_string($s)) $subfilters[$key] = new Zend_Ldap_Filter_String($s);
+            else if (!($s instanceof Zend_Ldap_Filter_Abstract)) {
+                /**
+                 * @see Zend_Ldap_Filter_Exception
+                 */
+                require_once 'Zend/Ldap/Filter/Exception.php';
+                throw new Zend_Ldap_Filter_Exception('Only strings or Zend_Ldap_Filter_Abstract allowed.');
+            }
+        }
+        $this->_subfilters = $subfilters;
+        $this->_symbol = $symbol;
+    }
+
+    /**
+     * Adds a filter to this grouping filter.
+     *
+     * @param  Zend_Ldap_Filter_Abstract $filter
+     * @return Zend_Ldap_Filter_Logical
+     */
+    public function addFilter(Zend_Ldap_Filter_Abstract $filter)
+    {
+        $new = clone $this;
+        $new->_subfilters[] = $filter;
+        return $new;
+    }
+
+    /**
+     * Returns a string representation of the filter.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        $return = '(' . $this->_symbol;
+        foreach ($this->_subfilters as $sub) $return .= $sub->toString();
+        $return .= ')';
+        return $return;
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/Mask.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/Mask.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/Mask.php	(revision 5146)
@@ -0,0 +1,66 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Mask.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_String
+ */
+require_once 'Zend/Ldap/Filter/String.php';
+
+
+/**
+ * Zend_Ldap_Filter_Mask provides a simple string filter to be used with a mask.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter_Mask extends Zend_Ldap_Filter_String
+{
+    /**
+     * Creates a Zend_Ldap_Filter_String.
+     *
+     * @param string $mask
+     * @param string $value,...
+     */
+    public function __construct($mask, $value)
+    {
+        $args = func_get_args();
+        array_shift($args);
+        for ($i = 0; $i<count($args); $i++) {
+            $args[$i] = self::escapeValue($args[$i]);
+        }
+        $filter = vsprintf($mask, $args);
+        parent::__construct($filter);
+    }
+
+    /**
+     * Returns a string representation of the filter.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return $this->_filter;
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/Exception.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/Exception.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/Exception.php	(revision 5146)
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter_Exception extends Zend_Exception
+{
+}
Index: trunk/library/Zend/Ldap/Filter/Not.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/Not.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/Not.php	(revision 5146)
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Not.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_Abstract
+ */
+require_once 'Zend/Ldap/Filter/Abstract.php';
+
+/**
+ * Zend_Ldap_Filter_Not provides a negation filter.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter_Not extends Zend_Ldap_Filter_Abstract
+{
+    /**
+     * The underlying filter.
+     *
+     * @var Zend_Ldap_Filter_Abstract
+     */
+    private $_filter;
+
+    /**
+     * Creates a Zend_Ldap_Filter_Not.
+     *
+     * @param Zend_Ldap_Filter_Abstract $filter
+     */
+    public function __construct(Zend_Ldap_Filter_Abstract $filter)
+    {
+        $this->_filter = $filter;
+    }
+
+    /**
+     * Negates the filter.
+     *
+     * @return Zend_Ldap_Filter_Abstract
+     */
+    public function negate()
+    {
+        return $this->_filter;
+    }
+
+    /**
+     * Returns a string representation of the filter.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return '(!' . $this->_filter->toString() . ')';
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/Or.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/Or.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/Or.php	(revision 5146)
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Or.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_Logical
+ */
+require_once 'Zend/Ldap/Filter/Logical.php';
+
+/**
+ * Zend_Ldap_Filter_Or provides an 'or' filter.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter_Or extends Zend_Ldap_Filter_Logical
+{
+    /**
+     * Creates an 'or' grouping filter.
+     *
+     * @param array $subfilters
+     */
+    public function __construct(array $subfilters)
+    {
+        parent::__construct($subfilters, self::TYPE_OR);
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/And.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/And.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/And.php	(revision 5146)
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: And.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_Logical
+ */
+require_once 'Zend/Ldap/Filter/Logical.php';
+
+/**
+ * Zend_Ldap_Filter_And provides an 'and' filter.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter_And extends Zend_Ldap_Filter_Logical
+{
+    /**
+     * Creates an 'and' grouping filter.
+     *
+     * @param array $subfilters
+     */
+    public function __construct(array $subfilters)
+    {
+        parent::__construct($subfilters, self::TYPE_AND);
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/Abstract.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/Abstract.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/Abstract.php	(revision 5146)
@@ -0,0 +1,157 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Abstract.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Zend_Ldap_Filter_Abstract provides a base implementation for filters.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Ldap_Filter_Abstract
+{
+    /**
+     * Returns a string representation of the filter.
+     *
+     * @return string
+     */
+    abstract public function toString();
+
+    /**
+     * Returns a string representation of the filter.
+     * @see toString()
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->toString();
+    }
+
+    /**
+     * Negates the filter.
+     *
+     * @return Zend_Ldap_Filter_Abstract
+     */
+    public function negate()
+    {
+        /**
+         * Zend_Ldap_Filter_Not
+         */
+        require_once 'Zend/Ldap/Filter/Not.php';
+        return new Zend_Ldap_Filter_Not($this);
+    }
+
+    /**
+     * Creates an 'and' filter.
+     *
+     * @param  Zend_Ldap_Filter_Abstract $filter,...
+     * @return Zend_Ldap_Filter_And
+     */
+    public function addAnd($filter)
+    {
+        /**
+         * Zend_Ldap_Filter_And
+         */
+        require_once 'Zend/Ldap/Filter/And.php';
+        $fa = func_get_args();
+        $args = array_merge(array($this), $fa);
+        return new Zend_Ldap_Filter_And($args);
+    }
+
+    /**
+     * Creates an 'or' filter.
+     *
+     * @param  Zend_Ldap_Filter_Abstract $filter,...
+     * @return Zend_Ldap_Filter_Or
+     */
+    public function addOr($filter)
+    {
+        /**
+         * Zend_Ldap_Filter_Or
+         */
+        require_once 'Zend/Ldap/Filter/Or.php';
+        $fa = func_get_args();
+        $args = array_merge(array($this), $fa);
+        return new Zend_Ldap_Filter_Or($args);
+    }
+
+    /**
+     * Escapes the given VALUES according to RFC 2254 so that they can be safely used in LDAP filters.
+     *
+     * Any control characters with an ACII code < 32 as well as the characters with special meaning in
+     * LDAP filters "*", "(", ")", and "\" (the backslash) are converted into the representation of a
+     * backslash followed by two hex digits representing the hexadecimal value of the character.
+     * @see Net_LDAP2_Util::escape_filter_value() from Benedikt Hallinger <beni@php.net>
+     * @link http://pear.php.net/package/Net_LDAP2
+     * @author Benedikt Hallinger <beni@php.net>
+     *
+     * @param  string|array $values Array of values to escape
+     * @return array Array $values, but escaped
+     */
+    public static function escapeValue($values = array())
+    {
+        /**
+         * @see Zend_Ldap_Converter
+         */
+        require_once 'Zend/Ldap/Converter.php';
+
+        if (!is_array($values)) $values = array($values);
+        foreach ($values as $key => $val) {
+            // Escaping of filter meta characters
+            $val = str_replace(array('\\', '*', '(', ')'), array('\5c', '\2a', '\28', '\29'), $val);
+            // ASCII < 32 escaping
+            $val = Zend_Ldap_Converter::ascToHex32($val);
+            if (null === $val) $val = '\0';  // apply escaped "null" if string is empty
+            $values[$key] = $val;
+        }
+        return (count($values) == 1) ? $values[0] : $values;
+    }
+
+    /**
+     * Undoes the conversion done by {@link escapeValue()}.
+     *
+     * Converts any sequences of a backslash followed by two hex digits into the corresponding character.
+     * @see Net_LDAP2_Util::escape_filter_value() from Benedikt Hallinger <beni@php.net>
+     * @link http://pear.php.net/package/Net_LDAP2
+     * @author Benedikt Hallinger <beni@php.net>
+     *
+     * @param  string|array $values Array of values to escape
+     * @return array Array $values, but unescaped
+     */
+    public static function unescapeValue($values = array())
+    {
+        /**
+         * @see Zend_Ldap_Converter
+         */
+        require_once 'Zend/Ldap/Converter.php';
+
+        if (!is_array($values)) $values = array($values);
+        foreach ($values as $key => $value) {
+            // Translate hex code into ascii
+            $values[$key] = Zend_Ldap_Converter::hex32ToAsc($value);
+        }
+        return (count($values) == 1) ? $values[0] : $values;
+    }
+}
Index: trunk/library/Zend/Ldap/Filter/String.php
===================================================================
--- trunk/library/Zend/Ldap/Filter/String.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter/String.php	(revision 5146)
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: String.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_Abstract
+ */
+require_once 'Zend/Ldap/Filter/Abstract.php';
+
+/**
+ * Zend_Ldap_Filter_String provides a simple custom string filter.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter_String extends Zend_Ldap_Filter_Abstract
+{
+    /**
+     * The filter.
+     *
+     * @var string
+     */
+    protected $_filter;
+
+    /**
+     * Creates a Zend_Ldap_Filter_String.
+     *
+     * @param string $filter
+     */
+    public function __construct($filter)
+    {
+        $this->_filter = $filter;
+    }
+
+    /**
+     * Returns a string representation of the filter.
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return '(' . $this->_filter . ')';
+    }
+}
Index: trunk/library/Zend/Ldap/Node/RootDse/eDirectory.php
===================================================================
--- trunk/library/Zend/Ldap/Node/RootDse/eDirectory.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/RootDse/eDirectory.php	(revision 5146)
@@ -0,0 +1,160 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: eDirectory.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_RootDse
+ */
+require_once 'Zend/Ldap/Node/RootDse.php';
+
+/**
+ * Zend_Ldap_Node_RootDse provides a simple data-container for the RootDSE node of
+ * a Novell eDirectory server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_RootDse_eDirectory extends Zend_Ldap_Node_RootDse
+{
+    /**
+     * Determines if the extension is supported
+     *
+     * @param  string|array $oids oid(s) to check
+     * @return boolean
+     */
+    public function supportsExtension($oids)
+    {
+        return $this->attributeHasValue('supportedExtension', $oids);
+    }
+
+    /**
+     * Gets the vendorName.
+     *
+     * @return string|null
+     */
+    public function getVendorName()
+    {
+        return $this->getAttribute('vendorName', 0);
+    }
+
+    /**
+     * Gets the vendorVersion.
+     *
+     * @return string|null
+     */
+    public function getVendorVersion()
+    {
+        return $this->getAttribute('vendorVersion', 0);
+    }
+
+    /**
+     * Gets the dsaName.
+     *
+     * @return string|null
+     */
+    public function getDsaName()
+    {
+        return $this->getAttribute('dsaName', 0);
+    }
+
+    /**
+     * Gets the server statistics "errors".
+     *
+     * @return string|null
+     */
+    public function getStatisticsErrors()
+    {
+        return $this->getAttribute('errors', 0);
+    }
+
+    /**
+     * Gets the server statistics "securityErrors".
+     *
+     * @return string|null
+     */
+    public function getStatisticsSecurityErrors()
+    {
+        return $this->getAttribute('securityErrors', 0);
+    }
+
+    /**
+     * Gets the server statistics "chainings".
+     *
+     * @return string|null
+     */
+    public function getStatisticsChainings()
+    {
+        return $this->getAttribute('chainings', 0);
+    }
+
+    /**
+     * Gets the server statistics "referralsReturned".
+     *
+     * @return string|null
+     */
+    public function getStatisticsReferralsReturned()
+    {
+        return $this->getAttribute('referralsReturned', 0);
+    }
+
+    /**
+     * Gets the server statistics "extendedOps".
+     *
+     * @return string|null
+     */
+    public function getStatisticsExtendedOps()
+    {
+        return $this->getAttribute('extendedOps', 0);
+    }
+
+    /**
+     * Gets the server statistics "abandonOps".
+     *
+     * @return string|null
+     */
+    public function getStatisticsAbandonOps()
+    {
+        return $this->getAttribute('abandonOps', 0);
+    }
+
+    /**
+     * Gets the server statistics "wholeSubtreeSearchOps".
+     *
+     * @return string|null
+     */
+    public function getStatisticsWholeSubtreeSearchOps()
+    {
+        return $this->getAttribute('wholeSubtreeSearchOps', 0);
+    }
+
+    /**
+     * Gets the server type
+     *
+     * @return int
+     */
+    public function getServerType()
+    {
+        return self::SERVER_TYPE_EDIRECTORY;
+    }
+}
Index: trunk/library/Zend/Ldap/Node/RootDse/ActiveDirectory.php
===================================================================
--- trunk/library/Zend/Ldap/Node/RootDse/ActiveDirectory.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/RootDse/ActiveDirectory.php	(revision 5146)
@@ -0,0 +1,247 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: ActiveDirectory.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_RootDse
+ */
+require_once 'Zend/Ldap/Node/RootDse.php';
+
+/**
+ * Zend_Ldap_Node_RootDse provides a simple data-container for the RootDSE node of
+ * an Active Directory server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_RootDse_ActiveDirectory extends Zend_Ldap_Node_RootDse
+{
+    /**
+     * Gets the configurationNamingContext.
+     *
+     * @return string|null
+     */
+    public function getConfigurationNamingContext()
+    {
+        return $this->getAttribute('configurationNamingContext', 0);
+    }
+
+    /**
+     * Gets the currentTime.
+     *
+     * @return string|null
+     */
+    public function getCurrentTime()
+    {
+        return $this->getAttribute('currentTime', 0);
+    }
+
+    /**
+     * Gets the defaultNamingContext.
+     *
+     * @return string|null
+     */
+    public function getDefaultNamingContext()
+    {
+        return $this->getAttribute('defaultNamingContext', 0);
+    }
+
+    /**
+     * Gets the dnsHostName.
+     *
+     * @return string|null
+     */
+    public function getDnsHostName()
+    {
+        return $this->getAttribute('dnsHostName', 0);
+    }
+
+    /**
+     * Gets the domainControllerFunctionality.
+     *
+     * @return string|null
+     */
+    public function getDomainControllerFunctionality()
+    {
+        return $this->getAttribute('domainControllerFunctionality', 0);
+    }
+
+    /**
+     * Gets the domainFunctionality.
+     *
+     * @return string|null
+     */
+    public function getDomainFunctionality()
+    {
+        return $this->getAttribute('domainFunctionality', 0);
+    }
+
+    /**
+     * Gets the dsServiceName.
+     *
+     * @return string|null
+     */
+    public function getDsServiceName()
+    {
+        return $this->getAttribute('dsServiceName', 0);
+    }
+
+    /**
+     * Gets the forestFunctionality.
+     *
+     * @return string|null
+     */
+    public function getForestFunctionality()
+    {
+        return $this->getAttribute('forestFunctionality', 0);
+    }
+
+    /**
+     * Gets the highestCommittedUSN.
+     *
+     * @return string|null
+     */
+    public function getHighestCommittedUSN()
+    {
+        return $this->getAttribute('highestCommittedUSN', 0);
+    }
+
+    /**
+     * Gets the isGlobalCatalogReady.
+     *
+     * @return string|null
+     */
+    public function getIsGlobalCatalogReady()
+    {
+        return $this->getAttribute('isGlobalCatalogReady', 0);
+    }
+
+    /**
+     * Gets the isSynchronized.
+     *
+     * @return string|null
+     */
+    public function getIsSynchronized()
+    {
+        return $this->getAttribute('isSynchronized', 0);
+    }
+
+    /**
+     * Gets the ldapServiceName.
+     *
+     * @return string|null
+     */
+    public function getLdapServiceName()
+    {
+        return $this->getAttribute('ldapServiceName', 0);
+    }
+
+    /**
+     * Gets the rootDomainNamingContext.
+     *
+     * @return string|null
+     */
+    public function getRootDomainNamingContext()
+    {
+        return $this->getAttribute('rootDomainNamingContext', 0);
+    }
+
+    /**
+     * Gets the schemaNamingContext.
+     *
+     * @return string|null
+     */
+    public function getSchemaNamingContext()
+    {
+        return $this->getAttribute('schemaNamingContext', 0);
+    }
+
+    /**
+     * Gets the serverName.
+     *
+     * @return string|null
+     */
+    public function getServerName()
+    {
+        return $this->getAttribute('serverName', 0);
+    }
+
+    /**
+     * Determines if the capability is supported
+     *
+     * @param string|string|array $oids capability(s) to check
+     * @return boolean
+     */
+    public function supportsCapability($oids)
+    {
+        return $this->attributeHasValue('supportedCapabilities', $oids);
+    }
+
+    /**
+     * Determines if the control is supported
+     *
+     * @param string|array $oids control oid(s) to check
+     * @return boolean
+     */
+    public function supportsControl($oids)
+    {
+        return $this->attributeHasValue('supportedControl', $oids);
+    }
+
+    /**
+     * Determines if the version is supported
+     *
+     * @param string|array $policies policy(s) to check
+     * @return boolean
+     */
+    public function supportsPolicy($policies)
+    {
+        return $this->attributeHasValue('supportedLDAPPolicies', $policies);
+    }
+
+    /**
+     * Gets the server type
+     *
+     * @return int
+     */
+    public function getServerType()
+    {
+        return self::SERVER_TYPE_ACTIVEDIRECTORY;
+    }
+
+    /**
+     * Returns the schema DN
+     *
+     * @return Zend_Ldap_Dn
+     */
+    public function getSchemaDn()
+    {
+        $schemaDn = $this->getSchemaNamingContext();
+        /**
+         * @see Zend_Ldap_Dn
+         */
+        require_once 'Zend/Ldap/Dn.php';
+        return Zend_Ldap_Dn::fromString($schemaDn);
+    }
+}
Index: trunk/library/Zend/Ldap/Node/RootDse/OpenLdap.php
===================================================================
--- trunk/library/Zend/Ldap/Node/RootDse/OpenLdap.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/RootDse/OpenLdap.php	(revision 5146)
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: OpenLdap.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_RootDse
+ */
+require_once 'Zend/Ldap/Node/RootDse.php';
+
+/**
+ * Zend_Ldap_Node_RootDse provides a simple data-container for the RootDSE node of
+ * an OpenLDAP server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_RootDse_OpenLdap extends Zend_Ldap_Node_RootDse
+{
+    /**
+     * Gets the configContext.
+     *
+     * @return string|null
+     */
+    public function getConfigContext()
+    {
+        return $this->getAttribute('configContext', 0);
+    }
+
+    /**
+     * Gets the monitorContext.
+     *
+     * @return string|null
+     */
+    public function getMonitorContext()
+    {
+        return $this->getAttribute('monitorContext', 0);
+    }
+
+    /**
+     * Determines if the control is supported
+     *
+     * @param  string|array $oids control oid(s) to check
+     * @return boolean
+     */
+    public function supportsControl($oids)
+    {
+        return $this->attributeHasValue('supportedControl', $oids);
+    }
+
+    /**
+     * Determines if the extension is supported
+     *
+     * @param  string|array $oids oid(s) to check
+     * @return boolean
+     */
+    public function supportsExtension($oids)
+    {
+        return $this->attributeHasValue('supportedExtension', $oids);
+    }
+
+    /**
+     * Determines if the feature is supported
+     *
+     * @param  string|array $oids feature oid(s) to check
+     * @return boolean
+     */
+    public function supportsFeature($oids)
+    {
+        return $this->attributeHasValue('supportedFeatures', $oids);
+    }
+
+    /**
+     * Gets the server type
+     *
+     * @return int
+     */
+    public function getServerType()
+    {
+        return self::SERVER_TYPE_OPENLDAP;
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/Item.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/Item.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/Item.php	(revision 5146)
@@ -0,0 +1,163 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Item.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Zend_Ldap_Node_Schema_Item provides a base implementation for managing schema
+ * items like objectClass and attribute.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Ldap_Node_Schema_Item implements ArrayAccess, Countable
+{
+    /**
+     * The underlying data
+     *
+     * @var array
+     */
+    protected $_data;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data
+     */
+    public function __construct(array $data)
+    {
+        $this->setData($data);
+    }
+
+    /**
+     * Sets the data
+     *
+     * @param  array $data
+     * @return Zend_Ldap_Node_Schema_Item Provides a fluid interface
+     */
+    public function setData(array $data)
+    {
+        $this->_data = $data;
+        return $this;
+    }
+
+    /**
+     * Gets the data
+     *
+     * @return array
+     */
+    public function getData()
+    {
+        return $this->_data;
+    }
+
+    /**
+     * Gets a specific attribute from this item
+     *
+     * @param  string $name
+     * @return mixed
+     */
+    public function __get($name)
+    {
+        if (array_key_exists($name, $this->_data)) {
+            return $this->_data[$name];
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Checks whether a specific attribute exists.
+     *
+     * @param  string $name
+     * @return boolean
+     */
+    public function __isset($name)
+    {
+        return (array_key_exists($name, $this->_data));
+    }
+
+    /**
+     * Always throws BadMethodCallException
+     * Implements ArrayAccess.
+     *
+     * This method is needed for a full implementation of ArrayAccess
+     *
+     * @param  string $name
+     * @param  mixed $value
+     * @return null
+     * @throws BadMethodCallException
+     */
+    public function offsetSet($name, $value)
+    {
+        throw new BadMethodCallException();
+    }
+
+    /**
+     * Gets a specific attribute from this item
+     *
+     * @param  string $name
+     * @return mixed
+     */
+    public function offsetGet($name)
+    {
+        return $this->__get($name);
+    }
+
+    /**
+     * Always throws BadMethodCallException
+     * Implements ArrayAccess.
+     *
+     * This method is needed for a full implementation of ArrayAccess
+     *
+     * @param  string $name
+     * @return null
+     * @throws BadMethodCallException
+     */
+    public function offsetUnset($name)
+    {
+        throw new BadMethodCallException();
+    }
+
+    /**
+     * Checks whether a specific attribute exists.
+     *
+     * @param  string $name
+     * @return boolean
+     */
+    public function offsetExists($name)
+    {
+        return $this->__isset($name);
+    }
+
+    /**
+     * Returns the number of attributes.
+     * Implements Countable
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return count($this->_data);
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/AttributeType/ActiveDirectory.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/AttributeType/ActiveDirectory.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/AttributeType/ActiveDirectory.php	(revision 5146)
@@ -0,0 +1,104 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: ActiveDirectory.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Schema_Item
+ */
+require_once 'Zend/Ldap/Node/Schema/Item.php';
+/**
+ * @see Zend_Ldap_Node_Schema_AttributeType_Interface
+ */
+require_once 'Zend/Ldap/Node/Schema/AttributeType/Interface.php';
+
+/**
+ * Zend_Ldap_Node_Schema_AttributeType_ActiveDirectory provides access to the attribute type
+ * schema information on an Active Directory server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema_AttributeType_ActiveDirectory extends Zend_Ldap_Node_Schema_Item
+    implements Zend_Ldap_Node_Schema_AttributeType_Interface
+{
+    /**
+     * Gets the attribute name
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->ldapdisplayname[0];
+    }
+
+    /**
+     * Gets the attribute OID
+     *
+     * @return string
+     */
+    public function getOid()
+    {
+
+    }
+
+    /**
+     * Gets the attribute syntax
+     *
+     * @return string
+     */
+    public function getSyntax()
+    {
+
+    }
+
+    /**
+     * Gets the attribute maximum length
+     *
+     * @return int|null
+     */
+    public function getMaxLength()
+    {
+
+    }
+
+    /**
+     * Returns if the attribute is single-valued.
+     *
+     * @return boolean
+     */
+    public function isSingleValued()
+    {
+
+    }
+
+    /**
+     * Gets the attribute description
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/AttributeType/Interface.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/AttributeType/Interface.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/AttributeType/Interface.php	(revision 5146)
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Interface.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Zend_Ldap_Node_Schema_AttributeType_Interface provides a contract for schema attribute-types.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Ldap_Node_Schema_AttributeType_Interface
+{
+    /**
+     * Gets the attribute name
+     *
+     * @return string
+     */
+    public function getName();
+
+    /**
+     * Gets the attribute OID
+     *
+     * @return string
+     */
+    public function getOid();
+
+    /**
+     * Gets the attribute syntax
+     *
+     * @return string
+     */
+    public function getSyntax();
+
+    /**
+     * Gets the attribute maximum length
+     *
+     * @return int|null
+     */
+    public function getMaxLength();
+
+    /**
+     * Returns if the attribute is single-valued.
+     *
+     * @return boolean
+     */
+    public function isSingleValued();
+
+    /**
+     * Gets the attribute description
+     *
+     * @return string
+     */
+    public function getDescription();
+}
Index: trunk/library/Zend/Ldap/Node/Schema/AttributeType/OpenLdap.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/AttributeType/OpenLdap.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/AttributeType/OpenLdap.php	(revision 5146)
@@ -0,0 +1,129 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: OpenLdap.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Schema_Item
+ */
+require_once 'Zend/Ldap/Node/Schema/Item.php';
+/**
+ * @see Zend_Ldap_Node_Schema_AttributeType_Interface
+ */
+require_once 'Zend/Ldap/Node/Schema/AttributeType/Interface.php';
+
+/**
+ * Zend_Ldap_Node_Schema_AttributeType_OpenLdap provides access to the attribute type
+ * schema information on an OpenLDAP server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema_AttributeType_OpenLdap extends Zend_Ldap_Node_Schema_Item
+    implements Zend_Ldap_Node_Schema_AttributeType_Interface
+{
+    /**
+     * Gets the attribute name
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Gets the attribute OID
+     *
+     * @return string
+     */
+    public function getOid()
+    {
+        return $this->oid;
+    }
+
+    /**
+     * Gets the attribute syntax
+     *
+     * @return string
+     */
+    public function getSyntax()
+    {
+        if ($this->syntax === null) {
+            $parent = $this->getParent();
+            if ($parent === null) return null;
+            else return $parent->getSyntax();
+        } else {
+            return $this->syntax;
+        }
+    }
+
+    /**
+     * Gets the attribute maximum length
+     *
+     * @return int|null
+     */
+    public function getMaxLength()
+    {
+        $maxLength = $this->{'max-length'};
+        if ($maxLength === null) {
+            $parent = $this->getParent();
+            if ($parent === null) return null;
+            else return $parent->getMaxLength();
+        } else {
+            return (int)$maxLength;
+        }
+    }
+
+    /**
+     * Returns if the attribute is single-valued.
+     *
+     * @return boolean
+     */
+    public function isSingleValued()
+    {
+        return $this->{'single-value'};
+    }
+
+    /**
+     * Gets the attribute description
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->desc;
+    }
+
+    /**
+     * Returns the parent attribute type in the inhertitance tree if one exists
+     *
+     * @return Zend_Ldap_Node_Schema_AttributeType_OpenLdap|null
+     */
+    public function getParent()
+    {
+        if (count($this->_parents) === 1) {
+            return $this->_parents[0];
+        }
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/ActiveDirectory.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/ActiveDirectory.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/ActiveDirectory.php	(revision 5146)
@@ -0,0 +1,103 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: ActiveDirectory.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Schema
+ */
+require_once 'Zend/Ldap/Node/Schema.php';
+/**
+ * @see Zend_Ldap_Node_Schema_AttributeType_ActiveDirectory
+ */
+require_once 'Zend/Ldap/Node/Schema/AttributeType/ActiveDirectory.php';
+/**
+ * @see Zend_Ldap_Node_Schema_ObjectClass_ActiveDirectory
+ */
+require_once 'Zend/Ldap/Node/Schema/ObjectClass/ActiveDirectory.php';
+
+/**
+ * Zend_Ldap_Node_Schema_ActiveDirectory provides a simple data-container for the Schema node of
+ * an Active Directory server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema_ActiveDirectory extends Zend_Ldap_Node_Schema
+{
+    /**
+     * The attribute Types
+     *
+     * @var array
+     */
+    protected $_attributeTypes = array();
+    /**
+     * The object classes
+     *
+     * @var array
+     */
+    protected $_objectClasses = array();
+
+    /**
+     * Parses the schema
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  Zend_Ldap    $ldap
+     * @return Zend_Ldap_Node_Schema Provides a fluid interface
+     */
+    protected function _parseSchema(Zend_Ldap_Dn $dn, Zend_Ldap $ldap)
+    {
+        parent::_parseSchema($dn, $ldap);
+        foreach ($ldap->search('(objectClass=classSchema)', $dn,
+                Zend_Ldap::SEARCH_SCOPE_ONE) as $node) {
+            $val = new Zend_Ldap_Node_Schema_ObjectClass_ActiveDirectory($node);
+            $this->_objectClasses[$val->getName()] = $val;
+        }
+        foreach ($ldap->search('(objectClass=attributeSchema)', $dn,
+                Zend_Ldap::SEARCH_SCOPE_ONE) as $node) {
+            $val = new Zend_Ldap_Node_Schema_AttributeType_ActiveDirectory($node);
+            $this->_attributeTypes[$val->getName()] = $val;
+        }
+        return $this;
+    }
+
+    /**
+     * Gets the attribute Types
+     *
+     * @return array
+     */
+    public function getAttributeTypes()
+    {
+        return $this->_attributeTypes;
+    }
+
+    /**
+     * Gets the object classes
+     *
+     * @return array
+     */
+    public function getObjectClasses()
+    {
+        return $this->_objectClasses;
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/OpenLdap.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/OpenLdap.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/OpenLdap.php	(revision 5146)
@@ -0,0 +1,502 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: OpenLdap.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Schema
+ */
+require_once 'Zend/Ldap/Node/Schema.php';
+/**
+ * @see Zend_Ldap_Node_Schema_AttributeType_OpenLdap
+ */
+require_once 'Zend/Ldap/Node/Schema/AttributeType/OpenLdap.php';
+/**
+ * @see Zend_Ldap_Node_Schema_ObjectClass_OpenLdap
+ */
+require_once 'Zend/Ldap/Node/Schema/ObjectClass/OpenLdap.php';
+
+/**
+ * Zend_Ldap_Node_Schema_OpenLdap provides a simple data-container for the Schema node of
+ * an OpenLDAP server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema_OpenLdap extends Zend_Ldap_Node_Schema
+{
+    /**
+     * The attribute Types
+     *
+     * @var array
+     */
+    protected $_attributeTypes = null;
+    /**
+     * The object classes
+     *
+     * @var array
+     */
+    protected $_objectClasses = null;
+    /**
+     * The LDAP syntaxes
+     *
+     * @var array
+     */
+    protected $_ldapSyntaxes = null;
+    /**
+     * The matching rules
+     *
+     * @var array
+     */
+    protected $_matchingRules = null;
+    /**
+     * The matching rule use
+     *
+     * @var array
+     */
+    protected $_matchingRuleUse = null;
+
+    /**
+     * Parses the schema
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  Zend_Ldap    $ldap
+     * @return Zend_Ldap_Node_Schema Provides a fluid interface
+     */
+    protected function _parseSchema(Zend_Ldap_Dn $dn, Zend_Ldap $ldap)
+    {
+        parent::_parseSchema($dn, $ldap);
+        $this->_loadAttributeTypes();
+        $this->_loadLdapSyntaxes();
+        $this->_loadMatchingRules();
+        $this->_loadMatchingRuleUse();
+        $this->_loadObjectClasses();
+        return $this;
+    }
+
+    /**
+     * Gets the attribute Types
+     *
+     * @return array
+     */
+    public function getAttributeTypes()
+    {
+        return $this->_attributeTypes;
+    }
+
+    /**
+     * Gets the object classes
+     *
+     * @return array
+     */
+    public function getObjectClasses()
+    {
+        return $this->_objectClasses;
+    }
+
+    /**
+     * Gets the LDAP syntaxes
+     *
+     * @return array
+     */
+    public function getLdapSyntaxes()
+    {
+        return $this->_ldapSyntaxes;
+    }
+
+    /**
+     * Gets the matching rules
+     *
+     * @return array
+     */
+    public function getMatchingRules()
+    {
+        return $this->_matchingRules;
+    }
+
+    /**
+     * Gets the matching rule use
+     *
+     * @return array
+     */
+    public function getMatchingRuleUse()
+    {
+        return $this->_matchingRuleUse;
+    }
+
+    /**
+     * Loads the attribute Types
+     *
+     * @return void
+     */
+    protected function _loadAttributeTypes()
+    {
+        $this->_attributeTypes = array();
+        foreach ($this->getAttribute('attributeTypes') as $value) {
+            $val = $this->_parseAttributeType($value);
+            $val = new Zend_Ldap_Node_Schema_AttributeType_OpenLdap($val);
+            $this->_attributeTypes[$val->getName()] = $val;
+
+        }
+        foreach ($this->_attributeTypes as $val) {
+            if (count($val->sup) > 0) {
+                $this->_resolveInheritance($val, $this->_attributeTypes);
+            }
+            foreach ($val->aliases as $alias) {
+                $this->_attributeTypes[$alias] = $val;
+            }
+        }
+        ksort($this->_attributeTypes, SORT_STRING);
+    }
+
+    /**
+     * Parses an attributeType value
+     *
+     * @param  string $value
+     * @return array
+     */
+    protected function _parseAttributeType($value)
+    {
+        $attributeType = array(
+            'oid'                  => null,
+            'name'                 => null,
+            'desc'                 => null,
+            'obsolete'             => false,
+            'sup'                  => null,
+            'equality'             => null,
+            'ordering'             => null,
+            'substr'               => null,
+            'syntax'               => null,
+            'max-length'           => null,
+            'single-value'         => false,
+            'collective'           => false,
+            'no-user-modification' => false,
+            'usage'                => 'userApplications',
+            '_string'              => $value,
+            '_parents'             => array());
+
+        $tokens = $this->_tokenizeString($value);
+        $attributeType['oid'] = array_shift($tokens); // first token is the oid
+        $this->_parseLdapSchemaSyntax($attributeType, $tokens);
+
+        if (array_key_exists('syntax', $attributeType)) {
+            // get max length from syntax
+            if (preg_match('/^(.+){(\d+)}$/', $attributeType['syntax'], $matches)) {
+                $attributeType['syntax'] = $matches[1];
+                $attributeType['max-length'] = $matches[2];
+            }
+        }
+
+        $this->_ensureNameAttribute($attributeType);
+
+        return $attributeType;
+    }
+
+    /**
+     * Loads the object classes
+     *
+     * @return void
+     */
+    protected function _loadObjectClasses()
+    {
+        $this->_objectClasses = array();
+        foreach ($this->getAttribute('objectClasses') as $value) {
+            $val = $this->_parseObjectClass($value);
+            $val = new Zend_Ldap_Node_Schema_ObjectClass_OpenLdap($val);
+            $this->_objectClasses[$val->getName()] = $val;
+        }
+        foreach ($this->_objectClasses as $val) {
+            if (count($val->sup) > 0) {
+                $this->_resolveInheritance($val, $this->_objectClasses);
+            }
+            foreach ($val->aliases as $alias) {
+                $this->_objectClasses[$alias] = $val;
+            }
+        }
+        ksort($this->_objectClasses, SORT_STRING);
+    }
+
+    /**
+     * Parses an objectClasses value
+     *
+     * @param string $value
+     * @return array
+     */
+    protected function _parseObjectClass($value)
+    {
+        $objectClass = array(
+            'oid'        => null,
+            'name'       => null,
+            'desc'       => null,
+            'obsolete'   => false,
+            'sup'        => array(),
+            'abstract'   => false,
+            'structural' => false,
+            'auxiliary'  => false,
+            'must'       => array(),
+            'may'        => array(),
+            '_string'    => $value,
+            '_parents'   => array());
+
+        $tokens = $this->_tokenizeString($value);
+        $objectClass['oid'] = array_shift($tokens); // first token is the oid
+        $this->_parseLdapSchemaSyntax($objectClass, $tokens);
+
+        $this->_ensureNameAttribute($objectClass);
+
+        return $objectClass;
+    }
+
+    /**
+     * Resolves inheritance in objectClasses and attributes
+     *
+     * @param Zend_Ldap_Node_Schema_Item $node
+     * @param array                      $repository
+     */
+    protected function _resolveInheritance(Zend_Ldap_Node_Schema_Item $node, array $repository)
+    {
+        $data = $node->getData();
+        $parents = $data['sup'];
+        if ($parents === null || !is_array($parents) || count($parents) < 1) return;
+        foreach ($parents as $parent) {
+            if (!array_key_exists($parent, $repository)) continue;
+            if (!array_key_exists('_parents', $data) || !is_array($data['_parents'])) {
+               $data['_parents'] = array();
+           }
+           $data['_parents'][] = $repository[$parent];
+        }
+        $node->setData($data);
+    }
+
+    /**
+     * Loads the LDAP syntaxes
+     *
+     * @return void
+     */
+    protected function _loadLdapSyntaxes()
+    {
+        $this->_ldapSyntaxes = array();
+        foreach ($this->getAttribute('ldapSyntaxes') as $value) {
+            $val = $this->_parseLdapSyntax($value);
+            $this->_ldapSyntaxes[$val['oid']] = $val;
+        }
+        ksort($this->_ldapSyntaxes, SORT_STRING);
+    }
+
+    /**
+     * Parses an ldapSyntaxes value
+     *
+     * @param  string $value
+     * @return array
+     */
+    protected function _parseLdapSyntax($value)
+    {
+        $ldapSyntax = array(
+            'oid'      => null,
+            'desc'     => null,
+            '_string' => $value);
+
+        $tokens = $this->_tokenizeString($value);
+        $ldapSyntax['oid'] = array_shift($tokens); // first token is the oid
+        $this->_parseLdapSchemaSyntax($ldapSyntax, $tokens);
+
+        return $ldapSyntax;
+    }
+
+    /**
+     * Loads the matching rules
+     *
+     * @return void
+     */
+    protected function _loadMatchingRules()
+    {
+        $this->_matchingRules = array();
+        foreach ($this->getAttribute('matchingRules') as $value) {
+            $val = $this->_parseMatchingRule($value);
+            $this->_matchingRules[$val['name']] = $val;
+        }
+        ksort($this->_matchingRules, SORT_STRING);
+    }
+
+    /**
+     * Parses an matchingRules value
+     *
+     * @param  string $value
+     * @return array
+     */
+    protected function _parseMatchingRule($value)
+    {
+        $matchingRule = array(
+            'oid'      => null,
+            'name'     => null,
+            'desc'     => null,
+            'obsolete' => false,
+            'syntax'   => null,
+            '_string'  => $value);
+
+        $tokens = $this->_tokenizeString($value);
+        $matchingRule['oid'] = array_shift($tokens); // first token is the oid
+        $this->_parseLdapSchemaSyntax($matchingRule, $tokens);
+
+        $this->_ensureNameAttribute($matchingRule);
+
+        return $matchingRule;
+    }
+
+    /**
+     * Loads the matching rule use
+     *
+     * @return void
+     */
+    protected function _loadMatchingRuleUse()
+    {
+        $this->_matchingRuleUse = array();
+        foreach ($this->getAttribute('matchingRuleUse') as $value) {
+            $val = $this->_parseMatchingRuleUse($value);
+            $this->_matchingRuleUse[$val['name']] = $val;
+        }
+        ksort($this->_matchingRuleUse, SORT_STRING);
+    }
+
+    /**
+     * Parses an matchingRuleUse value
+     *
+     * @param  string $value
+     * @return array
+     */
+    protected function _parseMatchingRuleUse($value)
+    {
+        $matchingRuleUse = array(
+            'oid'      => null,
+            'name'     => null,
+            'desc'     => null,
+            'obsolete' => false,
+            'applies'  => array(),
+            '_string'  => $value);
+
+        $tokens = $this->_tokenizeString($value);
+        $matchingRuleUse['oid'] = array_shift($tokens); // first token is the oid
+        $this->_parseLdapSchemaSyntax($matchingRuleUse, $tokens);
+
+        $this->_ensureNameAttribute($matchingRuleUse);
+
+        return $matchingRuleUse;
+    }
+
+    /**
+     * Ensures that a name element is present and that it is single-values.
+     *
+     * @param array $data
+     */
+    protected function _ensureNameAttribute(array &$data)
+    {
+        if (!array_key_exists('name', $data) || empty($data['name'])) {
+            // force a name
+            $data['name'] = $data['oid'];
+        }
+        if (is_array($data['name'])) {
+            // make one name the default and put the other ones into aliases
+            $aliases = $data['name'];
+            $data['name'] = array_shift($aliases);
+            $data['aliases'] = $aliases;
+        } else {
+            $data['aliases'] = array();
+        }
+    }
+
+    /**
+     * Parse the given tokens into a data structure
+     *
+     * @param  array $data
+     * @param  array $tokens
+     * @return void
+     */
+    protected function _parseLdapSchemaSyntax(array &$data, array $tokens)
+    {
+        // tokens that have no value associated
+        $noValue = array('single-value',
+            'obsolete',
+            'collective',
+            'no-user-modification',
+            'abstract',
+            'structural',
+            'auxiliary');
+        // tokens that can have multiple values
+        $multiValue = array('must', 'may', 'sup');
+
+        while (count($tokens) > 0) {
+            $token = strtolower(array_shift($tokens));
+            if (in_array($token, $noValue)) {
+                $data[$token] = true; // single value token
+            } else {
+                $data[$token] = array_shift($tokens);
+                // this one follows a string or a list if it is multivalued
+                if ($data[$token] == '(') {
+                    // this creates the list of values and cycles through the tokens
+                    // until the end of the list is reached ')'
+                    $data[$token] = array();
+                    while ($tmp = array_shift($tokens)) {
+                        if ($tmp == ')') break;
+                        if ($tmp != '$') {
+                            $data[$token][] = Zend_Ldap_Attribute::convertFromLdapValue($tmp);
+                        }
+                    }
+                } else {
+                    $data[$token] = Zend_Ldap_Attribute::convertFromLdapValue($data[$token]);
+                }
+                // create a array if the value should be multivalued but was not
+                if (in_array($token, $multiValue) && !is_array($data[$token])) {
+                    $data[$token] = array($data[$token]);
+                }
+            }
+        }
+    }
+
+    /**
+    * Tokenizes the given value into an array
+    *
+    * @param  string $value
+    * @return array tokens
+    */
+    protected function _tokenizeString($value)
+    {
+        $tokens = array();
+        $matches = array();
+        // this one is taken from PEAR::Net_LDAP2
+        $pattern = "/\s* (?:([()]) | ([^'\s()]+) | '((?:[^']+|'[^\s)])*)') \s*/x";
+        preg_match_all($pattern, $value, $matches);
+        $cMatches = count($matches[0]);
+        $cPattern = count($matches);
+        for ($i = 0; $i < $cMatches; $i++) {     // number of tokens (full pattern match)
+            for ($j = 1; $j < $cPattern; $j++) { // each subpattern
+                $tok = trim($matches[$j][$i]);
+                if (!empty($tok)) {              // pattern match in this subpattern
+                    $tokens[$i] = $tok;          // this is the token
+                }
+            }
+        }
+        if ($tokens[0] == '(') array_shift($tokens);
+        if ($tokens[count($tokens) - 1] == ')') array_pop($tokens);
+        return $tokens;
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/ObjectClass/ActiveDirectory.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/ObjectClass/ActiveDirectory.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/ObjectClass/ActiveDirectory.php	(revision 5146)
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: ActiveDirectory.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Schema_Item
+ */
+require_once 'Zend/Ldap/Node/Schema/Item.php';
+/**
+ * @see Zend_Ldap_Node_Schema_ObjectClass_Interface
+ */
+require_once 'Zend/Ldap/Node/Schema/ObjectClass/Interface.php';
+
+/**
+ * Zend_Ldap_Node_Schema_ObjectClass_ActiveDirectory provides access to the objectClass
+ * schema information on an Active Directory server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema_ObjectClass_ActiveDirectory extends Zend_Ldap_Node_Schema_Item
+    implements Zend_Ldap_Node_Schema_ObjectClass_Interface
+{
+    /**
+     * Gets the objectClass name
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->ldapdisplayname[0];
+    }
+
+    /**
+     * Gets the objectClass OID
+     *
+     * @return string
+     */
+    public function getOid()
+    {
+
+    }
+
+    /**
+     * Gets the attributes that this objectClass must contain
+     *
+     * @return array
+     */
+    public function getMustContain()
+    {
+
+    }
+
+    /**
+     * Gets the attributes that this objectClass may contain
+     *
+     * @return array
+     */
+    public function getMayContain()
+    {
+
+    }
+
+    /**
+     * Gets the objectClass description
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+
+    }
+
+    /**
+     * Gets the objectClass type
+     *
+     * @return integer
+     */
+    public function getType()
+    {
+
+    }
+
+    /**
+     * Returns the parent objectClasses of this class.
+     * This includes structural, abstract and auxiliary objectClasses
+     *
+     * @return array
+     */
+    public function getParentClasses()
+    {
+
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema/ObjectClass/Interface.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/ObjectClass/Interface.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/ObjectClass/Interface.php	(revision 5146)
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Interface.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Zend_Ldap_Node_Schema_ObjectClass_Interface provides a contract for schema objectClasses.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Ldap_Node_Schema_ObjectClass_Interface
+{
+    /**
+     * Gets the objectClass name
+     *
+     * @return string
+     */
+    public function getName();
+
+    /**
+     * Gets the objectClass OID
+     *
+     * @return string
+     */
+    public function getOid();
+
+    /**
+     * Gets the attributes that this objectClass must contain
+     *
+     * @return array
+     */
+    public function getMustContain();
+
+    /**
+     * Gets the attributes that this objectClass may contain
+     *
+     * @return array
+     */
+    public function getMayContain();
+
+    /**
+     * Gets the objectClass description
+     *
+     * @return string
+     */
+    public function getDescription();
+
+    /**
+     * Gets the objectClass type
+     *
+     * @return integer
+     */
+    public function getType();
+
+    /**
+     * Returns the parent objectClasses of this class.
+     * This includes structural, abstract and auxiliary objectClasses
+     *
+     * @return array
+     */
+    public function getParentClasses();
+}
Index: trunk/library/Zend/Ldap/Node/Schema/ObjectClass/OpenLdap.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema/ObjectClass/OpenLdap.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema/ObjectClass/OpenLdap.php	(revision 5146)
@@ -0,0 +1,175 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: OpenLdap.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Schema_Item
+ */
+require_once 'Zend/Ldap/Node/Schema/Item.php';
+/**
+ * @see Zend_Ldap_Node_Schema_ObjectClass_Interface
+ */
+require_once 'Zend/Ldap/Node/Schema/ObjectClass/Interface.php';
+
+/**
+ * Zend_Ldap_Node_Schema_ObjectClass_OpenLdap provides access to the objectClass
+ * schema information on an OpenLDAP server.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema_ObjectClass_OpenLdap extends Zend_Ldap_Node_Schema_Item
+    implements Zend_Ldap_Node_Schema_ObjectClass_Interface
+{
+    /**
+     * All inherited "MUST" attributes
+     *
+     * @var array
+     */
+    protected $_inheritedMust = null;
+    /**
+     * All inherited "MAY" attributes
+     *
+     * @var array
+     */
+    protected $_inheritedMay = null;
+
+
+    /**
+     * Gets the objectClass name
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Gets the objectClass OID
+     *
+     * @return string
+     */
+    public function getOid()
+    {
+        return $this->oid;
+    }
+
+    /**
+     * Gets the attributes that this objectClass must contain
+     *
+     * @return array
+     */
+    public function getMustContain()
+    {
+        if ($this->_inheritedMust === null) {
+            $this->_resolveInheritance();
+        }
+        return $this->_inheritedMust;
+    }
+
+    /**
+     * Gets the attributes that this objectClass may contain
+     *
+     * @return array
+     */
+    public function getMayContain()
+    {
+        if ($this->_inheritedMay === null) {
+            $this->_resolveInheritance();
+        }
+        return $this->_inheritedMay;
+    }
+
+    /**
+     * Resolves the inheritance tree
+     *
+     * @return void
+     */
+    protected function _resolveInheritance()
+    {
+        $must = $this->must;
+        $may = $this->may;
+        foreach ($this->getParents() as $p) {
+            $must = array_merge($must, $p->getMustContain());
+            $may = array_merge($may, $p->getMayContain());
+        }
+        $must = array_unique($must);
+        $may = array_unique($may);
+        $may = array_diff($may, $must);
+        sort($must, SORT_STRING);
+        sort($may, SORT_STRING);
+        $this->_inheritedMust = $must;
+        $this->_inheritedMay = $may;
+    }
+
+    /**
+     * Gets the objectClass description
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->desc;
+    }
+
+    /**
+     * Gets the objectClass type
+     *
+     * @return integer
+     */
+    public function getType()
+    {
+        if ($this->structural) {
+            return Zend_Ldap_Node_Schema::OBJECTCLASS_TYPE_STRUCTURAL;
+        } else if ($this->abstract) {
+            return Zend_Ldap_Node_Schema::OBJECTCLASS_TYPE_ABSTRACT;
+        } else if ($this->auxiliary) {
+            return Zend_Ldap_Node_Schema::OBJECTCLASS_TYPE_AUXILIARY;
+        } else {
+            return Zend_Ldap_Node_Schema::OBJECTCLASS_TYPE_UNKNOWN;
+        }
+    }
+
+    /**
+     * Returns the parent objectClasses of this class.
+     * This includes structural, abstract and auxiliary objectClasses
+     *
+     * @return array
+     */
+    public function getParentClasses()
+    {
+        return $this->sup;
+    }
+
+    /**
+     * Returns the parent object classes in the inhertitance tree if one exists
+     *
+     * @return array of Zend_Ldap_Node_Schema_ObjectClass_OpenLdap
+     */
+    public function getParents()
+    {
+        return $this->_parents;
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Collection.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Collection.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Collection.php	(revision 5146)
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Collection.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Collection
+ */
+require_once 'Zend/Ldap/Collection.php';
+
+
+/**
+ * Zend_Ldap_Node_Collection provides a collecion of nodes.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Collection extends Zend_Ldap_Collection
+{
+    /**
+     * Creates the data structure for the given entry data
+     *
+     * @param  array $data
+     * @return Zend_Ldap_Node
+     */
+    protected function _createEntry(array $data)
+    {
+        /**
+         * @see Zend_Ldap_Node
+         */
+        require_once 'Zend/Ldap/Node.php';
+        $node = Zend_Ldap_Node::fromArray($data, true);
+        $node->attachLdap($this->_iterator->getLdap());
+        return $node;
+    }
+
+    /**
+     * Return the child key (DN).
+     * Implements Iterator and RecursiveIterator
+     *
+     * @return string
+     */
+    public function key()
+    {
+        return $this->_iterator->key();
+    }
+}
Index: trunk/library/Zend/Ldap/Node/RootDse.php
===================================================================
--- trunk/library/Zend/Ldap/Node/RootDse.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/RootDse.php	(revision 5146)
@@ -0,0 +1,158 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: RootDse.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Abstract
+ */
+require_once 'Zend/Ldap/Node/Abstract.php';
+
+/**
+ * Zend_Ldap_Node_RootDse provides a simple data-container for the RootDSE node.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage RootDSE
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_RootDse extends Zend_Ldap_Node_Abstract
+{
+    const SERVER_TYPE_GENERIC         = 1;
+    const SERVER_TYPE_OPENLDAP        = 2;
+    const SERVER_TYPE_ACTIVEDIRECTORY = 3;
+    const SERVER_TYPE_EDIRECTORY      = 4;
+
+    /**
+     * Factory method to create the RootDSE.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node_RootDse
+     * @throws Zend_Ldap_Exception
+     */
+    public static function create(Zend_Ldap $ldap)
+    {
+        $dn = Zend_Ldap_Dn::fromString('');
+        $data = $ldap->getEntry($dn, array('*', '+'), true);
+        if (isset($data['domainfunctionality'])) {
+            /**
+             * @see Zend_Ldap_Node_RootDse_ActiveDirectory
+             */
+            require_once 'Zend/Ldap/Node/RootDse/ActiveDirectory.php';
+            return new Zend_Ldap_Node_RootDse_ActiveDirectory($dn, $data);
+        } else if (isset($data['dsaname'])) {
+            /**
+             * @see Zend_Ldap_Node_RootDse_ActiveDirectory
+             */
+            require_once 'Zend/Ldap/Node/RootDse/eDirectory.php';
+            return new Zend_Ldap_Node_RootDse_eDirectory($dn, $data);
+        } else if (isset($data['structuralobjectclass']) &&
+                $data['structuralobjectclass'][0] === 'OpenLDAProotDSE') {
+            /**
+             * @see Zend_Ldap_Node_RootDse_OpenLdap
+             */
+            require_once 'Zend/Ldap/Node/RootDse/OpenLdap.php';
+            return new Zend_Ldap_Node_RootDse_OpenLdap($dn, $data);
+        } else {
+            return new self($dn, $data);
+        }
+    }
+
+    /**
+     * Constructor.
+     *
+     * Constructor is protected to enforce the use of factory methods.
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  array        $data
+     */
+    protected function __construct(Zend_Ldap_Dn $dn, array $data)
+    {
+        parent::__construct($dn, $data, true);
+    }
+
+    /**
+     * Gets the namingContexts.
+     *
+     * @return array
+     */
+    public function getNamingContexts()
+    {
+        return $this->getAttribute('namingContexts', null);
+    }
+
+    /**
+     * Gets the subschemaSubentry.
+     *
+     * @return string|null
+     */
+    public function getSubschemaSubentry()
+    {
+        return $this->getAttribute('subschemaSubentry', 0);
+    }
+
+    /**
+     * Determines if the version is supported
+     *
+     * @param  string|int|array $versions version(s) to check
+     * @return boolean
+     */
+    public function supportsVersion($versions)
+    {
+        return $this->attributeHasValue('supportedLDAPVersion', $versions);
+    }
+
+    /**
+     * Determines if the sasl mechanism is supported
+     *
+     * @param  string|array $mechlist SASL mechanisms to check
+     * @return boolean
+     */
+    public function supportsSaslMechanism($mechlist)
+    {
+        return $this->attributeHasValue('supportedSASLMechanisms', $mechlist);
+    }
+
+    /**
+     * Gets the server type
+     *
+     * @return int
+     */
+    public function getServerType()
+    {
+        return self::SERVER_TYPE_GENERIC;
+    }
+
+    /**
+     * Returns the schema DN
+     *
+     * @return Zend_Ldap_Dn
+     */
+    public function getSchemaDn()
+    {
+        $schemaDn = $this->getSubschemaSubentry();
+        /**
+         * @see Zend_Ldap_Dn
+         */
+        require_once 'Zend/Ldap/Dn.php';
+        return Zend_Ldap_Dn::fromString($schemaDn);
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Schema.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Schema.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Schema.php	(revision 5146)
@@ -0,0 +1,120 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Schema.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node_Abstract
+ */
+require_once 'Zend/Ldap/Node/Abstract.php';
+
+/**
+ * Zend_Ldap_Node_Schema provides a simple data-container for the Schema node.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Schema
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_Schema extends Zend_Ldap_Node_Abstract
+{
+    const OBJECTCLASS_TYPE_UNKNOWN    = 0;
+    const OBJECTCLASS_TYPE_STRUCTURAL = 1;
+    const OBJECTCLASS_TYPE_ABSTRACT   = 3;
+    const OBJECTCLASS_TYPE_AUXILIARY  = 4;
+
+    /**
+     * Factory method to create the Schema node.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node_Schema
+     * @throws Zend_Ldap_Exception
+     */
+    public static function create(Zend_Ldap $ldap)
+    {
+        $dn = $ldap->getRootDse()->getSchemaDn();
+        $data = $ldap->getEntry($dn, array('*', '+'), true);
+        switch ($ldap->getRootDse()->getServerType()) {
+            case Zend_Ldap_Node_RootDse::SERVER_TYPE_ACTIVEDIRECTORY:
+                /**
+                 * @see Zend_Ldap_Node_Schema_ActiveDirectory
+                 */
+                require_once 'Zend/Ldap/Node/Schema/ActiveDirectory.php';
+                return new Zend_Ldap_Node_Schema_ActiveDirectory($dn, $data, $ldap);
+            case Zend_Ldap_Node_RootDse::SERVER_TYPE_OPENLDAP:
+                /**
+                 * @see Zend_Ldap_Node_RootDse_ActiveDirectory
+                 */
+                require_once 'Zend/Ldap/Node/Schema/OpenLdap.php';
+                return new Zend_Ldap_Node_Schema_OpenLdap($dn, $data, $ldap);
+            case Zend_Ldap_Node_RootDse::SERVER_TYPE_EDIRECTORY:
+            default:
+                return new self($dn, $data, $ldap);
+        }
+    }
+
+    /**
+     * Constructor.
+     *
+     * Constructor is protected to enforce the use of factory methods.
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  array        $data
+     * @param  Zend_Ldap    $ldap
+     */
+    protected function __construct(Zend_Ldap_Dn $dn, array $data, Zend_Ldap $ldap)
+    {
+        parent::__construct($dn, $data, true);
+        $this->_parseSchema($dn, $ldap);
+    }
+
+    /**
+     * Parses the schema
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  Zend_Ldap    $ldap
+     * @return Zend_Ldap_Node_Schema Provides a fluid interface
+     */
+    protected function _parseSchema(Zend_Ldap_Dn $dn, Zend_Ldap $ldap)
+    {
+        return $this;
+    }
+
+    /**
+     * Gets the attribute Types
+     *
+     * @return array
+     */
+    public function getAttributeTypes()
+    {
+        return array();
+    }
+
+    /**
+     * Gets the object classes
+     *
+     * @return array
+     */
+    public function getObjectClasses()
+    {
+        return array();
+    }
+}
Index: trunk/library/Zend/Ldap/Node/ChildrenIterator.php
===================================================================
--- trunk/library/Zend/Ldap/Node/ChildrenIterator.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/ChildrenIterator.php	(revision 5146)
@@ -0,0 +1,209 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: ChildrenIterator.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Node
+ */
+require_once 'Zend/Ldap/Node.php';
+
+/**
+ * Zend_Ldap_Node_ChildrenIterator provides an iterator to a collection of children nodes.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node_ChildrenIterator implements Iterator, Countable, RecursiveIterator, ArrayAccess
+{
+    /**
+     * An array of Zend_Ldap_Node objects
+     *
+     * @var array
+     */
+    private $_data;
+
+    /**
+     * Constructor.
+     *
+     * @param  array $data
+     * @return void
+     */
+    public function __construct(array $data)
+    {
+        $this->_data = $data;
+    }
+
+    /**
+     * Returns the number of child nodes.
+     * Implements Countable
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return count($this->_data);
+    }
+
+    /**
+     * Return the current child.
+     * Implements Iterator
+     *
+     * @return Zend_Ldap_Node
+     */
+    public function current()
+    {
+        return current($this->_data);
+    }
+
+    /**
+     * Return the child'd RDN.
+     * Implements Iterator
+     *
+     * @return string
+     */
+    public function key()
+    {
+        return key($this->_data);
+    }
+
+    /**
+     * Move forward to next child.
+     * Implements Iterator
+     */
+    public function next()
+    {
+        next($this->_data);
+    }
+
+    /**
+     * Rewind the Iterator to the first child.
+     * Implements Iterator
+     */
+    public function rewind()
+    {
+        reset($this->_data);
+    }
+
+    /**
+     * Check if there is a current child
+     * after calls to rewind() or next().
+     * Implements Iterator
+     *
+     * @return boolean
+     */
+    public function valid()
+    {
+        return (current($this->_data)!==false);
+    }
+
+    /**
+     * Checks if current node has children.
+     * Returns whether the current element has children.
+     *
+     * @return boolean
+     */
+    public function hasChildren()
+    {
+        if ($this->current() instanceof Zend_Ldap_Node) {
+            return $this->current()->hasChildren();
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the children for the current node.
+     *
+     * @return Zend_Ldap_Node_ChildrenIterator
+     */
+    public function getChildren()
+    {
+        if ($this->current() instanceof Zend_Ldap_Node) {
+            return $this->current()->getChildren();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns a child with a given RDN.
+     * Implements ArrayAccess.
+     *
+     * @param  string $rdn
+     * @return Zend_Ldap_node
+     */
+    public function offsetGet($rdn)
+    {
+        if ($this->offsetExists($rdn)) {
+            return $this->_data[$rdn];
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Checks whether a given rdn exists.
+     * Implements ArrayAccess.
+     *
+     * @param  string $rdn
+     * @return boolean
+     */
+    public function offsetExists($rdn)
+    {
+        return (array_key_exists($rdn, $this->_data));
+    }
+
+    /**
+     * Does nothing.
+     * Implements ArrayAccess.
+     *
+     * @param  string $name
+     * @return null
+     */
+    public function offsetUnset($name) { }
+
+    /**
+     * Does nothing.
+     * Implements ArrayAccess.
+     *
+     * @param  string $name
+     * @param  mixed $value
+     * @return null
+     */
+    public function offsetSet($name, $value) { }
+
+    /**
+     * Get all children as an array
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        $data = array();
+        foreach ($this as $rdn => $node) {
+            $data[$rdn] = $node;
+        }
+        return $data;
+    }
+}
Index: trunk/library/Zend/Ldap/Node/Abstract.php
===================================================================
--- trunk/library/Zend/Ldap/Node/Abstract.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node/Abstract.php	(revision 5146)
@@ -0,0 +1,485 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Abstract.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Ldap_Attribute
+ */
+require_once 'Zend/Ldap/Attribute.php';
+/**
+ * @see Zend_Ldap_Dn
+ */
+require_once 'Zend/Ldap/Dn.php';
+
+/**
+ * Zend_Ldap_Node_Abstract provides a bas eimplementation for LDAP nodes
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Ldap_Node_Abstract implements ArrayAccess, Countable
+{
+    protected static $_systemAttributes=array('createtimestamp', 'creatorsname',
+        'entrycsn', 'entrydn', 'entryuuid', 'hassubordinates', 'modifiersname',
+        'modifytimestamp', 'structuralobjectclass', 'subschemasubentry',
+        'distinguishedname', 'instancetype', 'name', 'objectcategory', 'objectguid',
+        'usnchanged', 'usncreated', 'whenchanged', 'whencreated');
+
+    /**
+     * Holds the node's DN.
+     *
+     * @var Zend_Ldap_Dn
+     */
+    protected $_dn;
+
+    /**
+     * Holds the node's current data.
+     *
+     * @var array
+     */
+    protected $_currentData;
+
+    /**
+     * Constructor.
+     *
+     * Constructor is protected to enforce the use of factory methods.
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  array        $data
+     * @param  boolean      $fromDataSource
+     */
+    protected function __construct(Zend_Ldap_Dn $dn, array $data, $fromDataSource)
+    {
+        $this->_dn = $dn;
+        $this->_loadData($data, $fromDataSource);
+    }
+
+    /**
+     * @param  array   $data
+     * @param  boolean $fromDataSource
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _loadData(array $data, $fromDataSource)
+    {
+        if (array_key_exists('dn', $data)) {
+            unset($data['dn']);
+        }
+        ksort($data, SORT_STRING);
+        $this->_currentData = $data;
+    }
+
+    /**
+     * Reload node attributes from LDAP.
+     *
+     * This is an online method.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node_Abstract Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function reload(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $data = $ldap->getEntry($this->_getDn(), array('*', '+'), true);
+            $this->_loadData($data, true);
+        }
+        return $this;
+    }
+
+    /**
+     * Gets the DN of the current node as a Zend_Ldap_Dn.
+     *
+     * This is an offline method.
+     *
+     * @return Zend_Ldap_Dn
+     */
+    protected function _getDn()
+    {
+        return $this->_dn;
+    }
+
+    /**
+     * Gets the DN of the current node as a Zend_Ldap_Dn.
+     * The method returns a clone of the node's DN to prohibit modification.
+     *
+     * This is an offline method.
+     *
+     * @return Zend_Ldap_Dn
+     */
+    public function getDn()
+    {
+        $dn = clone $this->_getDn();
+        return $dn;
+    }
+
+    /**
+     * Gets the DN of the current node as a string.
+     *
+     * This is an offline method.
+     *
+     * @param  string $caseFold
+     * @return string
+     */
+    public function getDnString($caseFold = null)
+    {
+        return $this->_getDn()->toString($caseFold);
+    }
+
+    /**
+     * Gets the DN of the current node as an array.
+     *
+     * This is an offline method.
+     *
+     * @param  string $caseFold
+     * @return array
+     */
+    public function getDnArray($caseFold = null)
+    {
+        return $this->_getDn()->toArray($caseFold);
+    }
+
+    /**
+     * Gets the RDN of the current node as a string.
+     *
+     * This is an offline method.
+     *
+     * @param  string $caseFold
+     * @return string
+     */
+    public function getRdnString($caseFold = null)
+    {
+        return $this->_getDn()->getRdnString($caseFold);
+    }
+
+    /**
+     * Gets the RDN of the current node as an array.
+     *
+     * This is an offline method.
+     *
+     * @param  string $caseFold
+     * @return array
+     */
+    public function getRdnArray($caseFold = null)
+    {
+        return $this->_getDn()->getRdn($caseFold);
+    }
+
+    /**
+     * Gets the objectClass of the node
+     *
+     * @return array
+     */
+    public function getObjectClass()
+    {
+        return $this->getAttribute('objectClass', null);
+    }
+
+    /**
+     * Gets all attributes of node.
+     *
+     * The collection contains all attributes.
+     *
+     * This is an offline method.
+     *
+     * @param  boolean $includeSystemAttributes
+     * @return array
+     */
+    public function getAttributes($includeSystemAttributes = true)
+    {
+        $data = array();
+        foreach ($this->getData($includeSystemAttributes) as $name => $value) {
+            $data[$name] = $this->getAttribute($name, null);
+        }
+        return $data;
+    }
+
+    /**
+     * Returns the DN of the current node. {@see getDnString()}
+     *
+     * @return string
+     */
+    public function toString()
+    {
+        return $this->getDnString();
+    }
+
+    /**
+     * Cast to string representation {@see toString()}
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->toString();
+    }
+
+    /**
+     * Returns an array representation of the current node
+     *
+     * @param  boolean $includeSystemAttributes
+     * @return array
+     */
+    public function toArray($includeSystemAttributes = true)
+    {
+        $attributes = $this->getAttributes($includeSystemAttributes);
+        return array_merge(array('dn' => $this->getDnString()), $attributes);
+    }
+
+    /**
+     * Returns a JSON representation of the current node
+     *
+     * @param  boolean $includeSystemAttributes
+     * @return string
+     */
+    public function toJson($includeSystemAttributes = true)
+    {
+        return json_encode($this->toArray($includeSystemAttributes));
+    }
+
+    /**
+     * Gets node attributes.
+     *
+     * The array contains all attributes in its internal format (no conversion).
+     *
+     * This is an offline method.
+     *
+     * @param  boolean $includeSystemAttributes
+     * @return array
+     */
+    public function getData($includeSystemAttributes = true)
+    {
+        if ($includeSystemAttributes === false) {
+            $data = array();
+            foreach ($this->_currentData as $key => $value) {
+                if (!in_array($key, self::$_systemAttributes)) {
+                    $data[$key] = $value;
+                }
+            }
+            return $data;
+        } else {
+            return $this->_currentData;
+        }
+    }
+
+    /**
+     * Checks whether a given attribute exists.
+     *
+     * If $emptyExists is false empty attributes (containing only array()) are
+     * treated as non-existent returning false.
+     * If $emptyExists is true empty attributes are treated as existent returning
+     * true. In this case method returns false only if the attribute name is
+     * missing in the key-collection.
+     *
+     * @param  string  $name
+     * @param  boolean $emptyExists
+     * @return boolean
+     */
+    public function existsAttribute($name, $emptyExists = false)
+    {
+        $name = strtolower($name);
+        if (isset($this->_currentData[$name])) {
+            if ($emptyExists) return true;
+            return count($this->_currentData[$name])>0;
+        }
+        else return false;
+    }
+
+    /**
+     * Checks if the given value(s) exist in the attribute
+     *
+     * @param  string      $attribName
+     * @param  mixed|array $value
+     * @return boolean
+     */
+    public function attributeHasValue($attribName, $value)
+    {
+        return Zend_Ldap_Attribute::attributeHasValue($this->_currentData, $attribName, $value);
+    }
+
+    /**
+     * Gets a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string  $name
+     * @param  integer $index
+     * @return mixed
+     * @throws Zend_Ldap_Exception
+     */
+    public function getAttribute($name, $index = null)
+    {
+        if ($name == 'dn') {
+            return $this->getDnString();
+        }
+        else {
+            return Zend_Ldap_Attribute::getAttribute($this->_currentData, $name, $index);
+        }
+    }
+
+    /**
+     * Gets a LDAP date/time attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string  $name
+     * @param  integer $index
+     * @return array|integer
+     * @throws Zend_Ldap_Exception
+     */
+    public function getDateTimeAttribute($name, $index = null)
+    {
+        return Zend_Ldap_Attribute::getDateTimeAttribute($this->_currentData, $name, $index);
+    }
+
+    /**
+     * Sets a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return null
+     * @throws BadMethodCallException
+     */
+    public function __set($name, $value)
+    {
+        throw new BadMethodCallException();
+    }
+
+    /**
+     * Gets a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return array
+     * @throws Zend_Ldap_Exception
+     */
+    public function __get($name)
+    {
+        return $this->getAttribute($name, null);
+    }
+
+    /**
+     * Deletes a LDAP attribute.
+     *
+     * This method deletes the attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return null
+     * @throws BadMethodCallException
+     */
+    public function __unset($name)
+    {
+        throw new BadMethodCallException();
+    }
+
+    /**
+     * Checks whether a given attribute exists.
+     *
+     * Empty attributes will be treated as non-existent.
+     *
+     * @param  string $name
+     * @return boolean
+     */
+    public function __isset($name)
+    {
+        return $this->existsAttribute($name, false);
+    }
+
+    /**
+     * Sets a LDAP attribute.
+     * Implements ArrayAccess.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return null
+     * @throws BadMethodCallException
+     */
+    public function offsetSet($name, $value)
+    {
+        throw new BadMethodCallException();
+    }
+
+    /**
+     * Gets a LDAP attribute.
+     * Implements ArrayAccess.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return array
+     * @throws Zend_Ldap_Exception
+     */
+    public function offsetGet($name)
+    {
+        return $this->getAttribute($name, null);
+    }
+
+    /**
+     * Deletes a LDAP attribute.
+     * Implements ArrayAccess.
+     *
+     * This method deletes the attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return null
+     * @throws BadMethodCallException
+     */
+    public function offsetUnset($name)
+    {
+        throw new BadMethodCallException();
+    }
+
+    /**
+     * Checks whether a given attribute exists.
+     * Implements ArrayAccess.
+     *
+     * Empty attributes will be treated as non-existent.
+     *
+     * @param  string $name
+     * @return boolean
+     */
+    public function offsetExists($name)
+    {
+        return $this->existsAttribute($name, false);
+    }
+
+    /**
+     * Returns the number of attributes in node.
+     * Implements Countable
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return count($this->_currentData);
+    }
+}
Index: trunk/library/Zend/Ldap/Converter/Exception.php
===================================================================
--- trunk/library/Zend/Ldap/Converter/Exception.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Converter/Exception.php	(revision 5146)
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Converter_Exception extends Zend_Exception
+{
+}
Index: trunk/library/Zend/Ldap/Attribute.php
===================================================================
--- trunk/library/Zend/Ldap/Attribute.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Attribute.php	(revision 5146)
@@ -0,0 +1,417 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Attribute.php 23486 2010-12-10 04:05:30Z mjh_ca $
+ */
+
+/**
+ * @see Zend_Ldap_Converter
+ */
+require_once 'Zend/Ldap/Converter.php';
+
+/**
+ * Zend_Ldap_Attribute is a collection of LDAP attribute related functions.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Attribute
+{
+    const PASSWORD_HASH_MD5   = 'md5';
+    const PASSWORD_HASH_SMD5  = 'smd5';
+    const PASSWORD_HASH_SHA   = 'sha';
+    const PASSWORD_HASH_SSHA  = 'ssha';
+    const PASSWORD_UNICODEPWD = 'unicodePwd';
+
+    /**
+     * Sets a LDAP attribute.
+     *
+     * @param  array                    $data
+     * @param  string                   $attribName
+     * @param  scalar|array|Traversable $value
+     * @param  boolean                  $append
+     * @return void
+     */
+    public static function setAttribute(array &$data, $attribName, $value, $append = false)
+    {
+        $attribName = strtolower($attribName);
+        $valArray = array();
+        if (is_array($value) || ($value instanceof Traversable))
+        {
+            foreach ($value as $v)
+            {
+                $v = self::_valueToLdap($v);
+                if ($v !== null) $valArray[] = $v;
+            }
+        }
+        else if ($value !== null)
+        {
+            $value = self::_valueToLdap($value);
+            if ($value !== null) $valArray[] = $value;
+        }
+
+        if ($append === true && isset($data[$attribName]))
+        {
+            if (is_string($data[$attribName])) $data[$attribName] = array($data[$attribName]);
+            $data[$attribName] = array_merge($data[$attribName], $valArray);
+        }
+        else
+        {
+            $data[$attribName] = $valArray;
+        }
+    }
+
+    /**
+     * Gets a LDAP attribute.
+     *
+     * @param  array   $data
+     * @param  string  $attribName
+     * @param  integer $index
+     * @return array|mixed
+     */
+    public static function getAttribute(array $data, $attribName, $index = null)
+    {
+        $attribName = strtolower($attribName);
+        if ($index === null) {
+            if (!isset($data[$attribName])) return array();
+            $retArray = array();
+            foreach ($data[$attribName] as $v)
+            {
+                $retArray[] = self::_valueFromLdap($v);
+            }
+            return $retArray;
+        } else if (is_int($index)) {
+            if (!isset($data[$attribName])) {
+                return null;
+            } else if ($index >= 0 && $index<count($data[$attribName])) {
+                return self::_valueFromLdap($data[$attribName][$index]);
+            } else {
+                return null;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Checks if the given value(s) exist in the attribute
+     *
+     * @param array       $data
+     * @param string      $attribName
+     * @param mixed|array $value
+     * @return boolean
+     */
+    public static function attributeHasValue(array &$data, $attribName, $value)
+    {
+        $attribName = strtolower($attribName);
+        if (!isset($data[$attribName])) return false;
+
+        if (is_scalar($value)) {
+            $value = array($value);
+        }
+
+        foreach ($value as $v) {
+            $v = self::_valueToLdap($v);
+            if (!in_array($v, $data[$attribName], true)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Removes duplicate values from a LDAP attribute
+     *
+     * @param array  $data
+     * @param string $attribName
+     * @return void
+     */
+    public static function removeDuplicatesFromAttribute(array &$data, $attribName)
+    {
+        $attribName = strtolower($attribName);
+        if (!isset($data[$attribName])) return;
+        $data[$attribName] = array_values(array_unique($data[$attribName]));
+    }
+
+    /**
+     * Remove given values from a LDAP attribute
+     *
+     * @param array       $data
+     * @param string      $attribName
+     * @param mixed|array $value
+     * @return void
+     */
+    public static function removeFromAttribute(array &$data, $attribName, $value)
+    {
+        $attribName = strtolower($attribName);
+        if (!isset($data[$attribName])) return;
+
+        if (is_scalar($value)) {
+            $value = array($value);
+        }
+
+        $valArray = array();
+        foreach ($value as $v)
+        {
+            $v = self::_valueToLdap($v);
+            if ($v !== null) $valArray[] = $v;
+        }
+
+        $resultArray = $data[$attribName];
+        foreach ($valArray as $rv) {
+            $keys = array_keys($resultArray, $rv);
+            foreach ($keys as $k) {
+                unset($resultArray[$k]);
+            }
+        }
+        $resultArray = array_values($resultArray);
+        $data[$attribName] = $resultArray;
+    }
+
+    /**
+     * @param  mixed $value
+     * @return string|null
+     */
+    private static function _valueToLdap($value)
+    {
+        return Zend_Ldap_Converter::toLdap($value);
+    }
+
+    /**
+     * @param  string $value
+     * @return mixed
+     */
+    private static function _valueFromLdap($value)
+    {
+        try {
+            $return = Zend_Ldap_Converter::fromLdap($value, Zend_Ldap_Converter::STANDARD, false);
+            if ($return instanceof DateTime) {
+                return Zend_Ldap_Converter::toLdapDateTime($return, false);
+            } else {
+                return $return;
+            }
+        } catch (InvalidArgumentException $e) {
+            return $value;
+        }
+    }
+
+    /**
+     * Converts a PHP data type into its LDAP representation
+     *
+     * @deprected    use Zend_Ldap_Converter instead
+     * @param          mixed $value
+     * @return         string|null - null if the PHP data type cannot be converted.
+     */
+    public static function convertToLdapValue($value)
+    {
+        return self::_valueToLdap($value);
+    }
+
+    /**
+     * Converts an LDAP value into its PHP data type
+     *
+     * @deprected    use Zend_Ldap_Converter instead
+     * @param          string $value
+     * @return         mixed
+     */
+    public static function convertFromLdapValue($value)
+    {
+        return self::_valueFromLdap($value);
+    }
+
+    /**
+     * Converts a timestamp into its LDAP date/time representation
+     *
+     * @param  integer $value
+     * @param  boolean $utc
+     * @return string|null - null if the value cannot be converted.
+     */
+    public static function convertToLdapDateTimeValue($value, $utc = false)
+    {
+        return self::_valueToLdapDateTime($value, $utc);
+    }
+
+    /**
+     * Converts LDAP date/time representation into a timestamp
+     *
+     * @param  string $value
+     * @return integer|null - null if the value cannot be converted.
+     */
+    public static function convertFromLdapDateTimeValue($value)
+    {
+        return self::_valueFromLdapDateTime($value);
+    }
+
+    /**
+     * Sets a LDAP password.
+     *
+     * @param  array       $data
+     * @param  string      $password
+     * @param  string      $hashType
+     * @param  string|null $attribName
+     * @return null
+     */
+    public static function setPassword(array &$data, $password, $hashType = self::PASSWORD_HASH_MD5,
+        $attribName = null)
+    {
+        if ($attribName === null) {
+            if ($hashType === self::PASSWORD_UNICODEPWD) {
+                $attribName = 'unicodePwd';
+            } else {
+                $attribName = 'userPassword';
+            }
+        }
+
+        $hash = self::createPassword($password, $hashType);
+        self::setAttribute($data, $attribName, $hash, false);
+    }
+
+    /**
+     * Creates a LDAP password.
+     *
+     * @param  string $password
+     * @param  string $hashType
+     * @return string
+     */
+    public static function createPassword($password, $hashType = self::PASSWORD_HASH_MD5)
+    {
+        switch ($hashType) {
+            case self::PASSWORD_UNICODEPWD:
+                /* see:
+                 * http://msdn.microsoft.com/en-us/library/cc223248(PROT.10).aspx
+                 */
+                $password = '"' . $password . '"';
+                if (function_exists('mb_convert_encoding')) {
+                    $password = mb_convert_encoding($password, 'UTF-16LE', 'UTF-8');
+                } else if (function_exists('iconv')) {
+                    $password = iconv('UTF-8', 'UTF-16LE', $password);
+                } else {
+                    $len = strlen($password);
+                    $new = '';
+                    for($i=0; $i < $len; $i++) {
+                        $new .= $password[$i] . "\x00";
+                    }
+                    $password = $new;
+                }
+                return $password;
+            case self::PASSWORD_HASH_SSHA:
+                $salt    = substr(sha1(uniqid(mt_rand(), true), true), 0, 4);
+                $rawHash = sha1($password . $salt, true) . $salt;
+                $method  = '{SSHA}';
+                break;
+            case self::PASSWORD_HASH_SHA:
+                $rawHash = sha1($password, true);
+                $method  = '{SHA}';
+                break;
+            case self::PASSWORD_HASH_SMD5:
+                $salt    = substr(sha1(uniqid(mt_rand(), true), true), 0, 4);
+                $rawHash = md5($password . $salt, true) . $salt;
+                $method  = '{SMD5}';
+                break;
+            case self::PASSWORD_HASH_MD5:
+            default:
+                $rawHash = md5($password, true);
+                $method  = '{MD5}';
+                break;
+        }
+        return $method . base64_encode($rawHash);
+    }
+
+    /**
+     * Sets a LDAP date/time attribute.
+     *
+     * @param  array                     $data
+     * @param  string                    $attribName
+     * @param  integer|array|Traversable $value
+     * @param  boolean                   $utc
+     * @param  boolean                   $append
+     * @return null
+     */
+    public static function setDateTimeAttribute(array &$data, $attribName, $value, $utc = false,
+        $append = false)
+    {
+        $convertedValues = array();
+        if (is_array($value) || ($value instanceof Traversable))
+        {
+            foreach ($value as $v) {
+                $v = self::_valueToLdapDateTime($v, $utc);
+                if ($v !== null) $convertedValues[] = $v;
+            }
+        }
+        else if ($value !== null) {
+            $value = self::_valueToLdapDateTime($value, $utc);
+            if ($value !== null) $convertedValues[] = $value;
+        }
+        self::setAttribute($data, $attribName, $convertedValues, $append);
+    }
+
+    /**
+     * @param  integer $value
+     * @param  boolean $utc
+     * @return string|null
+     */
+    private static function _valueToLdapDateTime($value, $utc)
+    {
+        if (is_int($value)) {
+            return Zend_Ldap_Converter::toLdapDateTime($value, $utc);
+        }
+        else return null;
+    }
+
+    /**
+     * Gets a LDAP date/time attribute.
+     *
+     * @param  array   $data
+     * @param  string  $attribName
+     * @param  integer $index
+     * @return array|integer
+     */
+    public static function getDateTimeAttribute(array $data, $attribName, $index = null)
+    {
+        $values = self::getAttribute($data, $attribName, $index);
+        if (is_array($values)) {
+            for ($i = 0; $i<count($values); $i++) {
+                $newVal = self::_valueFromLdapDateTime($values[$i]);
+                if ($newVal !== null) $values[$i] = $newVal;
+            }
+        }
+        else {
+            $newVal = self::_valueFromLdapDateTime($values);
+            if ($newVal !== null) $values = $newVal;
+        }
+        return $values;
+    }
+
+    /**
+     * @param  string|DateTime $value
+     * @return integer|null
+     */
+    private static function _valueFromLdapDateTime($value)
+    {
+        if ($value instanceof DateTime) {
+            return $value->format('U');
+        } else if (is_string($value)) {
+            try {
+                return Zend_Ldap_Converter::fromLdapDateTime($value, false)->format('U');
+            } catch (InvalidArgumentException $e) {
+                return null;
+            }
+        } else return null;
+    }
+}
Index: trunk/library/Zend/Ldap/Collection/Iterator/Default.php
===================================================================
--- trunk/library/Zend/Ldap/Collection/Iterator/Default.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Collection/Iterator/Default.php	(revision 5146)
@@ -0,0 +1,310 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Default.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Zend_Ldap_Collection_Iterator_Default is the default collection iterator implementation
+ * using ext/ldap
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Collection_Iterator_Default implements Iterator, Countable
+{
+    const ATTRIBUTE_TO_LOWER  = 1;
+    const ATTRIBUTE_TO_UPPER  = 2;
+    const ATTRIBUTE_NATIVE    = 3;
+
+    /**
+     * LDAP Connection
+     *
+     * @var Zend_Ldap
+     */
+    protected $_ldap = null;
+
+    /**
+     * Result identifier resource
+     *
+     * @var resource
+     */
+    protected $_resultId = null;
+
+    /**
+     * Current result entry identifier
+     *
+     * @var resource
+     */
+    protected $_current = null;
+
+    /**
+     * Number of items in query result
+     *
+     * @var integer
+     */
+    protected $_itemCount = -1;
+
+    /**
+     * The method that will be applied to the attribute's names.
+     *
+     * @var  integer|callback
+     */
+    protected $_attributeNameTreatment = self::ATTRIBUTE_TO_LOWER;
+
+    /**
+     * Constructor.
+     *
+     * @param  Zend_Ldap $ldap
+     * @param  resource  $resultId
+     * @return void
+     */
+    public function __construct(Zend_Ldap $ldap, $resultId)
+    {
+        $this->_ldap = $ldap;
+        $this->_resultId = $resultId;
+        $this->_itemCount = @ldap_count_entries($ldap->getResource(), $resultId);
+        if ($this->_itemCount === false) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception($this->_ldap, 'counting entries');
+        }
+    }
+
+    public function __destruct()
+    {
+        $this->close();
+    }
+
+    /**
+     * Closes the current result set
+     *
+     * @return bool
+     */
+    public function close()
+    {
+        $isClosed = false;
+        if (is_resource($this->_resultId)) {
+             $isClosed = @ldap_free_result($this->_resultId);
+             $this->_resultId = null;
+             $this->_current = null;
+        }
+        return $isClosed;
+    }
+
+    /**
+     * Gets the current LDAP connection.
+     *
+     * @return Zend_Ldap
+     */
+    public function getLdap()
+    {
+        return $this->_ldap;
+    }
+
+    /**
+     * Sets the attribute name treatment.
+     *
+     * Can either be one of the following constants
+     * - Zend_Ldap_Collection_Iterator_Default::ATTRIBUTE_TO_LOWER
+     * - Zend_Ldap_Collection_Iterator_Default::ATTRIBUTE_TO_UPPER
+     * - Zend_Ldap_Collection_Iterator_Default::ATTRIBUTE_NATIVE
+     * or a valid callback accepting the attribute's name as it's only
+     * argument and returning the new attribute's name.
+     *
+     * @param  integer|callback $attributeNameTreatment
+     * @return Zend_Ldap_Collection_Iterator_Default Provides a fluent interface
+     */
+    public function setAttributeNameTreatment($attributeNameTreatment)
+    {
+        if (is_callable($attributeNameTreatment)) {
+            if (is_string($attributeNameTreatment) && !function_exists($attributeNameTreatment)) {
+                $this->_attributeNameTreatment = self::ATTRIBUTE_TO_LOWER;
+            } else if (is_array($attributeNameTreatment) &&
+                    !method_exists($attributeNameTreatment[0], $attributeNameTreatment[1])) {
+                $this->_attributeNameTreatment = self::ATTRIBUTE_TO_LOWER;
+            } else {
+                $this->_attributeNameTreatment = $attributeNameTreatment;
+            }
+        } else {
+            $attributeNameTreatment = (int)$attributeNameTreatment;
+            switch ($attributeNameTreatment) {
+                case self::ATTRIBUTE_TO_LOWER:
+                case self::ATTRIBUTE_TO_UPPER:
+                case self::ATTRIBUTE_NATIVE:
+                    $this->_attributeNameTreatment = $attributeNameTreatment;
+                    break;
+                default:
+                    $this->_attributeNameTreatment = self::ATTRIBUTE_TO_LOWER;
+                    break;
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Returns the currently set attribute name treatment
+     *
+     * @return integer|callback
+     */
+    public function getAttributeNameTreatment()
+    {
+        return $this->_attributeNameTreatment;
+    }
+
+    /**
+     * Returns the number of items in current result
+     * Implements Countable
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return $this->_itemCount;
+    }
+
+    /**
+     * Return the current result item
+     * Implements Iterator
+     *
+     * @return array|null
+     * @throws Zend_Ldap_Exception
+     */
+    public function current()
+    {
+        if (!is_resource($this->_current)) {
+            $this->rewind();
+        }
+        if (!is_resource($this->_current)) {
+            return null;
+        }
+
+        $entry = array('dn' => $this->key());
+        $ber_identifier = null;
+        $name = @ldap_first_attribute($this->_ldap->getResource(), $this->_current,
+            $ber_identifier);
+        while ($name) {
+            $data = @ldap_get_values_len($this->_ldap->getResource(), $this->_current, $name);
+            unset($data['count']);
+
+            switch($this->_attributeNameTreatment) {
+                case self::ATTRIBUTE_TO_LOWER:
+                    $attrName = strtolower($name);
+                    break;
+                case self::ATTRIBUTE_TO_UPPER:
+                    $attrName = strtoupper($name);
+                    break;
+                case self::ATTRIBUTE_NATIVE:
+                    $attrName = $name;
+                    break;
+                default:
+                    $attrName = call_user_func($this->_attributeNameTreatment, $name);
+                    break;
+            }
+            $entry[$attrName] = $data;
+            $name = @ldap_next_attribute($this->_ldap->getResource(), $this->_current,
+                $ber_identifier);
+        }
+        ksort($entry, SORT_LOCALE_STRING);
+        return $entry;
+    }
+
+    /**
+     * Return the result item key
+     * Implements Iterator
+     *
+     * @return string|null
+     */
+    public function key()
+    {
+        if (!is_resource($this->_current)) {
+            $this->rewind();
+        }
+        if (is_resource($this->_current)) {
+            $currentDn = @ldap_get_dn($this->_ldap->getResource(), $this->_current);
+            if ($currentDn === false) {
+                /** @see Zend_Ldap_Exception */
+                require_once 'Zend/Ldap/Exception.php';
+                throw new Zend_Ldap_Exception($this->_ldap, 'getting dn');
+            }
+            return $currentDn;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Move forward to next result item
+     * Implements Iterator
+     *
+     * @throws Zend_Ldap_Exception
+     */
+    public function next()
+    {
+        if (is_resource($this->_current)) {
+            $this->_current = @ldap_next_entry($this->_ldap->getResource(), $this->_current);
+            /** @see Zend_Ldap_Exception */
+            require_once 'Zend/Ldap/Exception.php';
+            if ($this->_current === false) {
+                $msg = $this->_ldap->getLastError($code);
+                if ($code === Zend_Ldap_Exception::LDAP_SIZELIMIT_EXCEEDED) {
+                    // we have reached the size limit enforced by the server
+                    return;
+                } else if ($code > Zend_Ldap_Exception::LDAP_SUCCESS) {
+                     throw new Zend_Ldap_Exception($this->_ldap, 'getting next entry (' . $msg . ')');
+                }
+            }
+        }
+    }
+
+    /**
+     * Rewind the Iterator to the first result item
+     * Implements Iterator
+     *
+     * @throws Zend_Ldap_Exception
+     */
+    public function rewind()
+    {
+        if (is_resource($this->_resultId)) {
+            $this->_current = @ldap_first_entry($this->_ldap->getResource(), $this->_resultId);
+            /** @see Zend_Ldap_Exception */
+            require_once 'Zend/Ldap/Exception.php';
+            if ($this->_current === false &&
+                    $this->_ldap->getLastErrorCode() > Zend_Ldap_Exception::LDAP_SUCCESS) {
+                throw new Zend_Ldap_Exception($this->_ldap, 'getting first entry');
+            }
+        }
+    }
+
+    /**
+     * Check if there is a current result item
+     * after calls to rewind() or next()
+     * Implements Iterator
+     *
+     * @return boolean
+     */
+    public function valid()
+    {
+        return (is_resource($this->_current));
+    }
+
+}
Index: trunk/library/Zend/Ldap/Filter.php
===================================================================
--- trunk/library/Zend/Ldap/Filter.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Filter.php	(revision 5146)
@@ -0,0 +1,265 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Filter.php 22662 2010-07-24 17:37:36Z mabe $
+ */
+
+/**
+ * @see Zend_Ldap_Filter_String
+ */
+require_once 'Zend/Ldap/Filter/String.php';
+
+/**
+ * Zend_Ldap_Filter.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Filter
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Filter extends Zend_Ldap_Filter_String
+{
+    const TYPE_EQUALS         = '=';
+    const TYPE_GREATER        = '>';
+    const TYPE_GREATEROREQUAL = '>=';
+    const TYPE_LESS           = '<';
+    const TYPE_LESSOREQUAL    = '<=';
+    const TYPE_APPROX         = '~=';
+
+    /**
+     * Creates an 'equals' filter.
+     * (attr=value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function equals($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_EQUALS, null, null);
+    }
+
+    /**
+     * Creates a 'begins with' filter.
+     * (attr=value*)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function begins($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_EQUALS, null, '*');
+    }
+
+    /**
+     * Creates an 'ends with' filter.
+     * (attr=*value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function ends($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_EQUALS, '*', null);
+    }
+
+    /**
+     * Creates a 'contains' filter.
+     * (attr=*value*)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function contains($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_EQUALS, '*', '*');
+    }
+
+    /**
+     * Creates a 'greater' filter.
+     * (attr>value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function greater($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_GREATER, null, null);
+    }
+
+    /**
+     * Creates a 'greater or equal' filter.
+     * (attr>=value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function greaterOrEqual($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_GREATEROREQUAL, null, null);
+    }
+
+    /**
+     * Creates a 'less' filter.
+     * (attr<value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function less($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_LESS, null, null);
+    }
+
+    /**
+     * Creates an 'less or equal' filter.
+     * (attr<=value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function lessOrEqual($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_LESSOREQUAL, null, null);
+    }
+
+    /**
+     * Creates an 'approx' filter.
+     * (attr~=value)
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @return Zend_Ldap_Filter
+     */
+    public static function approx($attr, $value)
+    {
+        return new self($attr, $value, self::TYPE_APPROX, null, null);
+    }
+
+    /**
+     * Creates an 'any' filter.
+     * (attr=*)
+     *
+     * @param  string $attr
+     * @return Zend_Ldap_Filter
+     */
+    public static function any($attr)
+    {
+        return new self($attr, '', self::TYPE_EQUALS, '*', null);
+    }
+
+    /**
+     * Creates a simple custom string filter.
+     *
+     * @param  string $filter
+     * @return Zend_Ldap_Filter_String
+     */
+    public static function string($filter)
+    {
+        return new Zend_Ldap_Filter_String($filter);
+    }
+
+    /**
+     * Creates a simple string filter to be used with a mask.
+     *
+     * @param string $mask
+     * @param string $value
+     * @return Zend_Ldap_Filter_Mask
+     */
+    public static function mask($mask, $value)
+    {
+        /**
+         * Zend_Ldap_Filter_Mask
+         */
+        require_once 'Zend/Ldap/Filter/Mask.php';
+        return new Zend_Ldap_Filter_Mask($mask, $value);
+    }
+
+    /**
+     * Creates an 'and' filter.
+     *
+     * @param  Zend_Ldap_Filter_Abstract $filter,...
+     * @return Zend_Ldap_Filter_And
+     */
+    public static function andFilter($filter)
+    {
+        /**
+         * Zend_Ldap_Filter_And
+         */
+        require_once 'Zend/Ldap/Filter/And.php';
+        return new Zend_Ldap_Filter_And(func_get_args());
+    }
+
+    /**
+     * Creates an 'or' filter.
+     *
+     * @param  Zend_Ldap_Filter_Abstract $filter,...
+     * @return Zend_Ldap_Filter_Or
+     */
+    public static function orFilter($filter)
+    {
+        /**
+         * Zend_Ldap_Filter_Or
+         */
+        require_once 'Zend/Ldap/Filter/Or.php';
+        return new Zend_Ldap_Filter_Or(func_get_args());
+    }
+
+    /**
+     * Create a filter string.
+     *
+     * @param  string $attr
+     * @param  string $value
+     * @param  string $filtertype
+     * @param  string $prepend
+     * @param  string $append
+     * @return string
+     */
+    private static function _createFilterString($attr, $value, $filtertype, $prepend = null, $append = null)
+    {
+        $str = $attr . $filtertype;
+        if ($prepend !== null) $str .= $prepend;
+        $str .= self::escapeValue($value);
+        if ($append !== null) $str .= $append;
+        return $str;
+    }
+
+    /**
+     * Creates a new Zend_Ldap_Filter.
+     *
+     * @param string $attr
+     * @param string $value
+     * @param string $filtertype
+     * @param string $prepend
+     * @param string $append
+     */
+    public function __construct($attr, $value, $filtertype, $prepend = null, $append = null)
+    {
+        $filter = self::_createFilterString($attr, $value, $filtertype, $prepend, $append);
+        parent::__construct($filter);
+    }
+}
Index: trunk/library/Zend/Ldap/Node.php
===================================================================
--- trunk/library/Zend/Ldap/Node.php	(revision 5146)
+++ trunk/library/Zend/Ldap/Node.php	(revision 5146)
@@ -0,0 +1,1101 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Node.php 22662 2010-07-24 17:37:36Z mabe $
+ */
+
+/**
+ * @see Zend_Ldap
+ */
+require_once 'Zend/Ldap.php';
+/**
+ * @see Zend_Ldap_Node_Abstract
+ */
+require_once 'Zend/Ldap/Node/Abstract.php';
+
+/**
+ * Zend_Ldap_Node provides an object oriented view into a LDAP node.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node extends Zend_Ldap_Node_Abstract implements Iterator, RecursiveIterator
+{
+    /**
+     * Holds the node's new DN if node is renamed.
+     *
+     * @var Zend_Ldap_Dn
+     */
+    protected $_newDn;
+    /**
+     * Holds the node's orginal attributes (as loaded).
+     *
+     * @var array
+     */
+    protected $_originalData;
+    /**
+     * This node will be added
+     *
+     * @var boolean
+     */
+    protected $_new;
+    /**
+     * This node will be deleted
+     *
+     * @var boolean
+     */
+    protected $_delete;
+    /**
+     * Holds the connection to the LDAP server if in connected mode.
+     *
+     * @var Zend_Ldap
+     */
+    protected $_ldap;
+
+    /**
+     * Holds an array of the current node's children.
+     *
+     * @var array
+     */
+    protected $_children;
+
+    /**
+     * Controls iteration status
+     *
+     * @var boolean
+     */
+    private $_iteratorRewind = false;
+
+    /**
+     * Constructor.
+     *
+     * Constructor is protected to enforce the use of factory methods.
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  array        $data
+     * @param  boolean      $fromDataSource
+     * @param  Zend_Ldap    $ldap
+     * @throws Zend_Ldap_Exception
+     */
+    protected function __construct(Zend_Ldap_Dn $dn, array $data, $fromDataSource, Zend_Ldap $ldap = null)
+    {
+        parent::__construct($dn, $data, $fromDataSource);
+        if ($ldap !== null) $this->attachLdap($ldap);
+        else $this->detachLdap();
+    }
+
+    /**
+     * Serialization callback
+     *
+     * Only DN and attributes will be serialized.
+     *
+     * @return array
+     */
+    public function __sleep()
+    {
+        return array('_dn', '_currentData', '_newDn', '_originalData',
+            '_new', '_delete', '_children');
+    }
+
+    /**
+     * Deserialization callback
+     *
+     * Enforces a detached node.
+     *
+     * @return null
+     */
+    public function __wakeup()
+    {
+        $this->detachLdap();
+    }
+
+    /**
+     * Gets the current LDAP connection.
+     *
+     * @return Zend_Ldap
+     * @throws Zend_Ldap_Exception
+     */
+    public function getLdap()
+    {
+        if ($this->_ldap === null) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'No LDAP connection specified.', Zend_Ldap_Exception::LDAP_OTHER);
+        }
+        else return $this->_ldap;
+    }
+
+    /**
+     * Attach node to an LDAP connection
+     *
+     * This is an offline method.
+     *
+     * @uses   Zend_Ldap_Dn::isChildOf()
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function attachLdap(Zend_Ldap $ldap)
+    {
+        if (!Zend_Ldap_Dn::isChildOf($this->_getDn(), $ldap->getBaseDn())) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'LDAP connection is not responsible for given node.',
+                Zend_Ldap_Exception::LDAP_OTHER);
+        }
+
+        if ($ldap !== $this->_ldap) {
+            $this->_ldap = $ldap;
+            if (is_array($this->_children)) {
+                foreach ($this->_children as $child) {
+                    $child->attachLdap($ldap);
+                }
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Detach node from LDAP connection
+     *
+     * This is an offline method.
+     *
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function detachLdap()
+    {
+        $this->_ldap = null;
+        if (is_array($this->_children)) {
+            foreach ($this->_children as $child) {
+                $child->detachLdap();
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Checks if the current node is attached to a LDAP server.
+     *
+     * This is an offline method.
+     *
+     * @return boolean
+     */
+    public function isAttached()
+    {
+        return ($this->_ldap !== null);
+    }
+
+    /**
+     * @param  array   $data
+     * @param  boolean $fromDataSource
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _loadData(array $data, $fromDataSource)
+    {
+        parent::_loadData($data, $fromDataSource);
+        if ($fromDataSource === true) {
+            $this->_originalData = $data;
+        } else {
+            $this->_originalData = array();
+        }
+        $this->_children = null;
+        $this->_markAsNew(($fromDataSource === true) ? false : true);
+        $this->_markAsToBeDeleted(false);
+    }
+
+    /**
+     * Factory method to create a new detached Zend_Ldap_Node for a given DN.
+     *
+     * @param  string|array|Zend_Ldap_Dn $dn
+     * @param  array                     $objectClass
+     * @return Zend_Ldap_Node
+     * @throws Zend_Ldap_Exception
+     */
+    public static function create($dn, array $objectClass = array())
+    {
+        if (is_string($dn) || is_array($dn)) {
+            $dn = Zend_Ldap_Dn::factory($dn);
+        } else if ($dn instanceof Zend_Ldap_Dn) {
+            $dn = clone $dn;
+        } else {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.');
+        }
+        $new = new self($dn, array(), false, null);
+        $new->_ensureRdnAttributeValues();
+        $new->setAttribute('objectClass', $objectClass);
+        return $new;
+    }
+
+    /**
+     * Factory method to create an attached Zend_Ldap_Node for a given DN.
+     *
+     * @param  string|array|Zend_Ldap_Dn $dn
+     * @param  Zend_Ldap                 $ldap
+     * @return Zend_Ldap_Node|null
+     * @throws Zend_Ldap_Exception
+     */
+    public static function fromLdap($dn, Zend_Ldap $ldap)
+    {
+        if (is_string($dn) || is_array($dn)) {
+            $dn = Zend_Ldap_Dn::factory($dn);
+        } else if ($dn instanceof Zend_Ldap_Dn) {
+            $dn = clone $dn;
+        } else {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.');
+        }
+        $data = $ldap->getEntry($dn, array('*', '+'), true);
+        if ($data === null) {
+            return null;
+        }
+        $entry = new self($dn, $data, true, $ldap);
+        return $entry;
+    }
+
+    /**
+     * Factory method to create a detached Zend_Ldap_Node from array data.
+     *
+     * @param  array   $data
+     * @param  boolean $fromDataSource
+     * @return Zend_Ldap_Node
+     * @throws Zend_Ldap_Exception
+     */
+    public static function fromArray(array $data, $fromDataSource = false)
+    {
+        if (!array_key_exists('dn', $data)) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, '\'dn\' key is missing in array.');
+        }
+        if (is_string($data['dn']) || is_array($data['dn'])) {
+            $dn = Zend_Ldap_Dn::factory($data['dn']);
+        } else if ($data['dn'] instanceof Zend_Ldap_Dn) {
+            $dn = clone $data['dn'];
+        } else {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, '\'dn\' key is of a wrong data type.');
+        }
+        $fromDataSource = ($fromDataSource === true) ? true : false;
+        $new = new self($dn, $data, $fromDataSource, null);
+        $new->_ensureRdnAttributeValues();
+        return $new;
+    }
+
+    /**
+     * Ensures that teh RDN attributes are correctly set.
+     *
+     * @return void
+     */
+    protected function _ensureRdnAttributeValues()
+    {
+        foreach ($this->getRdnArray() as $key => $value) {
+            Zend_Ldap_Attribute::setAttribute($this->_currentData, $key, $value, false);
+        }
+    }
+
+    /**
+     * Marks this node as new.
+     *
+     * Node will be added (instead of updated) on calling update() if $new is true.
+     *
+     * @param boolean $new
+     */
+    protected function _markAsNew($new)
+    {
+        $this->_new = ($new === false) ? false : true;
+    }
+
+    /**
+     * Tells if the node is consiedered as new (not present on the server)
+     *
+     * Please note, that this doesn't tell you if the node is present on the server.
+     * Use {@link exits()} to see if a node is already there.
+     *
+     * @return boolean
+     */
+    public function isNew()
+    {
+        return $this->_new;
+    }
+
+    /**
+     * Marks this node as to be deleted.
+     *
+     * Node will be deleted on calling update() if $delete is true.
+     *
+     * @param boolean $delete
+     */
+    protected function _markAsToBeDeleted($delete)
+    {
+        $this->_delete = ($delete === true) ? true : false;
+    }
+
+
+    /**
+    * Is this node going to be deleted once update() is called?
+    *
+    * @return boolean
+    */
+    public function willBeDeleted()
+    {
+        return $this->_delete;
+    }
+
+    /**
+     * Marks this node as to be deleted
+     *
+     * Node will be deleted on calling update() if $delete is true.
+     *
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function delete()
+    {
+        $this->_markAsToBeDeleted(true);
+        return $this;
+    }
+
+    /**
+    * Is this node going to be moved once update() is called?
+    *
+    * @return boolean
+    */
+    public function willBeMoved()
+    {
+        if ($this->isNew() || $this->willBeDeleted()) {
+            return false;
+        } else if ($this->_newDn !== null) {
+            return ($this->_dn != $this->_newDn);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Sends all pending changes to the LDAP server
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function update(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $this->attachLdap($ldap);
+        }
+        $ldap = $this->getLdap();
+        if (!($ldap instanceof Zend_Ldap)) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'No LDAP connection available');
+        }
+
+        if ($this->willBeDeleted()) {
+            if ($ldap->exists($this->_dn)) {
+                $ldap->delete($this->_dn);
+            }
+            return $this;
+        }
+
+        if ($this->isNew()) {
+            $data = $this->getData();
+            $ldap->add($this->_getDn(), $data);
+            $this->_loadData($data, true);
+            return $this;
+        }
+
+        $changedData = $this->getChangedData();
+        if ($this->willBeMoved()) {
+            $recursive = $this->hasChildren();
+            $ldap->rename($this->_dn, $this->_newDn, $recursive, false);
+            foreach ($this->_newDn->getRdn() as $key => $value) {
+                if (array_key_exists($key, $changedData)) {
+                    unset($changedData[$key]);
+                }
+            }
+            $this->_dn = $this->_newDn;
+            $this->_newDn = null;
+        }
+        if (count($changedData) > 0) {
+            $ldap->update($this->_getDn(), $changedData);
+        }
+        $this->_originalData = $this->_currentData;
+        return $this;
+    }
+
+    /**
+     * Gets the DN of the current node as a Zend_Ldap_Dn.
+     *
+     * This is an offline method.
+     *
+     * @return Zend_Ldap_Dn
+     */
+    protected function _getDn()
+    {
+        return ($this->_newDn === null) ? parent::_getDn() : $this->_newDn;
+    }
+
+    /**
+     * Gets the current DN of the current node as a Zend_Ldap_Dn.
+     * The method returns a clone of the node's DN to prohibit modification.
+     *
+     * This is an offline method.
+     *
+     * @return Zend_Ldap_Dn
+     */
+    public function getCurrentDn()
+    {
+        $dn = clone parent::_getDn();
+        return $dn;
+    }
+
+    /**
+     * Sets the new DN for this node
+     *
+     * This is an offline method.
+     *
+     * @param  Zend_Ldap_Dn|string|array $newDn
+     * @throws Zend_Ldap_Exception
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function setDn($newDn)
+    {
+        if ($newDn instanceof Zend_Ldap_Dn) {
+            $this->_newDn = clone $newDn;
+        } else {
+            $this->_newDn = Zend_Ldap_Dn::factory($newDn);
+        }
+        $this->_ensureRdnAttributeValues();
+        return $this;
+    }
+
+    /**
+     * {@see setDn()}
+     *
+     * This is an offline method.
+     *
+     * @param  Zend_Ldap_Dn|string|array $newDn
+     * @throws Zend_Ldap_Exception
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function move($newDn)
+    {
+        return $this->setDn($newDn);
+    }
+
+    /**
+     * {@see setDn()}
+     *
+     * This is an offline method.
+     *
+     * @param  Zend_Ldap_Dn|string|array $newDn
+     * @throws Zend_Ldap_Exception
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function rename($newDn)
+    {
+        return $this->setDn($newDn);
+    }
+
+    /**
+     * Sets the objectClass.
+     *
+     * This is an offline method.
+     *
+     * @param  array|string $value
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function setObjectClass($value)
+    {
+        $this->setAttribute('objectClass', $value);
+        return $this;
+    }
+
+    /**
+     * Appends to the objectClass.
+     *
+     * This is an offline method.
+     *
+     * @param  array|string $value
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function appendObjectClass($value)
+    {
+        $this->appendToAttribute('objectClass', $value);
+        return $this;
+    }
+
+    /**
+     * Returns a LDIF representation of the current node
+     *
+     * @param  array $options Additional options used during encoding
+     * @return string
+     */
+    public function toLdif(array $options = array())
+    {
+        $attributes = array_merge(array('dn' => $this->getDnString()), $this->getData(false));
+        /**
+         * Zend_Ldap_Ldif_Encoder
+         */
+        require_once 'Zend/Ldap/Ldif/Encoder.php';
+        return Zend_Ldap_Ldif_Encoder::encode($attributes, $options);
+    }
+
+    /**
+     * Gets changed node data.
+     *
+     * The array contains all changed attributes.
+     * This format can be used in {@link Zend_Ldap::add()} and {@link Zend_Ldap::update()}.
+     *
+     * This is an offline method.
+     *
+     * @return array
+     */
+    public function getChangedData()
+    {
+        $changed = array();
+        foreach ($this->_currentData as $key => $value) {
+            if (!array_key_exists($key, $this->_originalData) && !empty($value)) {
+                $changed[$key] = $value;
+            } else if ($this->_originalData[$key] !== $this->_currentData[$key]) {
+                $changed[$key] = $value;
+            }
+        }
+        return $changed;
+    }
+
+    /**
+     * Returns all changes made.
+     *
+     * This is an offline method.
+     *
+     * @return array
+     */
+    public function getChanges()
+    {
+        $changes = array(
+            'add'     => array(),
+            'delete'  => array(),
+            'replace' => array());
+        foreach ($this->_currentData as $key => $value) {
+            if (!array_key_exists($key, $this->_originalData) && !empty($value)) {
+                $changes['add'][$key] = $value;
+            } else if (count($this->_originalData[$key]) === 0 && !empty($value)) {
+                $changes['add'][$key] = $value;
+            } else if ($this->_originalData[$key] !== $this->_currentData[$key]) {
+                if (empty($value)) {
+                    $changes['delete'][$key] = $value;
+                } else {
+                    $changes['replace'][$key] = $value;
+                }
+            }
+        }
+        return $changes;
+    }
+
+    /**
+     * Sets a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function setAttribute($name, $value)
+    {
+        $this->_setAttribute($name, $value, false);
+        return $this;
+    }
+
+    /**
+     * Appends to a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function appendToAttribute($name, $value)
+    {
+        $this->_setAttribute($name, $value, true);
+        return $this;
+    }
+
+    /**
+     * Checks if the attribute can be set and sets it accordingly.
+     *
+     * @param  string  $name
+     * @param  mixed   $value
+     * @param  boolean $append
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _setAttribute($name, $value, $append)
+    {
+        $this->_assertChangeableAttribute($name);
+        Zend_Ldap_Attribute::setAttribute($this->_currentData, $name, $value, $append);
+    }
+
+    /**
+     * Sets a LDAP date/time attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string        $name
+     * @param  integer|array $value
+     * @param  boolean       $utc
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function setDateTimeAttribute($name, $value, $utc = false)
+    {
+        $this->_setDateTimeAttribute($name, $value, $utc, false);
+        return $this;
+    }
+
+    /**
+     * Appends to a LDAP date/time attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string        $name
+     * @param  integer|array $value
+     * @param  boolean       $utc
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function appendToDateTimeAttribute($name, $value, $utc = false)
+    {
+        $this->_setDateTimeAttribute($name, $value, $utc, true);
+        return $this;
+    }
+
+    /**
+     * Checks if the attribute can be set and sets it accordingly.
+     *
+     * @param  string        $name
+     * @param  integer|array $value
+     * @param  boolean       $utc
+     * @param  boolean       $append
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _setDateTimeAttribute($name, $value, $utc, $append)
+    {
+        $this->_assertChangeableAttribute($name);
+        Zend_Ldap_Attribute::setDateTimeAttribute($this->_currentData, $name, $value, $utc, $append);
+    }
+
+    /**
+     * Sets a LDAP password.
+     *
+     * @param  string $password
+     * @param  string $hashType
+     * @param  string $attribName
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function setPasswordAttribute($password, $hashType = Zend_Ldap_Attribute::PASSWORD_HASH_MD5,
+        $attribName = 'userPassword')
+    {
+        $this->_assertChangeableAttribute($attribName);
+        Zend_Ldap_Attribute::setPassword($this->_currentData, $password, $hashType, $attribName);
+        return $this;
+    }
+
+    /**
+     * Deletes a LDAP attribute.
+     *
+     * This method deletes the attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function deleteAttribute($name)
+    {
+        if ($this->existsAttribute($name, true)) {
+            $this->_setAttribute($name, null, false);
+        }
+        return $this;
+    }
+
+    /**
+     * Removes duplicate values from a LDAP attribute
+     *
+     * @param  string $attribName
+     * @return void
+     */
+    public function removeDuplicatesFromAttribute($attribName)
+    {
+        Zend_Ldap_Attribute::removeDuplicatesFromAttribute($this->_currentData, $attribName);
+    }
+
+    /**
+     * Remove given values from a LDAP attribute
+     *
+     * @param  string      $attribName
+     * @param  mixed|array $value
+     * @return void
+     */
+    public function removeFromAttribute($attribName, $value)
+    {
+        Zend_Ldap_Attribute::removeFromAttribute($this->_currentData, $attribName, $value);
+    }
+
+    /**
+     * @param  string $name
+     * @return boolean
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _assertChangeableAttribute($name)
+    {
+        $name = strtolower($name);
+        $rdn = $this->getRdnArray(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER);
+        if ($name == 'dn') {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'DN cannot be changed.');
+        }
+        else if (array_key_exists($name, $rdn)) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s part of the RDN');
+        } else if (in_array($name, self::$_systemAttributes)) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s read-only');
+        }
+        else return true;
+    }
+
+    /**
+     * Sets a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return null
+     * @throws Zend_Ldap_Exception
+     */
+    public function __set($name, $value)
+    {
+        $this->setAttribute($name, $value);
+    }
+
+    /**
+     * Deletes a LDAP attribute.
+     *
+     * This method deletes the attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return null
+     * @throws Zend_Ldap_Exception
+     */
+    public function __unset($name)
+    {
+        $this->deleteAttribute($name);
+    }
+
+    /**
+     * Sets a LDAP attribute.
+     * Implements ArrayAccess.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return null
+     * @throws Zend_Ldap_Exception
+     */
+    public function offsetSet($name, $value)
+    {
+        $this->setAttribute($name, $value);
+    }
+
+    /**
+     * Deletes a LDAP attribute.
+     * Implements ArrayAccess.
+     *
+     * This method deletes the attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return null
+     * @throws Zend_Ldap_Exception
+     */
+    public function offsetUnset($name)
+    {
+        $this->deleteAttribute($name);
+    }
+
+    /**
+     * Check if node exists on LDAP.
+     *
+     * This is an online method.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return boolean
+     * @throws Zend_Ldap_Exception
+     */
+    public function exists(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $this->attachLdap($ldap);
+        }
+        $ldap = $this->getLdap();
+        return $ldap->exists($this->_getDn());
+    }
+
+    /**
+     * Reload node attributes from LDAP.
+     *
+     * This is an online method.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function reload(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $this->attachLdap($ldap);
+        }
+        $ldap = $this->getLdap();
+        parent::reload($ldap);
+        return $this;
+    }
+
+    /**
+     * Search current subtree with given options.
+     *
+     * This is an online method.
+     *
+     * @param  string|Zend_Ldap_Filter_Abstract $filter
+     * @param  integer                          $scope
+     * @param  string                           $sort
+     * @return Zend_Ldap_Node_Collection
+     * @throws Zend_Ldap_Exception
+     */
+    public function searchSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB, $sort = null)
+    {
+        /**
+         * @see Zend_Ldap_Node_Collection
+         */
+        require_once 'Zend/Ldap/Node/Collection.php';
+        return $this->getLdap()->search($filter, $this->_getDn(), $scope, array('*', '+'), $sort,
+            'Zend_Ldap_Node_Collection');
+    }
+
+    /**
+     * Count items in current subtree found by given filter.
+     *
+     * This is an online method.
+     *
+     * @param  string|Zend_Ldap_Filter_Abstract $filter
+     * @param  integer                          $scope
+     * @return integer
+     * @throws Zend_Ldap_Exception
+     */
+    public function countSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB)
+    {
+        return $this->getLdap()->count($filter, $this->_getDn(), $scope);
+    }
+
+    /**
+     * Count children of current node.
+     *
+     * This is an online method.
+     *
+     * @return integer
+     * @throws Zend_Ldap_Exception
+     */
+    public function countChildren()
+    {
+        return $this->countSubtree('(objectClass=*)', Zend_Ldap::SEARCH_SCOPE_ONE);
+    }
+
+    /**
+     * Gets children of current node.
+     *
+     * This is an online method.
+     *
+     * @param  string|Zend_Ldap_Filter_Abstract $filter
+     * @param  string                           $sort
+     * @return Zend_Ldap_Node_Collection
+     * @throws Zend_Ldap_Exception
+     */
+    public function searchChildren($filter, $sort = null)
+    {
+        return $this->searchSubtree($filter, Zend_Ldap::SEARCH_SCOPE_ONE, $sort);
+    }
+
+    /**
+     * Checks if current node has children.
+     * Returns whether the current element has children.
+     *
+     * Can be used offline but returns false if children have not been retrieved yet.
+     *
+     * @return boolean
+     * @throws Zend_Ldap_Exception
+     */
+    public function hasChildren()
+    {
+        if (!is_array($this->_children)) {
+            if ($this->isAttached()) {
+                return ($this->countChildren() > 0);
+            } else {
+                return false;
+            }
+        } else {
+            return (count($this->_children) > 0);
+        }
+    }
+
+    /**
+     * Returns the children for the current node.
+     *
+     * Can be used offline but returns an empty array if children have not been retrieved yet.
+     *
+     * @return Zend_Ldap_Node_ChildrenIterator
+     * @throws Zend_Ldap_Exception
+     */
+    public function getChildren()
+    {
+        if (!is_array($this->_children)) {
+            $this->_children = array();
+            if ($this->isAttached()) {
+                $children = $this->searchChildren('(objectClass=*)', null);
+                foreach ($children as $child) {
+                    $this->_children[$child->getRdnString(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER)] = $child;
+                }
+            }
+        }
+        /**
+         * @see Zend_Ldap_Node_ChildrenIterator
+         */
+        require_once 'Zend/Ldap/Node/ChildrenIterator.php';
+        return new Zend_Ldap_Node_ChildrenIterator($this->_children);
+    }
+
+    /**
+     * Returns the parent of the current node.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node
+     * @throws Zend_Ldap_Exception
+     */
+    public function getParent(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $this->attachLdap($ldap);
+        }
+        $ldap = $this->getLdap();
+        $parentDn = $this->_getDn()->getParentDn(1);
+        return self::fromLdap($parentDn, $ldap);
+    }
+
+    /**
+     * Return the current attribute.
+     * Implements Iterator
+     *
+     * @return array
+     */
+    public function current()
+    {
+        return $this;
+    }
+
+    /**
+     * Return the attribute name.
+     * Implements Iterator
+     *
+     * @return string
+     */
+    public function key()
+    {
+        return $this->getRdnString();
+    }
+
+    /**
+     * Move forward to next attribute.
+     * Implements Iterator
+     */
+    public function next()
+    {
+        $this->_iteratorRewind = false;
+    }
+
+    /**
+     * Rewind the Iterator to the first attribute.
+     * Implements Iterator
+     */
+    public function rewind()
+    {
+        $this->_iteratorRewind = true;
+    }
+
+    /**
+     * Check if there is a current attribute
+     * after calls to rewind() or next().
+     * Implements Iterator
+     *
+     * @return boolean
+     */
+    public function valid()
+    {
+        return $this->_iteratorRewind;
+    }
+}
