Index: sandbox/2.2.0.2/library/Zend/Validate.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Validate.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Validate.php	(revision 4456)
@@ -0,0 +1,290 @@
+<?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_Validate
+ * @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: Validate.php 21339 2010-03-05 15:32:25Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Interface
+ */
+require_once 'Zend/Validate/Interface.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @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_Validate implements Zend_Validate_Interface
+{
+    /**
+     * Validator chain
+     *
+     * @var array
+     */
+    protected $_validators = array();
+
+    /**
+     * Array of validation failure messages
+     *
+     * @var array
+     */
+    protected $_messages = array();
+
+    /**
+     * Default Namespaces
+     *
+     * @var array
+     */
+    protected static $_defaultNamespaces = array();
+
+    /**
+     * Array of validation failure message codes
+     *
+     * @var array
+     * @deprecated Since 1.5.0
+     */
+    protected $_errors = array();
+
+    /**
+     * Adds a validator to the end of the chain
+     *
+     * If $breakChainOnFailure is true, then if the validator fails, the next validator in the chain,
+     * if one exists, will not be executed.
+     *
+     * @param  Zend_Validate_Interface $validator
+     * @param  boolean                 $breakChainOnFailure
+     * @return Zend_Validate Provides a fluent interface
+     */
+    public function addValidator(Zend_Validate_Interface $validator, $breakChainOnFailure = false)
+    {
+        $this->_validators[] = array(
+            'instance' => $validator,
+            'breakChainOnFailure' => (boolean) $breakChainOnFailure
+            );
+        return $this;
+    }
+
+    /**
+     * Returns true if and only if $value passes all validations in the chain
+     *
+     * Validators are run in the order in which they were added to the chain (FIFO).
+     *
+     * @param  mixed $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        $this->_messages = array();
+        $this->_errors   = array();
+        $result = true;
+        foreach ($this->_validators as $element) {
+            $validator = $element['instance'];
+            if ($validator->isValid($value)) {
+                continue;
+            }
+            $result = false;
+            $messages = $validator->getMessages();
+            $this->_messages = array_merge($this->_messages, $messages);
+            $this->_errors   = array_merge($this->_errors,   array_keys($messages));
+            if ($element['breakChainOnFailure']) {
+                break;
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns array of validation failure messages
+     *
+     * @return array
+     */
+    public function getMessages()
+    {
+        return $this->_messages;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns array of validation failure message codes
+     *
+     * @return array
+     * @deprecated Since 1.5.0
+     */
+    public function getErrors()
+    {
+        return $this->_errors;
+    }
+
+    /**
+     * Returns the set default namespaces
+     *
+     * @return array
+     */
+    public static function getDefaultNamespaces()
+    {
+        return self::$_defaultNamespaces;
+    }
+
+    /**
+     * Sets new default namespaces
+     *
+     * @param array|string $namespace
+     * @return null
+     */
+    public static function setDefaultNamespaces($namespace)
+    {
+        if (!is_array($namespace)) {
+            $namespace = array((string) $namespace);
+        }
+
+        self::$_defaultNamespaces = $namespace;
+    }
+
+    /**
+     * Adds a new default namespace
+     *
+     * @param array|string $namespace
+     * @return null
+     */
+    public static function addDefaultNamespaces($namespace)
+    {
+        if (!is_array($namespace)) {
+            $namespace = array((string) $namespace);
+        }
+
+        self::$_defaultNamespaces = array_unique(array_merge(self::$_defaultNamespaces, $namespace));
+    }
+
+    /**
+     * Returns true when defaultNamespaces are set
+     *
+     * @return boolean
+     */
+    public static function hasDefaultNamespaces()
+    {
+        return (!empty(self::$_defaultNamespaces));
+    }
+
+    /**
+     * @param  mixed    $value
+     * @param  string   $classBaseName
+     * @param  array    $args          OPTIONAL
+     * @param  mixed    $namespaces    OPTIONAL
+     * @return boolean
+     * @throws Zend_Validate_Exception
+     */
+    public static function is($value, $classBaseName, array $args = array(), $namespaces = array())
+    {
+        $namespaces = array_merge((array) $namespaces, self::$_defaultNamespaces, array('Zend_Validate'));
+        $className  = ucfirst($classBaseName);
+        try {
+            if (!class_exists($className, false)) {
+                require_once 'Zend/Loader.php';
+                foreach($namespaces as $namespace) {
+                    $class = $namespace . '_' . $className;
+                    $file  = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
+                    if (Zend_Loader::isReadable($file)) {
+                        Zend_Loader::loadClass($class);
+                        $className = $class;
+                        break;
+                    }
+                }
+            }
+
+            $class = new ReflectionClass($className);
+            if ($class->implementsInterface('Zend_Validate_Interface')) {
+                if ($class->hasMethod('__construct')) {
+                    $keys    = array_keys($args);
+                    $numeric = false;
+                    foreach($keys as $key) {
+                        if (is_numeric($key)) {
+                            $numeric = true;
+                            break;
+                        }
+                    }
+
+                    if ($numeric) {
+                        $object = $class->newInstanceArgs($args);
+                    } else {
+                        $object = $class->newInstance($args);
+                    }
+                } else {
+                    $object = $class->newInstance();
+                }
+
+                return $object->isValid($value);
+            }
+        } catch (Zend_Validate_Exception $ze) {
+            // if there is an exception while validating throw it
+            throw $ze;
+        } catch (Exception $e) {
+            // fallthrough and continue for missing validation classes
+        }
+
+        require_once 'Zend/Validate/Exception.php';
+        throw new Zend_Validate_Exception("Validate class not found from basename '$classBaseName'");
+    }
+
+    /**
+     * Returns the maximum allowed message length
+     *
+     * @return integer
+     */
+    public static function getMessageLength()
+    {
+        require_once 'Zend/Validate/Abstract.php';
+        return Zend_Validate_Abstract::getMessageLength();
+    }
+
+    /**
+     * Sets the maximum allowed message length
+     *
+     * @param integer $length
+     */
+    public static function setMessageLength($length = -1)
+    {
+        require_once 'Zend/Validate/Abstract.php';
+        Zend_Validate_Abstract::setMessageLength($length);
+    }
+
+    /**
+     * Returns the default translation object
+     *
+     * @return Zend_Translate_Adapter|null
+     */
+    public static function getDefaultTranslator($translator = null)
+    {
+        require_once 'Zend/Validate/Abstract.php';
+        return Zend_Validate_Abstract::getDefaultTranslator();
+    }
+
+    /**
+     * Sets a default translation object for all validation objects
+     *
+     * @param Zend_Translate|Zend_Translate_Adapter|null $translator
+     */
+    public static function setDefaultTranslator($translator = null)
+    {
+        require_once 'Zend/Validate/Abstract.php';
+        Zend_Validate_Abstract::setDefaultTranslator($translator);
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Ldap/Collection.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Ldap/Collection.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Ldap/Collection.php	(revision 4456)
@@ -0,0 +1,239 @@
+<?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: Collection.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Zend_Ldap_Collection wraps a list of LDAP entries.
+ *
+ * @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 implements Iterator, Countable
+{
+    /**
+     * Iterator
+     *
+     * @var Zend_Ldap_Collection_Iterator_Default
+     */
+    protected $_iterator = null;
+
+    /**
+     * Current item number
+     *
+     * @var integer
+     */
+    protected $_current = -1;
+
+    /**
+     * Container for item caching to speed up multiple iterations
+     *
+     * @var array
+     */
+    protected $_cache = array();
+
+    /**
+     * Constructor.
+     *
+     * @param Zend_Ldap_Collection_Iterator_Default $iterator
+     */
+    public function __construct(Zend_Ldap_Collection_Iterator_Default $iterator)
+    {
+        $this->_iterator = $iterator;
+    }
+
+    public function __destruct()
+    {
+        $this->close();
+    }
+
+    /**
+     * Closes the current result set
+     *
+     * @return boolean
+     */
+    public function close()
+    {
+        return $this->_iterator->close();
+    }
+
+    /**
+     * Get all entries as an array
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        $data = array();
+        foreach ($this as $item) {
+            $data[] = $item;
+        }
+        return $data;
+    }
+
+    /**
+     * Get first entry
+     *
+     * @return array
+     */
+    public function getFirst()
+    {
+        if ($this->count() > 0) {
+            $this->rewind();
+            return $this->current();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the underlying iterator
+     *
+     * @return Zend_Ldap_Collection_Iterator_Default
+     */
+    public function getInnerIterator()
+    {
+        return $this->_iterator;
+    }
+
+    /**
+     * Returns the number of items in current result
+     * Implements Countable
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return $this->_iterator->count();
+    }
+
+    /**
+     * Return the current result item
+     * Implements Iterator
+     *
+     * @return array|null
+     * @throws Zend_Ldap_Exception
+     */
+    public function current()
+    {
+        if ($this->count() > 0) {
+            if ($this->_current < 0) {
+                $this->rewind();
+            }
+            if (!array_key_exists($this->_current, $this->_cache)) {
+                $current = $this->_iterator->current();
+                if ($current === null) {
+                    return null;
+                }
+                $this->_cache[$this->_current] = $this->_createEntry($current);
+            }
+            return $this->_cache[$this->_current];
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Creates the data structure for the given entry data
+     *
+     * @param  array $data
+     * @return array
+     */
+    protected function _createEntry(array $data)
+    {
+        return $data;
+    }
+
+    /**
+     * Return the current result item DN
+     *
+     * @return string|null
+     */
+    public function dn()
+    {
+        if ($this->count() > 0) {
+            if ($this->_current < 0) {
+                $this->rewind();
+            }
+            return $this->_iterator->key();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Return the current result item key
+     * Implements Iterator
+     *
+     * @return int|null
+     */
+    public function key()
+    {
+        if ($this->count() > 0) {
+            if ($this->_current < 0) {
+                $this->rewind();
+            }
+            return $this->_current;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Move forward to next result item
+     * Implements Iterator
+     *
+     * @throws Zend_Ldap_Exception
+     */
+    public function next()
+    {
+        $this->_iterator->next();
+        $this->_current++;
+    }
+
+    /**
+     * Rewind the Iterator to the first result item
+     * Implements Iterator
+     *
+     * @throws Zend_Ldap_Exception
+     */
+    public function rewind()
+    {
+        $this->_iterator->rewind();
+        $this->_current = 0;
+    }
+
+    /**
+     * Check if there is a current result item
+     * after calls to rewind() or next()
+     * Implements Iterator
+     *
+     * @return boolean
+     */
+    public function valid()
+    {
+        if (isset($this->_cache[$this->_current])) {
+            return true;
+        } else {
+            return $this->_iterator->valid();
+        }
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Ldap/Dn.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Ldap/Dn.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Ldap/Dn.php	(revision 4456)
@@ -0,0 +1,794 @@
+<?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: Dn.php 22662 2010-07-24 17:37:36Z mabe $
+ */
+
+/**
+ * Zend_Ldap_Dn provides an API for DN manipulation
+ *
+ * @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_Dn implements ArrayAccess
+{
+    const ATTR_CASEFOLD_NONE  = 'none';
+    const ATTR_CASEFOLD_UPPER = 'upper';
+    const ATTR_CASEFOLD_LOWER = 'lower';
+
+    /**
+     * The default case fold to use
+     *
+     * @var string
+     */
+    protected static $_defaultCaseFold = self::ATTR_CASEFOLD_NONE;
+
+    /**
+     * The case fold used for this instance
+     *
+     * @var string
+     */
+    protected $_caseFold;
+
+    /**
+     * The DN data
+     *
+     * @var array
+     */
+    protected $_dn;
+
+    /**
+     * Creates a DN from an array or a string
+     *
+     * @param  string|array $dn
+     * @param  string|null  $caseFold
+     * @return Zend_Ldap_Dn
+     * @throws Zend_Ldap_Exception
+     */
+    public static function factory($dn, $caseFold = null)
+    {
+        if (is_array($dn)) {
+            return self::fromArray($dn, $caseFold);
+        } else if (is_string($dn)) {
+            return self::fromString($dn, $caseFold);
+        } else {
+            /**
+             * Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'Invalid argument type for $dn');
+        }
+    }
+
+    /**
+     * Creates a DN from a string
+     *
+     * @param  string      $dn
+     * @param  string|null $caseFold
+     * @return Zend_Ldap_Dn
+     * @throws Zend_Ldap_Exception
+     */
+    public static function fromString($dn, $caseFold = null)
+    {
+        $dn = trim($dn);
+        if (empty($dn)) {
+            $dnArray = array();
+        } else {
+            $dnArray = self::explodeDn((string)$dn);
+        }
+        return new self($dnArray, $caseFold);
+    }
+
+    /**
+     * Creates a DN from an array
+     *
+     * @param  array       $dn
+     * @param  string|null $caseFold
+     * @return Zend_Ldap_Dn
+     * @throws Zend_Ldap_Exception
+     */
+    public static function fromArray(array $dn, $caseFold = null)
+    {
+         return new self($dn, $caseFold);
+    }
+
+    /**
+     * Constructor
+     *
+     * @param array       $dn
+     * @param string|null $caseFold
+     */
+    protected function __construct(array $dn, $caseFold)
+    {
+        $this->_dn = $dn;
+        $this->setCaseFold($caseFold);
+    }
+
+    /**
+     * Gets the RDN of the current DN
+     *
+     * @param  string $caseFold
+     * @return array
+     * @throws Zend_Ldap_Exception if DN has no RDN (empty array)
+     */
+    public function getRdn($caseFold = null)
+    {
+        $caseFold = self::_sanitizeCaseFold($caseFold, $this->_caseFold);
+        return self::_caseFoldRdn($this->get(0, 1, $caseFold), null);
+    }
+
+    /**
+     * Gets the RDN of the current DN as a string
+     *
+     * @param  string $caseFold
+     * @return string
+     * @throws Zend_Ldap_Exception if DN has no RDN (empty array)
+     */
+    public function getRdnString($caseFold = null)
+    {
+        $caseFold = self::_sanitizeCaseFold($caseFold, $this->_caseFold);
+        return self::implodeRdn($this->getRdn(), $caseFold);
+    }
+
+    /**
+     * Get the parent DN $levelUp levels up the tree
+     *
+     * @param  int $levelUp
+     * @return Zend_Ldap_Dn
+     */
+    public function getParentDn($levelUp = 1)
+    {
+        $levelUp = (int)$levelUp;
+        if ($levelUp < 1 || $levelUp >= count($this->_dn)) {
+            /**
+             * Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'Cannot retrieve parent DN with given $levelUp');
+        }
+        $newDn = array_slice($this->_dn, $levelUp);
+        return new self($newDn, $this->_caseFold);
+    }
+
+    /**
+     * Get a DN part
+     *
+     * @param  int    $index
+     * @param  int    $length
+     * @param  string $caseFold
+     * @return array
+     * @throws Zend_Ldap_Exception if index is illegal
+     */
+    public function get($index, $length = 1, $caseFold = null)
+    {
+        $caseFold = self::_sanitizeCaseFold($caseFold, $this->_caseFold);
+        $this->_assertIndex($index);
+        $length = (int)$length;
+        if ($length <= 0) {
+            $length = 1;
+        }
+        if ($length === 1) {
+            return self::_caseFoldRdn($this->_dn[$index], $caseFold);
+        }
+        else {
+            return self::_caseFoldDn(array_slice($this->_dn, $index, $length, false), $caseFold);
+        }
+    }
+
+    /**
+     * Set a DN part
+     *
+     * @param  int   $index
+     * @param  array $value
+     * @return Zend_Ldap_Dn Provides a fluent interface
+     * @throws Zend_Ldap_Exception if index is illegal
+     */
+    public function set($index, array $value)
+    {
+        $this->_assertIndex($index);
+        self::_assertRdn($value);
+        $this->_dn[$index] = $value;
+        return $this;
+    }
+
+    /**
+     * Remove a DN part
+     *
+     * @param  int $index
+     * @param  int $length
+     * @return Zend_Ldap_Dn Provides a fluent interface
+     * @throws Zend_Ldap_Exception if index is illegal
+     */
+    public function remove($index, $length = 1)
+    {
+        $this->_assertIndex($index);
+        $length = (int)$length;
+        if ($length <= 0) {
+            $length = 1;
+        }
+        array_splice($this->_dn, $index, $length, null);
+        return $this;
+    }
+
+    /**
+     * Append a DN part
+     *
+     * @param  array $value
+     * @return Zend_Ldap_Dn Provides a fluent interface
+     */
+    public function append(array $value)
+    {
+        self::_assertRdn($value);
+        $this->_dn[] = $value;
+        return $this;
+    }
+
+    /**
+     * Prepend a DN part
+     *
+     * @param  array $value
+     * @return Zend_Ldap_Dn Provides a fluent interface
+     */
+    public function prepend(array $value)
+    {
+        self::_assertRdn($value);
+        array_unshift($this->_dn, $value);
+        return $this;
+    }
+
+    /**
+     * Insert a DN part
+     *
+     * @param  int   $index
+     * @param  array $value
+     * @return Zend_Ldap_Dn Provides a fluent interface
+     * @throws Zend_Ldap_Exception if index is illegal
+     */
+    public function insert($index, array $value)
+    {
+        $this->_assertIndex($index);
+        self::_assertRdn($value);
+        $first = array_slice($this->_dn, 0, $index + 1);
+        $second = array_slice($this->_dn, $index + 1);
+        $this->_dn = array_merge($first, array($value), $second);
+        return $this;
+    }
+
+    /**
+     * Assert index is correct and usable
+     *
+     * @param  mixed $index
+     * @return boolean
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _assertIndex($index)
+    {
+        if (!is_int($index)) {
+            /**
+             * Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'Parameter $index must be an integer');
+        }
+        if ($index < 0 || $index >= count($this->_dn)) {
+            /**
+             * Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'Parameter $index out of bounds');
+        }
+        return true;
+    }
+
+    /**
+     * Assert if value is in a correct RDN format
+     *
+     * @param  array $value
+     * @return boolean
+     * @throws Zend_Ldap_Exception
+     */
+    protected static function _assertRdn(array $value)
+    {
+        if (count($value)<1) {
+            /**
+             * Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'RDN Array is malformed: it must have at least one item');
+        }
+
+        foreach (array_keys($value) as $key) {
+            if (!is_string($key)) {
+                /**
+                 * Zend_Ldap_Exception
+                 */
+                require_once 'Zend/Ldap/Exception.php';
+                throw new Zend_Ldap_Exception(null, 'RDN Array is malformed: it must use string keys');
+            }
+        }
+    }
+
+    /**
+     * Sets the case fold
+     *
+     * @param string|null $caseFold
+     */
+    public function setCaseFold($caseFold)
+    {
+        $this->_caseFold = self::_sanitizeCaseFold($caseFold, self::$_defaultCaseFold);
+    }
+
+    /**
+     * Return DN as a string
+     *
+     * @param  string $caseFold
+     * @return string
+     * @throws Zend_Ldap_Exception
+     */
+    public function toString($caseFold = null)
+    {
+        $caseFold = self::_sanitizeCaseFold($caseFold, $this->_caseFold);
+        return self::implodeDn($this->_dn, $caseFold);
+    }
+
+    /**
+     * Return DN as an array
+     *
+     * @param  string $caseFold
+     * @return array
+     */
+    public function toArray($caseFold = null)
+    {
+        $caseFold = self::_sanitizeCaseFold($caseFold, $this->_caseFold);
+
+        if ($caseFold === self::ATTR_CASEFOLD_NONE) {
+            return $this->_dn;
+        } else {
+            return self::_caseFoldDn($this->_dn, $caseFold);
+        }
+    }
+
+    /**
+     * Do a case folding on a RDN
+     *
+     * @param  array  $part
+     * @param  string $caseFold
+     * @return array
+     */
+    protected static function _caseFoldRdn(array $part, $caseFold)
+    {
+        switch ($caseFold) {
+            case self::ATTR_CASEFOLD_UPPER:
+                return array_change_key_case($part, CASE_UPPER);
+            case self::ATTR_CASEFOLD_LOWER:
+                return array_change_key_case($part, CASE_LOWER);
+            case self::ATTR_CASEFOLD_NONE:
+            default:
+                return $part;
+        }
+    }
+
+    /**
+     * Do a case folding on a DN ort part of it
+     *
+     * @param  array  $dn
+     * @param  string $caseFold
+     * @return array
+     */
+    protected static function _caseFoldDn(array $dn, $caseFold)
+    {
+        $return = array();
+        foreach ($dn as $part) {
+            $return[] = self::_caseFoldRdn($part, $caseFold);
+        }
+        return $return;
+    }
+
+    /**
+     * Cast to string representation {@see toString()}
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->toString();
+    }
+
+    /**
+     * Required by the ArrayAccess implementation
+     *
+     * @param  int $offset
+     * @return boolean
+     */
+    public function offsetExists($offset)
+    {
+        $offset = (int)$offset;
+        if ($offset < 0 || $offset >= count($this->_dn)) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Proxy to {@see get()}
+     * Required by the ArrayAccess implementation
+     *
+     * @param  int $offset
+     * @return array
+     */
+     public function offsetGet($offset)
+     {
+         return $this->get($offset, 1, null);
+     }
+
+     /**
+      * Proxy to {@see set()}
+      * Required by the ArrayAccess implementation
+      *
+      * @param int   $offset
+      * @param array $value
+      */
+     public function offsetSet($offset, $value)
+     {
+         $this->set($offset, $value);
+     }
+
+     /**
+      * Proxy to {@see remove()}
+      * Required by the ArrayAccess implementation
+      *
+      * @param int $offset
+      */
+     public function offsetUnset($offset)
+     {
+         $this->remove($offset, 1);
+     }
+
+    /**
+     * Sets the default case fold
+     *
+     * @param string $caseFold
+     */
+    public static function setDefaultCaseFold($caseFold)
+    {
+        self::$_defaultCaseFold = self::_sanitizeCaseFold($caseFold, self::ATTR_CASEFOLD_NONE);
+    }
+
+    /**
+     * Sanitizes the case fold
+     *
+     * @param  string $caseFold
+     * @return string
+     */
+    protected static function _sanitizeCaseFold($caseFold, $default)
+    {
+        switch ($caseFold) {
+            case self::ATTR_CASEFOLD_NONE:
+            case self::ATTR_CASEFOLD_UPPER:
+            case self::ATTR_CASEFOLD_LOWER:
+                return $caseFold;
+                break;
+            default:
+                return $default;
+                break;
+        }
+    }
+
+    /**
+     * Escapes a DN value according to RFC 2253
+     *
+     * Escapes the given VALUES according to RFC 2253 so that they can be safely used in LDAP DNs.
+     * The characters ",", "+", """, "\", "<", ">", ";", "#", " = " with a special meaning in RFC 2252
+     * are preceeded by ba backslash. Control characters with an ASCII code < 32 are represented as \hexpair.
+     * Finally all leading and trailing spaces are converted to sequences of \20.
+     * @see Net_LDAP2_Util::escape_dn_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 An array containing the DN values that should be escaped
+     * @return array The 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('\\\\', '\,', '\+', '\"', '\<', '\>', '\;', '\#', '\='), $val);
+            $val = Zend_Ldap_Converter::ascToHex32($val);
+
+            // Convert all leading and trailing spaces to sequences of \20.
+            if (preg_match('/^(\s*)(.+?)(\s*)$/', $val, $matches)) {
+                $val = $matches[2];
+                for ($i = 0; $i<strlen($matches[1]); $i++) {
+                    $val = '\20' . $val;
+                }
+                for ($i = 0; $i<strlen($matches[3]); $i++) {
+                    $val = $val . '\20';
+                }
+            }
+            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()}.
+     *
+     * Any escape sequence starting with a baskslash - hexpair or special character -
+     * will be transformed back to the corresponding character.
+     * @see Net_LDAP2_Util::escape_dn_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 DN Values
+     * @return array Same as $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 => $val) {
+            // strip slashes from special chars
+            $val = str_replace(array('\\\\', '\,', '\+', '\"', '\<', '\>', '\;', '\#', '\='),
+                array('\\', ',', '+', '"', '<', '>', ';', '#', '=', ), $val);
+            $values[$key] = Zend_Ldap_Converter::hex32ToAsc($val);
+        }
+        return (count($values) == 1) ? $values[0] : $values;
+    }
+
+    /**
+     * Creates an array containing all parts of the given DN.
+     *
+     * Array will be of type
+     * array(
+     *      array("cn" => "name1", "uid" => "user"),
+     *      array("cn" => "name2"),
+     *      array("dc" => "example"),
+     *      array("dc" => "org")
+     * )
+     * for a DN of cn=name1+uid=user,cn=name2,dc=example,dc=org.
+     *
+     * @param  string $dn
+     * @param  array  $keys     An optional array to receive DN keys (e.g. CN, OU, DC, ...)
+     * @param  array  $vals     An optional array to receive DN values
+     * @param  string $caseFold
+     * @return array
+     * @throws Zend_Ldap_Exception
+     */
+    public static function explodeDn($dn, array &$keys = null, array &$vals = null,
+        $caseFold = self::ATTR_CASEFOLD_NONE)
+    {
+        $k = array();
+        $v = array();
+        if (!self::checkDn($dn, $k, $v, $caseFold)) {
+            /**
+             * Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'DN is malformed');
+        }
+        $ret = array();
+        for ($i = 0; $i < count($k); $i++) {
+            if (is_array($k[$i]) && is_array($v[$i]) && (count($k[$i]) === count($v[$i]))) {
+                $multi = array();
+                for ($j = 0; $j < count($k[$i]); $j++) {
+                    $key=$k[$i][$j];
+                    $val=$v[$i][$j];
+                    $multi[$key] = $val;
+                }
+                $ret[] = $multi;
+            } else if (is_string($k[$i]) && is_string($v[$i])) {
+                $ret[] = array($k[$i] => $v[$i]);
+            }
+        }
+        if ($keys !== null) $keys = $k;
+        if ($vals !== null) $vals = $v;
+        return $ret;
+    }
+
+    /**
+     * @param  string $dn       The DN to parse
+     * @param  array  $keys     An optional array to receive DN keys (e.g. CN, OU, DC, ...)
+     * @param  array  $vals     An optional array to receive DN values
+     * @param  string $caseFold
+     * @return boolean True if the DN was successfully parsed or false if the string is not a valid DN.
+     */
+    public static function checkDn($dn, array &$keys = null, array &$vals = null,
+        $caseFold = self::ATTR_CASEFOLD_NONE)
+    {
+        /* This is a classic state machine parser. Each iteration of the
+         * loop processes one character. State 1 collects the key. When equals ( = )
+         * is encountered the state changes to 2 where the value is collected
+         * until a comma (,) or semicolon (;) is encountered after which we switch back
+         * to state 1. If a backslash (\) is encountered, state 3 is used to collect the
+         * following character without engaging the logic of other states.
+         */
+        $key = null;
+        $value = null;
+        $slen = strlen($dn);
+        $state = 1;
+        $ko = $vo = 0;
+        $multi = false;
+        $ka = array();
+        $va = array();
+        for ($di = 0; $di <= $slen; $di++) {
+            $ch = ($di == $slen) ? 0 : $dn[$di];
+            switch ($state) {
+                case 1: // collect key
+                    if ($ch === '=') {
+                        $key = trim(substr($dn, $ko, $di - $ko));
+                        if ($caseFold == self::ATTR_CASEFOLD_LOWER) $key = strtolower($key);
+                        else if ($caseFold == self::ATTR_CASEFOLD_UPPER) $key = strtoupper($key);
+                        if (is_array($multi)) {
+                            $keyId = strtolower($key);
+                            if (in_array($keyId, $multi)) {
+                                return false;
+                            }
+                            $ka[count($ka)-1][] = $key;
+                            $multi[] = $keyId;
+                        } else {
+                            $ka[] = $key;
+                        }
+                        $state = 2;
+                        $vo = $di + 1;
+                    } else if ($ch === ',' || $ch === ';' || $ch === '+') {
+                        return false;
+                    }
+                    break;
+                case 2: // collect value
+                    if ($ch === '\\') {
+                        $state = 3;
+                    } else if ($ch === ',' || $ch === ';' || $ch === 0 || $ch === '+') {
+                        $value = self::unescapeValue(trim(substr($dn, $vo, $di - $vo)));
+                        if (is_array($multi)) {
+                            $va[count($va)-1][] = $value;
+                        } else {
+                            $va[] = $value;
+                        }
+                        $state = 1;
+                        $ko = $di + 1;
+                        if ($ch === '+' && $multi === false) {
+                            $lastKey = array_pop($ka);
+                            $lastVal = array_pop($va);
+                            $ka[] = array($lastKey);
+                            $va[] = array($lastVal);
+                            $multi = array(strtolower($lastKey));
+                        } else if ($ch === ','|| $ch === ';' || $ch === 0) {
+                            $multi = false;
+                        }
+                    } else if ($ch === '=') {
+                        return false;
+                    }
+                    break;
+                case 3: // escaped
+                    $state = 2;
+                    break;
+            }
+        }
+
+        if ($keys !== null) {
+            $keys = $ka;
+        }
+        if ($vals !== null) {
+            $vals = $va;
+        }
+
+        return ($state === 1 && $ko > 0);
+    }
+
+    /**
+     * Returns a DN part in the form $attribute = $value
+     *
+     * This method supports the creation of multi-valued RDNs
+     * $part must contain an even number of elemets.
+     *
+     * @param  array  $attribute
+     * @param  string $caseFold
+     * @return string
+     * @throws Zend_Ldap_Exception
+     */
+    public static function implodeRdn(array $part, $caseFold = null)
+    {
+        self::_assertRdn($part);
+        $part = self::_caseFoldRdn($part, $caseFold);
+        $rdnParts = array();
+        foreach ($part as $key => $value) {
+            $value = self::escapeValue($value);
+            $keyId = strtolower($key);
+            $rdnParts[$keyId] =  implode('=', array($key, $value));
+        }
+        ksort($rdnParts, SORT_STRING);
+        return implode('+', $rdnParts);
+    }
+
+    /**
+     * Implodes an array in the form delivered by {@link explodeDn()}
+     * to a DN string.
+     *
+     * $dnArray must be of type
+     * array(
+     *      array("cn" => "name1", "uid" => "user"),
+     *      array("cn" => "name2"),
+     *      array("dc" => "example"),
+     *      array("dc" => "org")
+     * )
+     *
+     * @param  array  $dnArray
+     * @param  string $caseFold
+     * @param  string $separator
+     * @return string
+     * @throws Zend_Ldap_Exception
+     */
+    public static function implodeDn(array $dnArray, $caseFold = null, $separator = ',')
+    {
+        $parts = array();
+        foreach ($dnArray as $p) {
+            $parts[] = self::implodeRdn($p, $caseFold);
+        }
+        return implode($separator, $parts);
+    }
+
+    /**
+     * Checks if given $childDn is beneath $parentDn subtree.
+     *
+     * @param  string|Zend_Ldap_Dn $childDn
+     * @param  string|Zend_Ldap_Dn $parentDn
+     * @return boolean
+     */
+    public static function isChildOf($childDn, $parentDn)
+    {
+        try {
+            $keys = array();
+            $vals = array();
+            if ($childDn instanceof Zend_Ldap_Dn) {
+                $cdn = $childDn->toArray(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER);
+            } else {
+                $cdn = self::explodeDn($childDn, $keys, $vals, Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER);
+            }
+            if ($parentDn instanceof Zend_Ldap_Dn) {
+                $pdn = $parentDn->toArray(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER);
+            } else {
+                $pdn = self::explodeDn($parentDn, $keys, $vals, Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER);
+            }
+        }
+        catch (Zend_Ldap_Exception $e) {
+            return false;
+        }
+
+        $startIndex = count($cdn)-count($pdn);
+        if ($startIndex<0) return false;
+        for ($i = 0; $i<count($pdn); $i++) {
+            if ($cdn[$i+$startIndex] != $pdn[$i]) return false;
+        }
+        return true;
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Ldap/Converter.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Ldap/Converter.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Ldap/Converter.php	(revision 4456)
@@ -0,0 +1,396 @@
+<?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: Converter.php 23486 2010-12-10 04:05:30Z mjh_ca $
+ */
+
+/**
+ * Zend_Ldap_Converter is a collection of useful LDAP related conversion 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_Converter
+{
+    const STANDARD         = 0;
+    const BOOLEAN          = 1;
+    const GENERALIZED_TIME = 2;
+
+    /**
+     * Converts all ASCII chars < 32 to "\HEX"
+     *
+     * @see Net_LDAP2_Util::asc2hex32() from Benedikt Hallinger <beni@php.net>
+     * @link http://pear.php.net/package/Net_LDAP2
+     * @author Benedikt Hallinger <beni@php.net>
+     *
+     * @param  string $string String to convert
+     * @return string
+     */
+    public static function ascToHex32($string)
+    {
+        for ($i = 0; $i<strlen($string); $i++) {
+            $char = substr($string, $i, 1);
+            if (ord($char)<32) {
+                $hex = dechex(ord($char));
+                if (strlen($hex) == 1) $hex = '0' . $hex;
+                $string = str_replace($char, '\\' . $hex, $string);
+            }
+        }
+        return $string;
+    }
+
+    /**
+     * Converts all Hex expressions ("\HEX") to their original ASCII characters
+     *
+     * @see Net_LDAP2_Util::hex2asc() from Benedikt Hallinger <beni@php.net>,
+     * heavily based on work from DavidSmith@byu.net
+     * @link http://pear.php.net/package/Net_LDAP2
+     * @author Benedikt Hallinger <beni@php.net>, heavily based on work from DavidSmith@byu.net
+     *
+     * @param  string $string String to convert
+     * @return string
+     */
+    public static function hex32ToAsc($string)
+    {
+        $string = preg_replace("/\\\([0-9A-Fa-f]{2})/e", "''.chr(hexdec('\\1')).''", $string);
+        return $string;
+    }
+
+    /**
+     * Convert any value to an LDAP-compatible value.
+     *
+     * By setting the <var>$type</var>-parameter the conversion of a certain
+     * type can be forced
+     *
+     * @todo write more tests
+     *
+     * @param    mixed     $value     The value to convert
+     * @param    int       $ytpe      The conversion type to use
+     * @return    string
+     * @throws    Zend_Ldap_Converter_Exception
+     */
+    public static function toLdap($value, $type = self::STANDARD)
+    {
+        try {
+            switch ($type) {
+                case self::BOOLEAN:
+                    return self::toldapBoolean($value);
+                    break;
+                case self::GENERALIZED_TIME:
+                    return self::toLdapDatetime($value);
+                    break;
+                default:
+                    if (is_string($value)) {
+                        return $value;
+                    } else if (is_int($value) || is_float($value)) {
+                        return (string)$value;
+                    } else if (is_bool($value)) {
+                        return self::toldapBoolean($value);
+                    } else if (is_object($value)) {
+                        if ($value instanceof DateTime) {
+                            return self::toLdapDatetime($value);
+                        } else if ($value instanceof Zend_Date) {
+                            return self::toLdapDatetime($value);
+                        } else {
+                            return self::toLdapSerialize($value);
+                        }
+                    } else if (is_array($value)) {
+                        return self::toLdapSerialize($value);
+                    } else if (is_resource($value) && get_resource_type($value) === 'stream') {
+                        return stream_get_contents($value);
+                    } else {
+                        return null;
+                    }
+                    break;
+            }
+        } catch (Exception $e) {
+            throw new Zend_Ldap_Converter_Exception($e->getMessage(), $e->getCode(), $e);
+        }
+    }
+
+    /**
+     * Converts a date-entity to an LDAP-compatible date-string
+     *
+     * The date-entity <var>$date</var> can be either a timestamp, a
+     * DateTime Object, a string that is parseable by strtotime() or a Zend_Date
+     * Object.
+     *
+     * @param    integer|string|DateTimt|Zend_Date        $date    The date-entity
+     * @param    boolean                                    $asUtc    Whether to return the LDAP-compatible date-string
+     *                                                          as UTC or as local value
+     * @return    string
+     * @throws    InvalidArgumentException
+     */
+    public static function toLdapDateTime($date, $asUtc = true)
+    {
+        if (!($date instanceof DateTime)) {
+            if (is_int($date)) {
+                $date = new DateTime('@' . $date);
+                $date->setTimezone(new DateTimeZone(date_default_timezone_get()));
+            } else if (is_string($date)) {
+                $date = new DateTime($date);
+            } else if ($date instanceof Zend_Date) {
+                $date = new DateTime($date->get(Zend_Date::ISO_8601));
+            } else {
+                throw new InvalidArgumentException('Parameter $date is not of the expected type');
+            }
+        }
+        $timezone = $date->format('O');
+        if (true === $asUtc) {
+            $date->setTimezone(new DateTimeZone('UTC'));
+            $timezone = 'Z';
+        }
+        if ( '+0000' === $timezone ) {
+            $timezone = 'Z';
+        }
+        return $date->format('YmdHis') . $timezone;
+    }
+
+    /**
+     * Convert a boolean value to an LDAP-compatible string
+     *
+     * This converts a boolean value of TRUE, an integer-value of 1 and a
+     * case-insensitive string 'true' to an LDAP-compatible 'TRUE'. All other
+     * other values are converted to an LDAP-compatible 'FALSE'.
+     *
+     * @param    boolean|integer|string        $value    The boolean value to encode
+     * @return    string
+     */
+    public static function toLdapBoolean($value)
+    {
+        $return = 'FALSE';
+        if (!is_scalar($value)) {
+            return $return;
+        }
+        if (true === $value || 'true' === strtolower($value) || 1 === $value) {
+            $return = 'TRUE';
+        }
+        return $return;
+    }
+
+    /**
+     * Serialize any value for storage in LDAP
+     *
+     * @param    mixed        $value    The value to serialize
+     * @return    string
+     */
+    public static function toLdapSerialize($value)
+    {
+        return serialize($value);
+    }
+
+    /**
+     * Convert an LDAP-compatible value to a corresponding PHP-value.
+     *
+     * By setting the <var>$type</var>-parameter the conversion of a certain
+     * type can be forced
+     * .
+     * @param    string    $value             The value to convert
+     * @param    int        $ytpe              The conversion type to use
+     * @param    boolean    $dateTimeAsUtc    Return DateTime values in UTC timezone
+     * @return    mixed
+     * @throws    Zend_Ldap_Converter_Exception
+     */
+    public static function fromLdap($value, $type = self::STANDARD, $dateTimeAsUtc = true)
+    {
+        switch ($type) {
+            case self::BOOLEAN:
+                return self::fromldapBoolean($value);
+                break;
+            case self::GENERALIZED_TIME:
+                return self::fromLdapDateTime($value);
+                break;
+            default:
+                if (is_numeric($value)) {
+                    return (float)$value;
+                } else if ('TRUE' === $value || 'FALSE' === $value) {
+                    return self::fromLdapBoolean($value);
+                }
+                if (preg_match('/^\d{4}[\d\+\-Z\.]*$/', $value)) {
+                    return self::fromLdapDateTime($value, $dateTimeAsUtc);
+                }
+                try {
+                    return self::fromLdapUnserialize($value);
+                } catch (UnexpectedValueException $e) { }
+                break;
+        }
+        return $value;
+    }
+
+    /**
+     * Convert an LDAP-Generalized-Time-entry into a DateTime-Object
+     *
+     * CAVEAT: The DateTime-Object returned will alwasy be set to UTC-Timezone.
+     *
+     * @param    string        $date    The generalized-Time
+     * @param    boolean        $asUtc    Return the DateTime with UTC timezone
+     * @return    DateTime
+     * @throws    InvalidArgumentException if a non-parseable-format is given
+     */
+    public static function fromLdapDateTime($date, $asUtc = true)
+    {
+        $datepart = array ();
+        if (!preg_match('/^(\d{4})/', $date, $datepart) ) {
+            throw new InvalidArgumentException('Invalid date format found');
+        }
+
+        if ($datepart[1] < 4) {
+            throw new InvalidArgumentException('Invalid date format found (too short)');
+        }
+
+        $time = array (
+            // The year is mandatory!
+            'year'   => $datepart[1],
+            'month'  => 1,
+            'day'    => 1,
+            'hour'   => 0,
+            'minute' => 0,
+            'second' => 0,
+            'offdir' => '+',
+            'offsethours' => 0,
+            'offsetminutes' => 0
+        );
+
+        $length = strlen($date);
+
+        // Check for month.
+        if ($length >= 6) {
+            $month = substr($date, 4, 2);
+            if ($month < 1 || $month > 12) {
+                throw new InvalidArgumentException('Invalid date format found (invalid month)');
+            }
+            $time['month'] = $month;
+        }
+
+        // Check for day
+        if ($length >= 8) {
+            $day = substr($date, 6, 2);
+            if ($day < 1 || $day > 31) {
+                throw new InvalidArgumentException('Invalid date format found (invalid day)');
+            }
+            $time['day'] = $day;
+        }
+
+        // Check for Hour
+        if ($length >= 10) {
+            $hour = substr($date, 8, 2);
+            if ($hour < 0 || $hour > 23) {
+                throw new InvalidArgumentException('Invalid date format found (invalid hour)');
+            }
+            $time['hour'] = $hour;
+        }
+
+        // Check for minute
+        if ($length >= 12) {
+            $minute = substr($date, 10, 2);
+            if ($minute < 0 || $minute > 59) {
+                throw new InvalidArgumentException('Invalid date format found (invalid minute)');
+            }
+            $time['minute'] = $minute;
+        }
+
+        // Check for seconds
+        if ($length >= 14) {
+            $second = substr($date, 12, 2);
+            if ($second < 0 || $second > 59) {
+                throw new InvalidArgumentException('Invalid date format found (invalid second)');
+            }
+            $time['second'] = $second;
+        }
+
+        // Set Offset
+        $offsetRegEx = '/([Z\-\+])(\d{2}\'?){0,1}(\d{2}\'?){0,1}$/';
+        $off         = array ();
+        if (preg_match($offsetRegEx, $date, $off)) {
+            $offset = $off[1];
+            if ($offset == '+' || $offset == '-') {
+                $time['offdir'] = $offset;
+                // we have an offset, so lets calculate it.
+                if (isset($off[2])) {
+                    $offsetHours = substr($off[2], 0, 2);
+                    if ($offsetHours < 0 || $offsetHours > 12) {
+                        throw new InvalidArgumentException('Invalid date format found (invalid offset hour)');
+                    }
+                    $time['offsethours'] = $offsetHours;
+                }
+                if (isset($off[3])) {
+                    $offsetMinutes = substr($off[3], 0, 2);
+                    if ($offsetMinutes < 0 || $offsetMinutes > 59) {
+                        throw new InvalidArgumentException('Invalid date format found (invalid offset minute)');
+                    }
+                    $time['offsetminutes'] = $offsetMinutes;
+                }
+            }
+        }
+
+        // Raw-Data is present, so lets create a DateTime-Object from it.
+        $offset = $time['offdir']
+                . str_pad($time['offsethours'],2,'0',STR_PAD_LEFT)
+                . str_pad($time['offsetminutes'],2,'0',STR_PAD_LEFT);
+        $timestring = $time['year'] . '-'
+                    . str_pad($time['month'], 2, '0', STR_PAD_LEFT) . '-'
+                    . str_pad($time['day'], 2, '0', STR_PAD_LEFT) . ' '
+                    . str_pad($time['hour'], 2, '0', STR_PAD_LEFT) . ':'
+                    . str_pad($time['minute'], 2, '0', STR_PAD_LEFT) . ':'
+                    . str_pad($time['second'], 2, '0', STR_PAD_LEFT)
+                    . $time['offdir']
+                    . str_pad($time['offsethours'], 2, '0', STR_PAD_LEFT)
+                    . str_pad($time['offsetminutes'], 2, '0', STR_PAD_LEFT);
+        $date = new DateTime($timestring);
+        if ($asUtc) {
+            $date->setTimezone(new DateTimeZone('UTC'));
+        }
+        return $date;
+    }
+
+    /**
+     * Convert an LDAP-compatible boolean value into a PHP-compatible one
+     *
+     * @param    string        $value        The value to convert
+     * @return    boolean
+     * @throws    InvalidArgumentException
+     */
+    public static function fromLdapBoolean($value)
+    {
+        if ( 'TRUE' === $value ) {
+            return true;
+        } else if ( 'FALSE' === $value ) {
+            return false;
+        } else {
+            throw new InvalidArgumentException('The given value is not a boolean value');
+        }
+    }
+
+    /**
+     * Unserialize a serialized value to return the corresponding object
+     *
+     * @param    string        $value    The value to convert
+     * @return    mixed
+     * @throws    UnexpectedValueException
+     */
+    public static function fromLdapUnserialize($value)
+    {
+        $v = @unserialize($value);
+        if (false===$v && $value != 'b:0;') {
+            throw new UnexpectedValueException('The given value could not be unserialized');
+        }
+        return $v;
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Ldap/Ldif/Encoder.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Ldap/Ldif/Encoder.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Ldap/Ldif/Encoder.php	(revision 4456)
@@ -0,0 +1,304 @@
+<?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 Ldif
+ * @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: Encoder.php 21005 2010-02-09 13:16:26Z sgehrig $
+ */
+
+/**
+ * Zend_Ldap_Ldif_Encoder provides methods to encode and decode LDAP data into/from LDIF.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Ldif
+ * @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_Ldif_Encoder
+{
+    /**
+     * Additional options used during encoding
+     *
+     * @var array
+     */
+    protected $_options = array(
+        'sort'    => true,
+        'version' => 1,
+        'wrap'    => 78
+    );
+
+    /**
+     * @var boolean
+     */
+    protected $_versionWritten = false;
+
+    /**
+     * Constructor.
+     *
+     * @param  array $options Additional options used during encoding
+     * @return void
+     */
+    protected function __construct(array $options = array())
+    {
+        $this->_options = array_merge($this->_options, $options);
+    }
+
+    /**
+     * Decodes the string $string into an array of LDIF items
+     *
+     * @param  string $string
+     * @return array
+     */
+    public static function decode($string)
+    {
+        $encoder = new self(array());
+        return $encoder->_decode($string);
+    }
+
+    /**
+     * Decodes the string $string into an array of LDIF items
+     *
+     * @param  string $string
+     * @return array
+     */
+    protected function _decode($string)
+    {
+        $items = array();
+        $item = array();
+        $last = null;
+        foreach (explode("\n", $string) as $line) {
+            $line = rtrim($line, "\x09\x0A\x0D\x00\x0B");
+            $matches = array();
+            if (substr($line, 0, 1) === ' ' && $last !== null) {
+                $last[2] .= substr($line, 1);
+            } else if (substr($line, 0, 1) === '#') {
+                continue;
+            } else if (preg_match('/^([a-z0-9;-]+)(:[:<]?\s*)([^:<]*)$/i', $line, $matches)) {
+                $name = strtolower($matches[1]);
+                $type = trim($matches[2]);
+                $value = $matches[3];
+                if ($last !== null) {
+                    $this->_pushAttribute($last, $item);
+                }
+                if ($name === 'version') {
+                    continue;
+                } else if (count($item) > 0 && $name === 'dn') {
+                    $items[] = $item;
+                    $item = array();
+                    $last = null;
+                }
+                $last = array($name, $type, $value);
+            } else if (trim($line) === '') {
+                continue;
+            }
+        }
+        if ($last !== null) {
+            $this->_pushAttribute($last, $item);
+        }
+        $items[] = $item;
+        return (count($items)>1) ? $items : $items[0];
+    }
+
+    /**
+     * Pushes a decoded attribute to the stack
+     *
+     * @param array $attribute
+     * @param array $entry
+     */
+    protected function _pushAttribute(array $attribute, array &$entry)
+    {
+        $name = $attribute[0];
+        $type = $attribute[1];
+        $value = $attribute[2];
+        if ($type === '::') {
+            $value = base64_decode($value);
+        }
+        if ($name === 'dn') {
+            $entry[$name] = $value;
+        } else if (isset($entry[$name]) && $value !== '') {
+            $entry[$name][] = $value;
+        } else {
+            $entry[$name] = ($value !== '') ? array($value) : array();
+        }
+    }
+
+    /**
+     * Encode $value into a LDIF representation
+     *
+     * @param  mixed $value   The value to be encoded
+     * @param  array $options Additional options used during encoding
+     * @return string The encoded value
+     */
+    public static function encode($value, array $options = array())
+    {
+        $encoder = new self($options);
+        return $encoder->_encode($value);
+    }
+
+    /**
+     * Recursive driver which determines the type of value to be encoded
+     * and then dispatches to the appropriate method.
+     *
+     * @param  mixed $value The value to be encoded
+     * @return string Encoded value
+     */
+    protected function _encode($value)
+    {
+        if (is_scalar($value)) {
+            return $this->_encodeString($value);
+        } else if (is_array($value)) {
+            return $this->_encodeAttributes($value);
+        } else if ($value instanceof Zend_Ldap_Node) {
+            return $value->toLdif($this->_options);
+        }
+        return null;
+    }
+
+    /**
+     * Encodes $string according to RFC2849
+     *
+     * @link http://www.faqs.org/rfcs/rfc2849.html
+     *
+     * @param  string $string
+     * @param  boolen $base64
+     * @return string
+     */
+    protected function _encodeString($string, &$base64 = null)
+    {
+        $string = (string)$string;
+        if (!is_numeric($string) && empty($string)) {
+            return '';
+        }
+
+        /*
+         * SAFE-INIT-CHAR = %x01-09 / %x0B-0C / %x0E-1F /
+         *                  %x21-39 / %x3B / %x3D-7F
+         *                ; any value <= 127 except NUL, LF, CR,
+         *                ; SPACE, colon (":", ASCII 58 decimal)
+         *                ; and less-than ("<" , ASCII 60 decimal)
+         *
+         */
+        $unsafe_init_char = array(0, 10, 13, 32, 58, 60);
+        /*
+         * SAFE-CHAR      = %x01-09 / %x0B-0C / %x0E-7F
+         *                ; any value <= 127 decimal except NUL, LF,
+         *                ; and CR
+         */
+        $unsafe_char      = array(0, 10, 13);
+
+        $base64 = false;
+        for ($i = 0; $i < strlen($string); $i++) {
+            $char = ord(substr($string, $i, 1));
+            if ($char >= 127) {
+                $base64 = true;
+                break;
+            } else if ($i === 0 && in_array($char, $unsafe_init_char)) {
+                $base64 = true;
+                break;
+            } else if (in_array($char, $unsafe_char)) {
+                $base64 = true;
+                break;
+            }
+        }
+        // Test for ending space
+        if (substr($string, -1) == ' ') {
+            $base64 = true;
+        }
+
+        if ($base64 === true) {
+            $string = base64_encode($string);
+        }
+
+        return $string;
+    }
+
+    /**
+     * Encodes an attribute with $name and $value according to RFC2849
+     *
+     * @link http://www.faqs.org/rfcs/rfc2849.html
+     *
+     * @param  string       $name
+     * @param  array|string $value
+     * @return string
+     */
+    protected function _encodeAttribute($name, $value)
+    {
+        if (!is_array($value)) {
+            $value = array($value);
+        }
+
+        $output = '';
+
+        if (count($value) < 1) {
+            return $name . ': ';
+        }
+
+        foreach ($value as $v) {
+            $base64 = null;
+            $v = $this->_encodeString($v, $base64);
+            $attribute = $name . ':';
+            if ($base64 === true) {
+                $attribute .= ': ' . $v;
+            } else {
+                $attribute .= ' ' . $v;
+            }
+            if (isset($this->_options['wrap']) && strlen($attribute) > $this->_options['wrap']) {
+                $attribute = trim(chunk_split($attribute, $this->_options['wrap'], PHP_EOL . ' '));
+            }
+            $output .= $attribute . PHP_EOL;
+        }
+        return trim($output, PHP_EOL);
+    }
+
+    /**
+     * Encodes a collection of attributes according to RFC2849
+     *
+     * @link http://www.faqs.org/rfcs/rfc2849.html
+     *
+     * @param  array $attributes
+     * @return string
+     */
+    protected function _encodeAttributes(array $attributes)
+    {
+        $string = '';
+        $attributes = array_change_key_case($attributes, CASE_LOWER);
+        if (!$this->_versionWritten && array_key_exists('dn', $attributes) && isset($this->_options['version'])
+                && array_key_exists('objectclass', $attributes)) {
+            $string .= sprintf('version: %d', $this->_options['version']) . PHP_EOL;
+            $this->_versionWritten = true;
+        }
+
+        if (isset($this->_options['sort']) && $this->_options['sort'] === true) {
+            ksort($attributes, SORT_STRING);
+            if (array_key_exists('objectclass', $attributes)) {
+                $oc = $attributes['objectclass'];
+                unset($attributes['objectclass']);
+                $attributes = array_merge(array('objectclass' => $oc), $attributes);
+            }
+            if (array_key_exists('dn', $attributes)) {
+                $dn = $attributes['dn'];
+                unset($attributes['dn']);
+                $attributes = array_merge(array('dn' => $dn), $attributes);
+            }
+        }
+        foreach ($attributes as $key => $value) {
+            $string .= $this->_encodeAttribute($key, $value) . PHP_EOL;
+        }
+        return trim($string, PHP_EOL);
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Mail/Storage/Folder.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Mail/Storage/Folder.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Mail/Storage/Folder.php	(revision 4456)
@@ -0,0 +1,236 @@
+<?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_Mail
+ * @subpackage Storage
+ * @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: Folder.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @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_Mail_Storage_Folder implements RecursiveIterator
+{
+    /**
+     * subfolders of folder array(localName => Zend_Mail_Storage_Folder folder)
+     * @var array
+     */
+    protected $_folders;
+
+    /**
+     * local name (name of folder in parent folder)
+     * @var string
+     */
+    protected $_localName;
+
+    /**
+     * global name (absolute name of folder)
+     * @var string
+     */
+    protected $_globalName;
+
+    /**
+     * folder is selectable if folder is able to hold messages, else it's just a parent folder
+     * @var bool
+     */
+    protected $_selectable = true;
+
+    /**
+     * create a new mail folder instance
+     *
+     * @param string $localName  name of folder in current subdirectory
+     * @param string $globalName absolute name of folder
+     * @param bool   $selectable if true folder holds messages, if false it's just a parent for subfolders
+     * @param array  $folders    init with given instances of Zend_Mail_Storage_Folder as subfolders
+     */
+    public function __construct($localName, $globalName = '', $selectable = true, array $folders = array())
+    {
+        $this->_localName  = $localName;
+        $this->_globalName = $globalName ? $globalName : $localName;
+        $this->_selectable = $selectable;
+        $this->_folders    = $folders;
+    }
+
+    /**
+     * implements RecursiveIterator::hasChildren()
+     *
+     * @return bool current element has children
+     */
+    public function hasChildren()
+    {
+        $current = $this->current();
+        return $current && $current instanceof Zend_Mail_Storage_Folder && !$current->isLeaf();
+    }
+
+    /**
+     * implements RecursiveIterator::getChildren()
+     *
+     * @return Zend_Mail_Storage_Folder same as self::current()
+     */
+    public function getChildren()
+    {
+        return $this->current();
+    }
+
+    /**
+     * implements Iterator::valid()
+     *
+     * @return bool check if there's a current element
+     */
+    public function valid()
+    {
+        return key($this->_folders) !== null;
+    }
+
+    /**
+     * implements Iterator::next()
+     *
+     * @return null
+     */
+    public function next()
+    {
+        next($this->_folders);
+    }
+
+    /**
+     * implements Iterator::key()
+     *
+     * @return string key/local name of current element
+     */
+    public function key()
+    {
+        return key($this->_folders);
+    }
+
+    /**
+     * implements Iterator::current()
+     *
+     * @return Zend_Mail_Storage_Folder current folder
+     */
+    public function current()
+    {
+        return current($this->_folders);
+    }
+
+    /**
+     * implements Iterator::rewind()
+     *
+     * @return null
+     */
+    public function rewind()
+    {
+        reset($this->_folders);
+    }
+
+    /**
+     * get subfolder named $name
+     *
+     * @param  string $name wanted subfolder
+     * @return Zend_Mail_Storage_Folder folder named $folder
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function __get($name)
+    {
+        if (!isset($this->_folders[$name])) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception("no subfolder named $name");
+        }
+
+        return $this->_folders[$name];
+    }
+
+    /**
+     * add or replace subfolder named $name
+     *
+     * @param string $name local name of subfolder
+     * @param Zend_Mail_Storage_Folder $folder instance for new subfolder
+     * @return null
+     */
+    public function __set($name, Zend_Mail_Storage_Folder $folder)
+    {
+        $this->_folders[$name] = $folder;
+    }
+
+    /**
+     * remove subfolder named $name
+     *
+     * @param string $name local name of subfolder
+     * @return null
+     */
+    public function __unset($name)
+    {
+        unset($this->_folders[$name]);
+    }
+
+    /**
+     * magic method for easy output of global name
+     *
+     * @return string global name of folder
+     */
+    public function __toString()
+    {
+        return (string)$this->getGlobalName();
+    }
+
+    /**
+     * get local name
+     *
+     * @return string local name
+     */
+    public function getLocalName()
+    {
+        return $this->_localName;
+    }
+
+    /**
+     * get global name
+     *
+     * @return string global name
+     */
+    public function getGlobalName()
+    {
+        return $this->_globalName;
+    }
+
+    /**
+     * is this folder selectable?
+     *
+     * @return bool selectable
+     */
+    public function isSelectable()
+    {
+        return $this->_selectable;
+    }
+
+    /**
+     * check if folder has no subfolder
+     *
+     * @return bool true if no subfolders
+     */
+    public function isLeaf()
+    {
+        return empty($this->_folders);
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Mail/Storage/Maildir.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Mail/Storage/Maildir.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Mail/Storage/Maildir.php	(revision 4456)
@@ -0,0 +1,475 @@
+<?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_Mail
+ * @subpackage Storage
+ * @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: Maildir.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Mail_Storage_Abstract
+ */
+require_once 'Zend/Mail/Storage/Abstract.php';
+
+/**
+ * @see Zend_Mail_Message_File
+ */
+require_once 'Zend/Mail/Message/File.php';
+
+/**
+ * @see Zend_Mail_Storage
+ */
+require_once 'Zend/Mail/Storage.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @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_Mail_Storage_Maildir extends Zend_Mail_Storage_Abstract
+{
+    /**
+     * used message class, change it in an extened class to extend the returned message class
+     * @var string
+     */
+    protected $_messageClass = 'Zend_Mail_Message_File';
+
+    /**
+     * data of found message files in maildir dir
+     * @var array
+     */
+    protected $_files = array();
+
+    /**
+     * known flag chars in filenames
+     *
+     * This list has to be in alphabetical order for setFlags()
+     *
+     * @var array
+     */
+    protected static $_knownFlags = array('D' => Zend_Mail_Storage::FLAG_DRAFT,
+                                          'F' => Zend_Mail_Storage::FLAG_FLAGGED,
+                                          'P' => Zend_Mail_Storage::FLAG_PASSED,
+                                          'R' => Zend_Mail_Storage::FLAG_ANSWERED,
+                                          'S' => Zend_Mail_Storage::FLAG_SEEN,
+                                          'T' => Zend_Mail_Storage::FLAG_DELETED);
+
+    // TODO: getFlags($id) for fast access if headers are not needed (i.e. just setting flags)?
+
+    /**
+     * Count messages all messages in current box
+     *
+     * @return int number of messages
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function countMessages($flags = null)
+    {
+        if ($flags === null) {
+            return count($this->_files);
+        }
+
+        $count = 0;
+        if (!is_array($flags)) {
+            foreach ($this->_files as $file) {
+                if (isset($file['flaglookup'][$flags])) {
+                    ++$count;
+                }
+            }
+            return $count;
+        }
+
+        $flags = array_flip($flags);
+           foreach ($this->_files as $file) {
+               foreach ($flags as $flag => $v) {
+                   if (!isset($file['flaglookup'][$flag])) {
+                       continue 2;
+                   }
+               }
+               ++$count;
+           }
+           return $count;
+    }
+
+    /**
+     * Get one or all fields from file structure. Also checks if message is valid
+     *
+     * @param  int         $id    message number
+     * @param  string|null $field wanted field
+     * @return string|array wanted field or all fields as array
+     * @throws Zend_Mail_Storage_Exception
+     */
+    protected function _getFileData($id, $field = null)
+    {
+        if (!isset($this->_files[$id - 1])) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('id does not exist');
+        }
+
+        if (!$field) {
+            return $this->_files[$id - 1];
+        }
+
+        if (!isset($this->_files[$id - 1][$field])) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('field does not exist');
+        }
+
+        return $this->_files[$id - 1][$field];
+    }
+
+    /**
+     * Get a list of messages with number and size
+     *
+     * @param  int|null $id number of message or null for all messages
+     * @return int|array size of given message of list with all messages as array(num => size)
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getSize($id = null)
+    {
+        if ($id !== null) {
+            $filedata = $this->_getFileData($id);
+            return isset($filedata['size']) ? $filedata['size'] : filesize($filedata['filename']);
+        }
+
+        $result = array();
+        foreach ($this->_files as $num => $data) {
+            $result[$num + 1] = isset($data['size']) ? $data['size'] : filesize($data['filename']);
+        }
+
+        return $result;
+    }
+
+
+
+    /**
+     * Fetch a message
+     *
+     * @param  int $id number of message
+     * @return Zend_Mail_Message_File
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getMessage($id)
+    {
+        // TODO that's ugly, would be better to let the message class decide
+        if (strtolower($this->_messageClass) == 'zend_mail_message_file' || is_subclass_of($this->_messageClass, 'zend_mail_message_file')) {
+            return new $this->_messageClass(array('file'  => $this->_getFileData($id, 'filename'),
+                                                  'flags' => $this->_getFileData($id, 'flags')));
+        }
+
+        return new $this->_messageClass(array('handler' => $this, 'id' => $id, 'headers' => $this->getRawHeader($id),
+                                              'flags'   => $this->_getFileData($id, 'flags')));
+    }
+
+    /*
+     * Get raw header of message or part
+     *
+     * @param  int               $id       number of message
+     * @param  null|array|string $part     path to part or null for messsage header
+     * @param  int               $topLines include this many lines with header (after an empty line)
+     * @return string raw header
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getRawHeader($id, $part = null, $topLines = 0)
+    {
+        if ($part !== null) {
+            // TODO: implement
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('not implemented');
+        }
+
+        $fh = fopen($this->_getFileData($id, 'filename'), 'r');
+
+        $content = '';
+        while (!feof($fh)) {
+            $line = fgets($fh);
+            if (!trim($line)) {
+                break;
+            }
+            $content .= $line;
+        }
+
+        fclose($fh);
+        return $content;
+    }
+
+    /*
+     * Get raw content of message or part
+     *
+     * @param  int               $id   number of message
+     * @param  null|array|string $part path to part or null for messsage content
+     * @return string raw content
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getRawContent($id, $part = null)
+    {
+        if ($part !== null) {
+            // TODO: implement
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('not implemented');
+        }
+
+        $fh = fopen($this->_getFileData($id, 'filename'), 'r');
+
+        while (!feof($fh)) {
+            $line = fgets($fh);
+            if (!trim($line)) {
+                break;
+            }
+        }
+
+        $content = stream_get_contents($fh);
+        fclose($fh);
+        return $content;
+    }
+
+    /**
+     * Create instance with parameters
+     * Supported parameters are:
+     *   - dirname dirname of mbox file
+     *
+     * @param  $params array mail reader specific parameters
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function __construct($params)
+    {
+        if (is_array($params)) {
+            $params = (object)$params;
+        }
+
+        if (!isset($params->dirname) || !is_dir($params->dirname)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('no valid dirname given in params');
+        }
+
+        if (!$this->_isMaildir($params->dirname)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('invalid maildir given');
+        }
+
+        $this->_has['top'] = true;
+        $this->_has['flags'] = true;
+        $this->_openMaildir($params->dirname);
+    }
+
+    /**
+     * check if a given dir is a valid maildir
+     *
+     * @param string $dirname name of dir
+     * @return bool dir is valid maildir
+     */
+    protected function _isMaildir($dirname)
+    {
+        if (file_exists($dirname . '/new') && !is_dir($dirname . '/new')) {
+            return false;
+        }
+        if (file_exists($dirname . '/tmp') && !is_dir($dirname . '/tmp')) {
+            return false;
+        }
+        return is_dir($dirname . '/cur');
+    }
+
+    /**
+     * open given dir as current maildir
+     *
+     * @param string $dirname name of maildir
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    protected function _openMaildir($dirname)
+    {
+        if ($this->_files) {
+            $this->close();
+        }
+
+        $dh = @opendir($dirname . '/cur/');
+        if (!$dh) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot open maildir');
+        }
+        $this->_getMaildirFiles($dh, $dirname . '/cur/');
+        closedir($dh);
+
+        $dh = @opendir($dirname . '/new/');
+        if ($dh) {
+            $this->_getMaildirFiles($dh, $dirname . '/new/', array(Zend_Mail_Storage::FLAG_RECENT));
+            closedir($dh);
+        } else if (file_exists($dirname . '/new/')) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot read recent mails in maildir');
+        }
+    }
+
+    /**
+     * find all files in opened dir handle and add to maildir files
+     *
+     * @param resource $dh            dir handle used for search
+     * @param string   $dirname       dirname of dir in $dh
+     * @param array    $default_flags default flags for given dir
+     * @return null
+     */
+    protected function _getMaildirFiles($dh, $dirname, $default_flags = array())
+    {
+        while (($entry = readdir($dh)) !== false) {
+            if ($entry[0] == '.' || !is_file($dirname . $entry)) {
+                continue;
+            }
+
+            @list($uniq, $info) = explode(':', $entry, 2);
+            @list(,$size) = explode(',', $uniq, 2);
+            if ($size && $size[0] == 'S' && $size[1] == '=') {
+                $size = substr($size, 2);
+            }
+            if (!ctype_digit($size)) {
+                $size = null;
+            }
+            @list($version, $flags) = explode(',', $info, 2);
+            if ($version != 2) {
+                $flags = '';
+            }
+
+            $named_flags = $default_flags;
+            $length = strlen($flags);
+            for ($i = 0; $i < $length; ++$i) {
+                $flag = $flags[$i];
+                $named_flags[$flag] = isset(self::$_knownFlags[$flag]) ? self::$_knownFlags[$flag] : $flag;
+            }
+
+            $data = array('uniq'       => $uniq,
+                          'flags'      => $named_flags,
+                          'flaglookup' => array_flip($named_flags),
+                          'filename'   => $dirname . $entry);
+            if ($size !== null) {
+                $data['size'] = (int)$size;
+            }
+            $this->_files[] = $data;
+        }
+    }
+
+
+    /**
+     * Close resource for mail lib. If you need to control, when the resource
+     * is closed. Otherwise the destructor would call this.
+     *
+     * @return void
+     */
+    public function close()
+    {
+        $this->_files = array();
+    }
+
+
+    /**
+     * Waste some CPU cycles doing nothing.
+     *
+     * @return void
+     */
+    public function noop()
+    {
+        return true;
+    }
+
+
+    /**
+     * stub for not supported message deletion
+     *
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function removeMessage($id)
+    {
+        /**
+         * @see Zend_Mail_Storage_Exception
+         */
+        require_once 'Zend/Mail/Storage/Exception.php';
+        throw new Zend_Mail_Storage_Exception('maildir is (currently) read-only');
+    }
+
+    /**
+     * get unique id for one or all messages
+     *
+     * if storage does not support unique ids it's the same as the message number
+     *
+     * @param int|null $id message number
+     * @return array|string message number for given message or all messages as array
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getUniqueId($id = null)
+    {
+        if ($id) {
+            return $this->_getFileData($id, 'uniq');
+        }
+
+        $ids = array();
+        foreach ($this->_files as $num => $file) {
+            $ids[$num + 1] = $file['uniq'];
+        }
+        return $ids;
+    }
+
+    /**
+     * get a message number from a unique id
+     *
+     * I.e. if you have a webmailer that supports deleting messages you should use unique ids
+     * as parameter and use this method to translate it to message number right before calling removeMessage()
+     *
+     * @param string $id unique id
+     * @return int message number
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function getNumberByUniqueId($id)
+    {
+        foreach ($this->_files as $num => $file) {
+            if ($file['uniq'] == $id) {
+                return $num + 1;
+            }
+        }
+
+        /**
+         * @see Zend_Mail_Storage_Exception
+         */
+        require_once 'Zend/Mail/Storage/Exception.php';
+        throw new Zend_Mail_Storage_Exception('unique id not found');
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Mail/Storage/Abstract.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Mail/Storage/Abstract.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Mail/Storage/Abstract.php	(revision 4456)
@@ -0,0 +1,366 @@
+<?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_Mail
+ * @subpackage Storage
+ * @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 $
+ */
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @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_Mail_Storage_Abstract implements Countable, ArrayAccess, SeekableIterator
+{
+    /**
+     * class capabilities with default values
+     * @var array
+     */
+    protected $_has = array('uniqueid'  => true,
+                            'delete'    => false,
+                            'create'    => false,
+                            'top'       => false,
+                            'fetchPart' => true,
+                            'flags'     => false);
+
+    /**
+     * current iteration position
+     * @var int
+     */
+    protected $_iterationPos = 0;
+
+    /**
+     * maximum iteration position (= message count)
+     * @var null|int
+     */
+    protected $_iterationMax = null;
+
+    /**
+     * used message class, change it in an extened class to extend the returned message class
+     * @var string
+     */
+    protected $_messageClass = 'Zend_Mail_Message';
+
+    /**
+     * Getter for has-properties. The standard has properties
+     * are: hasFolder, hasUniqueid, hasDelete, hasCreate, hasTop
+     *
+     * The valid values for the has-properties are:
+     *   - true if a feature is supported
+     *   - false if a feature is not supported
+     *   - null is it's not yet known or it can't be know if a feature is supported
+     *
+     * @param  string $var  property name
+     * @return bool         supported or not
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function __get($var)
+    {
+        if (strpos($var, 'has') === 0) {
+            $var = strtolower(substr($var, 3));
+            return isset($this->_has[$var]) ? $this->_has[$var] : null;
+        }
+
+        /**
+         * @see Zend_Mail_Storage_Exception
+         */
+        require_once 'Zend/Mail/Storage/Exception.php';
+        throw new Zend_Mail_Storage_Exception($var . ' not found');
+    }
+
+
+    /**
+     * Get a full list of features supported by the specific mail lib and the server
+     *
+     * @return array list of features as array(featurename => true|false[|null])
+     */
+    public function getCapabilities()
+    {
+        return $this->_has;
+    }
+
+
+    /**
+     * Count messages messages in current box/folder
+     *
+     * @return int number of messages
+     * @throws Zend_Mail_Storage_Exception
+     */
+    abstract public function countMessages();
+
+
+    /**
+     * Get a list of messages with number and size
+     *
+     * @param  int $id  number of message
+     * @return int|array size of given message of list with all messages as array(num => size)
+     */
+    abstract public function getSize($id = 0);
+
+
+    /**
+     * Get a message with headers and body
+     *
+     * @param  $id int number of message
+     * @return Zend_Mail_Message
+     */
+    abstract public function getMessage($id);
+
+
+    /**
+     * Get raw header of message or part
+     *
+     * @param  int               $id       number of message
+     * @param  null|array|string $part     path to part or null for messsage header
+     * @param  int               $topLines include this many lines with header (after an empty line)
+     * @return string raw header
+     */
+    abstract public function getRawHeader($id, $part = null, $topLines = 0);
+
+    /**
+     * Get raw content of message or part
+     *
+     * @param  int               $id   number of message
+     * @param  null|array|string $part path to part or null for messsage content
+     * @return string raw content
+     */
+    abstract public function getRawContent($id, $part = null);
+
+    /**
+     * Create instance with parameters
+     *
+     * @param  array $params mail reader specific parameters
+     * @throws Zend_Mail_Storage_Exception
+     */
+    abstract public function __construct($params);
+
+
+    /**
+     * Destructor calls close() and therefore closes the resource.
+     */
+    public function __destruct()
+    {
+        $this->close();
+    }
+
+
+    /**
+     * Close resource for mail lib. If you need to control, when the resource
+     * is closed. Otherwise the destructor would call this.
+     *
+     * @return null
+     */
+    abstract public function close();
+
+
+    /**
+     * Keep the resource alive.
+     *
+     * @return null
+     */
+    abstract public function noop();
+
+    /**
+     * delete a message from current box/folder
+     *
+     * @return null
+     */
+    abstract public function removeMessage($id);
+
+    /**
+     * get unique id for one or all messages
+     *
+     * if storage does not support unique ids it's the same as the message number
+     *
+     * @param int|null $id message number
+     * @return array|string message number for given message or all messages as array
+     * @throws Zend_Mail_Storage_Exception
+     */
+    abstract public function getUniqueId($id = null);
+
+    /**
+     * get a message number from a unique id
+     *
+     * I.e. if you have a webmailer that supports deleting messages you should use unique ids
+     * as parameter and use this method to translate it to message number right before calling removeMessage()
+     *
+     * @param string $id unique id
+     * @return int message number
+     * @throws Zend_Mail_Storage_Exception
+     */
+    abstract public function getNumberByUniqueId($id);
+
+    // interface implementations follows
+
+    /**
+     * Countable::count()
+     *
+     * @return   int
+     */
+     public function count()
+     {
+        return $this->countMessages();
+     }
+
+
+     /**
+      * ArrayAccess::offsetExists()
+      *
+      * @param    int     $id
+      * @return   boolean
+      */
+     public function offsetExists($id)
+     {
+        try {
+            if ($this->getMessage($id)) {
+                return true;
+            }
+        } catch(Zend_Mail_Storage_Exception $e) {}
+
+        return false;
+     }
+
+
+     /**
+      * ArrayAccess::offsetGet()
+      *
+      * @param    int $id
+      * @return   Zend_Mail_Message message object
+      */
+     public function offsetGet($id)
+     {
+        return $this->getMessage($id);
+     }
+
+
+     /**
+      * ArrayAccess::offsetSet()
+      *
+      * @param    id     $id
+      * @param    mixed  $value
+      * @throws   Zend_Mail_Storage_Exception
+      * @return   void
+      */
+     public function offsetSet($id, $value)
+     {
+        /**
+         * @see Zend_Mail_Storage_Exception
+         */
+        require_once 'Zend/Mail/Storage/Exception.php';
+        throw new Zend_Mail_Storage_Exception('cannot write mail messages via array access');
+     }
+
+
+     /**
+      * ArrayAccess::offsetUnset()
+      *
+      * @param    int   $id
+      * @return   boolean success
+      */
+     public function offsetUnset($id)
+     {
+        return $this->removeMessage($id);
+     }
+
+
+     /**
+      * Iterator::rewind()
+      *
+      * Rewind always gets the new count from the storage. Thus if you use
+      * the interfaces and your scripts take long you should use reset()
+      * from time to time.
+      *
+      * @return   void
+      */
+     public function rewind()
+     {
+        $this->_iterationMax = $this->countMessages();
+        $this->_iterationPos = 1;
+     }
+
+
+     /**
+      * Iterator::current()
+      *
+      * @return   Zend_Mail_Message current message
+      */
+     public function current()
+     {
+        return $this->getMessage($this->_iterationPos);
+     }
+
+
+     /**
+      * Iterator::key()
+      *
+      * @return   int id of current position
+      */
+     public function key()
+     {
+        return $this->_iterationPos;
+     }
+
+
+     /**
+      * Iterator::next()
+      *
+      * @return   void
+      */
+     public function next()
+     {
+        ++$this->_iterationPos;
+     }
+
+
+     /**
+      * Iterator::valid()
+      *
+      * @return   boolean
+      */
+     public function valid()
+     {
+        if ($this->_iterationMax === null) {
+          $this->_iterationMax = $this->countMessages();
+        }
+        return $this->_iterationPos && $this->_iterationPos <= $this->_iterationMax;
+     }
+
+
+     /**
+      * SeekableIterator::seek()
+      *
+      * @param  int $pos
+      * @return void
+      * @throws OutOfBoundsException
+      */
+     public function seek($pos)
+     {
+        if ($this->_iterationMax === null) {
+          $this->_iterationMax = $this->countMessages();
+        }
+
+        if ($pos > $this->_iterationMax) {
+            throw new OutOfBoundsException('this position does not exist');
+        }
+        $this->_iterationPos = $pos;
+     }
+
+}
Index: sandbox/2.2.0.2/library/Zend/Mail/Storage/Writable/Interface.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Mail/Storage/Writable/Interface.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Mail/Storage/Writable/Interface.php	(revision 4456)
@@ -0,0 +1,108 @@
+<?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_Mail
+ * @subpackage Storage
+ * @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 $
+ */
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @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_Mail_Storage_Writable_Interface
+{
+    /**
+     * create a new folder
+     *
+     * This method also creates parent folders if necessary. Some mail storages may restrict, which folder
+     * may be used as parent or which chars may be used in the folder name
+     *
+     * @param string                          $name         global name of folder, local name if $parentFolder is set
+     * @param string|Zend_Mail_Storage_Folder $parentFolder parent folder for new folder, else root folder is parent
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function createFolder($name, $parentFolder = null);
+
+    /**
+     * remove a folder
+     *
+     * @param string|Zend_Mail_Storage_Folder $name      name or instance of folder
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function removeFolder($name);
+
+    /**
+     * rename and/or move folder
+     *
+     * The new name has the same restrictions as in createFolder()
+     *
+     * @param string|Zend_Mail_Storage_Folder $oldName name or instance of folder
+     * @param string                          $newName new global name of folder
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function renameFolder($oldName, $newName);
+
+    /**
+     * append a new message to mail storage
+     *
+     * @param  string|Zend_Mail_Message|Zend_Mime_Message $message message as string or instance of message class
+     * @param  null|string|Zend_Mail_Storage_Folder       $folder  folder for new message, else current folder is taken
+     * @param  null|array                                 $flags   set flags for new message, else a default set is used
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function appendMessage($message, $folder = null, $flags = null);
+
+    /**
+     * copy an existing message
+     *
+     * @param  int                             $id     number of message
+     * @param  string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function copyMessage($id, $folder);
+
+    /**
+     * move an existing message
+     *
+     * @param  int                             $id     number of message
+     * @param  string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function moveMessage($id, $folder);
+
+    /**
+     * set flags for message
+     *
+     * NOTE: this method can't set the recent flag.
+     *
+     * @param  int   $id    number of message
+     * @param  array $flags new flags for message
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function setFlags($id, $flags);
+}
Index: sandbox/2.2.0.2/library/Zend/Mail/Storage/Writable/Maildir.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Mail/Storage/Writable/Maildir.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Mail/Storage/Writable/Maildir.php	(revision 4456)
@@ -0,0 +1,1049 @@
+<?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_Mail
+ * @subpackage Storage
+ * @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: Maildir.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Mail_Storage_Folder_Maildir
+ */
+require_once 'Zend/Mail/Storage/Folder/Maildir.php';
+
+/**
+ * @see Zend_Mail_Storage_Writable_Interface
+ */
+require_once 'Zend/Mail/Storage/Writable/Interface.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @subpackage Storage
+ * @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_Mail_Storage_Writable_Maildir extends    Zend_Mail_Storage_Folder_Maildir
+                                         implements Zend_Mail_Storage_Writable_Interface
+{
+    // TODO: init maildir (+ constructor option create if not found)
+
+    /**
+     * use quota and size of quota if given
+     * @var bool|int
+     */
+    protected $_quota;
+
+    /**
+     * create a new maildir
+     *
+     * If the given dir is already a valid maildir this will not fail.
+     *
+     * @param string $dir directory for the new maildir (may already exist)
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public static function initMaildir($dir)
+    {
+        if (file_exists($dir)) {
+            if (!is_dir($dir)) {
+                /**
+                 * @see Zend_Mail_Storage_Exception
+                 */
+                require_once 'Zend/Mail/Storage/Exception.php';
+                throw new Zend_Mail_Storage_Exception('maildir must be a directory if already exists');
+            }
+        } else {
+            if (!mkdir($dir)) {
+                /**
+                 * @see Zend_Mail_Storage_Exception
+                 */
+                require_once 'Zend/Mail/Storage/Exception.php';
+                $dir = dirname($dir);
+                if (!file_exists($dir)) {
+                    throw new Zend_Mail_Storage_Exception("parent $dir not found");
+                } else if (!is_dir($dir)) {
+                    throw new Zend_Mail_Storage_Exception("parent $dir not a directory");
+                } else {
+                    throw new Zend_Mail_Storage_Exception('cannot create maildir');
+                }
+            }
+        }
+
+        foreach (array('cur', 'tmp', 'new') as $subdir) {
+            if (!@mkdir($dir . DIRECTORY_SEPARATOR . $subdir)) {
+                // ignore if dir exists (i.e. was already valid maildir or two processes try to create one)
+                if (!file_exists($dir . DIRECTORY_SEPARATOR . $subdir)) {
+                    /**
+                     * @see Zend_Mail_Storage_Exception
+                     */
+                    require_once 'Zend/Mail/Storage/Exception.php';
+                    throw new Zend_Mail_Storage_Exception('could not create subdir ' . $subdir);
+                }
+            }
+        }
+    }
+
+    /**
+     * Create instance with parameters
+     * Additional parameters are (see parent for more):
+     *   - create if true a new maildir is create if none exists
+     *
+     * @param  $params array mail reader specific parameters
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function __construct($params) {
+        if (is_array($params)) {
+            $params = (object)$params;
+        }
+
+        if (!empty($params->create) && isset($params->dirname) && !file_exists($params->dirname . DIRECTORY_SEPARATOR . 'cur')) {
+            self::initMaildir($params->dirname);
+        }
+
+        parent::__construct($params);
+    }
+
+    /**
+     * create a new folder
+     *
+     * This method also creates parent folders if necessary. Some mail storages may restrict, which folder
+     * may be used as parent or which chars may be used in the folder name
+     *
+     * @param   string                          $name         global name of folder, local name if $parentFolder is set
+     * @param   string|Zend_Mail_Storage_Folder $parentFolder parent folder for new folder, else root folder is parent
+     * @return  string only used internally (new created maildir)
+     * @throws  Zend_Mail_Storage_Exception
+     */
+    public function createFolder($name, $parentFolder = null)
+    {
+        if ($parentFolder instanceof Zend_Mail_Storage_Folder) {
+            $folder = $parentFolder->getGlobalName() . $this->_delim . $name;
+        } else if ($parentFolder != null) {
+            $folder = rtrim($parentFolder, $this->_delim) . $this->_delim . $name;
+        } else {
+            $folder = $name;
+        }
+
+        $folder = trim($folder, $this->_delim);
+
+        // first we check if we try to create a folder that does exist
+        $exists = null;
+        try {
+            $exists = $this->getFolders($folder);
+        } catch (Zend_Mail_Exception $e) {
+            // ok
+        }
+        if ($exists) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('folder already exists');
+        }
+
+        if (strpos($folder, $this->_delim . $this->_delim) !== false) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('invalid name - folder parts may not be empty');
+        }
+
+        if (strpos($folder, 'INBOX' . $this->_delim) === 0) {
+            $folder = substr($folder, 6);
+        }
+
+        $fulldir = $this->_rootdir . '.' . $folder;
+
+        // check if we got tricked and would create a dir outside of the rootdir or not as direct child
+        if (strpos($folder, DIRECTORY_SEPARATOR) !== false || strpos($folder, '/') !== false
+            || dirname($fulldir) . DIRECTORY_SEPARATOR != $this->_rootdir) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('invalid name - no directory seprator allowed in folder name');
+        }
+
+        // has a parent folder?
+        $parent = null;
+        if (strpos($folder, $this->_delim)) {
+            // let's see if the parent folder exists
+            $parent = substr($folder, 0, strrpos($folder, $this->_delim));
+            try {
+                $this->getFolders($parent);
+            } catch (Zend_Mail_Exception $e) {
+                // does not - create parent folder
+                $this->createFolder($parent);
+            }
+        }
+
+        if (!@mkdir($fulldir) || !@mkdir($fulldir . DIRECTORY_SEPARATOR . 'cur')) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('error while creating new folder, may be created incompletly');
+        }
+
+        mkdir($fulldir . DIRECTORY_SEPARATOR . 'new');
+        mkdir($fulldir . DIRECTORY_SEPARATOR . 'tmp');
+
+        $localName = $parent ? substr($folder, strlen($parent) + 1) : $folder;
+        $this->getFolders($parent)->$localName = new Zend_Mail_Storage_Folder($localName, $folder, true);
+
+        return $fulldir;
+    }
+
+    /**
+     * remove a folder
+     *
+     * @param   string|Zend_Mail_Storage_Folder $name      name or instance of folder
+     * @return  null
+     * @throws  Zend_Mail_Storage_Exception
+     */
+    public function removeFolder($name)
+    {
+        // TODO: This could fail in the middle of the task, which is not optimal.
+        // But there is no defined standard way to mark a folder as removed and there is no atomar fs-op
+        // to remove a directory. Also moving the folder to a/the trash folder is not possible, as
+        // all parent folders must be created. What we could do is add a dash to the front of the
+        // directory name and it should be ignored as long as other processes obey the standard.
+
+        if ($name instanceof Zend_Mail_Storage_Folder) {
+            $name = $name->getGlobalName();
+        }
+
+        $name = trim($name, $this->_delim);
+        if (strpos($name, 'INBOX' . $this->_delim) === 0) {
+            $name = substr($name, 6);
+        }
+
+        // check if folder exists and has no children
+        if (!$this->getFolders($name)->isLeaf()) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('delete children first');
+        }
+
+        if ($name == 'INBOX' || $name == DIRECTORY_SEPARATOR || $name == '/') {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('wont delete INBOX');
+        }
+
+        if ($name == $this->getCurrentFolder()) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('wont delete selected folder');
+        }
+
+        foreach (array('tmp', 'new', 'cur', '.') as $subdir) {
+            $dir = $this->_rootdir . '.' . $name . DIRECTORY_SEPARATOR . $subdir;
+            if (!file_exists($dir)) {
+                continue;
+            }
+            $dh = opendir($dir);
+            if (!$dh) {
+                /**
+                 * @see Zend_Mail_Storage_Exception
+                 */
+                require_once 'Zend/Mail/Storage/Exception.php';
+                throw new Zend_Mail_Storage_Exception("error opening $subdir");
+            }
+            while (($entry = readdir($dh)) !== false) {
+                if ($entry == '.' || $entry == '..') {
+                    continue;
+                }
+                if (!unlink($dir . DIRECTORY_SEPARATOR . $entry)) {
+                    /**
+                     * @see Zend_Mail_Storage_Exception
+                     */
+                    require_once 'Zend/Mail/Storage/Exception.php';
+                    throw new Zend_Mail_Storage_Exception("error cleaning $subdir");
+                }
+            }
+            closedir($dh);
+            if ($subdir !== '.') {
+                if (!rmdir($dir)) {
+                    /**
+                     * @see Zend_Mail_Storage_Exception
+                     */
+                    require_once 'Zend/Mail/Storage/Exception.php';
+                    throw new Zend_Mail_Storage_Exception("error removing $subdir");
+                }
+            }
+        }
+
+        if (!rmdir($this->_rootdir . '.' . $name)) {
+            // at least we should try to make it a valid maildir again
+            mkdir($this->_rootdir . '.' . $name . DIRECTORY_SEPARATOR . 'cur');
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception("error removing maindir");
+        }
+
+        $parent = strpos($name, $this->_delim) ? substr($name, 0, strrpos($name, $this->_delim)) : null;
+        $localName = $parent ? substr($name, strlen($parent) + 1) : $name;
+        unset($this->getFolders($parent)->$localName);
+    }
+
+    /**
+     * rename and/or move folder
+     *
+     * The new name has the same restrictions as in createFolder()
+     *
+     * @param   string|Zend_Mail_Storage_Folder $oldName name or instance of folder
+     * @param   string                          $newName new global name of folder
+     * @return  null
+     * @throws  Zend_Mail_Storage_Exception
+     */
+    public function renameFolder($oldName, $newName)
+    {
+        // TODO: This is also not atomar and has similar problems as removeFolder()
+
+        if ($oldName instanceof Zend_Mail_Storage_Folder) {
+            $oldName = $oldName->getGlobalName();
+        }
+
+        $oldName = trim($oldName, $this->_delim);
+        if (strpos($oldName, 'INBOX' . $this->_delim) === 0) {
+            $oldName = substr($oldName, 6);
+        }
+
+        $newName = trim($newName, $this->_delim);
+        if (strpos($newName, 'INBOX' . $this->_delim) === 0) {
+            $newName = substr($newName, 6);
+        }
+
+        if (strpos($newName, $oldName . $this->_delim) === 0) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('new folder cannot be a child of old folder');
+        }
+
+        // check if folder exists and has no children
+        $folder = $this->getFolders($oldName);
+
+        if ($oldName == 'INBOX' || $oldName == DIRECTORY_SEPARATOR || $oldName == '/') {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('wont rename INBOX');
+        }
+
+        if ($oldName == $this->getCurrentFolder()) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('wont rename selected folder');
+        }
+
+        $newdir = $this->createFolder($newName);
+
+        if (!$folder->isLeaf()) {
+            foreach ($folder as $k => $v) {
+                $this->renameFolder($v->getGlobalName(), $newName . $this->_delim . $k);
+            }
+        }
+
+        $olddir = $this->_rootdir . '.' . $folder;
+        foreach (array('tmp', 'new', 'cur') as $subdir) {
+            $subdir = DIRECTORY_SEPARATOR . $subdir;
+            if (!file_exists($olddir . $subdir)) {
+                continue;
+            }
+            // using copy or moving files would be even better - but also much slower
+            if (!rename($olddir . $subdir, $newdir . $subdir)) {
+                /**
+                 * @see Zend_Mail_Storage_Exception
+                 */
+                require_once 'Zend/Mail/Storage/Exception.php';
+                throw new Zend_Mail_Storage_Exception('error while moving ' . $subdir);
+            }
+        }
+        // create a dummy if removing fails - otherwise we can't read it next time
+        mkdir($olddir . DIRECTORY_SEPARATOR . 'cur');
+        $this->removeFolder($oldName);
+    }
+
+    /**
+     * create a uniqueid for maildir filename
+     *
+     * This is nearly the format defined in the maildir standard. The microtime() call should already
+     * create a uniqueid, the pid is for multicore/-cpu machine that manage to call this function at the
+     * exact same time, and uname() gives us the hostname for multiple machines accessing the same storage.
+     *
+     * If someone disables posix we create a random number of the same size, so this method should also
+     * work on Windows - if you manage to get maildir working on Windows.
+     * Microtime could also be disabled, altough I've never seen it.
+     *
+     * @return string new uniqueid
+     */
+    protected function _createUniqueId()
+    {
+        $id = '';
+        $id .= function_exists('microtime') ? microtime(true) : (time() . ' ' . rand(0, 100000));
+        $id .= '.' . (function_exists('posix_getpid') ? posix_getpid() : rand(50, 65535));
+        $id .= '.' . php_uname('n');
+
+        return $id;
+    }
+
+    /**
+     * open a temporary maildir file
+     *
+     * makes sure tmp/ exists and create a file with a unique name
+     * you should close the returned filehandle!
+     *
+     * @param   string $folder name of current folder without leading .
+     * @return  array array('dirname' => dir of maildir folder, 'uniq' => unique id, 'filename' => name of create file
+     *                     'handle'  => file opened for writing)
+     * @throws  Zend_Mail_Storage_Exception
+     */
+    protected function _createTmpFile($folder = 'INBOX')
+    {
+        if ($folder == 'INBOX') {
+            $tmpdir = $this->_rootdir . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
+        } else {
+            $tmpdir = $this->_rootdir . '.' . $folder . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
+        }
+        if (!file_exists($tmpdir)) {
+            if (!mkdir($tmpdir)) {
+                /**
+                 * @see Zend_Mail_Storage_Exception
+                 */
+                require_once 'Zend/Mail/Storage/Exception.php';
+                throw new Zend_Mail_Storage_Exception('problems creating tmp dir');
+            }
+        }
+
+        // we should retry to create a unique id if a file with the same name exists
+        // to avoid a script timeout we only wait 1 second (instead of 2) and stop
+        // after a defined retry count
+        // if you change this variable take into account that it can take up to $max_tries seconds
+        // normally we should have a valid unique name after the first try, we're just following the "standard" here
+        $max_tries = 5;
+        for ($i = 0; $i < $max_tries; ++$i) {
+            $uniq = $this->_createUniqueId();
+            if (!file_exists($tmpdir . $uniq)) {
+                // here is the race condition! - as defined in the standard
+                // to avoid having a long time between stat()ing the file and creating it we're opening it here
+                // to mark the filename as taken
+                $fh = fopen($tmpdir . $uniq, 'w');
+                if (!$fh) {
+                    /**
+                     * @see Zend_Mail_Storage_Exception
+                     */
+                    require_once 'Zend/Mail/Storage/Exception.php';
+                    throw new Zend_Mail_Storage_Exception('could not open temp file');
+                }
+                break;
+            }
+            sleep(1);
+        }
+
+        if (!$fh) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception("tried $max_tries unique ids for a temp file, but all were taken"
+                                                . ' - giving up');
+        }
+
+        return array('dirname' => $this->_rootdir . '.' . $folder, 'uniq' => $uniq, 'filename' => $tmpdir . $uniq,
+                     'handle' => $fh);
+    }
+
+    /**
+     * create an info string for filenames with given flags
+     *
+     * @param   array $flags wanted flags, with the reference you'll get the set flags with correct key (= char for flag)
+     * @return  string info string for version 2 filenames including the leading colon
+     * @throws  Zend_Mail_Storage_Exception
+     */
+    protected function _getInfoString(&$flags)
+    {
+        // accessing keys is easier, faster and it removes duplicated flags
+        $wanted_flags = array_flip($flags);
+        if (isset($wanted_flags[Zend_Mail_Storage::FLAG_RECENT])) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('recent flag may not be set');
+        }
+
+        $info = ':2,';
+        $flags = array();
+        foreach (Zend_Mail_Storage_Maildir::$_knownFlags as $char => $flag) {
+            if (!isset($wanted_flags[$flag])) {
+                continue;
+            }
+            $info .= $char;
+            $flags[$char] = $flag;
+            unset($wanted_flags[$flag]);
+        }
+
+        if (!empty($wanted_flags)) {
+            $wanted_flags = implode(', ', array_keys($wanted_flags));
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('unknown flag(s): ' . $wanted_flags);
+        }
+
+        return $info;
+    }
+
+    /**
+     * append a new message to mail storage
+     *
+     * @param   string|stream                              $message message as string or stream resource
+     * @param   null|string|Zend_Mail_Storage_Folder       $folder  folder for new message, else current folder is taken
+     * @param   null|array                                 $flags   set flags for new message, else a default set is used
+     * @param   bool                                       $recent  handle this mail as if recent flag has been set,
+     *                                                              should only be used in delivery
+     * @throws  Zend_Mail_Storage_Exception
+     */
+     // not yet * @param string|Zend_Mail_Message|Zend_Mime_Message $message message as string or instance of message class
+
+    public function appendMessage($message, $folder = null, $flags = null, $recent = false)
+    {
+        if ($this->_quota && $this->checkQuota()) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('storage is over quota!');
+        }
+
+        if ($folder === null) {
+            $folder = $this->_currentFolder;
+        }
+
+        if (!($folder instanceof Zend_Mail_Storage_Folder)) {
+            $folder = $this->getFolders($folder);
+        }
+
+        if ($flags === null) {
+            $flags = array(Zend_Mail_Storage::FLAG_SEEN);
+        }
+        $info = $this->_getInfoString($flags);
+        $temp_file = $this->_createTmpFile($folder->getGlobalName());
+
+        // TODO: handle class instances for $message
+        if (is_resource($message) && get_resource_type($message) == 'stream') {
+            stream_copy_to_stream($message, $temp_file['handle']);
+        } else {
+            fputs($temp_file['handle'], $message);
+        }
+        fclose($temp_file['handle']);
+
+        // we're adding the size to the filename for maildir++
+        $size = filesize($temp_file['filename']);
+        if ($size !== false) {
+            $info = ',S=' . $size . $info;
+        }
+        $new_filename = $temp_file['dirname'] . DIRECTORY_SEPARATOR;
+        $new_filename .= $recent ? 'new' : 'cur';
+        $new_filename .= DIRECTORY_SEPARATOR . $temp_file['uniq'] . $info;
+
+        // we're throwing any exception after removing our temp file and saving it to this variable instead
+        $exception = null;
+
+        if (!link($temp_file['filename'], $new_filename)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            $exception = new Zend_Mail_Storage_Exception('cannot link message file to final dir');
+        }
+        @unlink($temp_file['filename']);
+
+        if ($exception) {
+            throw $exception;
+        }
+
+        $this->_files[] = array('uniq'     => $temp_file['uniq'],
+                                'flags'    => $flags,
+                                'filename' => $new_filename);
+        if ($this->_quota) {
+            $this->_addQuotaEntry((int)$size, 1);
+        }
+    }
+
+    /**
+     * copy an existing message
+     *
+     * @param   int                             $id     number of message
+     * @param   string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
+     * @return  null
+     * @throws  Zend_Mail_Storage_Exception
+     */
+    public function copyMessage($id, $folder)
+    {
+        if ($this->_quota && $this->checkQuota()) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('storage is over quota!');
+        }
+
+        if (!($folder instanceof Zend_Mail_Storage_Folder)) {
+            $folder = $this->getFolders($folder);
+        }
+
+        $filedata = $this->_getFileData($id);
+        $old_file = $filedata['filename'];
+        $flags = $filedata['flags'];
+
+        // copied message can't be recent
+        while (($key = array_search(Zend_Mail_Storage::FLAG_RECENT, $flags)) !== false) {
+            unset($flags[$key]);
+        }
+        $info = $this->_getInfoString($flags);
+
+        // we're creating the copy as temp file before moving to cur/
+        $temp_file = $this->_createTmpFile($folder->getGlobalName());
+        // we don't write directly to the file
+        fclose($temp_file['handle']);
+
+        // we're adding the size to the filename for maildir++
+        $size = filesize($old_file);
+        if ($size !== false) {
+            $info = ',S=' . $size . $info;
+        }
+
+        $new_file = $temp_file['dirname'] . DIRECTORY_SEPARATOR . 'cur' . DIRECTORY_SEPARATOR . $temp_file['uniq'] . $info;
+
+        // we're throwing any exception after removing our temp file and saving it to this variable instead
+        $exception = null;
+
+        if (!copy($old_file, $temp_file['filename'])) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            $exception = new Zend_Mail_Storage_Exception('cannot copy message file');
+        } else if (!link($temp_file['filename'], $new_file)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            $exception = new Zend_Mail_Storage_Exception('cannot link message file to final dir');
+        }
+        @unlink($temp_file['filename']);
+
+        if ($exception) {
+            throw $exception;
+        }
+
+        if ($folder->getGlobalName() == $this->_currentFolder
+            || ($this->_currentFolder == 'INBOX' && $folder->getGlobalName() == '/')) {
+            $this->_files[] = array('uniq'     => $temp_file['uniq'],
+                                    'flags'    => $flags,
+                                    'filename' => $new_file);
+        }
+
+        if ($this->_quota) {
+            $this->_addQuotaEntry((int)$size, 1);
+        }
+    }
+
+    /**
+     * move an existing message
+     *
+     * @param  int                             $id     number of message
+     * @param  string|Zend_Mail_Storage_Folder $folder name or instance of targer folder
+     * @return null
+     * @throws Zend_Mail_Storage_Exception
+     */
+    public function moveMessage($id, $folder) {
+        if (!($folder instanceof Zend_Mail_Storage_Folder)) {
+            $folder = $this->getFolders($folder);
+        }
+
+        if ($folder->getGlobalName() == $this->_currentFolder
+            || ($this->_currentFolder == 'INBOX' && $folder->getGlobalName() == '/')) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('target is current folder');
+        }
+
+        $filedata = $this->_getFileData($id);
+        $old_file = $filedata['filename'];
+        $flags = $filedata['flags'];
+
+        // moved message can't be recent
+        while (($key = array_search(Zend_Mail_Storage::FLAG_RECENT, $flags)) !== false) {
+            unset($flags[$key]);
+        }
+        $info = $this->_getInfoString($flags);
+
+        // reserving a new name
+        $temp_file = $this->_createTmpFile($folder->getGlobalName());
+        fclose($temp_file['handle']);
+
+        // we're adding the size to the filename for maildir++
+        $size = filesize($old_file);
+        if ($size !== false) {
+            $info = ',S=' . $size . $info;
+        }
+
+        $new_file = $temp_file['dirname'] . DIRECTORY_SEPARATOR . 'cur' . DIRECTORY_SEPARATOR . $temp_file['uniq'] . $info;
+
+        // we're throwing any exception after removing our temp file and saving it to this variable instead
+        $exception = null;
+
+        if (!rename($old_file, $new_file)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            $exception = new Zend_Mail_Storage_Exception('cannot move message file');
+        }
+        @unlink($temp_file['filename']);
+
+        if ($exception) {
+            throw $exception;
+        }
+
+        unset($this->_files[$id - 1]);
+        // remove the gap
+        $this->_files = array_values($this->_files);
+    }
+
+
+    /**
+     * set flags for message
+     *
+     * NOTE: this method can't set the recent flag.
+     *
+     * @param   int   $id    number of message
+     * @param   array $flags new flags for message
+     * @throws  Zend_Mail_Storage_Exception
+     */
+    public function setFlags($id, $flags)
+    {
+        $info = $this->_getInfoString($flags);
+        $filedata = $this->_getFileData($id);
+
+        // NOTE: double dirname to make sure we always move to cur. if recent flag has been set (message is in new) it will be moved to cur.
+        $new_filename = dirname(dirname($filedata['filename'])) . DIRECTORY_SEPARATOR . 'cur' . DIRECTORY_SEPARATOR . "$filedata[uniq]$info";
+
+        if (!@rename($filedata['filename'], $new_filename)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot rename file');
+        }
+
+        $filedata['flags']    = $flags;
+        $filedata['filename'] = $new_filename;
+
+        $this->_files[$id - 1] = $filedata;
+    }
+
+
+    /**
+     * stub for not supported message deletion
+     *
+     * @return  null
+     * @throws  Zend_Mail_Storage_Exception
+     */
+    public function removeMessage($id)
+    {
+        $filename = $this->_getFileData($id, 'filename');
+
+        if ($this->_quota) {
+            $size = filesize($filename);
+        }
+
+        if (!@unlink($filename)) {
+            /**
+             * @see Zend_Mail_Storage_Exception
+             */
+            require_once 'Zend/Mail/Storage/Exception.php';
+            throw new Zend_Mail_Storage_Exception('cannot remove message');
+        }
+        unset($this->_files[$id - 1]);
+        // remove the gap
+        $this->_files = array_values($this->_files);
+        if ($this->_quota) {
+            $this->_addQuotaEntry(0 - (int)$size, -1);
+        }
+    }
+
+    /**
+     * enable/disable quota and set a quota value if wanted or needed
+     *
+     * You can enable/disable quota with true/false. If you don't have
+     * a MDA or want to enforce a quota value you can also set this value
+     * here. Use array('size' => SIZE_QUOTA, 'count' => MAX_MESSAGE) do
+     * define your quota. Order of these fields does matter!
+     *
+     * @param bool|array $value new quota value
+     * @return null
+     */
+    public function setQuota($value) {
+        $this->_quota = $value;
+    }
+
+    /**
+     * get currently set quota
+     *
+     * @see Zend_Mail_Storage_Writable_Maildir::setQuota()
+     *
+     * @return bool|array
+     */
+    public function getQuota($fromStorage = false) {
+        if ($fromStorage) {
+            $fh = @fopen($this->_rootdir . 'maildirsize', 'r');
+            if (!$fh) {
+                /**
+                 * @see Zend_Mail_Storage_Exception
+                 */
+                require_once 'Zend/Mail/Storage/Exception.php';
+                throw new Zend_Mail_Storage_Exception('cannot open maildirsize');
+            }
+            $definition = fgets($fh);
+            fclose($fh);
+            $definition = explode(',', trim($definition));
+            $quota = array();
+            foreach ($definition as $member) {
+                $key = $member[strlen($member) - 1];
+                if ($key == 'S' || $key == 'C') {
+                    $key = $key == 'C' ? 'count' : 'size';
+                }
+                $quota[$key] = substr($member, 0, -1);
+            }
+            return $quota;
+        }
+
+        return $this->_quota;
+    }
+
+    /**
+     * @see http://www.inter7.com/courierimap/README.maildirquota.html "Calculating maildirsize"
+     */
+    protected function _calculateMaildirsize() {
+        $timestamps = array();
+        $messages = 0;
+        $total_size = 0;
+
+        if (is_array($this->_quota)) {
+            $quota = $this->_quota;
+        } else {
+            try {
+                $quota = $this->getQuota(true);
+            } catch (Zend_Mail_Storage_Exception $e) {
+                throw new Zend_Mail_Storage_Exception('no quota definition found', 0, $e);
+            }
+        }
+
+        $folders = new RecursiveIteratorIterator($this->getFolders(), RecursiveIteratorIterator::SELF_FIRST);
+        foreach ($folders as $folder) {
+            $subdir = $folder->getGlobalName();
+            if ($subdir == 'INBOX') {
+                $subdir = '';
+            } else {
+                $subdir = '.' . $subdir;
+            }
+            if ($subdir == 'Trash') {
+                continue;
+            }
+
+            foreach (array('cur', 'new') as $subsubdir) {
+                $dirname = $this->_rootdir . $subdir . DIRECTORY_SEPARATOR . $subsubdir . DIRECTORY_SEPARATOR;
+                if (!file_exists($dirname)) {
+                    continue;
+                }
+                // NOTE: we are using mtime instead of "the latest timestamp". The latest would be atime
+                // and as we are accessing the directory it would make the whole calculation useless.
+                $timestamps[$dirname] = filemtime($dirname);
+
+                $dh = opendir($dirname);
+                // NOTE: Should have been checked in constructor. Not throwing an exception here, quotas will
+                // therefore not be fully enforeced, but next request will fail anyway, if problem persists.
+                if (!$dh) {
+                    continue;
+                }
+
+
+                while (($entry = readdir()) !== false) {
+                    if ($entry[0] == '.' || !is_file($dirname . $entry)) {
+                        continue;
+                    }
+
+                    if (strpos($entry, ',S=')) {
+                        strtok($entry, '=');
+                        $filesize = strtok(':');
+                        if (is_numeric($filesize)) {
+                            $total_size += $filesize;
+                            ++$messages;
+                            continue;
+                        }
+                    }
+                    $size = filesize($dirname . $entry);
+                    if ($size === false) {
+                        // ignore, as we assume file got removed
+                        continue;
+                    }
+                    $total_size += $size;
+                    ++$messages;
+                }
+            }
+        }
+
+        $tmp = $this->_createTmpFile();
+        $fh = $tmp['handle'];
+        $definition = array();
+        foreach ($quota as $type => $value) {
+            if ($type == 'size' || $type == 'count') {
+                $type = $type == 'count' ? 'C' : 'S';
+            }
+            $definition[] = $value . $type;
+        }
+        $definition = implode(',', $definition);
+        fputs($fh, "$definition\n");
+        fputs($fh, "$total_size $messages\n");
+        fclose($fh);
+        rename($tmp['filename'], $this->_rootdir . 'maildirsize');
+        foreach ($timestamps as $dir => $timestamp) {
+            if ($timestamp < filemtime($dir)) {
+                unlink($this->_rootdir . 'maildirsize');
+                break;
+            }
+        }
+
+        return array('size' => $total_size, 'count' => $messages, 'quota' => $quota);
+    }
+
+    /**
+     * @see http://www.inter7.com/courierimap/README.maildirquota.html "Calculating the quota for a Maildir++"
+     */
+    protected function _calculateQuota($forceRecalc = false) {
+        $fh = null;
+        $total_size = 0;
+        $messages   = 0;
+        $maildirsize = '';
+        if (!$forceRecalc && file_exists($this->_rootdir . 'maildirsize') && filesize($this->_rootdir . 'maildirsize') < 5120) {
+            $fh = fopen($this->_rootdir . 'maildirsize', 'r');
+        }
+        if ($fh) {
+            $maildirsize = fread($fh, 5120);
+            if (strlen($maildirsize) >= 5120) {
+                fclose($fh);
+                $fh = null;
+                $maildirsize = '';
+            }
+        }
+        if (!$fh) {
+            $result = $this->_calculateMaildirsize();
+            $total_size = $result['size'];
+            $messages   = $result['count'];
+            $quota      = $result['quota'];
+        } else {
+            $maildirsize = explode("\n", $maildirsize);
+            if (is_array($this->_quota)) {
+                $quota = $this->_quota;
+            } else {
+                $definition = explode(',', $maildirsize[0]);
+                $quota = array();
+                foreach ($definition as $member) {
+                    $key = $member[strlen($member) - 1];
+                    if ($key == 'S' || $key == 'C') {
+                        $key = $key == 'C' ? 'count' : 'size';
+                    }
+                    $quota[$key] = substr($member, 0, -1);
+                }
+            }
+            unset($maildirsize[0]);
+            foreach ($maildirsize as $line) {
+                list($size, $count) = explode(' ', trim($line));
+                $total_size += $size;
+                $messages   += $count;
+            }
+        }
+
+        $over_quota = false;
+        $over_quota = $over_quota || (isset($quota['size'])  && $total_size > $quota['size']);
+        $over_quota = $over_quota || (isset($quota['count']) && $messages   > $quota['count']);
+        // NOTE: $maildirsize equals false if it wasn't set (AKA we recalculated) or it's only
+        // one line, because $maildirsize[0] gets unsetted.
+        // Also we're using local time to calculate the 15 minute offset. Touching a file just for known the
+        // local time of the file storage isn't worth the hassle.
+        if ($over_quota && ($maildirsize || filemtime($this->_rootdir . 'maildirsize') > time() - 900)) {
+            $result = $this->_calculateMaildirsize();
+            $total_size = $result['size'];
+            $messages   = $result['count'];
+            $quota      = $result['quota'];
+            $over_quota = false;
+            $over_quota = $over_quota || (isset($quota['size'])  && $total_size > $quota['size']);
+            $over_quota = $over_quota || (isset($quota['count']) && $messages   > $quota['count']);
+        }
+
+        if ($fh) {
+            // TODO is there a safe way to keep the handle open for writing?
+            fclose($fh);
+        }
+
+        return array('size' => $total_size, 'count' => $messages, 'quota' => $quota, 'over_quota' => $over_quota);
+    }
+
+    protected function _addQuotaEntry($size, $count = 1) {
+        if (!file_exists($this->_rootdir . 'maildirsize')) {
+            // TODO: should get file handler from _calculateQuota
+        }
+        $size = (int)$size;
+        $count = (int)$count;
+        file_put_contents($this->_rootdir . 'maildirsize', "$size $count\n", FILE_APPEND);
+    }
+
+    /**
+     * check if storage is currently over quota
+     *
+     * @param bool $detailedResponse return known data of quota and current size and message count @see _calculateQuota()
+     * @return bool|array over quota state or detailed response
+     */
+    public function checkQuota($detailedResponse = false, $forceRecalc = false) {
+        $result = $this->_calculateQuota($forceRecalc);
+        return $detailedResponse ? $result : $result['over_quota'];
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Mail/Part.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Mail/Part.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Mail/Part.php	(revision 4456)
@@ -0,0 +1,520 @@
+<?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_Mail
+ * @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: Part.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * @see Zend_Mime_Decode
+ */
+require_once 'Zend/Mime/Decode.php';
+
+/**
+ * @see Zend_Mail_Part_Interface
+ */
+require_once 'Zend/Mail/Part/Interface.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mail
+ * @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_Mail_Part implements RecursiveIterator, Zend_Mail_Part_Interface
+{
+    /**
+     * headers of part as array
+     * @var null|array
+     */
+    protected $_headers;
+
+    /**
+     * raw part body
+     * @var null|string
+     */
+    protected $_content;
+
+    /**
+     * toplines as fetched with headers
+     * @var string
+     */
+    protected $_topLines = '';
+
+    /**
+     * parts of multipart message
+     * @var array
+     */
+    protected $_parts = array();
+
+    /**
+     * count of parts of a multipart message
+     * @var null|int
+     */
+    protected $_countParts;
+
+    /**
+     * current position of iterator
+     * @var int
+     */
+    protected $_iterationPos = 1;
+
+    /**
+     * mail handler, if late fetch is active
+     * @var null|Zend_Mail_Storage_Abstract
+     */
+    protected $_mail;
+
+    /**
+     * message number for mail handler
+     * @var int
+     */
+    protected $_messageNum = 0;
+
+    /**
+     * Public constructor
+     *
+     * Zend_Mail_Part supports different sources for content. The possible params are:
+     * - handler    a instance of Zend_Mail_Storage_Abstract for late fetch
+     * - id         number of message for handler
+     * - raw        raw content with header and body as string
+     * - headers    headers as array (name => value) or string, if a content part is found it's used as toplines
+     * - noToplines ignore content found after headers in param 'headers'
+     * - content    content as string
+     *
+     * @param   array $params  full message with or without headers
+     * @throws  Zend_Mail_Exception
+     */
+    public function __construct(array $params)
+    {
+        if (isset($params['handler'])) {
+            if (!$params['handler'] instanceof Zend_Mail_Storage_Abstract) {
+                /**
+                 * @see Zend_Mail_Exception
+                 */
+                require_once 'Zend/Mail/Exception.php';
+                throw new Zend_Mail_Exception('handler is not a valid mail handler');
+            }
+            if (!isset($params['id'])) {
+                /**
+                 * @see Zend_Mail_Exception
+                 */
+                require_once 'Zend/Mail/Exception.php';
+                throw new Zend_Mail_Exception('need a message id with a handler');
+            }
+
+            $this->_mail       = $params['handler'];
+            $this->_messageNum = $params['id'];
+        }
+
+        if (isset($params['raw'])) {
+            Zend_Mime_Decode::splitMessage($params['raw'], $this->_headers, $this->_content);
+        } else if (isset($params['headers'])) {
+            if (is_array($params['headers'])) {
+                $this->_headers = $params['headers'];
+            } else {
+                if (!empty($params['noToplines'])) {
+                    Zend_Mime_Decode::splitMessage($params['headers'], $this->_headers, $null);
+                } else {
+                    Zend_Mime_Decode::splitMessage($params['headers'], $this->_headers, $this->_topLines);
+                }
+            }
+            if (isset($params['content'])) {
+                $this->_content = $params['content'];
+            }
+        }
+    }
+
+    /**
+     * Check if part is a multipart message
+     *
+     * @return bool if part is multipart
+     */
+    public function isMultipart()
+    {
+        try {
+            return stripos($this->contentType, 'multipart/') === 0;
+        } catch(Zend_Mail_Exception $e) {
+            return false;
+        }
+    }
+
+
+    /**
+     * Body of part
+     *
+     * If part is multipart the raw content of this part with all sub parts is returned
+     *
+     * @return string body
+     * @throws Zend_Mail_Exception
+     */
+    public function getContent()
+    {
+        if ($this->_content !== null) {
+            return $this->_content;
+        }
+
+        if ($this->_mail) {
+            return $this->_mail->getRawContent($this->_messageNum);
+        } else {
+            /**
+             * @see Zend_Mail_Exception
+             */
+            require_once 'Zend/Mail/Exception.php';
+            throw new Zend_Mail_Exception('no content');
+        }
+    }
+
+    /**
+     * Return size of part
+     *
+     * Quite simple implemented currently (not decoding). Handle with care.
+     *
+     * @return int size
+     */
+    public function getSize() {
+        return strlen($this->getContent());
+    }
+
+
+    /**
+     * Cache content and split in parts if multipart
+     *
+     * @return null
+     * @throws Zend_Mail_Exception
+     */
+    protected function _cacheContent()
+    {
+        // caching content if we can't fetch parts
+        if ($this->_content === null && $this->_mail) {
+            $this->_content = $this->_mail->getRawContent($this->_messageNum);
+        }
+
+        if (!$this->isMultipart()) {
+            return;
+        }
+
+        // split content in parts
+        $boundary = $this->getHeaderField('content-type', 'boundary');
+        if (!$boundary) {
+            /**
+             * @see Zend_Mail_Exception
+             */
+            require_once 'Zend/Mail/Exception.php';
+            throw new Zend_Mail_Exception('no boundary found in content type to split message');
+        }
+        $parts = Zend_Mime_Decode::splitMessageStruct($this->_content, $boundary);
+        if ($parts === null) {
+            return;
+        }
+        $counter = 1;
+        foreach ($parts as $part) {
+            $this->_parts[$counter++] = new self(array('headers' => $part['header'], 'content' => $part['body']));
+        }
+    }
+
+    /**
+     * Get part of multipart message
+     *
+     * @param  int $num number of part starting with 1 for first part
+     * @return Zend_Mail_Part wanted part
+     * @throws Zend_Mail_Exception
+     */
+    public function getPart($num)
+    {
+        if (isset($this->_parts[$num])) {
+            return $this->_parts[$num];
+        }
+
+        if (!$this->_mail && $this->_content === null) {
+            /**
+             * @see Zend_Mail_Exception
+             */
+            require_once 'Zend/Mail/Exception.php';
+            throw new Zend_Mail_Exception('part not found');
+        }
+
+        if ($this->_mail && $this->_mail->hasFetchPart) {
+            // TODO: fetch part
+            // return
+        }
+
+        $this->_cacheContent();
+
+        if (!isset($this->_parts[$num])) {
+            /**
+             * @see Zend_Mail_Exception
+             */
+            require_once 'Zend/Mail/Exception.php';
+            throw new Zend_Mail_Exception('part not found');
+        }
+
+        return $this->_parts[$num];
+    }
+
+    /**
+     * Count parts of a multipart part
+     *
+     * @return int number of sub-parts
+     */
+    public function countParts()
+    {
+        if ($this->_countParts) {
+            return $this->_countParts;
+        }
+
+        $this->_countParts = count($this->_parts);
+        if ($this->_countParts) {
+            return $this->_countParts;
+        }
+
+        if ($this->_mail && $this->_mail->hasFetchPart) {
+            // TODO: fetch part
+            // return
+        }
+
+        $this->_cacheContent();
+
+        $this->_countParts = count($this->_parts);
+        return $this->_countParts;
+    }
+
+
+    /**
+     * Get all headers
+     *
+     * The returned headers are as saved internally. All names are lowercased. The value is a string or an array
+     * if a header with the same name occurs more than once.
+     *
+     * @return array headers as array(name => value)
+     */
+    public function getHeaders()
+    {
+        if ($this->_headers === null) {
+            if (!$this->_mail) {
+                $this->_headers = array();
+            } else {
+                $part = $this->_mail->getRawHeader($this->_messageNum);
+                Zend_Mime_Decode::splitMessage($part, $this->_headers, $null);
+            }
+        }
+
+        return $this->_headers;
+    }
+
+    /**
+     * Get a header in specificed format
+     *
+     * Internally headers that occur more than once are saved as array, all other as string. If $format
+     * is set to string implode is used to concat the values (with Zend_Mime::LINEEND as delim).
+     *
+     * @param  string $name   name of header, matches case-insensitive, but camel-case is replaced with dashes
+     * @param  string $format change type of return value to 'string' or 'array'
+     * @return string|array value of header in wanted or internal format
+     * @throws Zend_Mail_Exception
+     */
+    public function getHeader($name, $format = null)
+    {
+        if ($this->_headers === null) {
+            $this->getHeaders();
+        }
+
+        $lowerName = strtolower($name);
+
+        if ($this->headerExists($name) == false) {
+            $lowerName = strtolower(preg_replace('%([a-z])([A-Z])%', '\1-\2', $name));
+            if($this->headerExists($lowerName) == false) {
+                /**
+                 * @see Zend_Mail_Exception
+                 */
+                require_once 'Zend/Mail/Exception.php';
+                throw new Zend_Mail_Exception("no Header with Name $name or $lowerName found");
+            }
+        }
+        $name = $lowerName;
+
+        $header = $this->_headers[$name];
+
+        switch ($format) {
+            case 'string':
+                if (is_array($header)) {
+                    $header = implode(Zend_Mime::LINEEND, $header);
+                }
+                break;
+            case 'array':
+                $header = (array)$header;
+            default:
+                // do nothing
+        }
+
+        return $header;
+    }
+
+    /**
+     * Check wheater the Mail part has a specific header.
+     *
+     * @param  string $name
+     * @return boolean
+     */
+    public function headerExists($name)
+    {
+        $name = strtolower($name);
+        if(isset($this->_headers[$name])) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Get a specific field from a header like content type or all fields as array
+     *
+     * If the header occurs more than once, only the value from the first header
+     * is returned.
+     *
+     * Throws a Zend_Mail_Exception if the requested header does not exist. If
+     * the specific header field does not exist, returns null.
+     *
+     * @param  string $name       name of header, like in getHeader()
+     * @param  string $wantedPart the wanted part, default is first, if null an array with all parts is returned
+     * @param  string $firstName  key name for the first part
+     * @return string|array wanted part or all parts as array($firstName => firstPart, partname => value)
+     * @throws Zend_Exception, Zend_Mail_Exception
+     */
+    public function getHeaderField($name, $wantedPart = 0, $firstName = 0) {
+        return Zend_Mime_Decode::splitHeaderField(current($this->getHeader($name, 'array')), $wantedPart, $firstName);
+    }
+
+
+    /**
+     * Getter for mail headers - name is matched in lowercase
+     *
+     * This getter is short for Zend_Mail_Part::getHeader($name, 'string')
+     *
+     * @see Zend_Mail_Part::getHeader()
+     *
+     * @param  string $name header name
+     * @return string value of header
+     * @throws Zend_Mail_Exception
+     */
+    public function __get($name)
+    {
+        return $this->getHeader($name, 'string');
+    }
+
+    /**
+     * Isset magic method proxy to hasHeader
+     *
+     * This method is short syntax for Zend_Mail_Part::hasHeader($name);
+     *
+     * @see Zend_Mail_Part::hasHeader
+     *
+     * @param  string
+     * @return boolean
+     */
+    public function __isset($name)
+    {
+        return $this->headerExists($name);
+    }
+
+    /**
+     * magic method to get content of part
+     *
+     * @return string content
+     */
+    public function __toString()
+    {
+        return $this->getContent();
+    }
+
+    /**
+     * implements RecursiveIterator::hasChildren()
+     *
+     * @return bool current element has children/is multipart
+     */
+    public function hasChildren()
+    {
+        $current = $this->current();
+        return $current && $current instanceof Zend_Mail_Part && $current->isMultipart();
+    }
+
+    /**
+     * implements RecursiveIterator::getChildren()
+     *
+     * @return Zend_Mail_Part same as self::current()
+     */
+    public function getChildren()
+    {
+        return $this->current();
+    }
+
+    /**
+     * implements Iterator::valid()
+     *
+     * @return bool check if there's a current element
+     */
+    public function valid()
+    {
+        if ($this->_countParts === null) {
+            $this->countParts();
+        }
+        return $this->_iterationPos && $this->_iterationPos <= $this->_countParts;
+    }
+
+    /**
+     * implements Iterator::next()
+     *
+     * @return null
+     */
+    public function next()
+    {
+        ++$this->_iterationPos;
+    }
+
+    /**
+     * implements Iterator::key()
+     *
+     * @return string key/number of current part
+     */
+    public function key()
+    {
+        return $this->_iterationPos;
+    }
+
+    /**
+     * implements Iterator::current()
+     *
+     * @return Zend_Mail_Part current part
+     */
+    public function current()
+    {
+        return $this->getPart($this->_iterationPos);
+    }
+
+    /**
+     * implements Iterator::rewind()
+     *
+     * @return null
+     */
+    public function rewind()
+    {
+        $this->countParts();
+        $this->_iterationPos = 1;
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Exception.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Exception.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Exception.php	(revision 4456)
@@ -0,0 +1,96 @@
+<?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
+ * @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 23484 2010-12-10 03:57:59Z mjh_ca $
+ */
+
+/**
+* @category   Zend
+* @package    Zend
+* @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_Exception extends Exception
+{
+    /**
+     * @var null|Exception
+     */
+    private $_previous = null;
+
+    /**
+     * Construct the exception
+     *
+     * @param  string $msg
+     * @param  int $code
+     * @param  Exception $previous
+     * @return void
+     */
+    public function __construct($msg = '', $code = 0, Exception $previous = null)
+    {
+        if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+            parent::__construct($msg, (int) $code);
+            $this->_previous = $previous;
+        } else {
+            parent::__construct($msg, (int) $code, $previous);
+        }
+    }
+
+    /**
+     * Overloading
+     *
+     * For PHP < 5.3.0, provides access to the getPrevious() method.
+     *
+     * @param  string $method
+     * @param  array $args
+     * @return mixed
+     */
+    public function __call($method, array $args)
+    {
+        if ('getprevious' == strtolower($method)) {
+            return $this->_getPrevious();
+        }
+        return null;
+    }
+
+    /**
+     * String representation of the exception
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+            if (null !== ($e = $this->getPrevious())) {
+                return $e->__toString()
+                       . "\n\nNext "
+                       . parent::__toString();
+            }
+        }
+        return parent::__toString();
+    }
+
+    /**
+     * Returns previous Exception
+     *
+     * @return Exception|null
+     */
+    protected function _getPrevious()
+    {
+        return $this->_previous;
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Loader/Autoloader/Interface.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Loader/Autoloader/Interface.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Loader/Autoloader/Interface.php	(revision 4456)
@@ -0,0 +1,43 @@
+<?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_Loader
+ * @subpackage Autoloader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @version    $Id: Interface.php 22913 2010-08-29 00:28:02Z ramon $
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * Autoloader interface
+ *
+ * @package    Zend_Loader
+ * @subpackage Autoloader
+ * @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_Loader_Autoloader_Interface
+{
+    /**
+     * Autoload a class
+     *
+     * @abstract
+     * @param   string $class
+     * @return  mixed
+     *          False [if unable to load $class]
+     *          get_class($class) [if $class is successfully loaded]
+     */
+    public function autoload($class);
+}
Index: sandbox/2.2.0.2/library/Zend/Loader/Autoloader/Resource.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Loader/Autoloader/Resource.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Loader/Autoloader/Resource.php	(revision 4456)
@@ -0,0 +1,467 @@
+<?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_Loader
+ * @subpackage Autoloader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @version    $Id: Resource.php 23568 2010-12-20 08:13:20Z mjh_ca $
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/** Zend_Loader_Autoloader_Interface */
+require_once 'Zend/Loader/Autoloader/Interface.php';
+
+/**
+ * Resource loader
+ *
+ * @uses       Zend_Loader_Autoloader_Interface
+ * @package    Zend_Loader
+ * @subpackage Autoloader
+ * @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_Loader_Autoloader_Resource implements Zend_Loader_Autoloader_Interface
+{
+    /**
+     * @var string Base path to resource classes
+     */
+    protected $_basePath;
+
+    /**
+     * @var array Components handled within this resource
+     */
+    protected $_components = array();
+
+    /**
+     * @var string Default resource/component to use when using object registry
+     */
+    protected $_defaultResourceType;
+
+    /**
+     * @var string Namespace of classes within this resource
+     */
+    protected $_namespace;
+
+    /**
+     * @var array Available resource types handled by this resource autoloader
+     */
+    protected $_resourceTypes = array();
+
+    /**
+     * Constructor
+     *
+     * @param  array|Zend_Config $options Configuration options for resource autoloader
+     * @return void
+     */
+    public function __construct($options)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        }
+        if (!is_array($options)) {
+            require_once 'Zend/Loader/Exception.php';
+            throw new Zend_Loader_Exception('Options must be passed to resource loader constructor');
+        }
+
+        $this->setOptions($options);
+
+        $namespace = $this->getNamespace();
+        if ((null === $namespace)
+            || (null === $this->getBasePath())
+        ) {
+            require_once 'Zend/Loader/Exception.php';
+            throw new Zend_Loader_Exception('Resource loader requires both a namespace and a base path for initialization');
+        }
+
+        if (!empty($namespace)) {
+            $namespace .= '_';
+        }
+        require_once 'Zend/Loader/Autoloader.php';
+        Zend_Loader_Autoloader::getInstance()->unshiftAutoloader($this, $namespace);
+    }
+
+    /**
+     * Overloading: methods
+     *
+     * Allow retrieving concrete resource object instances using 'get<Resourcename>()'
+     * syntax. Example:
+     * <code>
+     * $loader = new Zend_Loader_Autoloader_Resource(array(
+     *     'namespace' => 'Stuff_',
+     *     'basePath'  => '/path/to/some/stuff',
+     * ))
+     * $loader->addResourceType('Model', 'models', 'Model');
+     *
+     * $foo = $loader->getModel('Foo'); // get instance of Stuff_Model_Foo class
+     * </code>
+     *
+     * @param  string $method
+     * @param  array $args
+     * @return mixed
+     * @throws Zend_Loader_Exception if method not beginning with 'get' or not matching a valid resource type is called
+     */
+    public function __call($method, $args)
+    {
+        if ('get' == substr($method, 0, 3)) {
+            $type  = strtolower(substr($method, 3));
+            if (!$this->hasResourceType($type)) {
+                require_once 'Zend/Loader/Exception.php';
+                throw new Zend_Loader_Exception("Invalid resource type $type; cannot load resource");
+            }
+            if (empty($args)) {
+                require_once 'Zend/Loader/Exception.php';
+                throw new Zend_Loader_Exception("Cannot load resources; no resource specified");
+            }
+            $resource = array_shift($args);
+            return $this->load($resource, $type);
+        }
+
+        require_once 'Zend/Loader/Exception.php';
+        throw new Zend_Loader_Exception("Method '$method' is not supported");
+    }
+
+    /**
+     * Helper method to calculate the correct class path
+     *
+     * @param string $class
+     * @return False if not matched other wise the correct path
+     */
+    public function getClassPath($class)
+    {
+        $segments          = explode('_', $class);
+        $namespaceTopLevel = $this->getNamespace();
+        $namespace         = '';
+
+        if (!empty($namespaceTopLevel)) {
+            $namespace = array_shift($segments);
+            if ($namespace != $namespaceTopLevel) {
+                // wrong prefix? we're done
+                return false;
+            }
+        }
+
+        if (count($segments) < 2) {
+            // assumes all resources have a component and class name, minimum
+            return false;
+        }
+
+        $final     = array_pop($segments);
+        $component = $namespace;
+        $lastMatch = false;
+        do {
+            $segment    = array_shift($segments);
+            $component .= empty($component) ? $segment : '_' . $segment;
+            if (isset($this->_components[$component])) {
+                $lastMatch = $component;
+            }
+        } while (count($segments));
+
+        if (!$lastMatch) {
+            return false;
+        }
+
+        $final = substr($class, strlen($lastMatch) + 1);
+        $path = $this->_components[$lastMatch];
+        $classPath = $path . '/' . str_replace('_', '/', $final) . '.php';
+
+        if (Zend_Loader::isReadable($classPath)) {
+            return $classPath;
+        }
+
+        return false;
+    }
+
+    /**
+     * Attempt to autoload a class
+     *
+     * @param  string $class
+     * @return mixed False if not matched, otherwise result if include operation
+     */
+    public function autoload($class)
+    {
+        $classPath = $this->getClassPath($class);
+        if (false !== $classPath) {
+            return include $classPath;
+        }
+        return false;
+    }
+
+    /**
+     * Set class state from options
+     *
+     * @param  array $options
+     * @return Zend_Loader_Autoloader_Resource
+     */
+    public function setOptions(array $options)
+    {
+        // Set namespace first, see ZF-10836
+        if (isset($options['namespace'])) {
+            $this->setNamespace($options['namespace']);
+            unset($options['namespace']);
+        }
+
+        $methods = get_class_methods($this);
+        foreach ($options as $key => $value) {
+            $method = 'set' . ucfirst($key);
+            if (in_array($method, $methods)) {
+                $this->$method($value);
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Set namespace that this autoloader handles
+     *
+     * @param  string $namespace
+     * @return Zend_Loader_Autoloader_Resource
+     */
+    public function setNamespace($namespace)
+    {
+        $this->_namespace = rtrim((string) $namespace, '_');
+        return $this;
+    }
+
+    /**
+     * Get namespace this autoloader handles
+     *
+     * @return string
+     */
+    public function getNamespace()
+    {
+        return $this->_namespace;
+    }
+
+    /**
+     * Set base path for this set of resources
+     *
+     * @param  string $path
+     * @return Zend_Loader_Autoloader_Resource
+     */
+    public function setBasePath($path)
+    {
+        $this->_basePath = (string) $path;
+        return $this;
+    }
+
+    /**
+     * Get base path to this set of resources
+     *
+     * @return string
+     */
+    public function getBasePath()
+    {
+        return $this->_basePath;
+    }
+
+    /**
+     * Add resource type
+     *
+     * @param  string $type identifier for the resource type being loaded
+     * @param  string $path path relative to resource base path containing the resource types
+     * @param  null|string $namespace sub-component namespace to append to base namespace that qualifies this resource type
+     * @return Zend_Loader_Autoloader_Resource
+     */
+    public function addResourceType($type, $path, $namespace = null)
+    {
+        $type = strtolower($type);
+        if (!isset($this->_resourceTypes[$type])) {
+            if (null === $namespace) {
+                require_once 'Zend/Loader/Exception.php';
+                throw new Zend_Loader_Exception('Initial definition of a resource type must include a namespace');
+            }
+            $namespaceTopLevel = $this->getNamespace();
+            $namespace = ucfirst(trim($namespace, '_'));
+            $this->_resourceTypes[$type] = array(
+                'namespace' => empty($namespaceTopLevel) ? $namespace : $namespaceTopLevel . '_' . $namespace,
+            );
+        }
+        if (!is_string($path)) {
+            require_once 'Zend/Loader/Exception.php';
+            throw new Zend_Loader_Exception('Invalid path specification provided; must be string');
+        }
+        $this->_resourceTypes[$type]['path'] = $this->getBasePath() . '/' . rtrim($path, '\/');
+
+        $component = $this->_resourceTypes[$type]['namespace'];
+        $this->_components[$component] = $this->_resourceTypes[$type]['path'];
+        return $this;
+    }
+
+    /**
+     * Add multiple resources at once
+     *
+     * $types should be an associative array of resource type => specification
+     * pairs. Each specification should be an associative array containing
+     * minimally the 'path' key (specifying the path relative to the resource
+     * base path) and optionally the 'namespace' key (indicating the subcomponent
+     * namespace to append to the resource namespace).
+     *
+     * As an example:
+     * <code>
+     * $loader->addResourceTypes(array(
+     *     'model' => array(
+     *         'path'      => 'models',
+     *         'namespace' => 'Model',
+     *     ),
+     *     'form' => array(
+     *         'path'      => 'forms',
+     *         'namespace' => 'Form',
+     *     ),
+     * ));
+     * </code>
+     *
+     * @param  array $types
+     * @return Zend_Loader_Autoloader_Resource
+     */
+    public function addResourceTypes(array $types)
+    {
+        foreach ($types as $type => $spec) {
+            if (!is_array($spec)) {
+                require_once 'Zend/Loader/Exception.php';
+                throw new Zend_Loader_Exception('addResourceTypes() expects an array of arrays');
+            }
+            if (!isset($spec['path'])) {
+                require_once 'Zend/Loader/Exception.php';
+                throw new Zend_Loader_Exception('addResourceTypes() expects each array to include a paths element');
+            }
+            $paths  = $spec['path'];
+            $namespace = null;
+            if (isset($spec['namespace'])) {
+                $namespace = $spec['namespace'];
+            }
+            $this->addResourceType($type, $paths, $namespace);
+        }
+        return $this;
+    }
+
+    /**
+     * Overwrite existing and set multiple resource types at once
+     *
+     * @see    Zend_Loader_Autoloader_Resource::addResourceTypes()
+     * @param  array $types
+     * @return Zend_Loader_Autoloader_Resource
+     */
+    public function setResourceTypes(array $types)
+    {
+        $this->clearResourceTypes();
+        return $this->addResourceTypes($types);
+    }
+
+    /**
+     * Retrieve resource type mappings
+     *
+     * @return array
+     */
+    public function getResourceTypes()
+    {
+        return $this->_resourceTypes;
+    }
+
+    /**
+     * Is the requested resource type defined?
+     *
+     * @param  string $type
+     * @return bool
+     */
+    public function hasResourceType($type)
+    {
+        return isset($this->_resourceTypes[$type]);
+    }
+
+    /**
+     * Remove the requested resource type
+     *
+     * @param  string $type
+     * @return Zend_Loader_Autoloader_Resource
+     */
+    public function removeResourceType($type)
+    {
+        if ($this->hasResourceType($type)) {
+            $namespace = $this->_resourceTypes[$type]['namespace'];
+            unset($this->_components[$namespace]);
+            unset($this->_resourceTypes[$type]);
+        }
+        return $this;
+    }
+
+    /**
+     * Clear all resource types
+     *
+     * @return Zend_Loader_Autoloader_Resource
+     */
+    public function clearResourceTypes()
+    {
+        $this->_resourceTypes = array();
+        $this->_components    = array();
+        return $this;
+    }
+
+    /**
+     * Set default resource type to use when calling load()
+     *
+     * @param  string $type
+     * @return Zend_Loader_Autoloader_Resource
+     */
+    public function setDefaultResourceType($type)
+    {
+        if ($this->hasResourceType($type)) {
+            $this->_defaultResourceType = $type;
+        }
+        return $this;
+    }
+
+    /**
+     * Get default resource type to use when calling load()
+     *
+     * @return string|null
+     */
+    public function getDefaultResourceType()
+    {
+        return $this->_defaultResourceType;
+    }
+
+    /**
+     * Object registry and factory
+     *
+     * Loads the requested resource of type $type (or uses the default resource
+     * type if none provided). If the resource has been loaded previously,
+     * returns the previous instance; otherwise, instantiates it.
+     *
+     * @param  string $resource
+     * @param  string $type
+     * @return object
+     * @throws Zend_Loader_Exception if resource type not specified or invalid
+     */
+    public function load($resource, $type = null)
+    {
+        if (null === $type) {
+            $type = $this->getDefaultResourceType();
+            if (empty($type)) {
+                require_once 'Zend/Loader/Exception.php';
+                throw new Zend_Loader_Exception('No resource type specified');
+            }
+        }
+        if (!$this->hasResourceType($type)) {
+            require_once 'Zend/Loader/Exception.php';
+            throw new Zend_Loader_Exception('Invalid resource type specified');
+        }
+        $namespace = $this->_resourceTypes[$type]['namespace'];
+        $class     = $namespace . '_' . ucfirst($resource);
+        if (!isset($this->_resources[$class])) {
+            $this->_resources[$class] = new $class;
+        }
+        return $this->_resources[$class];
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Mime/Exception.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Mime/Exception.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Mime/Exception.php	(revision 4456)
@@ -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_Mime
+ * @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 $
+ */
+
+
+/**
+ * Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mime
+ * @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_Mime_Exception extends Zend_Exception
+{}
+
Index: sandbox/2.2.0.2/library/Zend/Mime/Decode.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Mime/Decode.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Mime/Decode.php	(revision 4456)
@@ -0,0 +1,244 @@
+<?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_Mime
+ * @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: Decode.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Mime
+ */
+require_once 'Zend/Mime.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Mime
+ * @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_Mime_Decode
+{
+    /**
+     * Explode MIME multipart string into seperate parts
+     *
+     * Parts consist of the header and the body of each MIME part.
+     *
+     * @param  string $body     raw body of message
+     * @param  string $boundary boundary as found in content-type
+     * @return array parts with content of each part, empty if no parts found
+     * @throws Zend_Exception
+     */
+    public static function splitMime($body, $boundary)
+    {
+        // TODO: we're ignoring \r for now - is this function fast enough and is it safe to asume noone needs \r?
+        $body = str_replace("\r", '', $body);
+
+        $start = 0;
+        $res = array();
+        // find every mime part limiter and cut out the
+        // string before it.
+        // the part before the first boundary string is discarded:
+        $p = strpos($body, '--' . $boundary . "\n", $start);
+        if ($p === false) {
+            // no parts found!
+            return array();
+        }
+
+        // position after first boundary line
+        $start = $p + 3 + strlen($boundary);
+
+        while (($p = strpos($body, '--' . $boundary . "\n", $start)) !== false) {
+            $res[] = substr($body, $start, $p-$start);
+            $start = $p + 3 + strlen($boundary);
+        }
+
+        // no more parts, find end boundary
+        $p = strpos($body, '--' . $boundary . '--', $start);
+        if ($p===false) {
+            throw new Zend_Exception('Not a valid Mime Message: End Missing');
+        }
+
+        // the remaining part also needs to be parsed:
+        $res[] = substr($body, $start, $p-$start);
+        return $res;
+    }
+
+    /**
+     * decodes a mime encoded String and returns a
+     * struct of parts with header and body
+     *
+     * @param  string $message  raw message content
+     * @param  string $boundary boundary as found in content-type
+     * @param  string $EOL EOL string; defaults to {@link Zend_Mime::LINEEND}
+     * @return array|null parts as array('header' => array(name => value), 'body' => content), null if no parts found
+     * @throws Zend_Exception
+     */
+    public static function splitMessageStruct($message, $boundary, $EOL = Zend_Mime::LINEEND)
+    {
+        $parts = self::splitMime($message, $boundary);
+        if (count($parts) <= 0) {
+            return null;
+        }
+        $result = array();
+        foreach ($parts as $part) {
+            self::splitMessage($part, $headers, $body, $EOL);
+            $result[] = array('header' => $headers,
+                              'body'   => $body    );
+        }
+        return $result;
+    }
+
+    /**
+     * split a message in header and body part, if no header or an
+     * invalid header is found $headers is empty
+     *
+     * The charset of the returned headers depend on your iconv settings.
+     *
+     * @param  string $message raw message with header and optional content
+     * @param  array  $headers output param, array with headers as array(name => value)
+     * @param  string $body    output param, content of message
+     * @param  string $EOL EOL string; defaults to {@link Zend_Mime::LINEEND}
+     * @return null
+     */
+    public static function splitMessage($message, &$headers, &$body, $EOL = Zend_Mime::LINEEND)
+    {
+        // check for valid header at first line
+        $firstline = strtok($message, "\n");
+        if (!preg_match('%^[^\s]+[^:]*:%', $firstline)) {
+            $headers = array();
+            // TODO: we're ignoring \r for now - is this function fast enough and is it safe to asume noone needs \r?
+            $body = str_replace(array("\r", "\n"), array('', $EOL), $message);
+            return;
+        }
+
+        // find an empty line between headers and body
+        // default is set new line
+        if (strpos($message, $EOL . $EOL)) {
+            list($headers, $body) = explode($EOL . $EOL, $message, 2);
+        // next is the standard new line
+        } else if ($EOL != "\r\n" && strpos($message, "\r\n\r\n")) {
+            list($headers, $body) = explode("\r\n\r\n", $message, 2);
+        // next is the other "standard" new line
+        } else if ($EOL != "\n" && strpos($message, "\n\n")) {
+            list($headers, $body) = explode("\n\n", $message, 2);
+        // at last resort find anything that looks like a new line
+        } else {
+            @list($headers, $body) = @preg_split("%([\r\n]+)\\1%U", $message, 2);
+        }
+
+        $headers = iconv_mime_decode_headers($headers, ICONV_MIME_DECODE_CONTINUE_ON_ERROR);
+
+        if ($headers === false ) {
+            // an error occurs during the decoding
+            return;
+        }
+
+        // normalize header names
+        foreach ($headers as $name => $header) {
+            $lower = strtolower($name);
+            if ($lower == $name) {
+                continue;
+            }
+            unset($headers[$name]);
+            if (!isset($headers[$lower])) {
+                $headers[$lower] = $header;
+                continue;
+            }
+            if (is_array($headers[$lower])) {
+                $headers[$lower][] = $header;
+                continue;
+            }
+            $headers[$lower] = array($headers[$lower], $header);
+        }
+    }
+
+    /**
+     * split a content type in its different parts
+     *
+     * @param  string $type       content-type
+     * @param  string $wantedPart the wanted part, else an array with all parts is returned
+     * @return string|array wanted part or all parts as array('type' => content-type, partname => value)
+     */
+    public static function splitContentType($type, $wantedPart = null)
+    {
+        return self::splitHeaderField($type, $wantedPart, 'type');
+    }
+
+    /**
+     * split a header field like content type in its different parts
+     *
+     * @param  string $type       header field
+     * @param  string $wantedPart the wanted part, else an array with all parts is returned
+     * @param  string $firstName  key name for the first part
+     * @return string|array wanted part or all parts as array($firstName => firstPart, partname => value)
+     * @throws Zend_Exception
+     */
+    public static function splitHeaderField($field, $wantedPart = null, $firstName = 0)
+    {
+        $wantedPart = strtolower($wantedPart);
+        $firstName = strtolower($firstName);
+
+        // special case - a bit optimized
+        if ($firstName === $wantedPart) {
+            $field = strtok($field, ';');
+            return $field[0] == '"' ? substr($field, 1, -1) : $field;
+        }
+
+        $field = $firstName . '=' . $field;
+        if (!preg_match_all('%([^=\s]+)\s*=\s*("[^"]+"|[^;]+)(;\s*|$)%', $field, $matches)) {
+            throw new Zend_Exception('not a valid header field');
+        }
+
+        if ($wantedPart) {
+            foreach ($matches[1] as $key => $name) {
+                if (strcasecmp($name, $wantedPart)) {
+                    continue;
+                }
+                if ($matches[2][$key][0] != '"') {
+                    return $matches[2][$key];
+                }
+                return substr($matches[2][$key], 1, -1);
+            }
+            return null;
+        }
+
+        $split = array();
+        foreach ($matches[1] as $key => $name) {
+            $name = strtolower($name);
+            if ($matches[2][$key][0] == '"') {
+                $split[$name] = substr($matches[2][$key], 1, -1);
+            } else {
+                $split[$name] = $matches[2][$key];
+            }
+        }
+
+        return $split;
+    }
+
+    /**
+     * decode a quoted printable encoded string
+     *
+     * The charset of the returned string depends on your iconv settings.
+     *
+     * @param  string encoded string
+     * @return string decoded string
+     */
+    public static function decodeQuotedPrintable($string)
+    {
+        return iconv_mime_decode($string, ICONV_MIME_DECODE_CONTINUE_ON_ERROR);
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Mime/Message.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Mime/Message.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Mime/Message.php	(revision 4456)
@@ -0,0 +1,286 @@
+<?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_Mime
+ * @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: Message.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+
+/**
+ * Zend_Mime
+ */
+require_once 'Zend/Mime.php';
+
+/**
+ * Zend_Mime_Part
+ */
+require_once 'Zend/Mime/Part.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Mime
+ * @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_Mime_Message
+{
+
+    protected $_parts = array();
+    protected $_mime = null;
+
+    /**
+     * Returns the list of all Zend_Mime_Parts in the message
+     *
+     * @return array of Zend_Mime_Part
+     */
+    public function getParts()
+    {
+        return $this->_parts;
+    }
+
+    /**
+     * Sets the given array of Zend_Mime_Parts as the array for the message
+     *
+     * @param array $parts
+     */
+    public function setParts($parts)
+    {
+        $this->_parts = $parts;
+    }
+
+    /**
+     * Append a new Zend_Mime_Part to the current message
+     *
+     * @param Zend_Mime_Part $part
+     */
+    public function addPart(Zend_Mime_Part $part)
+    {
+        /**
+         * @todo check for duplicate object handle
+         */
+        $this->_parts[] = $part;
+    }
+
+    /**
+     * Check if message needs to be sent as multipart
+     * MIME message or if it has only one part.
+     *
+     * @return boolean
+     */
+    public function isMultiPart()
+    {
+        return (count($this->_parts) > 1);
+    }
+
+    /**
+     * Set Zend_Mime object for the message
+     *
+     * This can be used to set the boundary specifically or to use a subclass of
+     * Zend_Mime for generating the boundary.
+     *
+     * @param Zend_Mime $mime
+     */
+    public function setMime(Zend_Mime $mime)
+    {
+        $this->_mime = $mime;
+    }
+
+    /**
+     * Returns the Zend_Mime object in use by the message
+     *
+     * If the object was not present, it is created and returned. Can be used to
+     * determine the boundary used in this message.
+     *
+     * @return Zend_Mime
+     */
+    public function getMime()
+    {
+        if ($this->_mime === null) {
+            $this->_mime = new Zend_Mime();
+        }
+
+        return $this->_mime;
+    }
+
+    /**
+     * Generate MIME-compliant message from the current configuration
+     *
+     * This can be a multipart message if more than one MIME part was added. If
+     * only one part is present, the content of this part is returned. If no
+     * part had been added, an empty string is returned.
+     *
+     * Parts are seperated by the mime boundary as defined in Zend_Mime. If
+     * {@link setMime()} has been called before this method, the Zend_Mime
+     * object set by this call will be used. Otherwise, a new Zend_Mime object
+     * is generated and used.
+     *
+     * @param string $EOL EOL string; defaults to {@link Zend_Mime::LINEEND}
+     * @return string
+     */
+    public function generateMessage($EOL = Zend_Mime::LINEEND)
+    {
+        if (! $this->isMultiPart()) {
+            $body = array_shift($this->_parts);
+            $body = $body->getContent($EOL);
+        } else {
+            $mime = $this->getMime();
+
+            $boundaryLine = $mime->boundaryLine($EOL);
+            $body = 'This is a message in Mime Format.  If you see this, '
+                  . "your mail reader does not support this format." . $EOL;
+
+            foreach (array_keys($this->_parts) as $p) {
+                $body .= $boundaryLine
+                       . $this->getPartHeaders($p, $EOL)
+                       . $EOL
+                       . $this->getPartContent($p, $EOL);
+            }
+
+            $body .= $mime->mimeEnd($EOL);
+        }
+
+        return trim($body);
+    }
+
+    /**
+     * Get the headers of a given part as an array
+     *
+     * @param int $partnum
+     * @return array
+     */
+    public function getPartHeadersArray($partnum)
+    {
+        return $this->_parts[$partnum]->getHeadersArray();
+    }
+
+    /**
+     * Get the headers of a given part as a string
+     *
+     * @param int $partnum
+     * @return string
+     */
+    public function getPartHeaders($partnum, $EOL = Zend_Mime::LINEEND)
+    {
+        return $this->_parts[$partnum]->getHeaders($EOL);
+    }
+
+    /**
+     * Get the (encoded) content of a given part as a string
+     *
+     * @param int $partnum
+     * @return string
+     */
+    public function getPartContent($partnum, $EOL = Zend_Mime::LINEEND)
+    {
+        return $this->_parts[$partnum]->getContent($EOL);
+    }
+
+    /**
+     * Explode MIME multipart string into seperate parts
+     *
+     * Parts consist of the header and the body of each MIME part.
+     *
+     * @param string $body
+     * @param string $boundary
+     * @return array
+     */
+    protected static function _disassembleMime($body, $boundary)
+    {
+        $start = 0;
+        $res = array();
+        // find every mime part limiter and cut out the
+        // string before it.
+        // the part before the first boundary string is discarded:
+        $p = strpos($body, '--'.$boundary."\n", $start);
+        if ($p === false) {
+            // no parts found!
+            return array();
+        }
+
+        // position after first boundary line
+        $start = $p + 3 + strlen($boundary);
+
+        while (($p = strpos($body, '--' . $boundary . "\n", $start)) !== false) {
+            $res[] = substr($body, $start, $p-$start);
+            $start = $p + 3 + strlen($boundary);
+        }
+
+        // no more parts, find end boundary
+        $p = strpos($body, '--' . $boundary . '--', $start);
+        if ($p===false) {
+            throw new Zend_Exception('Not a valid Mime Message: End Missing');
+        }
+
+        // the remaining part also needs to be parsed:
+        $res[] = substr($body, $start, $p-$start);
+        return $res;
+    }
+
+    /**
+     * Decodes a MIME encoded string and returns a Zend_Mime_Message object with
+     * all the MIME parts set according to the given string
+     *
+     * @param string $message
+     * @param string $boundary
+     * @param string $EOL EOL string; defaults to {@link Zend_Mime::LINEEND}
+     * @return Zend_Mime_Message
+     */
+    public static function createFromMessage($message, $boundary, $EOL = Zend_Mime::LINEEND)
+    {
+        require_once 'Zend/Mime/Decode.php';
+        $parts = Zend_Mime_Decode::splitMessageStruct($message, $boundary, $EOL);
+
+        $res = new self();
+        foreach ($parts as $part) {
+            // now we build a new MimePart for the current Message Part:
+            $newPart = new Zend_Mime_Part($part['body']);
+            foreach ($part['header'] as $key => $value) {
+                /**
+                 * @todo check for characterset and filename
+                 */
+                switch(strtolower($key)) {
+                    case 'content-type':
+                        $newPart->type = $value;
+                        break;
+                    case 'content-transfer-encoding':
+                        $newPart->encoding = $value;
+                        break;
+                    case 'content-id':
+                        $newPart->id = trim($value,'<>');
+                        break;
+                    case 'content-disposition':
+                        $newPart->disposition = $value;
+                        break;
+                    case 'content-description':
+                        $newPart->description = $value;
+                        break;
+                    case 'content-location':
+                        $newPart->location = $value;
+                        break;
+                    case 'content-language':
+                        $newPart->language = $value;
+                        break;
+                    default:
+                        throw new Zend_Exception('Unknown header ignored for MimePart:' . $key);
+                }
+            }
+            $res->addPart($newPart);
+        }
+        return $res;
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Mime/Part.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Mime/Part.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Mime/Part.php	(revision 4456)
@@ -0,0 +1,217 @@
+<?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_Mime
+ * @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: Part.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Zend_Mime
+ */
+require_once 'Zend/Mime.php';
+
+/**
+ * Class representing a MIME part.
+ *
+ * @category   Zend
+ * @package    Zend_Mime
+ * @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_Mime_Part {
+
+    public $type = Zend_Mime::TYPE_OCTETSTREAM;
+    public $encoding = Zend_Mime::ENCODING_8BIT;
+    public $id;
+    public $disposition;
+    public $filename;
+    public $description;
+    public $charset;
+    public $boundary;
+    public $location;
+    public $language;
+    protected $_content;
+    protected $_isStream = false;
+
+
+    /**
+     * create a new Mime Part.
+     * The (unencoded) content of the Part as passed
+     * as a string or stream
+     *
+     * @param mixed $content  String or Stream containing the content
+     */
+    public function __construct($content)
+    {
+        $this->_content = $content;
+        if (is_resource($content)) {
+            $this->_isStream = true;
+        }
+    }
+
+    /**
+     * @todo setters/getters
+     * @todo error checking for setting $type
+     * @todo error checking for setting $encoding
+     */
+
+    /**
+     * check if this part can be read as a stream.
+     * if true, getEncodedStream can be called, otherwise
+     * only getContent can be used to fetch the encoded
+     * content of the part
+     *
+     * @return bool
+     */
+    public function isStream()
+    {
+      return $this->_isStream;
+    }
+
+    /**
+     * if this was created with a stream, return a filtered stream for
+     * reading the content. very useful for large file attachments.
+     *
+     * @return stream
+     * @throws Zend_Mime_Exception if not a stream or unable to append filter
+     */
+    public function getEncodedStream()
+    {
+        if (!$this->_isStream) {
+            require_once 'Zend/Mime/Exception.php';
+            throw new Zend_Mime_Exception('Attempt to get a stream from a string part');
+        }
+
+        //stream_filter_remove(); // ??? is that right?
+        switch ($this->encoding) {
+            case Zend_Mime::ENCODING_QUOTEDPRINTABLE:
+                $filter = stream_filter_append(
+                    $this->_content,
+                    'convert.quoted-printable-encode',
+                    STREAM_FILTER_READ,
+                    array(
+                        'line-length'      => 76,
+                        'line-break-chars' => Zend_Mime::LINEEND
+                    )
+                );
+                if (!is_resource($filter)) {
+                    require_once 'Zend/Mime/Exception.php';
+                    throw new Zend_Mime_Exception('Failed to append quoted-printable filter');
+                }
+                break;
+            case Zend_Mime::ENCODING_BASE64:
+                $filter = stream_filter_append(
+                    $this->_content,
+                    'convert.base64-encode',
+                    STREAM_FILTER_READ,
+                    array(
+                        'line-length'      => 76,
+                        'line-break-chars' => Zend_Mime::LINEEND
+                    )
+                );
+                if (!is_resource($filter)) {
+                    require_once 'Zend/Mime/Exception.php';
+                    throw new Zend_Mime_Exception('Failed to append base64 filter');
+                }
+                break;
+            default:
+        }
+        return $this->_content;
+    }
+
+    /**
+     * Get the Content of the current Mime Part in the given encoding.
+     *
+     * @return String
+     */
+    public function getContent($EOL = Zend_Mime::LINEEND)
+    {
+        if ($this->_isStream) {
+            return stream_get_contents($this->getEncodedStream());
+        } else {
+            return Zend_Mime::encode($this->_content, $this->encoding, $EOL);
+        }
+    }
+
+    /**
+     * Create and return the array of headers for this MIME part
+     *
+     * @access public
+     * @return array
+     */
+    public function getHeadersArray($EOL = Zend_Mime::LINEEND)
+    {
+        $headers = array();
+
+        $contentType = $this->type;
+        if ($this->charset) {
+            $contentType .= '; charset=' . $this->charset;
+        }
+
+        if ($this->boundary) {
+            $contentType .= ';' . $EOL
+                          . " boundary=\"" . $this->boundary . '"';
+        }
+
+        $headers[] = array('Content-Type', $contentType);
+
+        if ($this->encoding) {
+            $headers[] = array('Content-Transfer-Encoding', $this->encoding);
+        }
+
+        if ($this->id) {
+            $headers[]  = array('Content-ID', '<' . $this->id . '>');
+        }
+
+        if ($this->disposition) {
+            $disposition = $this->disposition;
+            if ($this->filename) {
+                $disposition .= '; filename="' . $this->filename . '"';
+            }
+            $headers[] = array('Content-Disposition', $disposition);
+        }
+
+        if ($this->description) {
+            $headers[] = array('Content-Description', $this->description);
+        }
+
+        if ($this->location) {
+            $headers[] = array('Content-Location', $this->location);
+        }
+
+        if ($this->language){
+            $headers[] = array('Content-Language', $this->language);
+        }
+
+        return $headers;
+    }
+
+    /**
+     * Return the headers for this part as a string
+     *
+     * @return String
+     */
+    public function getHeaders($EOL = Zend_Mime::LINEEND)
+    {
+        $res = '';
+        foreach ($this->getHeadersArray($EOL) as $header) {
+            $res .= $header[0] . ': ' . $header[1] . $EOL;
+        }
+
+        return $res;
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Validate/Regex.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Validate/Regex.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Validate/Regex.php	(revision 4456)
@@ -0,0 +1,147 @@
+<?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_Validate
+ * @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: Regex.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @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_Validate_Regex extends Zend_Validate_Abstract
+{
+    const INVALID   = 'regexInvalid';
+    const NOT_MATCH = 'regexNotMatch';
+    const ERROROUS  = 'regexErrorous';
+
+    /**
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::INVALID   => "Invalid type given. String, integer or float expected",
+        self::NOT_MATCH => "'%value%' does not match against pattern '%pattern%'",
+        self::ERROROUS  => "There was an internal error while using the pattern '%pattern%'",
+    );
+
+    /**
+     * @var array
+     */
+    protected $_messageVariables = array(
+        'pattern' => '_pattern'
+    );
+
+    /**
+     * Regular expression pattern
+     *
+     * @var string
+     */
+    protected $_pattern;
+
+    /**
+     * Sets validator options
+     *
+     * @param  string|Zend_Config $pattern
+     * @throws Zend_Validate_Exception On missing 'pattern' parameter
+     * @return void
+     */
+    public function __construct($pattern)
+    {
+        if ($pattern instanceof Zend_Config) {
+            $pattern = $pattern->toArray();
+        }
+
+        if (is_array($pattern)) {
+            if (array_key_exists('pattern', $pattern)) {
+                $pattern = $pattern['pattern'];
+            } else {
+                require_once 'Zend/Validate/Exception.php';
+                throw new Zend_Validate_Exception("Missing option 'pattern'");
+            }
+        }
+
+        $this->setPattern($pattern);
+    }
+
+    /**
+     * Returns the pattern option
+     *
+     * @return string
+     */
+    public function getPattern()
+    {
+        return $this->_pattern;
+    }
+
+    /**
+     * Sets the pattern option
+     *
+     * @param  string $pattern
+     * @throws Zend_Validate_Exception if there is a fatal error in pattern matching
+     * @return Zend_Validate_Regex Provides a fluent interface
+     */
+    public function setPattern($pattern)
+    {
+        $this->_pattern = (string) $pattern;
+        $status         = @preg_match($this->_pattern, "Test");
+
+        if (false === $status) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("Internal error while using the pattern '$this->_pattern'");
+        }
+
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value matches against the pattern option
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value) && !is_int($value) && !is_float($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue($value);
+
+        $status = @preg_match($this->_pattern, $value);
+        if (false === $status) {
+            $this->_error(self::ERROROUS);
+            return false;
+        }
+
+        if (!$status) {
+            $this->_error(self::NOT_MATCH);
+            return false;
+        }
+
+        return true;
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Validate/Isbn.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Validate/Isbn.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Validate/Isbn.php	(revision 4456)
@@ -0,0 +1,279 @@
+<?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_Validate
+ * @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: Isbn.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @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_Validate_Isbn extends Zend_Validate_Abstract
+{
+    const AUTO    = 'auto';
+    const ISBN10  = '10';
+    const ISBN13  = '13';
+    const INVALID = 'isbnInvalid';
+    const NO_ISBN = 'isbnNoIsbn';
+
+    /**
+     * Validation failure message template definitions.
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::INVALID => "Invalid type given. String or integer expected",
+        self::NO_ISBN => "'%value%' is no valid ISBN number",
+    );
+
+    /**
+     * Allowed type.
+     *
+     * @var string
+     */
+    protected $_type = self::AUTO;
+
+    /**
+     * Separator character.
+     *
+     * @var string
+     */
+    protected $_separator = '';
+
+    /**
+     * Set up options.
+     *
+     * @param  Zend_Config|array $options
+     * @throws Zend_Validate_Exception When $options is not valid
+     * @return void
+     */
+    public function __construct($options = array())
+    {
+        // prepare options
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        }
+        if (!is_array($options)) {
+            /**
+             * @see Zend_Validate_Exception
+             */
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Invalid options provided.');
+        }
+
+        // set type
+        if (array_key_exists('type', $options)) {
+            $this->setType($options['type']);
+        }
+
+        // set separator
+        if (array_key_exists('separator', $options)) {
+            $this->setSeparator($options['separator']);
+        }
+    }
+
+    /**
+     * Detect input format.
+     *
+     * @return string
+     */
+    protected function _detectFormat()
+    {
+        // prepare separator and pattern list
+        $sep      = quotemeta($this->_separator);
+        $patterns = array();
+        $lengths  = array();
+
+        // check for ISBN-10
+        if ($this->_type == self::ISBN10 || $this->_type == self::AUTO) {
+            if (empty($sep)) {
+                $pattern = '/^[0-9]{9}[0-9X]{1}$/';
+                $length  = 10;
+            } else {
+                $pattern = "/^[0-9]{1,7}[{$sep}]{1}[0-9]{1,7}[{$sep}]{1}[0-9]{1,7}[{$sep}]{1}[0-9X]{1}$/";
+                $length  = 13;
+            }
+
+            $patterns[$pattern] = self::ISBN10;
+            $lengths[$pattern]  = $length;
+        }
+
+        // check for ISBN-13
+        if ($this->_type == self::ISBN13 || $this->_type == self::AUTO) {
+            if (empty($sep)) {
+                $pattern = '/^[0-9]{13}$/';
+                $length  = 13;
+            } else {
+                $pattern = "/^[0-9]{1,9}[{$sep}]{1}[0-9]{1,5}[{$sep}]{1}[0-9]{1,9}[{$sep}]{1}[0-9]{1,9}[{$sep}]{1}[0-9]{1}$/";
+                $length  = 17;
+            }
+
+            $patterns[$pattern] = self::ISBN13;
+            $lengths[$pattern]  = $length;
+        }
+
+        // check pattern list
+        foreach ($patterns as $pattern => $type) {
+            if ((strlen($this->_value) == $lengths[$pattern]) && preg_match($pattern, $this->_value)) {
+                return $type;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface.
+     *
+     * Returns true if and only if $value is a valid ISBN.
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value) && !is_int($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $value = (string) $value;
+        $this->_setValue($value);
+
+        switch ($this->_detectFormat()) {
+            case self::ISBN10:
+                // sum
+                $isbn10 = str_replace($this->_separator, '', $value);
+                $sum    = 0;
+                for ($i = 0; $i < 9; $i++) {
+                    $sum += (10 - $i) * $isbn10{$i};
+                }
+
+                // checksum
+                $checksum = 11 - ($sum % 11);
+                if ($checksum == 11) {
+                    $checksum = '0';
+                } elseif ($checksum == 10) {
+                    $checksum = 'X';
+                }
+                break;
+
+            case self::ISBN13:
+                // sum
+                $isbn13 = str_replace($this->_separator, '', $value);
+                $sum    = 0;
+                for ($i = 0; $i < 12; $i++) {
+                    if ($i % 2 == 0) {
+                        $sum += $isbn13{$i};
+                    } else {
+                        $sum += 3 * $isbn13{$i};
+                    }
+                }
+                // checksum
+                $checksum = 10 - ($sum % 10);
+                if ($checksum == 10) {
+                    $checksum = '0';
+                }
+                break;
+
+            default:
+                $this->_error(self::NO_ISBN);
+                return false;
+        }
+
+        // validate
+        if (substr($this->_value, -1) != $checksum) {
+            $this->_error(self::NO_ISBN);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Set separator characters.
+     *
+     * It is allowed only empty string, hyphen and space.
+     *
+     * @param  string $separator
+     * @throws Zend_Validate_Exception When $separator is not valid
+     * @return Zend_Validate_Isbn Provides a fluent interface
+     */
+    public function setSeparator($separator)
+    {
+        // check separator
+        if (!in_array($separator, array('-', ' ', ''))) {
+            /**
+             * @see Zend_Validate_Exception
+             */
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Invalid ISBN separator.');
+        }
+
+        $this->_separator = $separator;
+        return $this;
+    }
+
+    /**
+     * Get separator characters.
+     *
+     * @return string
+     */
+    public function getSeparator()
+    {
+        return $this->_separator;
+    }
+
+    /**
+     * Set allowed ISBN type.
+     *
+     * @param  string $type
+     * @throws Zend_Validate_Exception When $type is not valid
+     * @return Zend_Validate_Isbn Provides a fluent interface
+     */
+    public function setType($type)
+    {
+        // check type
+        if (!in_array($type, array(self::AUTO, self::ISBN10, self::ISBN13))) {
+            /**
+             * @see Zend_Validate_Exception
+             */
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Invalid ISBN type');
+        }
+
+        $this->_type = $type;
+        return $this;
+    }
+
+    /**
+     * Get allowed ISBN type.
+     *
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->_type;
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Validate/Callback.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Validate/Callback.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Validate/Callback.php	(revision 4456)
@@ -0,0 +1,174 @@
+<?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_Validate
+ * @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: Callback.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @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_Validate_Callback extends Zend_Validate_Abstract
+{
+    /**
+     * Invalid callback
+     */
+    const INVALID_CALLBACK = 'callbackInvalid';
+
+    /**
+     * Invalid value
+     */
+    const INVALID_VALUE = 'callbackValue';
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::INVALID_VALUE    => "'%value%' is not valid",
+        self::INVALID_CALLBACK => "An exception has been raised within the callback",
+    );
+
+    /**
+     * Callback in a call_user_func format
+     *
+     * @var string|array
+     */
+    protected $_callback = null;
+
+    /**
+     * Default options to set for the filter
+     *
+     * @var mixed
+     */
+    protected $_options = array();
+
+    /**
+     * Sets validator options
+     *
+     * @param  string|array $callback
+     * @param  mixed   $max
+     * @param  boolean $inclusive
+     * @return void
+     */
+    public function __construct($callback = null)
+    {
+        if (is_callable($callback)) {
+            $this->setCallback($callback);
+        } elseif (is_array($callback)) {
+            if (isset($callback['callback'])) {
+                $this->setCallback($callback['callback']);
+            }
+            if (isset($callback['options'])) {
+                $this->setOptions($callback['options']);
+            }
+        }
+
+        if (null === ($initializedCallack = $this->getCallback())) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('No callback registered');
+        }
+    }
+
+    /**
+     * Returns the set callback
+     *
+     * @return mixed
+     */
+    public function getCallback()
+    {
+        return $this->_callback;
+    }
+
+    /**
+     * Sets the callback
+     *
+     * @param  string|array $callback
+     * @return Zend_Validate_Callback Provides a fluent interface
+     */
+    public function setCallback($callback)
+    {
+        if (!is_callable($callback)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Invalid callback given');
+        }
+        $this->_callback = $callback;
+        return $this;
+    }
+
+    /**
+     * Returns the set options for the callback
+     *
+     * @return mixed
+     */
+    public function getOptions()
+    {
+        return $this->_options;
+    }
+
+    /**
+     * Sets options for the callback
+     *
+     * @param  mixed $max
+     * @return Zend_Validate_Callback Provides a fluent interface
+     */
+    public function setOptions($options)
+    {
+        $this->_options = (array) $options;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the set callback returns
+     * for the provided $value
+     *
+     * @param  mixed $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        $this->_setValue($value);
+
+        $options  = $this->getOptions();
+        $callback = $this->getCallback();
+        $args     = func_get_args();
+        $options  = array_merge($args, $options);
+
+        try {
+            if (!call_user_func_array($callback, $options)) {
+                $this->_error(self::INVALID_VALUE);
+                return false;
+            }
+        } catch (Exception $e) {
+            $this->_error(self::INVALID_CALLBACK);
+            return false;
+        }
+
+        return true;
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Validate/Date.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Validate/Date.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Validate/Date.php	(revision 4456)
@@ -0,0 +1,259 @@
+<?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_Validate
+ * @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: Date.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @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_Validate_Date extends Zend_Validate_Abstract
+{
+    const INVALID        = 'dateInvalid';
+    const INVALID_DATE   = 'dateInvalidDate';
+    const FALSEFORMAT    = 'dateFalseFormat';
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::INVALID        => "Invalid type given. String, integer, array or Zend_Date expected",
+        self::INVALID_DATE   => "'%value%' does not appear to be a valid date",
+        self::FALSEFORMAT    => "'%value%' does not fit the date format '%format%'",
+    );
+
+    /**
+     * @var array
+     */
+    protected $_messageVariables = array(
+        'format'  => '_format'
+    );
+
+    /**
+     * Optional format
+     *
+     * @var string|null
+     */
+    protected $_format;
+
+    /**
+     * Optional locale
+     *
+     * @var string|Zend_Locale|null
+     */
+    protected $_locale;
+
+    /**
+     * Sets validator options
+     *
+     * @param  string|Zend_Config $options OPTIONAL
+     * @return void
+     */
+    public function __construct($options = array())
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } else if (!is_array($options)) {
+            $options = func_get_args();
+            $temp['format'] = array_shift($options);
+            if (!empty($options)) {
+                $temp['locale'] = array_shift($options);
+            }
+
+            $options = $temp;
+        }
+
+        if (array_key_exists('format', $options)) {
+            $this->setFormat($options['format']);
+        }
+
+        if (!array_key_exists('locale', $options)) {
+            require_once 'Zend/Registry.php';
+            if (Zend_Registry::isRegistered('Zend_Locale')) {
+                $options['locale'] = Zend_Registry::get('Zend_Locale');
+            }
+        }
+
+        if (array_key_exists('locale', $options)) {
+            $this->setLocale($options['locale']);
+        }
+    }
+
+    /**
+     * Returns the locale option
+     *
+     * @return string|Zend_Locale|null
+     */
+    public function getLocale()
+    {
+        return $this->_locale;
+    }
+
+    /**
+     * Sets the locale option
+     *
+     * @param  string|Zend_Locale $locale
+     * @return Zend_Validate_Date provides a fluent interface
+     */
+    public function setLocale($locale = null)
+    {
+        require_once 'Zend/Locale.php';
+        $this->_locale = Zend_Locale::findLocale($locale);
+        return $this;
+    }
+
+    /**
+     * Returns the locale option
+     *
+     * @return string|null
+     */
+    public function getFormat()
+    {
+        return $this->_format;
+    }
+
+    /**
+     * Sets the format option
+     *
+     * @param  string $format
+     * @return Zend_Validate_Date provides a fluent interface
+     */
+    public function setFormat($format = null)
+    {
+        $this->_format = $format;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if $value is a valid date of the format YYYY-MM-DD
+     * If optional $format or $locale is set the date format is checked
+     * according to Zend_Date, see Zend_Date::isDate()
+     *
+     * @param  string|array|Zend_Date $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value) && !is_int($value) && !is_float($value) &&
+            !is_array($value) && !($value instanceof Zend_Date)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue($value);
+
+        if (($this->_format !== null) || ($this->_locale !== null) || is_array($value) ||
+             $value instanceof Zend_Date) {
+            require_once 'Zend/Date.php';
+            if (!Zend_Date::isDate($value, $this->_format, $this->_locale)) {
+                if ($this->_checkFormat($value) === false) {
+                    $this->_error(self::FALSEFORMAT);
+                } else {
+                    $this->_error(self::INVALID_DATE);
+                }
+                return false;
+            }
+        } else {
+            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
+                $this->_format = 'yyyy-MM-dd';
+                $this->_error(self::FALSEFORMAT);
+                $this->_format = null;
+                return false;
+            }
+
+            list($year, $month, $day) = sscanf($value, '%d-%d-%d');
+
+            if (!checkdate($month, $day, $year)) {
+                $this->_error(self::INVALID_DATE);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Check if the given date fits the given format
+     *
+     * @param  string $value  Date to check
+     * @return boolean False when date does not fit the format
+     */
+    private function _checkFormat($value)
+    {
+        try {
+            require_once 'Zend/Locale/Format.php';
+            $parsed = Zend_Locale_Format::getDate($value, array(
+                                                  'date_format' => $this->_format, 'format_type' => 'iso',
+                                                  'fix_date' => false));
+            if (isset($parsed['year']) and ((strpos(strtoupper($this->_format), 'YY') !== false) and
+                (strpos(strtoupper($this->_format), 'YYYY') === false))) {
+                $parsed['year'] = Zend_Date::getFullYear($parsed['year']);
+            }
+        } catch (Exception $e) {
+            // Date can not be parsed
+            return false;
+        }
+
+        if (((strpos($this->_format, 'Y') !== false) or (strpos($this->_format, 'y') !== false)) and
+            (!isset($parsed['year']))) {
+            // Year expected but not found
+            return false;
+        }
+
+        if ((strpos($this->_format, 'M') !== false) and (!isset($parsed['month']))) {
+            // Month expected but not found
+            return false;
+        }
+
+        if ((strpos($this->_format, 'd') !== false) and (!isset($parsed['day']))) {
+            // Day expected but not found
+            return false;
+        }
+
+        if (((strpos($this->_format, 'H') !== false) or (strpos($this->_format, 'h') !== false)) and
+            (!isset($parsed['hour']))) {
+            // Hour expected but not found
+            return false;
+        }
+
+        if ((strpos($this->_format, 'm') !== false) and (!isset($parsed['minute']))) {
+            // Minute expected but not found
+            return false;
+        }
+
+        if ((strpos($this->_format, 's') !== false) and (!isset($parsed['second']))) {
+            // Second expected  but not found
+            return false;
+        }
+
+        // Date fits the format
+        return true;
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Validate/Interface.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Validate/Interface.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Validate/Interface.php	(revision 4456)
@@ -0,0 +1,54 @@
+<?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_Validate
+ * @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 23262 2010-10-27 14:03:36Z matthew $
+ */
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @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_Validate_Interface
+{
+    /**
+     * Returns true if and only if $value meets the validation requirements
+     *
+     * If $value fails validation, then this method returns false, and
+     * getMessages() will return an array of messages that explain why the
+     * validation failed.
+     *
+     * @param  mixed $value
+     * @return boolean
+     * @throws Zend_Validate_Exception If validation of $value is impossible
+     */
+    public function isValid($value);
+
+    /**
+     * Returns an array of messages that explain why the most recent isValid()
+     * call returned false. The array keys are validation failure message identifiers,
+     * and the array values are the corresponding human-readable message strings.
+     *
+     * If isValid() was never called or if the most recent isValid() call
+     * returned true, then this method returns an empty array.
+     *
+     * @return array
+     */
+    public function getMessages();
+}
Index: sandbox/2.2.0.2/library/Zend/Validate/PostCode.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Validate/PostCode.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Validate/PostCode.php	(revision 4456)
@@ -0,0 +1,210 @@
+<?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_Validate
+ * @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: PostCode.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @see Zend_Locale_Format
+ */
+require_once 'Zend/Locale/Format.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @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_Validate_PostCode extends Zend_Validate_Abstract
+{
+    const INVALID  = 'postcodeInvalid';
+    const NO_MATCH = 'postcodeNoMatch';
+
+    /**
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::INVALID  => "Invalid type given. String or integer expected",
+        self::NO_MATCH => "'%value%' does not appear to be a postal code",
+    );
+
+    /**
+     * Locale to use
+     *
+     * @var string
+     */
+    protected $_locale;
+
+    /**
+     * Manual postal code format
+     *
+     * @var unknown_type
+     */
+    protected $_format;
+
+    /**
+     * Constructor for the integer validator
+     *
+     * Accepts either a string locale, a Zend_Locale object, or an array or
+     * Zend_Config object containing the keys "locale" and/or "format".
+     *
+     * @param string|Zend_Locale|array|Zend_Config $options
+     * @throws Zend_Validate_Exception On empty format
+     */
+    public function __construct($options = null)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        }
+
+        if (empty($options)) {
+            require_once 'Zend/Registry.php';
+            if (Zend_Registry::isRegistered('Zend_Locale')) {
+                $this->setLocale(Zend_Registry::get('Zend_Locale'));
+            }
+        } elseif (is_array($options)) {
+            // Received
+            if (array_key_exists('locale', $options)) {
+                $this->setLocale($options['locale']);
+            }
+
+            if (array_key_exists('format', $options)) {
+                $this->setFormat($options['format']);
+            }
+        } elseif ($options instanceof Zend_Locale || is_string($options)) {
+            // Received Locale object or string locale
+            $this->setLocale($options);
+        }
+
+        $format = $this->getFormat();
+        if (empty($format)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("A postcode-format string has to be given for validation");
+        }
+    }
+
+    /**
+     * Returns the set locale
+     *
+     * @return string|Zend_Locale The set locale
+     */
+    public function getLocale()
+    {
+        return $this->_locale;
+    }
+
+    /**
+     * Sets the locale to use
+     *
+     * @param string|Zend_Locale $locale
+     * @throws Zend_Validate_Exception On unrecognised region
+     * @throws Zend_Validate_Exception On not detected format
+     * @return Zend_Validate_PostCode  Provides fluid interface
+     */
+    public function setLocale($locale = null)
+    {
+        require_once 'Zend/Locale.php';
+        $this->_locale = Zend_Locale::findLocale($locale);
+        $locale        = new Zend_Locale($this->_locale);
+        $region        = $locale->getRegion();
+        if (empty($region)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("Unable to detect a region for the locale '$locale'");
+        }
+
+        $format = Zend_Locale::getTranslation(
+            $locale->getRegion(),
+            'postaltoterritory',
+            $this->_locale
+        );
+
+        if (empty($format)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("Unable to detect a postcode format for the region '{$locale->getRegion()}'");
+        }
+
+        $this->setFormat($format);
+        return $this;
+    }
+
+    /**
+     * Returns the set postal code format
+     *
+     * @return string
+     */
+    public function getFormat()
+    {
+        return $this->_format;
+    }
+
+    /**
+     * Sets a self defined postal format as regex
+     *
+     * @param string $format
+     * @throws Zend_Validate_Exception On empty format
+     * @return Zend_Validate_PostCode  Provides fluid interface
+     */
+    public function setFormat($format)
+    {
+        if (empty($format) || !is_string($format)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("A postcode-format string has to be given for validation");
+        }
+
+        if ($format[0] !== '/') {
+            $format = '/^' . $format;
+        }
+
+        if ($format[strlen($format) - 1] !== '/') {
+            $format .= '$/';
+        }
+
+        $this->_format = $format;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value is a valid postalcode
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        $this->_setValue($value);
+        if (!is_string($value) && !is_int($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $format = $this->getFormat();
+        if (!preg_match($format, $value)) {
+            $this->_error(self::NO_MATCH);
+            return false;
+        }
+
+        return true;
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Validate/LessThan.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Validate/LessThan.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Validate/LessThan.php	(revision 4456)
@@ -0,0 +1,122 @@
+<?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_Validate
+ * @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: LessThan.php 20182 2010-01-10 21:12:01Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @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_Validate_LessThan extends Zend_Validate_Abstract
+{
+    const NOT_LESS = 'notLessThan';
+
+    /**
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::NOT_LESS => "'%value%' is not less than '%max%'"
+    );
+
+    /**
+     * @var array
+     */
+    protected $_messageVariables = array(
+        'max' => '_max'
+    );
+
+    /**
+     * Maximum value
+     *
+     * @var mixed
+     */
+    protected $_max;
+
+    /**
+     * Sets validator options
+     *
+     * @param  mixed|Zend_Config $max
+     * @return void
+     */
+    public function __construct($max)
+    {
+        if ($max instanceof Zend_Config) {
+            $max = $max->toArray();
+        }
+
+        if (is_array($max)) {
+            if (array_key_exists('max', $max)) {
+                $max = $max['max'];
+            } else {
+                require_once 'Zend/Validate/Exception.php';
+                throw new Zend_Validate_Exception("Missing option 'max'");
+            }
+        }
+
+        $this->setMax($max);
+    }
+
+    /**
+     * Returns the max option
+     *
+     * @return mixed
+     */
+    public function getMax()
+    {
+        return $this->_max;
+    }
+
+    /**
+     * Sets the max option
+     *
+     * @param  mixed $max
+     * @return Zend_Validate_LessThan Provides a fluent interface
+     */
+    public function setMax($max)
+    {
+        $this->_max = $max;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value is less than max option
+     *
+     * @param  mixed $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        $this->_setValue($value);
+        if ($this->_max <= $value) {
+            $this->_error(self::NOT_LESS);
+            return false;
+        }
+        return true;
+    }
+
+}
Index: sandbox/2.2.0.2/library/Zend/Validate/CreditCard.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Validate/CreditCard.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Validate/CreditCard.php	(revision 4456)
@@ -0,0 +1,317 @@
+<?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_Validate
+ * @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: CreditCard.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @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_Validate_CreditCard extends Zend_Validate_Abstract
+{
+    /**
+     * Detected CCI list
+     *
+     * @var string
+     */
+    const ALL              = 'All';
+    const AMERICAN_EXPRESS = 'American_Express';
+    const UNIONPAY         = 'Unionpay';
+    const DINERS_CLUB      = 'Diners_Club';
+    const DINERS_CLUB_US   = 'Diners_Club_US';
+    const DISCOVER         = 'Discover';
+    const JCB              = 'JCB';
+    const LASER            = 'Laser';
+    const MAESTRO          = 'Maestro';
+    const MASTERCARD       = 'Mastercard';
+    const SOLO             = 'Solo';
+    const VISA             = 'Visa';
+
+    const CHECKSUM       = 'creditcardChecksum';
+    const CONTENT        = 'creditcardContent';
+    const INVALID        = 'creditcardInvalid';
+    const LENGTH         = 'creditcardLength';
+    const PREFIX         = 'creditcardPrefix';
+    const SERVICE        = 'creditcardService';
+    const SERVICEFAILURE = 'creditcardServiceFailure';
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::CHECKSUM       => "'%value%' seems to contain an invalid checksum",
+        self::CONTENT        => "'%value%' must contain only digits",
+        self::INVALID        => "Invalid type given. String expected",
+        self::LENGTH         => "'%value%' contains an invalid amount of digits",
+        self::PREFIX         => "'%value%' is not from an allowed institute",
+        self::SERVICE        => "'%value%' seems to be an invalid creditcard number",
+        self::SERVICEFAILURE => "An exception has been raised while validating '%value%'",
+    );
+
+    /**
+     * List of allowed CCV lengths
+     *
+     * @var array
+     */
+    protected $_cardLength = array(
+        self::AMERICAN_EXPRESS => array(15),
+        self::DINERS_CLUB      => array(14),
+        self::DINERS_CLUB_US   => array(16),
+        self::DISCOVER         => array(16),
+        self::JCB              => array(16),
+        self::LASER            => array(16, 17, 18, 19),
+        self::MAESTRO          => array(12, 13, 14, 15, 16, 17, 18, 19),
+        self::MASTERCARD       => array(16),
+        self::SOLO             => array(16, 18, 19),
+        self::UNIONPAY         => array(16, 17, 18, 19),
+        self::VISA             => array(16),
+    );
+
+    /**
+     * List of accepted CCV provider tags
+     *
+     * @var array
+     */
+    protected $_cardType = array(
+        self::AMERICAN_EXPRESS => array('34', '37'),
+        self::DINERS_CLUB      => array('300', '301', '302', '303', '304', '305', '36'),
+        self::DINERS_CLUB_US   => array('54', '55'),
+        self::DISCOVER         => array('6011', '622126', '622127', '622128', '622129', '62213',
+                                        '62214', '62215', '62216', '62217', '62218', '62219',
+                                        '6222', '6223', '6224', '6225', '6226', '6227', '6228',
+                                        '62290', '62291', '622920', '622921', '622922', '622923',
+                                        '622924', '622925', '644', '645', '646', '647', '648',
+                                        '649', '65'),
+        self::JCB              => array('3528', '3529', '353', '354', '355', '356', '357', '358'),
+        self::LASER            => array('6304', '6706', '6771', '6709'),
+        self::MAESTRO          => array('5018', '5020', '5038', '6304', '6759', '6761', '6763'),
+        self::MASTERCARD       => array('51', '52', '53', '54', '55'),
+        self::SOLO             => array('6334', '6767'),
+        self::UNIONPAY         => array('622126', '622127', '622128', '622129', '62213', '62214',
+                                        '62215', '62216', '62217', '62218', '62219', '6222', '6223',
+                                        '6224', '6225', '6226', '6227', '6228', '62290', '62291',
+                                        '622920', '622921', '622922', '622923', '622924', '622925'),
+        self::VISA             => array('4'),
+    );
+
+    /**
+     * CCIs which are accepted by validation
+     *
+     * @var array
+     */
+    protected $_type = array();
+
+    /**
+     * Service callback for additional validation
+     *
+     * @var callback
+     */
+    protected $_service;
+
+    /**
+     * Constructor
+     *
+     * @param string|array $type OPTIONAL Type of CCI to allow
+     */
+    public function __construct($options = array())
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } else if (!is_array($options)) {
+            $options = func_get_args();
+            $temp['type'] = array_shift($options);
+            if (!empty($options)) {
+                $temp['service'] = array_shift($options);
+            }
+
+            $options = $temp;
+        }
+
+        if (!array_key_exists('type', $options)) {
+            $options['type'] = self::ALL;
+        }
+
+        $this->setType($options['type']);
+        if (array_key_exists('service', $options)) {
+            $this->setService($options['service']);
+        }
+    }
+
+    /**
+     * Returns a list of accepted CCIs
+     *
+     * @return array
+     */
+    public function getType()
+    {
+        return $this->_type;
+    }
+
+    /**
+     * Sets CCIs which are accepted by validation
+     *
+     * @param string|array $type Type to allow for validation
+     * @return Zend_Validate_CreditCard Provides a fluid interface
+     */
+    public function setType($type)
+    {
+        $this->_type = array();
+        return $this->addType($type);
+    }
+
+    /**
+     * Adds a CCI to be accepted by validation
+     *
+     * @param string|array $type Type to allow for validation
+     * @return Zend_Validate_CreditCard Provides a fluid interface
+     */
+    public function addType($type)
+    {
+        if (is_string($type)) {
+            $type = array($type);
+        }
+
+        foreach($type as $typ) {
+            if (defined('self::' . strtoupper($typ)) && !in_array($typ, $this->_type)) {
+                $this->_type[] = $typ;
+            }
+
+            if (($typ == self::ALL)) {
+                $this->_type = array_keys($this->_cardLength);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the actual set service
+     *
+     * @return callback
+     */
+    public function getService()
+    {
+        return $this->_service;
+    }
+
+    /**
+     * Sets a new callback for service validation
+     *
+     * @param unknown_type $service
+     */
+    public function setService($service)
+    {
+        if (!is_callable($service)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Invalid callback given');
+        }
+
+        $this->_service = $service;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value follows the Luhn algorithm (mod-10 checksum)
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        $this->_setValue($value);
+
+        if (!is_string($value)) {
+            $this->_error(self::INVALID, $value);
+            return false;
+        }
+
+        if (!ctype_digit($value)) {
+            $this->_error(self::CONTENT, $value);
+            return false;
+        }
+
+        $length = strlen($value);
+        $types  = $this->getType();
+        $foundp = false;
+        $foundl = false;
+        foreach ($types as $type) {
+            foreach ($this->_cardType[$type] as $prefix) {
+                if (substr($value, 0, strlen($prefix)) == $prefix) {
+                    $foundp = true;
+                    if (in_array($length, $this->_cardLength[$type])) {
+                        $foundl = true;
+                        break 2;
+                    }
+                }
+            }
+        }
+
+        if ($foundp == false){
+            $this->_error(self::PREFIX, $value);
+            return false;
+        }
+
+        if ($foundl == false) {
+            $this->_error(self::LENGTH, $value);
+            return false;
+        }
+
+        $sum    = 0;
+        $weight = 2;
+
+        for ($i = $length - 2; $i >= 0; $i--) {
+            $digit = $weight * $value[$i];
+            $sum += floor($digit / 10) + $digit % 10;
+            $weight = $weight % 2 + 1;
+        }
+
+        if ((10 - $sum % 10) % 10 != $value[$length - 1]) {
+            $this->_error(self::CHECKSUM, $value);
+            return false;
+        }
+
+        if (!empty($this->_service)) {
+            try {
+                require_once 'Zend/Validate/Callback.php';
+                $callback = new Zend_Validate_Callback($this->_service);
+                $callback->setOptions($this->_type);
+                if (!$callback->isValid($value)) {
+                    $this->_error(self::SERVICE, $value);
+                    return false;
+                }
+            } catch (Zend_Exception $e) {
+                $this->_error(self::SERVICEFAILURE, $value);
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Validate/Alpha.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Validate/Alpha.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Validate/Alpha.php	(revision 4456)
@@ -0,0 +1,150 @@
+<?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_Validate
+ * @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: Alpha.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @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_Validate_Alpha extends Zend_Validate_Abstract
+{
+    const INVALID      = 'alphaInvalid';
+    const NOT_ALPHA    = 'notAlpha';
+    const STRING_EMPTY = 'alphaStringEmpty';
+
+    /**
+     * Whether to allow white space characters; off by default
+     *
+     * @var boolean
+     * @deprecated
+     */
+    public $allowWhiteSpace;
+
+    /**
+     * Alphabetic filter used for validation
+     *
+     * @var Zend_Filter_Alpha
+     */
+    protected static $_filter = null;
+
+    /**
+     * Validation failure message template definitions
+     *
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::INVALID      => "Invalid type given. String expected",
+        self::NOT_ALPHA    => "'%value%' contains non alphabetic characters",
+        self::STRING_EMPTY => "'%value%' is an empty string"
+    );
+
+    /**
+     * Sets default option values for this instance
+     *
+     * @param  boolean|Zend_Config $allowWhiteSpace
+     * @return void
+     */
+    public function __construct($allowWhiteSpace = false)
+    {
+        if ($allowWhiteSpace instanceof Zend_Config) {
+            $allowWhiteSpace = $allowWhiteSpace->toArray();
+        }
+
+        if (is_array($allowWhiteSpace)) {
+            if (array_key_exists('allowWhiteSpace', $allowWhiteSpace)) {
+                $allowWhiteSpace = $allowWhiteSpace['allowWhiteSpace'];
+            } else {
+                $allowWhiteSpace = false;
+            }
+        }
+
+        $this->allowWhiteSpace = (boolean) $allowWhiteSpace;
+    }
+
+    /**
+     * Returns the allowWhiteSpace option
+     *
+     * @return boolean
+     */
+    public function getAllowWhiteSpace()
+    {
+        return $this->allowWhiteSpace;
+    }
+
+    /**
+     * Sets the allowWhiteSpace option
+     *
+     * @param boolean $allowWhiteSpace
+     * @return Zend_Filter_Alpha Provides a fluent interface
+     */
+    public function setAllowWhiteSpace($allowWhiteSpace)
+    {
+        $this->allowWhiteSpace = (boolean) $allowWhiteSpace;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value contains only alphabetic characters
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue($value);
+
+        if ('' === $value) {
+            $this->_error(self::STRING_EMPTY);
+            return false;
+        }
+
+        if (null === self::$_filter) {
+            /**
+             * @see Zend_Filter_Alpha
+             */
+            require_once 'Zend/Filter/Alpha.php';
+            self::$_filter = new Zend_Filter_Alpha();
+        }
+
+        self::$_filter->allowWhiteSpace = $this->allowWhiteSpace;
+
+        if ($value !== self::$_filter->filter($value)) {
+            $this->_error(self::NOT_ALPHA);
+            return false;
+        }
+
+        return true;
+    }
+
+}
Index: sandbox/2.2.0.2/library/Zend/Validate/InArray.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Validate/InArray.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Validate/InArray.php	(revision 4456)
@@ -0,0 +1,204 @@
+<?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_Validate
+ * @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: InArray.php 20352 2010-01-17 17:55:38Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @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_Validate_InArray extends Zend_Validate_Abstract
+{
+    const NOT_IN_ARRAY = 'notInArray';
+
+    /**
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::NOT_IN_ARRAY => "'%value%' was not found in the haystack",
+    );
+
+    /**
+     * Haystack of possible values
+     *
+     * @var array
+     */
+    protected $_haystack;
+
+    /**
+     * Whether a strict in_array() invocation is used
+     *
+     * @var boolean
+     */
+    protected $_strict = false;
+
+    /**
+     * Whether a recursive search should be done
+     *
+     * @var boolean
+     */
+    protected $_recursive = false;
+
+    /**
+     * Sets validator options
+     *
+     * @param  array|Zend_Config $haystack
+     * @return void
+     */
+    public function __construct($options)
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } else if (!is_array($options)) {
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception('Array expected as parameter');
+        } else {
+            $count = func_num_args();
+            $temp  = array();
+            if ($count > 1) {
+                $temp['haystack'] = func_get_arg(0);
+                $temp['strict']   = func_get_arg(1);
+                $options = $temp;
+            } else {
+                $temp = func_get_arg(0);
+                if (!array_key_exists('haystack', $options)) {
+                    $options = array();
+                    $options['haystack'] = $temp;
+                } else {
+                    $options = $temp;
+                }
+            }
+        }
+
+        $this->setHaystack($options['haystack']);
+        if (array_key_exists('strict', $options)) {
+            $this->setStrict($options['strict']);
+        }
+
+        if (array_key_exists('recursive', $options)) {
+            $this->setRecursive($options['recursive']);
+        }
+    }
+
+    /**
+     * Returns the haystack option
+     *
+     * @return mixed
+     */
+    public function getHaystack()
+    {
+        return $this->_haystack;
+    }
+
+    /**
+     * Sets the haystack option
+     *
+     * @param  mixed $haystack
+     * @return Zend_Validate_InArray Provides a fluent interface
+     */
+    public function setHaystack(array $haystack)
+    {
+        $this->_haystack = $haystack;
+        return $this;
+    }
+
+    /**
+     * Returns the strict option
+     *
+     * @return boolean
+     */
+    public function getStrict()
+    {
+        return $this->_strict;
+    }
+
+    /**
+     * Sets the strict option
+     *
+     * @param  boolean $strict
+     * @return Zend_Validate_InArray Provides a fluent interface
+     */
+    public function setStrict($strict)
+    {
+        $this->_strict = (boolean) $strict;
+        return $this;
+    }
+
+    /**
+     * Returns the recursive option
+     *
+     * @return boolean
+     */
+    public function getRecursive()
+    {
+        return $this->_recursive;
+    }
+
+    /**
+     * Sets the recursive option
+     *
+     * @param  boolean $recursive
+     * @return Zend_Validate_InArray Provides a fluent interface
+     */
+    public function setRecursive($recursive)
+    {
+        $this->_recursive = (boolean) $recursive;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if $value is contained in the haystack option. If the strict
+     * option is true, then the type of $value is also checked.
+     *
+     * @param  mixed $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        $this->_setValue($value);
+        if ($this->getRecursive()) {
+            $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($this->_haystack));
+            foreach($iterator as $element) {
+                if ($this->_strict) {
+                    if ($element === $value) {
+                        return true;
+                    }
+                } else if ($element == $value) {
+                    return true;
+                }
+            }
+        } else {
+            if (in_array($value, $this->_haystack, $this->_strict)) {
+                return true;
+            }
+        }
+
+        $this->_error(self::NOT_IN_ARRAY);
+        return false;
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Validate/StringLength.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Validate/StringLength.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Validate/StringLength.php	(revision 4456)
@@ -0,0 +1,253 @@
+<?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_Validate
+ * @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: StringLength.php 22668 2010-07-25 14:50:46Z thomas $
+ */
+
+/**
+ * @see Zend_Validate_Abstract
+ */
+require_once 'Zend/Validate/Abstract.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Validate
+ * @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_Validate_StringLength extends Zend_Validate_Abstract
+{
+    const INVALID   = 'stringLengthInvalid';
+    const TOO_SHORT = 'stringLengthTooShort';
+    const TOO_LONG  = 'stringLengthTooLong';
+
+    /**
+     * @var array
+     */
+    protected $_messageTemplates = array(
+        self::INVALID   => "Invalid type given. String expected",
+        self::TOO_SHORT => "'%value%' is less than %min% characters long",
+        self::TOO_LONG  => "'%value%' is more than %max% characters long",
+    );
+
+    /**
+     * @var array
+     */
+    protected $_messageVariables = array(
+        'min' => '_min',
+        'max' => '_max'
+    );
+
+    /**
+     * Minimum length
+     *
+     * @var integer
+     */
+    protected $_min;
+
+    /**
+     * Maximum length
+     *
+     * If null, there is no maximum length
+     *
+     * @var integer|null
+     */
+    protected $_max;
+
+    /**
+     * Encoding to use
+     *
+     * @var string|null
+     */
+    protected $_encoding;
+
+    /**
+     * Sets validator options
+     *
+     * @param  integer|array|Zend_Config $options
+     * @return void
+     */
+    public function __construct($options = array())
+    {
+        if ($options instanceof Zend_Config) {
+            $options = $options->toArray();
+        } else if (!is_array($options)) {
+            $options     = func_get_args();
+            $temp['min'] = array_shift($options);
+            if (!empty($options)) {
+                $temp['max'] = array_shift($options);
+            }
+
+            if (!empty($options)) {
+                $temp['encoding'] = array_shift($options);
+            }
+
+            $options = $temp;
+        }
+
+        if (!array_key_exists('min', $options)) {
+            $options['min'] = 0;
+        }
+
+        $this->setMin($options['min']);
+        if (array_key_exists('max', $options)) {
+            $this->setMax($options['max']);
+        }
+
+        if (array_key_exists('encoding', $options)) {
+            $this->setEncoding($options['encoding']);
+        }
+    }
+
+    /**
+     * Returns the min option
+     *
+     * @return integer
+     */
+    public function getMin()
+    {
+        return $this->_min;
+    }
+
+    /**
+     * Sets the min option
+     *
+     * @param  integer $min
+     * @throws Zend_Validate_Exception
+     * @return Zend_Validate_StringLength Provides a fluent interface
+     */
+    public function setMin($min)
+    {
+        if (null !== $this->_max && $min > $this->_max) {
+            /**
+             * @see Zend_Validate_Exception
+             */
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("The minimum must be less than or equal to the maximum length, but $min >"
+                                            . " $this->_max");
+        }
+        $this->_min = max(0, (integer) $min);
+        return $this;
+    }
+
+    /**
+     * Returns the max option
+     *
+     * @return integer|null
+     */
+    public function getMax()
+    {
+        return $this->_max;
+    }
+
+    /**
+     * Sets the max option
+     *
+     * @param  integer|null $max
+     * @throws Zend_Validate_Exception
+     * @return Zend_Validate_StringLength Provides a fluent interface
+     */
+    public function setMax($max)
+    {
+        if (null === $max) {
+            $this->_max = null;
+        } else if ($max < $this->_min) {
+            /**
+             * @see Zend_Validate_Exception
+             */
+            require_once 'Zend/Validate/Exception.php';
+            throw new Zend_Validate_Exception("The maximum must be greater than or equal to the minimum length, but "
+                                            . "$max < $this->_min");
+        } else {
+            $this->_max = (integer) $max;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the actual encoding
+     *
+     * @return string
+     */
+    public function getEncoding()
+    {
+        return $this->_encoding;
+    }
+
+    /**
+     * Sets a new encoding to use
+     *
+     * @param string $encoding
+     * @return Zend_Validate_StringLength
+     */
+    public function setEncoding($encoding = null)
+    {
+        if ($encoding !== null) {
+            $orig   = iconv_get_encoding('internal_encoding');
+            $result = iconv_set_encoding('internal_encoding', $encoding);
+            if (!$result) {
+                require_once 'Zend/Validate/Exception.php';
+                throw new Zend_Validate_Exception('Given encoding not supported on this OS!');
+            }
+
+            iconv_set_encoding('internal_encoding', $orig);
+        }
+
+        $this->_encoding = $encoding;
+        return $this;
+    }
+
+    /**
+     * Defined by Zend_Validate_Interface
+     *
+     * Returns true if and only if the string length of $value is at least the min option and
+     * no greater than the max option (when the max option is not null).
+     *
+     * @param  string $value
+     * @return boolean
+     */
+    public function isValid($value)
+    {
+        if (!is_string($value)) {
+            $this->_error(self::INVALID);
+            return false;
+        }
+
+        $this->_setValue($value);
+        if ($this->_encoding !== null) {
+            $length = iconv_strlen($value, $this->_encoding);
+        } else {
+            $length = iconv_strlen($value);
+        }
+
+        if ($length < $this->_min) {
+            $this->_error(self::TOO_SHORT);
+        }
+
+        if (null !== $this->_max && $this->_max < $length) {
+            $this->_error(self::TOO_LONG);
+        }
+
+        if (count($this->_messages)) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Loader.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Loader.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Loader.php	(revision 4456)
@@ -0,0 +1,329 @@
+<?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_Loader
+ * @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: Loader.php 23484 2010-12-10 03:57:59Z mjh_ca $
+ */
+
+/**
+ * Static methods for loading classes and files.
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @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_Loader
+{
+    /**
+     * Loads a class from a PHP file.  The filename must be formatted
+     * as "$class.php".
+     *
+     * If $dirs is a string or an array, it will search the directories
+     * in the order supplied, and attempt to load the first matching file.
+     *
+     * If $dirs is null, it will split the class name at underscores to
+     * generate a path hierarchy (e.g., "Zend_Example_Class" will map
+     * to "Zend/Example/Class.php").
+     *
+     * If the file was not found in the $dirs, or if no $dirs were specified,
+     * it will attempt to load it from PHP's include_path.
+     *
+     * @param string $class      - The full class name of a Zend component.
+     * @param string|array $dirs - OPTIONAL Either a path or an array of paths
+     *                             to search.
+     * @return void
+     * @throws Zend_Exception
+     */
+    public static function loadClass($class, $dirs = null)
+    {
+        if (class_exists($class, false) || interface_exists($class, false)) {
+            return;
+        }
+
+        if ((null !== $dirs) && !is_string($dirs) && !is_array($dirs)) {
+            require_once 'Zend/Exception.php';
+            throw new Zend_Exception('Directory argument must be a string or an array');
+        }
+
+        // Autodiscover the path from the class name
+        // Implementation is PHP namespace-aware, and based on
+        // Framework Interop Group reference implementation:
+        // http://groups.google.com/group/php-standards/web/psr-0-final-proposal
+        $className = ltrim($class, '\\');
+        $file      = '';
+        $namespace = '';
+        if ($lastNsPos = strripos($className, '\\')) {
+            $namespace = substr($className, 0, $lastNsPos);
+            $className = substr($className, $lastNsPos + 1);
+            $file      = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
+        }
+        $file .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
+
+        if (!empty($dirs)) {
+            // use the autodiscovered path
+            $dirPath = dirname($file);
+            if (is_string($dirs)) {
+                $dirs = explode(PATH_SEPARATOR, $dirs);
+            }
+            foreach ($dirs as $key => $dir) {
+                if ($dir == '.') {
+                    $dirs[$key] = $dirPath;
+                } else {
+                    $dir = rtrim($dir, '\\/');
+                    $dirs[$key] = $dir . DIRECTORY_SEPARATOR . $dirPath;
+                }
+            }
+            $file = basename($file);
+            self::loadFile($file, $dirs, true);
+        } else {
+            self::loadFile($file, null, true);
+        }
+
+        if (!class_exists($class, false) && !interface_exists($class, false)) {
+            require_once 'Zend/Exception.php';
+            throw new Zend_Exception("File \"$file\" does not exist or class \"$class\" was not found in the file");
+        }
+    }
+
+    /**
+     * Loads a PHP file.  This is a wrapper for PHP's include() function.
+     *
+     * $filename must be the complete filename, including any
+     * extension such as ".php".  Note that a security check is performed that
+     * does not permit extended characters in the filename.  This method is
+     * intended for loading Zend Framework files.
+     *
+     * If $dirs is a string or an array, it will search the directories
+     * in the order supplied, and attempt to load the first matching file.
+     *
+     * If the file was not found in the $dirs, or if no $dirs were specified,
+     * it will attempt to load it from PHP's include_path.
+     *
+     * If $once is TRUE, it will use include_once() instead of include().
+     *
+     * @param  string        $filename
+     * @param  string|array  $dirs - OPTIONAL either a path or array of paths
+     *                       to search.
+     * @param  boolean       $once
+     * @return boolean
+     * @throws Zend_Exception
+     */
+    public static function loadFile($filename, $dirs = null, $once = false)
+    {
+        self::_securityCheck($filename);
+
+        /**
+         * Search in provided directories, as well as include_path
+         */
+        $incPath = false;
+        if (!empty($dirs) && (is_array($dirs) || is_string($dirs))) {
+            if (is_array($dirs)) {
+                $dirs = implode(PATH_SEPARATOR, $dirs);
+            }
+            $incPath = get_include_path();
+            set_include_path($dirs . PATH_SEPARATOR . $incPath);
+        }
+
+        /**
+         * Try finding for the plain filename in the include_path.
+         */
+        if ($once) {
+            include_once $filename;
+        } else {
+            include $filename;
+        }
+
+        /**
+         * If searching in directories, reset include_path
+         */
+        if ($incPath) {
+            set_include_path($incPath);
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns TRUE if the $filename is readable, or FALSE otherwise.
+     * This function uses the PHP include_path, where PHP's is_readable()
+     * does not.
+     *
+     * Note from ZF-2900:
+     * If you use custom error handler, please check whether return value
+     *  from error_reporting() is zero or not.
+     * At mark of fopen() can not suppress warning if the handler is used.
+     *
+     * @param string   $filename
+     * @return boolean
+     */
+    public static function isReadable($filename)
+    {
+        if (is_readable($filename)) {
+            // Return early if the filename is readable without needing the
+            // include_path
+            return true;
+        }
+
+        if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN'
+            && preg_match('/^[a-z]:/i', $filename)
+        ) {
+            // If on windows, and path provided is clearly an absolute path,
+            // return false immediately
+            return false;
+        }
+
+        foreach (self::explodeIncludePath() as $path) {
+            if ($path == '.') {
+                if (is_readable($filename)) {
+                    return true;
+                }
+                continue;
+            }
+            $file = $path . '/' . $filename;
+            if (is_readable($file)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Explode an include path into an array
+     *
+     * If no path provided, uses current include_path. Works around issues that
+     * occur when the path includes stream schemas.
+     *
+     * @param  string|null $path
+     * @return array
+     */
+    public static function explodeIncludePath($path = null)
+    {
+        if (null === $path) {
+            $path = get_include_path();
+        }
+
+        if (PATH_SEPARATOR == ':') {
+            // On *nix systems, include_paths which include paths with a stream
+            // schema cannot be safely explode'd, so we have to be a bit more
+            // intelligent in the approach.
+            $paths = preg_split('#:(?!//)#', $path);
+        } else {
+            $paths = explode(PATH_SEPARATOR, $path);
+        }
+        return $paths;
+    }
+
+    /**
+     * spl_autoload() suitable implementation for supporting class autoloading.
+     *
+     * Attach to spl_autoload() using the following:
+     * <code>
+     * spl_autoload_register(array('Zend_Loader', 'autoload'));
+     * </code>
+     *
+     * @deprecated Since 1.8.0
+     * @param  string $class
+     * @return string|false Class name on success; false on failure
+     */
+    public static function autoload($class)
+    {
+        trigger_error(__CLASS__ . '::' . __METHOD__ . ' is deprecated as of 1.8.0 and will be removed with 2.0.0; use Zend_Loader_Autoloader instead', E_USER_NOTICE);
+        try {
+            @self::loadClass($class);
+            return $class;
+        } catch (Exception $e) {
+            return false;
+        }
+    }
+
+    /**
+     * Register {@link autoload()} with spl_autoload()
+     *
+     * @deprecated Since 1.8.0
+     * @param string $class (optional)
+     * @param boolean $enabled (optional)
+     * @return void
+     * @throws Zend_Exception if spl_autoload() is not found
+     * or if the specified class does not have an autoload() method.
+     */
+    public static function registerAutoload($class = 'Zend_Loader', $enabled = true)
+    {
+        trigger_error(__CLASS__ . '::' . __METHOD__ . ' is deprecated as of 1.8.0 and will be removed with 2.0.0; use Zend_Loader_Autoloader instead', E_USER_NOTICE);
+        require_once 'Zend/Loader/Autoloader.php';
+        $autoloader = Zend_Loader_Autoloader::getInstance();
+        $autoloader->setFallbackAutoloader(true);
+
+        if ('Zend_Loader' != $class) {
+            self::loadClass($class);
+            $methods = get_class_methods($class);
+            if (!in_array('autoload', (array) $methods)) {
+                require_once 'Zend/Exception.php';
+                throw new Zend_Exception("The class \"$class\" does not have an autoload() method");
+            }
+
+            $callback = array($class, 'autoload');
+
+            if ($enabled) {
+                $autoloader->pushAutoloader($callback);
+            } else {
+                $autoloader->removeAutoloader($callback);
+            }
+        }
+    }
+
+    /**
+     * Ensure that filename does not contain exploits
+     *
+     * @param  string $filename
+     * @return void
+     * @throws Zend_Exception
+     */
+    protected static function _securityCheck($filename)
+    {
+        /**
+         * Security check
+         */
+        if (preg_match('/[^a-z0-9\\/\\\\_.:-]/i', $filename)) {
+            require_once 'Zend/Exception.php';
+            throw new Zend_Exception('Security check: Illegal character in filename');
+        }
+    }
+
+    /**
+     * Attempt to include() the file.
+     *
+     * include() is not prefixed with the @ operator because if
+     * the file is loaded and contains a parse error, execution
+     * will halt silently and this is difficult to debug.
+     *
+     * Always set display_errors = Off on production servers!
+     *
+     * @param  string  $filespec
+     * @param  boolean $once
+     * @return boolean
+     * @deprecated Since 1.5.0; use loadFile() instead
+     */
+    protected static function _includeFile($filespec, $once = false)
+    {
+        if ($once) {
+            return include_once $filespec;
+        } else {
+            return include $filespec ;
+        }
+    }
+}
Index: sandbox/2.2.0.2/library/Zend/Registry.php
===================================================================
--- sandbox/2.2.0.2/library/Zend/Registry.php	(revision 4456)
+++ sandbox/2.2.0.2/library/Zend/Registry.php	(revision 4456)
@@ -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_Registry
+ * @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: Registry.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Generic storage class helps to manage global data.
+ *
+ * @category   Zend
+ * @package    Zend_Registry
+ * @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_Registry extends ArrayObject
+{
+    /**
+     * Class name of the singleton registry object.
+     * @var string
+     */
+    private static $_registryClassName = 'Zend_Registry';
+
+    /**
+     * Registry object provides storage for shared objects.
+     * @var Zend_Registry
+     */
+    private static $_registry = null;
+
+    /**
+     * Retrieves the default registry instance.
+     *
+     * @return Zend_Registry
+     */
+    public static function getInstance()
+    {
+        if (self::$_registry === null) {
+            self::init();
+        }
+
+        return self::$_registry;
+    }
+
+    /**
+     * Set the default registry instance to a specified instance.
+     *
+     * @param Zend_Registry $registry An object instance of type Zend_Registry,
+     *   or a subclass.
+     * @return void
+     * @throws Zend_Exception if registry is already initialized.
+     */
+    public static function setInstance(Zend_Registry $registry)
+    {
+        if (self::$_registry !== null) {
+            require_once 'Zend/Exception.php';
+            throw new Zend_Exception('Registry is already initialized');
+        }
+
+        self::setClassName(get_class($registry));
+        self::$_registry = $registry;
+    }
+
+    /**
+     * Initialize the default registry instance.
+     *
+     * @return void
+     */
+    protected static function init()
+    {
+        self::setInstance(new self::$_registryClassName());
+    }
+
+    /**
+     * Set the class name to use for the default registry instance.
+     * Does not affect the currently initialized instance, it only applies
+     * for the next time you instantiate.
+     *
+     * @param string $registryClassName
+     * @return void
+     * @throws Zend_Exception if the registry is initialized or if the
+     *   class name is not valid.
+     */
+    public static function setClassName($registryClassName = 'Zend_Registry')
+    {
+        if (self::$_registry !== null) {
+            require_once 'Zend/Exception.php';
+            throw new Zend_Exception('Registry is already initialized');
+        }
+
+        if (!is_string($registryClassName)) {
+            require_once 'Zend/Exception.php';
+            throw new Zend_Exception("Argument is not a class name");
+        }
+
+        /**
+         * @see Zend_Loader
+         */
+        if (!class_exists($registryClassName)) {
+            require_once 'Zend/Loader.php';
+            Zend_Loader::loadClass($registryClassName);
+        }
+
+        self::$_registryClassName = $registryClassName;
+    }
+
+    /**
+     * Unset the default registry instance.
+     * Primarily used in tearDown() in unit tests.
+     * @returns void
+     */
+    public static function _unsetInstance()
+    {
+        self::$_registry = null;
+    }
+
+    /**
+     * getter method, basically same as offsetGet().
+     *
+     * This method can be called from an object of type Zend_Registry, or it
+     * can be called statically.  In the latter case, it uses the default
+     * static instance stored in the class.
+     *
+     * @param string $index - get the value associated with $index
+     * @return mixed
+     * @throws Zend_Exception if no entry is registerd for $index.
+     */
+    public static function get($index)
+    {
+        $instance = self::getInstance();
+
+        if (!$instance->offsetExists($index)) {
+            require_once 'Zend/Exception.php';
+            throw new Zend_Exception("No entry is registered for key '$index'");
+        }
+
+        return $instance->offsetGet($index);
+    }
+
+    /**
+     * setter method, basically same as offsetSet().
+     *
+     * This method can be called from an object of type Zend_Registry, or it
+     * can be called statically.  In the latter case, it uses the default
+     * static instance stored in the class.
+     *
+     * @param string $index The location in the ArrayObject in which to store
+     *   the value.
+     * @param mixed $value The object to store in the ArrayObject.
+     * @return void
+     */
+    public static function set($index, $value)
+    {
+        $instance = self::getInstance();
+        $instance->offsetSet($index, $value);
+    }
+
+    /**
+     * Returns TRUE if the $index is a named value in the registry,
+     * or FALSE if $index was not found in the registry.
+     *
+     * @param  string $index
+     * @return boolean
+     */
+    public static function isRegistered($index)
+    {
+        if (self::$_registry === null) {
+            return false;
+        }
+        return self::$_registry->offsetExists($index);
+    }
+
+    /**
+     * Constructs a parent ArrayObject with default
+     * ARRAY_AS_PROPS to allow acces as an object
+     *
+     * @param array $array data array
+     * @param integer $flags ArrayObject flags
+     */
+    public function __construct($array = array(), $flags = parent::ARRAY_AS_PROPS)
+    {
+        parent::__construct($array, $flags);
+    }
+
+    /**
+     * @param string $index
+     * @returns mixed
+     *
+     * Workaround for http://bugs.php.net/bug.php?id=40442 (ZF-960).
+     */
+    public function offsetExists($index)
+    {
+        return array_key_exists($index, $this);
+    }
+
+}
