First round of H4-ifying Horde_Share.
authorMichael J. Rubinsky <mrubinsk@horde.org>
Wed, 19 May 2010 14:45:39 +0000 (10:45 -0400)
committerMichael J. Rubinsky <mrubinsk@horde.org>
Wed, 19 May 2010 14:45:39 +0000 (10:45 -0400)
Mostly PHP5 stuff and restructuring of package layout. Still coming:

Finish auditing apps for pear error checks from share package, and
the addition of a binder for Horde_Share

29 files changed:
ansel/lib/Ansel.php
ansel/lib/Faces/Base.php
ansel/lib/Gallery.php
ansel/lib/Storage.php
framework/Share/Share.php [deleted file]
framework/Share/Share/datatree.php [deleted file]
framework/Share/Share/kolab.php [deleted file]
framework/Share/Share/sql.php [deleted file]
framework/Share/Share/sql_hierarchical.php [deleted file]
framework/Share/lib/Horde/Share.php [new file with mode: 0644]
framework/Share/lib/Horde/Share/Datatree.php [new file with mode: 0644]
framework/Share/lib/Horde/Share/Exception.php [new file with mode: 0644]
framework/Share/lib/Horde/Share/Kolab.php [new file with mode: 0644]
framework/Share/lib/Horde/Share/Object.php [new file with mode: 0644]
framework/Share/lib/Horde/Share/Object/Datatree.php [new file with mode: 0644]
framework/Share/lib/Horde/Share/Object/Datatree/Share.php [new file with mode: 0644]
framework/Share/lib/Horde/Share/Object/Kolab.php [new file with mode: 0644]
framework/Share/lib/Horde/Share/Object/Sql.php [new file with mode: 0644]
framework/Share/lib/Horde/Share/Object/Sql/Hierarchical.php [new file with mode: 0644]
framework/Share/lib/Horde/Share/Sql.php [new file with mode: 0644]
framework/Share/lib/Horde/Share/Sql/Hierarchical.php [new file with mode: 0644]
framework/Share/package.xml
horde/services/shares/edit.php
kronolith/edit.php
kronolith/lib/Api.php
kronolith/lib/Driver/Sql.php
kronolith/lib/FreeBusy.php
kronolith/perms.php
whups/lib/Query.php

index e73cd7c..c745826 100644 (file)
  * @author  Michael J. Rubinsky <mrubinsk@horde.org>
  * @package Ansel
  */
-
-/** Horde_Share */
-require_once 'Horde/Share.php';
-
-/** Need to bring this in explicitly since we extend the object class */
-require_once 'Horde/Share/sql_hierarchical.php';
-
 class Ansel
 {
     /**
index a1201d5..5294355 100644 (file)
@@ -164,7 +164,7 @@ class Ansel_Faces_Base
         // should be encapsulated by the shares driver and not parsed from
         // an internally generated query string fragment. Will need to split
         // this out into two seperate operations somehow.
-        $share = substr($GLOBALS['ansel_storage']->shares->_getShareCriteria(
+        $share = substr($GLOBALS['ansel_storage']->shares->getShareCriteria(
             Horde_Auth::getAuth(), Horde_Perms::READ), 5);
 
         $sql = 'SELECT f.face_id, f.gallery_id, f.image_id, f.face_name FROM ansel_faces f, '
@@ -201,7 +201,7 @@ class Ansel_Faces_Base
     {
         // add gallery permission
         // FIXME: Ditto on the REALLY ugly hack comment from above!
-        $share = substr($GLOBALS['ansel_storage']->shares->_getShareCriteria(
+        $share = substr($GLOBALS['ansel_storage']->shares->getShareCriteria(
             Horde_Auth::getAuth(), Horde_Perms::READ), 5);
 
         $sql = 'SELECT COUNT(*) FROM ansel_faces f, '
index 8685adc..b3a7823 100644 (file)
@@ -28,7 +28,7 @@ class Ansel_Gallery extends Horde_Share_Object_sql_hierarchical
     public function __construct($attributes = array())
     {
         /* Pass on up the chain */
-        parent::Horde_Share_Object_sql_hierarchical($attributes);
+        parent::__construct($attributes);
         $this->setShareOb($GLOBALS['ansel_storage']->shares);
         $mode = isset($attributes['attribute_view_mode']) ? $attributes['attribute_view_mode'] : 'Normal';
         $this->_setModeHelper($mode);
index 522a124..5afd1d7 100644 (file)
@@ -66,7 +66,7 @@ class Ansel_Storage
                                                'sql_hierarchical');
 
         /* Ansel_Gallery is just a subclass of Horde_Share_Object */
-        $this->_shares->_shareObject = 'Ansel_Gallery';
+        $this->_shares->setShareClass('Ansel_Gallery');
 
         /* Database handle */
         $this->_db = $GLOBALS['ansel_db'];
@@ -721,7 +721,7 @@ class Ansel_Storage
 
         if (!count($galleries) && !count($slugs)) {
             $sql = 'SELECT DISTINCT ' . $this->_getImageFields('i') . ' FROM ansel_images i, '
-            . str_replace('WHERE' , ' WHERE i.gallery_id = s.share_id AND (', substr($this->_shares->_getShareCriteria(Horde_Auth::getAuth()), 5)) . ')';
+            . str_replace('WHERE' , ' WHERE i.gallery_id = s.share_id AND (', substr($this->_shares->getShareCriteria(Horde_Auth::getAuth()), 5)) . ')';
         } elseif (!count($slugs) && count($galleries)) {
             // Searching by gallery_id
             $sql = 'SELECT ' . $this->_getImageFields() . ' FROM ansel_images '
@@ -810,8 +810,8 @@ class Ansel_Storage
     public function listCategories($perm = Horde_Perms::SHOW, $from = 0, $count = 0)
     {
         $sql = 'SELECT DISTINCT attribute_category FROM '
-               . $this->_shares->_table;
-        $results = $this->_shares->_db->query($sql);
+               . $this->_shares->getTable();
+        $results = $this->_shares->getReadDb()->query($sql);
         if ($results instanceof PEAR_Error) {
             throw new Horde_Exception($results->getMessage());
         }
diff --git a/framework/Share/Share.php b/framework/Share/Share.php
deleted file mode 100644 (file)
index d30cb90..0000000
+++ /dev/null
@@ -1,908 +0,0 @@
-<?php
-/**
- * Horde_Share:: provides an interface to all shares a user might have.  Its
- * methods take care of any site-specific restrictions configured in in the
- * application's prefs.php and conf.php files.
- *
- * Copyright 2002-2010 The Horde Project (http://www.horde.org/)
- * Copyright 2002-2007 Infoteck Internet <webmaster@infoteck.qc.ca>
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author  Joel Vandal <joel@scopserv.com>
- * @author  Mike Cochrame <mike@graftonhall.co.nz>
- * @author  Chuck Hagenbuch <chuck@horde.org>
- * @author  Jan Schneider <jan@horde.org>
- * @author  Gunnar Wrobel <wrobel@pardus.de>
- * @package Horde_Share
- */
-class Horde_Share {
-
-    /**
-     * The application we're managing shares for.
-     *
-     * @var string
-     */
-    var $_app;
-
-    /**
-     * The root of the Share tree.
-     *
-     * @var mixed
-     */
-    var $_root = null;
-
-    /**
-     * A cache of all shares that have been retrieved, so we don't hit the
-     * backend again and again for them.
-     *
-     * @var array
-     */
-    var $_cache = array();
-
-    /**
-     * Id-name-map of already cached share objects.
-     *
-     * @var array
-     */
-    var $_shareMap = array();
-
-    /**
-     * Cache used for listShares().
-     *
-     * @var array
-     */
-    var $_listcache = array();
-
-    /**
-     * A list of objects that we're currently sorting, for reference during the
-     * sorting algorithm.
-     *
-     * @var array
-     */
-    var $_sortList;
-
-    /**
-     * Attempts to return a reference to a concrete Horde_Share instance.
-     *
-     * It will only create a new instance if no Horde_Share instance currently
-     * exists.
-     *
-     * @param string $app     The application that the shares relates to.
-     * @param string $driver  Type of concrete Share subclass to return,
-     *                        based on storage driver ($driver). The code is
-     *                        dynamically included.
-     *
-     * @return Horde_Share  The concrete Share reference, or false on an error.
-     */
-    public static function singleton($app, $driver = null)
-    {
-        static $shares = array();
-
-        // FIXME: This is a temporary solution until the configuration value
-        // actually exists and all apps call this code in the correct fashion.
-        $driver = basename($driver);
-        if (empty($driver)) {
-            if (!empty($GLOBALS['conf']['share']['driver'])) {
-                $driver = $GLOBALS['conf']['share']['driver'];
-            } else {
-                $driver = 'datatree';
-            }
-        }
-
-        $class = 'Horde_Share_' . $driver;
-        if (!class_exists($class)) {
-            include dirname(__FILE__) . '/Share/' . $driver . '.php';
-        }
-
-        $signature = $app . '_' . $driver;
-        if (!isset($shares[$signature]) &&
-            !empty($GLOBALS['conf']['share']['cache'])) {
-            $session = new Horde_SessionObjects();
-            $shares[$signature] = $session->query('horde_share_' . $app . '_' . $driver . '1');
-        }
-
-        if (empty($shares[$signature])) {
-            if (class_exists($class)) {
-                $shares[$signature] = new $class($app);
-            } else {
-                $result = PEAR::raiseError(sprintf(_("\"%s\" share driver not found."), $driver));
-                return $result;
-            }
-        }
-
-        if (!empty($GLOBALS['conf']['share']['cache'])) {
-            register_shutdown_function(array($shares[$signature], 'shutdown'));
-        }
-
-        return $shares[$signature];
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param string $app  The application that the shares belong to.
-     */
-    function Horde_Share($app)
-    {
-        $this->_app = $app;
-        $this->__wakeup();
-    }
-
-    /**
-     * Initializes the object.
-     *
-     * @throws Horde_Exception
-     */
-    function __wakeup()
-    {
-        try {
-            Horde::callHook('share_init', array($this, $this->_app));
-        } catch (Horde_Exception_HookNotSet $e) {}
-    }
-
-    /**
-     * Returns the properties that need to be serialized.
-     *
-     * @return array  List of serializable properties.
-     */
-    function __sleep()
-    {
-        $properties = get_object_vars($this);
-        unset($properties['_sortList']);
-        $properties = array_keys($properties);
-        return $properties;
-    }
-
-    /**
-     * Stores the object in the session cache.
-     */
-    function shutdown()
-    {
-        $driver = str_replace('horde_share_', '', Horde_String::lower(get_class($this)));
-        $session = new Horde_SessionObjects();
-        $session->overwrite('horde_share_' . $this->_app . '_' . $driver, $this, false);
-    }
-
-    /**
-     * Returns the application we're managing shares for.
-     *
-     * @return string  The application this share belongs to.
-     */
-    function getApp()
-    {
-        return $this->_app;
-    }
-
-    /**
-     * Returns a Horde_Share_Object object corresponding to the given share
-     * name, with the details retrieved appropriately.
-     *
-     * @param string $name  The name of the share to retrieve.
-     *
-     * @return Horde_Share_Object  The requested share.
-     */
-    function getShare($name)
-    {
-        if (isset($this->_cache[$name])) {
-            return $this->_cache[$name];
-        }
-
-        $share = $this->_getShare($name);
-        if (is_a($share, 'PEAR_Error')) {
-            return $share;
-        }
-        $share->setShareOb($this);
-        $this->_shareMap[$share->getId()] = $name;
-        $this->_cache[$name] = $share;
-
-        return $share;
-    }
-
-    /**
-     * Returns a Horde_Share_Object object corresponding to the given unique
-     * ID, with the details retrieved appropriately.
-     *
-     * @param string $cid  The id of the share to retrieve.
-     *
-     * @return Horde_Share_Object  The requested share.
-     */
-    function getShareById($cid)
-    {
-        if (!isset($this->_shareMap[$cid])) {
-            $share = $this->_getShareById($cid);
-            if (is_a($share, 'PEAR_Error')) {
-                return $share;
-            }
-            $share->setShareOb($this);
-            $name = $share->getName();
-            $this->_cache[$name] = $share;
-            $this->_shareMap[$cid] = $name;
-        }
-
-        return $this->_cache[$this->_shareMap[$cid]];
-    }
-
-    /**
-     * Returns an array of Horde_Share_Object objects corresponding to the
-     * given set of unique IDs, with the details retrieved appropriately.
-     *
-     * @param array $cids  The array of ids to retrieve.
-     *
-     * @return array  The requested shares.
-     */
-    function getShares($cids)
-    {
-        $all_shares = array();
-        $missing_ids = array();
-        foreach ($cids as $cid) {
-            if (isset($this->_shareMap[$cid])) {
-                $all_shares[$this->_shareMap[$cid]] = $this->_cache[$this->_shareMap[$cid]];
-            } else {
-                $missing_ids[] = $cid;
-            }
-        }
-
-        if (count($missing_ids)) {
-            $shares = $this->_getShares($missing_ids);
-            if (is_a($shares, 'PEAR_Error')) {
-                return $shares;
-            }
-
-            foreach (array_keys($shares) as $key) {
-                $this->_cache[$key] = $shares[$key];
-                $this->_cache[$key]->setShareOb($this);
-                $this->_shareMap[$shares[$key]->getId()] = $key;
-                $all_shares[$key] = $this->_cache[$key];
-            }
-        }
-
-        return $all_shares;
-    }
-
-    /**
-     * Lists *all* shares for the current app/share, regardless of
-     * permissions.
-     *
-     * This is for admin functionality and scripting tools, and shouldn't be
-     * called from user-level code!
-     *
-     * @return array  All shares for the current app/share.
-     */
-    function listAllShares()
-    {
-        $shares = $this->_listAllShares();
-        if (is_a($shares, 'PEAR_Error') || !count($shares)) {
-            return $shares;
-        }
-
-        $this->_sortList = $shares;
-        uasort($shares, array($this, '_sortShares'));
-        $this->_sortList = null;
-
-        return $shares;
-    }
-
-    /**
-     * Returns an array of all shares that $userid has access to.
-     *
-     * @param string $userid     The userid of the user to check access for.
-     * @param integer $perm      The level of permissions required.
-     * @param mixed $attributes  Restrict the shares counted to those
-     *                           matching $attributes. An array of
-     *                           attribute/values pairs or a share owner
-     *                           username.
-     *
-     * @return array  The shares the user has access to.
-     */
-    function listShares($userid, $perm = Horde_Perms::SHOW, $attributes = null,
-                        $from = 0, $count = 0, $sort_by = null, $direction = 0)
-    {
-        $shares = $this->_listShares($userid, $perm, $attributes, $from,
-                                     $count, $sort_by, $direction);
-        if (!count($shares)) {
-            return $shares;
-        }
-        if (is_a($shares, 'PEAR_Error')) {
-            return $shares;
-        }
-
-        /* Make sure getShares() didn't return an error. */
-        $shares = $this->getShares($shares);
-        if (is_a($shares, 'PEAR_Error')) {
-            return $shares;
-        }
-
-        if (is_null($sort_by)) {
-            $this->_sortList = $shares;
-            uasort($shares, array($this, '_sortShares'));
-            $this->_sortList = null;
-        }
-
-        try {
-            return Horde::callHook('share_list', array($userid, $perm, $attributes, $shares));
-        } catch (Horde_Exception_HookNotSet $e) {}
-
-        return $shares;
-    }
-
-    /**
-     * Returns an array of all system shares.
-     *
-     * @return array  All system shares.
-     */
-    function listSystemShares()
-    {
-        return array();
-    }
-
-    /**
-     * Returns the number of shares that $userid has access to.
-     *
-     * @param string $userid     The userid of the user to check access for.
-     * @param integer $perm      The level of permissions required.
-     * @param mixed $attributes  Restrict the shares counted to those
-     *                           matching $attributes. An array of
-     *                           attribute/values pairs or a share owner
-     *                           username.
-     *
-     * @return integer  The number of shares
-     */
-    function countShares($userid, $perm = Horde_Perms::SHOW, $attributes = null)
-    {
-        return $this->_countShares($userid, $perm, $attributes);
-    }
-
-    /**
-     * Returns a new share object.
-     *
-     * @param string $name  The share's name.
-     *
-     * @return Horde_Share_Object  A new share object.
-     */
-    function newShare($name)
-    {
-        if (empty($name)) {
-            return PEAR::raiseError('Share names must be non-empty');
-        }
-        $share = $this->_newShare($name);
-        $share->setShareOb($this);
-        $share->set('owner', Horde_Auth::getAuth());
-
-        return $share;
-    }
-
-    /**
-     * Adds a share to the shares system.
-     *
-     * The share must first be created with Horde_Share::newShare(), and have
-     * any initial details added to it, before this function is called.
-     *
-     * @param Horde_Share_Object $share  The new share object.
-     * @throws Horde_Exception
-     */
-    function addShare($share)
-    {
-        if (!is_a($share, 'Horde_Share_Object')) {
-            return PEAR::raiseError('Shares must be Horde_Share_Object objects or extend that class.');
-        }
-
-        try {
-            Horde::callHook('share_add', array($share));
-        } catch (Horde_Exception_HookNotSet $e) {}
-
-        $result = $this->_addShare($share);
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        /* Store new share in the caches. */
-        $id = $share->getId();
-        $name = $share->getName();
-        $this->_cache[$name] = $share;
-        $this->_shareMap[$id] = $name;
-
-        /* Reset caches that depend on unknown criteria. */
-        $this->_listCache = array();
-
-        return $result;
-    }
-
-    /**
-     * Removes a share from the shares system permanently.
-     *
-     * @param Horde_Share_Object $share  The share to remove.
-     *
-     * @throws Horde_Exception
-     */
-    function removeShare($share)
-    {
-        if (!is_a($share, 'Horde_Share_Object')) {
-            return PEAR::raiseError('Shares must be Horde_Share_Object objects or extend that class.');
-        }
-
-        try {
-            Horde::callHook('share_remove', array($share));
-        } catch (Horde_Exception_HookNotSet $e) {}
-
-        /* Remove share from the caches. */
-        $id = $share->getId();
-        unset($this->_shareMap[$id]);
-        unset($this->_cache[$share->getName()]);
-
-        /* Reset caches that depend on unknown criteria. */
-        $this->_listCache = array();
-
-        return $this->_removeShare($share);
-    }
-
-    /**
-     * Checks if a share exists in the system.
-     *
-     * @param string $share  The share to check.
-     *
-     * @return boolean  True if the share exists.
-     */
-    function exists($share)
-    {
-        if (isset($this->_cache[$share])) {
-            return true;
-        }
-
-        return $this->_exists($share);
-    }
-
-    /**
-     * Finds out what rights the given user has to this object.
-     *
-     * @see Horde_Perms::getPermissions
-     *
-     * @param mixed $share  The share that should be checked for the users
-     *                      permissions.
-     * @param string $user  The user to check for.
-     *
-     * @return mixed  A bitmask of permissions, a permission value, or an array
-     *                of permission values the user has, depending on the
-     *                permission type and whether the permission value is
-     *                ambiguous. False if there is no such permsission.
-     */
-    function getPermissions($share, $user = null)
-    {
-        if (is_a($share, 'PEAR_Error')) {
-            Horde::logMessage($share, 'ERR');
-            return false;
-        }
-
-        if (!is_a($share, 'Horde_Share_Object')) {
-            $share = $this->getShare($share);
-            if (is_a($share, 'PEAR_Error')) {
-                Horde::logMessage($share, 'ERR');
-                return false;
-            }
-        }
-
-        $perm = $share->getPermission();
-        return $GLOBALS['injector']->getInstance('Horde_Perms')->getPermissions($perm, $user);
-    }
-
-    /**
-     * Returns the Identity for a particular share owner.
-     *
-     * @deprecated
-     *
-     * @param mixed $share  The share to fetch the Identity for - either the
-     *                      string name, or the Horde_Share_Object object.
-     *
-     * @return Identity  An Identity instance.
-     */
-    function getIdentityByShare($share)
-    {
-        if (!is_a($share, 'Horde_Share_Object')) {
-            $share = $this->getShare($share);
-            if (is_a($share, 'PEAR_Error')) {
-                return null;
-            }
-        }
-
-        return $GLOBALS['injector']->getInstance('Horde_Prefs_Identity')->getIdentity($share->get('owner'));
-    }
-
-    /**
-     * Utility function to be used with uasort() for sorting arrays of
-     * Horde_Share objects.
-     *
-     * Example:
-     * <code>
-     * uasort($list, array('Horde_Share', '_sortShares'));
-     * </code>
-     *
-     * @access protected
-     */
-    function _sortShares($a, $b)
-    {
-        $aParts = explode(':', $a->getName());
-        $bParts = explode(':', $b->getName());
-
-        $min = min(count($aParts), count($bParts));
-        $idA = '';
-        $idB = '';
-        for ($i = 0; $i < $min; $i++) {
-            if ($idA) {
-                $idA .= ':';
-                $idB .= ':';
-            }
-            $idA .= $aParts[$i];
-            $idB .= $bParts[$i];
-
-            if ($idA != $idB) {
-                $curA = isset($this->_sortList[$idA]) ? $this->_sortList[$idA]->get('name') : '';
-                $curB = isset($this->_sortList[$idB]) ? $this->_sortList[$idB]->get('name') : '';
-                return strnatcasecmp($curA, $curB);
-            }
-        }
-
-        return count($aParts) > count($bParts);
-    }
-
-}
-
-/**
- * Abstract class for storing Share information.
- *
- * This class should be extended for the more specific drivers.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @author  Jan Schneider <jan@horde.org>
- * @author  Gunnar Wrobel <wrobel@pardus.de>
- * @package Horde_Share
- */
-class Horde_Share_Object {
-
-    /**
-     * The Horde_Share object which this share came from - needed for updating
-     * data in the backend to make changes stick, etc.
-     *
-     * @var Horde_Share
-     */
-    var $_shareOb;
-
-    /**
-     * Returns the properties that need to be serialized.
-     *
-     * @return array  List of serializable properties.
-     */
-    function __sleep()
-    {
-        $properties = get_object_vars($this);
-        unset($properties['_shareOb']);
-        $properties = array_keys($properties);
-        return $properties;
-    }
-
-    /**
-     * Associates a Share object with this share.
-     *
-     * @param Horde_Share $shareOb  The Share object.
-     */
-    function setShareOb($shareOb)
-    {
-        if (!is_a($shareOb, 'Horde_Share')) {
-            return PEAR::raiseError('This object needs a Horde_Share instance as storage handler!');
-        }
-        $this->_shareOb = $shareOb;
-    }
-
-    /**
-     * Sets an attribute value in this object.
-     *
-     * @param string $attribute  The attribute to set.
-     * @param mixed $value       The value for $attribute.
-     *
-     * @return mixed  True if setting the attribute did succeed, a PEAR_Error
-     *                otherwise.
-     */
-    function set($attribute, $value)
-    {
-        return $this->_set($attribute, $value);
-    }
-
-    /**
-     * Returns an attribute value from this object.
-     *
-     * @param string $attribute  The attribute to return.
-     *
-     * @return mixed  The value for $attribute.
-     */
-    function get($attribute)
-    {
-        return $this->_get($attribute);
-    }
-
-    /**
-     * Returns the ID of this share.
-     *
-     * @return string  The share's ID.
-     */
-    function getId()
-    {
-        return $this->_getId();
-    }
-
-    /**
-     * Returns the name of this share.
-     *
-     * @return string  The share's name.
-     */
-    function getName()
-    {
-        return $this->_getName();
-    }
-
-    /**
-     * Saves the current attribute values.
-     *
-     * @throws Horde_Exception
-     */
-    function save()
-    {
-        try {
-            Horde::callHook('share_modify', array($this));
-        } catch (Horde_Exception_HookNotSet $e) {}
-
-        return $this->_save();
-    }
-
-    /**
-     * Gives a user a certain privilege for this share.
-     *
-     * @param string $userid       The userid of the user.
-     * @param integer $permission  A Horde_Perms::* constant.
-     */
-    function addUserPermission($userid, $permission)
-    {
-        $perm = $this->getPermission();
-        $perm->addUserPermission($userid, $permission, false);
-        $this->setPermission($perm);
-    }
-
-    /**
-     * Removes a certain privilege for a user from this share.
-     *
-     * @param string $userid       The userid of the user.
-     * @param integer $permission  A Horde_Perms::* constant.
-     */
-    function removeUserPermission($userid, $permission)
-    {
-        $perm = $this->getPermission();
-        $perm->removeUserPermission($userid, $permission, false);
-        $this->setPermission($perm);
-    }
-
-    /**
-     * Gives a group certain privileges for this share.
-     *
-     * @param string $group        The group to add permissions for.
-     * @param integer $permission  A Horde_Perms::* constant.
-     */
-    function addGroupPermission($group, $permission)
-    {
-        $perm = $this->getPermission();
-        $perm->addGroupPermission($group, $permission, false);
-        $this->setPermission($perm);
-    }
-
-    /**
-     * Removes a certain privilege from a group.
-     *
-     * @param string $group         The group to remove permissions from.
-     * @param constant $permission  A Horde_Perms::* constant.
-     */
-    function removeGroupPermission($group, $permission)
-    {
-        $perm = $this->getPermission();
-        $perm->removeGroupPermission($group, $permission, false);
-        $this->setPermission($perm);
-    }
-
-    /**
-     * Removes a user from this share.
-     *
-     * @param string $userid  The userid of the user to remove.
-     */
-    function removeUser($userid)
-    {
-        /* Remove all $userid's permissions. */
-        $perm = $this->getPermission();
-        $perm->removeUserPermission($userid, Horde_Perms::SHOW, false);
-        $perm->removeUserPermission($userid, Horde_Perms::READ, false);
-        $perm->removeUserPermission($userid, Horde_Perms::EDIT, false);
-        $perm->removeUserPermission($userid, Horde_Perms::DELETE, false);
-        return $this->setPermission($perm);
-    }
-
-    /**
-     * Removes a group from this share.
-     *
-     * @param integer $groupId  The group to remove.
-     */
-    function removeGroup($groupId)
-    {
-        /* Remove all $groupId's permissions. */
-        $perm = $this->getPermission();
-        $perm->removeGroupPermission($groupId, Horde_Perms::SHOW, false);
-        $perm->removeGroupPermission($groupId, Horde_Perms::READ, false);
-        $perm->removeGroupPermission($groupId, Horde_Perms::EDIT, false);
-        $perm->removeGroupPermission($groupId, Horde_Perms::DELETE, false);
-        return $this->setPermission($perm);
-    }
-
-    /**
-     * Returns an array containing all the userids of the users with access to
-     * this share.
-     *
-     * @param integer $perm_level  List only users with this permission level.
-     *                             Defaults to all users.
-     *
-     * @return array  The users with access to this share.
-     */
-    function listUsers($perm_level = null)
-    {
-        $perm = $this->getPermission();
-        $results = array_keys($perm->getUserPermissions($perm_level));
-        // Always return the share's owner.
-        if ($this->get('owner')) {
-            array_push($results, $this->get('owner'));
-        }
-        return $results;
-    }
-
-    /**
-     * Returns an array containing all the groupids of the groups with access
-     * to this share.
-     *
-     * @param integer $perm_level  List only users with this permission level.
-     *                             Defaults to all users.
-     *
-     * @return array  The IDs of the groups with access to this share.
-     */
-    function listGroups($perm_level = null)
-    {
-        $perm = $this->getPermission();
-        return array_keys($perm->getGroupPermissions($perm_level));
-    }
-
-    /**
-     * Locks an item from this share, or the entire share if no item defined.
-     *
-     * @param string $item_uid  A uid of an item from this share.
-     *
-     * @return mixed   A lock ID on success, PEAR_Error on failure, false if:
-     *                  - The share is already locked
-     *                  - The item is already locked
-     *                  - A share lock was requested and an item is already
-     *                    locked in the share
-     */
-    function lock($item_uid = null)
-    {
-        try {
-            $locks = $GLOBALS['injector']->getInstance('Horde_Lock');
-        } catch (Horde_Lock_Exception $e) {
-            Horde::logMessage($e, 'ERR');
-            return PEAR::raiseError($e->getMessage());
-        }
-
-        $shareid = $this->getId();
-
-        // Default parameters.
-        $locktype = Horde_Lock::TYPE_EXCLUSIVE;
-        $timeout = 600;
-        $itemscope = $this->_shareOb->getApp() . ':' . $shareid;
-
-        if (!empty($item_uid)) {
-            // Check if the share is locked. Share locks are placed at app
-            // scope.
-            try {
-
-                $result = $locks->getLocks($this->_shareOb->getApp(), $shareid, $locktype);
-            } catch (Horde_Lock_Exception $e) {
-                return PEAR::raiseError($e->getMessage);
-            }
-            if (!empty($result)) {
-                // Lock found.
-                return false;
-            }
-            // Try to place the item lock at app:shareid scope.
-            return $locks->setLock(Horde_Auth::getAuth(), $itemscope, $item_uid,
-                                   $timeout, $locktype);
-        } else {
-            // Share lock requested. Check for locked items.
-            try {
-                $result = $locks->getLocks($itemscope, null, $locktype);
-            } catch (Horde_Lock_Exception $e) {
-                return PEAR::raiseError($e->getException);
-            }
-            if (!empty($result)) {
-                // Lock found.
-                return false;
-            }
-            // Try to place the share lock
-            return $locks->setLock(Horde_Auth::getAuth(), $this->_shareOb->getApp(),
-                                   $shareid, $timeout, $locktype);
-        }
-    }
-
-    /**
-     * Removes the lock for a lock ID.
-     *
-     * @param string $lockid  The lock ID as generated by a previous call
-     *                        to lock().
-     *
-     * @return mixed  True on success, PEAR_Error on failure.
-     */
-    function unlock($lockid)
-    {
-        try {
-            $locks = $GLOBALS['injector']->getInstance('Horde_Lock');
-        } catch (Horde_Lock_Exception $e) {
-            Horde::logMessage($e, 'ERR');
-            return PEAR::raiseError($e->getMessage());
-        }
-
-        return $locks->clearLock($lockid);
-    }
-
-    /**
-     * Checks for existing locks.
-     *
-     * First this checks for share locks and if none exists, checks for item
-     * locks (if item_uid defined).  It will return the first lock found.
-     *
-     * @param string $item_uid  A uid of an item from this share.
-     *
-     * @return mixed   Hash with the found lock information in 'lock' and the
-     *                 lock type ('share' or 'item') in 'type', or an empty
-     *                 array if there are no locks, or a PEAR_Error on failure.
-     */
-    function checkLocks($item_uid = null)
-    {
-        try {
-            $locks = $GLOBALS['injector']->getInstance('Horde_Lock');
-        } catch (Horde_Lock_Exception $e) {
-            Horde::logMessage($e, 'ERR');
-            return PEAR::raiseError($e->getMessage());
-        }
-
-        $shareid = $this->getId();
-        $locktype = Horde_Lock::TYPE_EXCLUSIVE;
-
-        // Check for share locks
-        try {
-            $result = $locks->getLocks($this->_shareOb->getApp(), $shareid, $locktype);
-        } catch (Horde_Lock_Exception $e) {
-            Horde::logMessage($e, 'ERR');
-            return PEAR::raiseError($e->getMessage());
-        }
-
-        if (empty($result) && !empty($item_uid)) {
-            // Check for item locks
-            $locktargettype = 'item';
-            try {
-                $result = $locks->getLocks($this->_shareOb->getApp() . ':' . $shareid, $item_uid, $locktype);
-            } catch (Horde_Lock_Exception $e) {
-                Horde::logMessage($e, 'ERR');
-                return PEAR::raiseError($e->getMessage());
-            }
-        } else {
-            $locktargettype = 'share';
-        }
-
-        if (empty($result)) {
-            return array();
-        }
-
-        return array('type' => $locktargettype,
-                     'lock' => reset($result));
-    }
-
-}
diff --git a/framework/Share/Share/datatree.php b/framework/Share/Share/datatree.php
deleted file mode 100644 (file)
index 282366d..0000000
+++ /dev/null
@@ -1,576 +0,0 @@
-<?php
-
-require_once 'Horde/DataTree.php';
-
-/**
- * Horde_Share_datatree:: provides the datatree backend for the horde share
- * driver.
- *
- * Copyright 2002-2010 The Horde Project (http://www.horde.org/)
- * Copyright 2002-2007 Infoteck Internet <webmaster@infoteck.qc.ca>
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author  Joel Vandal <joel@scopserv.com>
- * @author  Mike Cochrame <mike@graftonhall.co.nz>
- * @author  Chuck Hagenbuch <chuck@horde.org>
- * @author  Jan Schneider <jan@horde.org>
- * @author  Gunnar Wrobel <wrobel@pardus.de>
- * @package Horde_Share
- */
-class Horde_Share_datatree extends Horde_Share {
-
-    /**
-     * The Horde_Share_Object subclass to instantiate objects as
-     *
-     * @var string
-     */
-    var $_shareObject = 'Horde_Share_Object_datatree';
-
-    /**
-     * Pointer to a DataTree instance to manage/store shares
-     *
-     * @var DataTree
-     */
-    var $_datatree;
-
-    /**
-     * Initializes the object.
-     *
-     * @throws Horde_Exception
-     */
-    function __wakeup()
-    {
-        if (empty($GLOBALS['conf']['datatree']['driver'])) {
-            throw new Horde_Exception('You must configure a DataTree backend to use Shares.');
-        }
-
-        $driver = $GLOBALS['conf']['datatree']['driver'];
-        $this->_datatree = &DataTree::singleton(
-            $driver,
-            array_merge(Horde::getDriverConfig('datatree', $driver),
-                        array('group' => 'horde.shares.' . $this->_app))
-        );
-
-        foreach (array_keys($this->_cache) as $name) {
-            if (!is_a($this->_datatree, 'PEAR_Error')) {
-                $this->_cache[$name]->setShareOb($this);
-                $this->_cache[$name]->datatreeObject->setDataTree($this->_datatree);
-            }
-        }
-
-        parent::__wakeup();
-    }
-
-    /**
-     * Returns a Horde_Share_Object_datatree object corresponding to the given
-     * share name, with the details retrieved appropriately.
-     *
-     * @param string $name  The name of the share to retrieve.
-     *
-     * @return Horde_Share_Object_datatree  The requested share.
-     */
-    function &_getShare($name)
-    {
-        $datatreeObject = $this->_datatree->getObject($name, 'DataTreeObject_Share');
-        if (is_a($datatreeObject, 'PEAR_Error')) {
-            return $datatreeObject;
-        }
-        $share = new $this->_shareObject($datatreeObject);
-        return $share;
-    }
-
-    /**
-     * Returns a Horde_Share_Object_datatree object corresponding to the given
-     * unique ID, with the details retrieved appropriately.
-     *
-     * @param string $cid  The id of the share to retrieve.
-     *
-     * @return Horde_Share_Object_datatree  The requested share.
-     */
-    function &_getShareById($id)
-    {
-        $datatreeObject = $this->_datatree->getObjectById($id, 'DataTreeObject_Share');
-        if (is_a($datatreeObject, 'PEAR_Error')) {
-            return $datatreeObject;
-        }
-        $share = new $this->_shareObject($datatreeObject);
-        return $share;
-    }
-
-    /**
-     * Returns an array of Horde_Share_Object_datatree objects corresponding
-     * to the given set of unique IDs, with the details retrieved
-     * appropriately.
-     *
-     * @param array $cids  The array of ids to retrieve.
-     *
-     * @return array  The requested shares.
-     */
-    function &_getShares($ids)
-    {
-        $shares = array();
-        $objects = &$this->_datatree->getObjects($ids, 'DataTreeObject_Share');
-        if (is_a($objects, 'PEAR_Error')) {
-            return $objects;
-        }
-        foreach (array_keys($objects) as $key) {
-            if (is_a($objects[$key], 'PEAR_Error')) {
-                return $objects[$key];
-            }
-            $shares[$key] = new $this->_shareObject($objects[$key]);
-        }
-        return $shares;
-    }
-
-    /**
-     * Lists *all* shares for the current app/share, regardless of
-     * permissions.
-     *
-     * @return array  All shares for the current app/share.
-     */
-    function &_listAllShares()
-    {
-        $sharelist = $this->_datatree->get(DATATREE_FORMAT_FLAT, DATATREE_ROOT,
-                                           true);
-        if (is_a($sharelist, 'PEAR_Error') || !count($sharelist)) {
-            // If we got back an error or an empty array, just return it.
-            return $sharelist;
-        }
-        unset($sharelist[DATATREE_ROOT]);
-
-        return $this->getShares(array_keys($sharelist));
-    }
-
-    /**
-     * Returns an array of all shares that $userid has access to.
-     *
-     * @param string $userid     The userid of the user to check access for.
-     * @param integer $perm      The level of permissions required.
-     * @param mixed $attributes  Restrict the shares counted to those
-     *                           matching $attributes. An array of
-     *                           attribute/values pairs or a share owner
-     *                           username.
-     *
-     * @return array  The shares the user has access to.
-     */
-    function &_listShares($userid, $perm = Horde_Perms::SHOW,
-                          $attributes = null, $from = 0, $count = 0,
-                          $sort_by = null, $direction = 0)
-    {
-        $key = serialize(array($userid, $perm, $attributes));
-        if (empty($this->_listCache[$key])) {
-            $criteria = $this->_getShareCriteria($userid, $perm, $attributes);
-            $sharelist = $this->_datatree->getByAttributes($criteria,
-                                                           DATATREE_ROOT,
-                                                           true, 'id', $from,
-                                                           $count, $sort_by,
-                                                           null, $direction);
-            if (is_a($sharelist, 'PEAR_Error')) {
-                return $sharelist;
-            }
-            $this->_listCache[$key] = array_keys($sharelist);
-        }
-
-        return $this->_listCache[$key];
-    }
-
-    /**
-     * Returns the number of shares that $userid has access to.
-     *
-     * @param string $userid     The userid of the user to check access for.
-     * @param integer $perm      The level of permissions required.
-     * @param mixed $attributes  Restrict the shares counted to those
-     *                           matching $attributes. An array of
-     *                           attribute/values pairs or a share owner
-     *                           username.
-     *
-     * @return integer  The number of shares
-     */
-    function _countShares($userid, $perm = Horde_Perms::SHOW,
-                          $attributes = null)
-    {
-        $criteria = $this->_getShareCriteria($userid, $perm, $attributes);
-        return $this->_datatree->countByAttributes($criteria, DATATREE_ROOT, true, 'id');
-    }
-
-    /**
-     * Returns a new share object.
-     *
-     * @param string $name  The share's name.
-     *
-     * @return Horde_Share_Object_datatree  A new share object.
-     */
-    function &_newShare($name)
-    {
-        $datatreeObject = new DataTreeObject_Share($name);
-        $datatreeObject->setDataTree($this->_datatree);
-        $share = new $this->_shareObject($datatreeObject);
-        return $share;
-    }
-
-    /**
-     * Adds a share to the shares system.
-     *
-     * The share must first be created with
-     * Horde_Share_datatreee::_newShare(), and have any initial details added
-     * to it, before this function is called.
-     *
-     * @param Horde_Share_Object_datatree $share  The new share object.
-     */
-    function _addShare(&$share)
-    {
-        return $this->_datatree->add($share->datatreeObject);
-    }
-
-    /**
-     * Removes a share from the shares system permanently.
-     *
-     * @param Horde_Share_Object_datatree $share  The share to remove.
-     */
-    function _removeShare(&$share)
-    {
-        return $this->_datatree->remove($share->datatreeObject);
-    }
-
-    /**
-     * Checks if a share exists in the system.
-     *
-     * @param string $share  The share to check.
-     *
-     * @return boolean  True if the share exists.
-     */
-    function _exists($share)
-    {
-        return $this->_datatree->exists($share);
-    }
-
-    /**
-     * Returns an array of criteria for querying shares.
-     * @access protected
-     *
-     * @param string  $userid      The userid of the user to check access for.
-     * @param integer $perm        The level of permissions required.
-     * @param mixed   $attributes  Restrict the shares returned to those who
-     *                             have these attribute values.
-     *
-     * @return array  The criteria tree for fetching this user's shares.
-     */
-    function _getShareCriteria($userid, $perm = Horde_Perms::SHOW,
-                               $attributes = null)
-    {
-        if (!empty($userid)) {
-            $criteria = array(
-                'OR' => array(
-                    // (owner == $userid)
-                    array(
-                        'AND' => array(
-                            array('field' => 'name', 'op' => '=', 'test' => 'owner'),
-                            array('field' => 'value', 'op' => '=', 'test' => $userid))),
-
-                    // (name == perm_users and key == $userid and val & $perm)
-                    array(
-                        'AND' => array(
-                            array('field' => 'name', 'op' => '=', 'test' => 'perm_users'),
-                            array('field' => 'key', 'op' => '=', 'test' => $userid),
-                            array('field' => 'value', 'op' => '&', 'test' => $perm))),
-
-                    // (name == perm_creator and val & $perm)
-                    array(
-                        'AND' => array(
-                            array('field' => 'name', 'op' => '=', 'test' => 'perm_creator'),
-                            array('field' => 'value', 'op' => '&', 'test' => $perm))),
-
-                    // (name == perm_default and val & $perm)
-                    array(
-                        'AND' => array(
-                            array('field' => 'name', 'op' => '=', 'test' => 'perm_default'),
-                            array('field' => 'value', 'op' => '&', 'test' => $perm)))));
-
-            // If the user has any group memberships, check for those also.
-            require_once 'Horde/Group.php';
-            $group = &Group::singleton();
-            $groups = $group->getGroupMemberships($userid, true);
-            if (!is_a($groups, 'PEAR_Error') && $groups) {
-                // (name == perm_groups and key in ($groups) and val & $perm)
-                $criteria['OR'][] = array(
-                    'AND' => array(
-                        array('field' => 'name', 'op' => '=', 'test' => 'perm_groups'),
-                        array('field' => 'key', 'op' => 'IN', 'test' => array_keys($groups)),
-                        array('field' => 'value', 'op' => '&', 'test' => $perm)));
-            }
-        } else {
-            $criteria = array(
-                'AND' => array(
-                     array('field' => 'name', 'op' => '=', 'test' => 'perm_guest'),
-                     array('field' => 'value', 'op' => '&', 'test' => $perm)));
-        }
-
-        if (is_array($attributes)) {
-            // Build attribute/key filter.
-            foreach ($attributes as $key => $value) {
-                $criteria = array(
-                    'AND' => array(
-                        $criteria,
-                        array(
-                            'JOIN' => array(
-                                'AND' => array(
-                                    array('field' => 'name', 'op' => '=', 'test' => $key),
-                                    array('field' => 'value', 'op' => '=', 'test' => $value))))));
-            }
-        } elseif (!is_null($attributes)) {
-            // Restrict to shares owned by the user specified in the
-            // $attributes string.
-            $criteria = array(
-                'AND' => array(
-                    $criteria,
-                    array(
-                        'JOIN' => array(
-                            array('field' => 'name', 'op' => '=', 'test' => 'owner'),
-                            array('field' => 'value', 'op' => '=', 'test' => $attributes)))));
-        }
-
-        return $criteria;
-    }
-
-}
-
-/**
- * Extension of the Horde_Share_Object class for storing share information in
- * the DataTree driver.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @author  Jan Schneider <jan@horde.org>
- * @author  Gunnar Wrobel <wrobel@pardus.de>
- * @package Horde_Share
- */
-class Horde_Share_Object_datatree extends Horde_Share_Object {
-
-    /**
-     * The actual storage object that holds the data.
-     *
-     * @var mixed
-     */
-    var $datatreeObject;
-
-    /**
-     * Constructor.
-     *
-     * @param DataTreeObject_Share $datatreeObject  A DataTreeObject_Share
-     *                                              instance.
-     */
-    function Horde_Share_Object_datatree($datatreeObject)
-    {
-        if (is_a($datatreeObject, 'PEAR_Error')) {
-            debug_context();
-        }
-        $this->datatreeObject = $datatreeObject;
-    }
-
-    /**
-     * Sets an attribute value in this object.
-     *
-     * @param string $attribute  The attribute to set.
-     * @param mixed $value       The value for $attribute.
-     *
-     * @return mixed  True if setting the attribute did succeed, a PEAR_Error
-     *                otherwise.
-     */
-    function _set($attribute, $value)
-    {
-        return $this->datatreeObject->set($attribute, $value);
-    }
-
-    /**
-     * Returns one of the attributes of the object, or null if it isn't
-     * defined.
-     *
-     * @param string $attribute  The attribute to retrieve.
-     *
-     * @return mixed  The value of the attribute, or an empty string.
-     */
-    function _get($attribute)
-    {
-        return $this->datatreeObject->get($attribute);
-    }
-
-    /**
-     * Returns the ID of this share.
-     *
-     * @return string  The share's ID.
-     */
-    function _getId()
-    {
-        return $this->datatreeObject->getId();
-    }
-
-    /**
-     * Returns the name of this share.
-     *
-     * @return string  The share's name.
-     */
-    function _getName()
-    {
-        return $this->datatreeObject->getName();
-    }
-
-    /**
-     * Saves the current attribute values.
-     */
-    function _save()
-    {
-        return $this->datatreeObject->save();
-    }
-
-    /**
-     * Checks to see if a user has a given permission.
-     *
-     * @param string $userid       The userid of the user.
-     * @param integer $permission  A Horde_Perms::* constant to test for.
-     * @param string $creator      The creator of the event.
-     *
-     * @return boolean  Whether or not $userid has $permission.
-     */
-    function hasPermission($userid, $permission, $creator = null)
-    {
-        if ($userid && $userid == $this->datatreeObject->get('owner')) {
-            return true;
-        }
-
-        return $GLOBALS['injector']->getInstance('Horde_Perms')->hasPermission($this->getPermission(), $userid, $permission, $creator);
-    }
-
-    /**
-     * Sets the permission of this share.
-     *
-     * @param Horde_Perms_Permission $perm  Permission object.
-     * @param boolean $update         Should the share be saved
-     *                                after this operation?
-     *
-     * @return boolean  True if no error occured, PEAR_Error otherwise
-     */
-    function setPermission(&$perm, $update = true)
-    {
-        $this->datatreeObject->data['perm'] = $perm->getData();
-        if ($update) {
-            return $this->datatreeObject->save();
-        }
-        return true;
-    }
-
-    /**
-     * Returns the permission of this share.
-     *
-     * @return Horde_Persm_Permission  Permission object that represents the
-     *                           permissions on this share
-     */
-    function &getPermission()
-    {
-        $perm = new Horde_Perms_Permission($this->datatreeObject->getName());
-        $perm->data = isset($this->datatreeObject->data['perm'])
-            ? $this->datatreeObject->data['perm']
-            : array();
-
-        return $perm;
-    }
-
-}
-
-/**
- * Extension of the DataTreeObject class for storing Share information in the
- * DataTree driver. If you want to store specialized Share information, you
- * should extend this class instead of extending DataTreeObject directly.
- *
- * @author  Mike Cochrane <mike@graftonhall.co.nz>
- * @author  Jan Schneider <jan@horde.org>
- * @package Horde_Share
- */
-class DataTreeObject_Share extends DataTreeObject {
-
-    /**
-     * Returns the properties that need to be serialized.
-     *
-     * @return array  List of serializable properties.
-     */
-    function __sleep()
-    {
-        $properties = get_object_vars($this);
-        unset($properties['datatree']);
-        $properties = array_keys($properties);
-        return $properties;
-    }
-
-    /**
-     * Maps this object's attributes from the data array into a format that we
-     * can store in the attributes storage backend.
-     *
-     * @access protected
-     *
-     * @param boolean $permsonly  Only process permissions? Lets subclasses
-     *                            override part of this method while handling
-     *                            their additional attributes seperately.
-     *
-     * @return array  The attributes array.
-     */
-    function _toAttributes($permsonly = false)
-    {
-        // Default to no attributes.
-        $attributes = array();
-
-        foreach ($this->data as $key => $value) {
-            if ($key == 'perm') {
-                foreach ($value as $type => $perms) {
-                    if (is_array($perms)) {
-                        foreach ($perms as $member => $perm) {
-                            $attributes[] = array('name' => 'perm_' . $type,
-                                                  'key' => $member,
-                                                  'value' => $perm);
-                        }
-                    } else {
-                        $attributes[] = array('name' => 'perm_' . $type,
-                                              'key' => '',
-                                              'value' => $perms);
-                    }
-                }
-            } elseif (!$permsonly) {
-                $attributes[] = array('name' => $key,
-                                      'key' => '',
-                                      'value' => $value);
-            }
-        }
-
-        return $attributes;
-    }
-
-    /**
-     * Takes in a list of attributes from the backend and maps it to our
-     * internal data array.
-     *
-     * @access protected
-     *
-     * @param array $attributes   The list of attributes from the backend
-     *                            (attribute name, key, and value).
-     * @param boolean $permsonly  Only process permissions? Lets subclasses
-     *                            override part of this method while handling
-     *                            their additional attributes seperately.
-     */
-    function _fromAttributes($attributes, $permsonly = false)
-    {
-        // Initialize data array.
-        $this->data['perm'] = array();
-
-        foreach ($attributes as $attr) {
-            if (substr($attr['name'], 0, 4) == 'perm') {
-                if (!empty($attr['key'])) {
-                    $this->data['perm'][substr($attr['name'], 5)][$attr['key']] = $attr['value'];
-                } else {
-                    $this->data['perm'][substr($attr['name'], 5)] = $attr['value'];
-                }
-            } elseif (!$permsonly) {
-                $this->data[$attr['name']] = $attr['value'];
-            }
-        }
-    }
-
-}
diff --git a/framework/Share/Share/kolab.php b/framework/Share/Share/kolab.php
deleted file mode 100644 (file)
index af6cd49..0000000
+++ /dev/null
@@ -1,716 +0,0 @@
-<?php
-/**
- * @package Horde_Share
- */
-
-/**
- * Horde_Share_kolab:: provides the kolab backend for the horde share driver.
- *
- * Copyright 2004-2010 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author  Stuart Binge <omicron@mighty.co.za>
- * @author  Gunnar Wrobel <wrobel@pardus.de>
- * @package Horde_Share
- */
-class Horde_Share_kolab extends Horde_Share {
-
-    /**
-     * Our Kolab folder list handler
-     *
-     * @var Kolab_List
-     */
-    var $_list;
-
-    /**
-     * The share type
-     *
-     * @var string
-     */
-    var $_type;
-
-    /**
-     * A marker for the validity of the list cache
-     *
-     * @var int
-     */
-    var $_listCacheValidity;
-
-    /**
-     * The session handler.
-     *
-     * @var Horde_Kolab_Session
-     */
-    private $_session;
-
-    /**
-     * Initializes the object.
-     *
-     * @throws Horde_Exception
-     */
-    function __wakeup()
-    {
-        if (empty($GLOBALS['conf']['kolab']['enabled'])) {
-            throw new Horde_Exception('You must enable the kolab settings to use the Kolab Share driver.');
-        }
-
-        $this->_type = $this->_getFolderType($this->_app);
-        if (is_a($this->_type, 'PEAR_Error')) {
-            return $this->_type;
-        }
-
-        $this->_list = $GLOBALS['injector']->getInstance('Horde_Kolab_Storage');
-
-        parent::__wakeup();
-    }
-
-    private function _getFolderType($app)
-    {
-        switch ($app) {
-        case 'mnemo':
-            return 'note';
-        case 'kronolith':
-            return 'event';
-        case 'turba':
-            return 'contact';
-        case 'nag':
-            return 'task';
-        default:
-            return PEAR::raiseError(sprintf(_("The Horde/Kolab integration engine does not support \"%s\""), $app));
-        }
-    }
-
-    /**
-     * Returns the properties that need to be serialized.
-     *
-     * @return array  List of serializable properties.
-     */
-    function __sleep()
-    {
-        $properties = get_object_vars($this);
-        unset($properties['_sortList'], $properties['_list']);
-        $properties = array_keys($properties);
-        return $properties;
-    }
-
-    /**
-     * Returns a Horde_Share_Object_kolab object of the request folder.
-     *
-     * @param string $object  The share to fetch.
-     *
-     * @return Horde_Share_Object_kolab  The share object.
-     */
-    function &_getShare($object)
-    {
-        if (empty($object)) {
-            $error = PEAR::raiseError('No object requested.');
-            return $error;
-        }
-
-        /** Get the corresponding folder for this share ID */
-        $folder = $this->_list->getByShare($object, $this->_type);
-        if (is_a($folder, 'PEAR_Error')) {
-            return $folder;
-        }
-
-        /** Does the folder exist? */
-        if (!$folder->exists()) {
-            return PEAR::raiseError(sprintf(_("Share \"%s\" does not exist."), $object));
-        }
-
-        /** Create the object from the folder */
-        $share = new Horde_Share_Object_kolab($object, $this->_type);
-        $result = $share->setFolder($folder);
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        return $share;
-    }
-
-    /**
-     * Returns a Horde_Share_Object_kolab object of the requested folder.
-     *
-     * @param string $id  The id of the share to fetch.
-     *
-     * @return Horde_Share_Object_kolab  The share object.
-     */
-    function &_getShareById($id)
-    {
-        return $this->_getShare($id);
-    }
-
-    /**
-     * Returns an array of Horde_Share_Object_kolab objects corresponding to
-     * the requested folders.
-     *
-     * @param string $ids  The ids of the shares to fetch.
-     *
-     * @return array  An array of Horde_Share_Object_kolab objects.
-     */
-    function &_getShares($ids)
-    {
-        $objects = array();
-        foreach ($ids as $id) {
-            $result = &$this->_getShare($id);
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
-            }
-            $objects[$id] = &$result;
-        }
-        return $objects;
-    }
-
-    /**
-     * Lists *all* shares for the current app/share, regardless of
-     * permissions.
-     *
-     * Currently not implemented in this class.
-     *
-     * @return array  All shares for the current app/share.
-     */
-    function &_listAllShares()
-    {
-        $shares = array();
-        return $shares;
-    }
-
-    /**
-     * Returns an array of all shares that $userid has access to.
-     *
-     * @param string $userid     The userid of the user to check access for.
-     * @param integer $perm      The level of permissions required.
-     * @param mixed $attributes  Restrict the shares counted to those
-     *                           matching $attributes. An array of
-     *                           attribute/values pairs or a share owner
-     *                           username.
-     *
-     * @return array  The shares the user has access to.
-     */
-    function &_listShares($userid, $perm = Horde_Perms::SHOW,
-                          $attributes = null, $from = 0, $count = 0,
-                          $sort_by = null, $direction = 0)
-    {
-        $key = serialize(array($this->_type, $userid, $perm, $attributes));
-        if ($this->_list === false) {
-            $this->_listCache[$key] = array();
-        } else if (empty($this->_listCache[$key])
-            || $this->_list->validity != $this->_listCacheValidity) {
-            $sharelist = $this->_list->getByType($this->_type);
-            if (is_a($sharelist, 'PEAR_Error')) {
-                return $sharelist;
-            }
-
-            $shares = array();
-            foreach ($sharelist as $folder) {
-                $id = $folder->getShareId();
-                $share = &$this->getShare($id);
-                if (is_a($share, 'PEAR_Error')) {
-                    return $share;
-                }
-
-                $keep = true;
-                if (!$share->hasPermission($userid, $perm)) {
-                    $keep = false;
-                }
-                if (isset($attributes) && $keep) {
-                    if (is_array($attributes)) {
-                        foreach ($attributes as $key => $value) {
-                            if (!$share->get($key) == $value) {
-                                $keep = false;
-                                break;
-                            }
-                        }
-                    } elseif (!$share->get('owner') == $attributes) {
-                        $keep = false;
-                    }
-                }
-                if ($keep) {
-                    $shares[] = $id;
-                }
-            }
-            $this->_listCache[$key] = $shares;
-            $this->_listCacheValidity = $this->_list->validity;
-        }
-
-        return $this->_listCache[$key];
-    }
-
-    /**
-     * Returns the number of shares that $userid has access to.
-     *
-     * @param string $userid     The userid of the user to check access for.
-     * @param integer $perm      The level of permissions required.
-     * @param mixed $attributes  Restrict the shares counted to those
-     *                           matching $attributes. An array of
-     *                           attribute/values pairs or a share owner
-     *                           username.
-     *
-     * @return integer  The number of shares
-     */
-    function _countShares($userid, $perm = Horde_Perms::SHOW,
-                          $attributes = null)
-    {
-        $shares = $this->_listShares($userid, $perm, $attributes);
-        if (is_a($share, 'PEAR_Error')) {
-            return $share;
-        }
-
-        return count($shares);
-    }
-
-    /**
-     * Returns a new share object.
-     *
-     * @param string $name  The share's name.
-     *
-     * @return Horde_Share_Object_kolab  A new share object.
-     */
-    function &_newShare($name)
-    {
-        $storageObject = new Horde_Share_Object_kolab($name, $this->_type);
-        return $storageObject;
-    }
-
-    /**
-     * Adds a share to the shares system.
-     *
-     * The share must first be created with Horde_Share_kolab::_newShare(),
-     * and have any initial details added to it, before this function is
-     * called.
-     *
-     * @param Horde_Share_Object_kolab $share  The new share object.
-     */
-    function _addShare(&$share)
-    {
-        return $share->save();
-    }
-
-    /**
-     * Removes a share from the shares system permanently.
-     *
-     * @param Horde_Share_Object_kolab $share  The share to remove.
-     */
-    function _removeShare(&$share)
-    {
-        $share_id = $share->getName();
-
-        $result = $share->delete();
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-    }
-
-    /**
-     * Checks if a share exists in the system.
-     *
-     * @param string $share  The share to check.
-     *
-     * @return boolean  True if the share exists.
-     */
-    function _exists($object)
-    {
-        if (empty($object)) {
-            return false;
-        }
-
-        /** Get the corresponding folder for this share ID */
-        $folder = $this->_list->getByShare($object, $this->_type);
-        if (is_a($folder, 'PEAR_Error')) {
-            return $folder;
-        }
-
-        return $folder->exists();
-    }
-
-    /**
-     * Create a default share for the current app
-     *
-     * @return string The share ID of the new default share.
-     */
-    function getDefaultShare()
-    {
-        $default = $this->_list->getDefault($this->_type);
-        if (is_a($default, 'PEAR_Error')) {
-            return $default;
-        }
-        if ($default !== false) {
-            return $this->getShare($default->getShareId());
-        }
-
-        /** Okay, no default folder yet */
-        $share = $this->newShare(Horde_Auth::getAuth());
-        if (is_a($share, 'PEAR_Error')) {
-            return $share;
-        }
-        /** The value does not matter here as the share will rewrite it */
-        $share->set('name', '');
-        $result = $this->addShare($share);
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-        return $share;
-    }
-}
-
-/**
- * Extension of the Horde_Share_Object class for handling Kolab share
- * information.
- *
- * @author  Stuart Binge <omicron@mighty.co.za>
- * @author  Gunnar Wrobel <wrobel@pardus.de>
- * @package Horde_Share
- */
-class Horde_Share_Object_kolab extends Horde_Share_Object {
-
-    /**
-     * The Kolab folder this share is based on.
-     *
-     * @var Kolab_Folder
-     */
-    var $_folder;
-
-    /**
-     * The Kolab folder name.
-     *
-     * @var string
-     */
-    var $_folder_name;
-
-    /**
-     * A cache for the share attributes.
-     *
-     * @var array
-     */
-    var $_data;
-
-    /**
-     * Our Kolab folder list handler
-     *
-     * @var Kolab_List
-     */
-    var $_list;
-
-    /**
-     * The session handler.
-     *
-     * @var Horde_Kolab_Session
-     */
-    private $_session;
-
-    /**
-     * Constructor.
-     *
-     * Sets the folder name.
-     *
-     * @param string $id  The share id.
-     */
-    function Horde_Share_Object_kolab($id, $type)
-    {
-        // We actually ignore the random id string that all horde apps provide
-        // as initial name and wait for a set('name', 'xyz') call. But we want
-        // to know if we should create a default share.
-        if ($id == Horde_Auth::getAuth()) {
-            $this->_data['default'] = true;
-        } else {
-            $this->_data['default'] = false;
-        }
-        $this->_type = $type;
-        $this->__wakeup();
-    }
-
-    /**
-     * Associates a Share object with this share.
-     *
-     * @param Horde_Share $shareOb  The Share object.
-     */
-    function setShareOb(&$shareOb)
-    {
-        /** Ignore the parent as we don't need it */
-    }
-
-    /**
-     * Initializes the object.
-     */
-    function __wakeup()
-    {
-        $this->_list = $GLOBALS['injector']->getInstance('Horde_Kolab_Storage');
-        if (isset($this->_folder_name)) {
-            $this->_folder = $this->_list->getFolder($this->_folder_name);
-        }
-    }
-
-    /**
-     * Returns the properties that need to be serialized.
-     *
-     * @return array  List of serializable properties.
-     */
-    function __sleep()
-    {
-        $properties = get_object_vars($this);
-        unset($properties['_shareOb'], $properties['_list'],
-              $properties['_folder']);
-        $properties = array_keys($properties);
-        return $properties;
-    }
-
-    /**
-     * Returns the default share name for the current application.
-     *
-     * @return string  The default share name.
-     */
-    function getDefaultShareName()
-    {
-        switch ($this->_type) {
-        case 'contact':
-            return _("Contacts");
-        case 'note':
-            return _("Notes");
-        case 'event':
-            return _("Calendar");
-        case 'task':
-            return _("Tasks");
-        case 'filter':
-            return _("Filters");
-        case 'h-prefs':
-            return _("Preferences");
-        }
-    }
-
-    /**
-     * Sets the folder for this storage object.
-     *
-     * @param string $folder  Name of the Kolab folder.
-     * @param array  $perms  The permissions of the folder if they are known.
-     */
-    function setFolder(&$folder)
-    {
-        if (!isset($this->_folder)) {
-            $this->_folder = &$folder;
-            $this->_folder_name = $folder->name;
-        } else {
-            return PEAR::raiseError(_("The share has already been initialized!"));
-        }
-    }
-
-    /**
-     * Returns the ID of this share.
-     *
-     * @return string  The share's ID.
-     */
-    function _getId()
-    {
-        return $this->_folder->getShareId();
-    }
-
-    /**
-     * Returns the name of this share.
-     *
-     * @return string  The share's name.
-     */
-    function _getName()
-    {
-        return $this->_folder->getShareId();
-    }
-
-    /**
-     * Returns an attribute value from this object.
-     *
-     * @param string $attribute  The attribute to return.
-     *
-     * @return mixed  The value for $attribute.
-     */
-    function _get($attribute)
-    {
-        if (isset($this->_data[$attribute])) {
-            return $this->_data[$attribute];
-        }
-
-        if (!isset($this->_folder)) {
-            return $this->_folderError();
-        }
-
-        switch ($attribute) {
-        case 'owner':
-            $this->_data['owner'] = $this->_folder->getOwner();
-            break;
-
-        case 'name':
-            $this->_data['name'] = $this->_folder->getTitle();
-            break;
-
-        case 'type':
-            $this->_data['type'] = $this->_folder->getType();
-            break;
-
-        case 'params':
-            $params = @unserialize($this->_folder->getAttribute('params'));
-            $default = array('source' => 'kolab',
-                             'default' => $this->get('default'),
-                             'name' => $this->get('name'));
-            $type = $this->get('type');
-            if (!is_a($type, 'PEAR_Error') && $type == 'event') {
-                $default = array_merge($default, array(
-                                           'fbrelevance' => $this->_folder->getFbrelevance(),
-                                           'xfbaccess'   => $this->_folder->getXfbaccess()
-                                       ));
-            }
-            if (is_a($params, 'PEAR_Error') || $params == '') {
-                $params = $default;
-            }
-            $this->_data['params'] = serialize(array_merge($default, $params));
-            break;
-
-        case 'default':
-            $this->_data['default'] = $this->_folder->isDefault();
-            break;
-
-        default:
-            $annotation = $this->_folder->getAttribute($attribute);
-            if (is_a($annotation, 'PEAR_Error') || empty($annotation)) {
-                $annotation = '';
-            }
-            $this->_data[$attribute] = $annotation;
-            break;
-        }
-
-        return $this->_data[$attribute];
-    }
-
-    /**
-     * Sets an attribute value in this object.
-     *
-     * @param string $attribute  The attribute to set.
-     * @param mixed $value       The value for $attribute.
-     *
-     * @return mixed  True if setting the attribute did succeed, a PEAR_Error
-     *                otherwise.
-     */
-    function _set($attribute, $value)
-    {
-        switch ($attribute) {
-        case 'name':
-            /* On folder creation of default shares we wish to ignore
-             * the names provided by the Horde applications. We use
-             * the Kolab default names. */
-            if (!isset($this->_folder)) {
-                if ($this->get('default')) {
-                    $value = $this->getDefaultShareName();
-                }
-                $this->setFolder($this->_list->getNewFolder());
-            }
-            $this->_folder->setName($value);
-            $this->_data['name'] = $this->_folder->getTitle();
-            break;
-
-        case 'params':
-            $value = unserialize($value);
-            if (isset($value['default'])) {
-                $this->_data['default'] = $value['default'];
-                unset($value['default']);
-            }
-            $value = serialize($value);
-
-        default:
-            $this->_data[$attribute] = $value;
-        }
-    }
-
-    /**
-     * Saves the current attribute values.
-     */
-    function _save()
-    {
-        if (!isset($this->_folder)) {
-            return $this->_folderError();
-        }
-
-        $data = $this->_data;
-        /** The name is handled immediately when set */
-        unset($data['name']);
-        $data['type'] = $this->_type;
-
-        $result = $this->_folder->save($data);
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        /** The name may have changed */
-        $this->_data['name'] = $this->_folder->getTitle();
-        $this->_folder_name = $this->_folder->name;
-        return true;
-    }
-
-    /**
-     * Delete this share.
-     *
-     * @return boolean|PEAR_Error  True on success.
-     */
-    function delete()
-    {
-        if (!isset($this->_folder)) {
-            return $this->_folderError();
-        }
-        return $this->_folder->delete();
-    }
-
-    /**
-     * Checks to see if a user has a given permission.
-     *
-     * @param string $userid       The userid of the user.
-     * @param integer $permission  A Horde_Perms::* constant to test for.
-     * @param string $creator      The creator of the shared object.
-     *
-     * @return boolean|PEAR_Error  Whether or not $userid has $permission.
-     */
-    function hasPermission($userid, $permission, $creator = null)
-    {
-        if (!isset($this->_folder)) {
-            return $this->_folderError();
-        }
-        return $this->_folder->hasPermission($userid, $permission, $creator);
-    }
-
-    /**
-     * Returns the permissions from this storage object.
-     *
-     * @return Horde_Perms_Permission_Kolab|PEAR_Error  The permissions on the share.
-     */
-    function &getPermission()
-    {
-        if (!isset($this->_folder)) {
-            return $this->_folderError();
-        }
-        return $this->_folder->getPermission();
-    }
-
-    /**
-     * Sets the permissions on the share.
-     *
-     * @param Horde_Perms_Permission_Kolab $perms Permission object to folder on the
-     *                                     object.
-     * @param boolean $update              Save the updated information?
-     *
-     * @return boolean|PEAR_Error  True on success.
-     */
-    function setPermission(&$perms, $update = true)
-    {
-        if (!isset($this->_folder)) {
-            return $this->_folderError();
-        }
-        return $this->_folder->setPermission($perms, $update);
-    }
-
-    /**
-     * Return a standard error in case the share has not been
-     * correctly initialized.
-     *
-     * @return PEAR_Error  The PEAR_Error to return.
-     */
-    function _folderError()
-    {
-        return PEAR::raiseError(_("The Kolab share object has not been initialized yet!"));
-    }
-}
diff --git a/framework/Share/Share/sql.php b/framework/Share/Share/sql.php
deleted file mode 100644 (file)
index 50dc296..0000000
+++ /dev/null
@@ -1,1189 +0,0 @@
-<?php
-/**
- * Horde_Share_sql:: provides the sql backend for the horde share
- * driver.
- *
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author  Duck <duck@obala.net>
- * @package Horde_Share
- */
-
-/** The share has user permissions */
-define('HORDE_SHARE_SQL_FLAG_USERS', 1);
-
-/** The share has group permissions */
-define('HORDE_SHARE_SQL_FLAG_GROUPS', 2);
-
-/**
- * @package Horde_Share
- */
-class Horde_Share_sql extends Horde_Share {
-
-    /**
-     * Handle for the current database connection.
-     *
-     * @var DB
-     */
-    var $_db;
-
-    /**
-     * Handle for the current database connection, used for writing. Defaults
-     * to the same handle as $db if a separate write database is not required.
-     *
-     * @var DB
-     */
-    var $_write_db;
-
-    /**
-     * SQL connection parameters
-     */
-    var $_params = array();
-
-    /**
-     * Main share table for the current scope.
-     *
-     * @var string
-     */
-    var $_table;
-
-    /**
-     * The Horde_Share_Object subclass to instantiate objects as
-     *
-     * @var string
-     */
-    var $_shareObject = 'Horde_Share_Object_sql';
-
-    /**
-     * Initializes the object.
-     */
-    function __wakeup()
-    {
-        $this->_table = $this->_app . '_shares';
-        $this->_connect();
-
-        foreach (array_keys($this->_cache) as $name) {
-            $this->_cache[$name]->setShareOb($this);
-        }
-
-        parent::__wakeup();
-    }
-
-    /**
-     * Returns the properties that need to be serialized.
-     *
-     * @return array  List of serializable properties.
-     */
-    function __sleep()
-    {
-        $properties = get_object_vars($this);
-        unset($properties['_sortList'],
-              $properties['_db'],
-              $properties['_write_db']);
-        return array_keys($properties);
-    }
-
-    /**
-     * Get storage table
-     */
-    function getTable()
-    {
-        return $this->_table;
-    }
-
-    /**
-     * Refetence to write db
-     */
-    function getWriteDb()
-    {
-        return $this->_write_db;
-    }
-
-    /**
-     * Finds out if the share has user set
-     */
-    function _hasUsers($share)
-    {
-        return $share['share_flags'] & HORDE_SHARE_SQL_FLAG_USERS;
-    }
-
-    /**
-     * Finds out if the share has user set
-     */
-    function _hasGroups($share)
-    {
-        return $share['share_flags'] & HORDE_SHARE_SQL_FLAG_GROUPS;
-    }
-
-    /**
-     * Get users permissions
-     *
-     * @param array $share Share data array
-     */
-    function _getShareUsers(&$share)
-    {
-        if ($this->_hasUsers($share)) {
-            $stmt = $this->_db->prepare('SELECT user_uid, perm FROM ' . $this->_table . '_users WHERE share_id = ?');
-            if (is_a($stmt, 'PEAR_Error')) {
-                Horde::logMessage($stmt, 'ERR');
-                return $stmt;
-            }
-            $result = $stmt->execute(array($share['share_id']));
-            if (is_a($result, 'PEAR_Error')) {
-                Horde::logMessage($result, 'ERR');
-                return $result;
-            } elseif (!empty($result)) {
-                while ($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-                    $share['perm']['users'][$row['user_uid']] = (int)$row['perm'];
-                }
-            }
-            $stmt->free();
-            $result->free();
-        }
-    }
-
-    /**
-     * Get groups permissions
-     *
-     * @param array $share Share data array
-     */
-    function _getShareGroups(&$share)
-    {
-        if ($this->_hasGroups($share)) {
-            // Get groups permissions
-            $stmt = $this->_db->prepare('SELECT group_uid, perm FROM ' . $this->_table . '_groups WHERE share_id = ?');
-            if (is_a($stmt, 'PEAR_Error')) {
-                Horde::logMessage($stmt, 'ERR');
-                return $stmt;
-            }
-            $result = $stmt->execute(array($share['share_id']));
-            if (is_a($result, 'PEAR_Error')) {
-                Horde::logMessage($result, 'ERR');
-                return $result;
-            } elseif (!empty($result)) {
-                while ($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-                    $share['perm']['groups'][$row['group_uid']] = (int)$row['perm'];
-                }
-            }
-            $stmt->free();
-            $result->free();
-        }
-    }
-
-    /**
-     * Returns a Horde_Share_Object_sql object corresponding to the given
-     * share name, with the details retrieved appropriately.
-     *
-     * @param string $name  The name of the share to retrieve.
-     *
-     * @return Horde_Share_Object_sql  The requested share.
-     */
-    function _getShare($name)
-    {
-        $stmt = $this->_db->prepare('SELECT * FROM ' . $this->_table . ' WHERE share_name = ?');
-        if (is_a($stmt, 'PEAR_Error')) {
-            Horde::logMessage($stmt, 'ERR');
-            return $stmt;
-        }
-        $results = $stmt->execute(array($name));
-        if (is_a($results, 'PEAR_Error')) {
-            Horde::logMessage($results, 'ERR');
-            return $results;
-        }
-        $data = $results->fetchRow(MDB2_FETCHMODE_ASSOC);
-        if (is_a($data, 'PEAR_Error')) {
-            Horde::logMessage($data, 'ERR');
-            return $data;
-        } elseif (empty($data)) {
-            return PEAR::RaiseError(sprintf(_("Share \"%s\" does not exist."), $name));
-        }
-        $stmt->free();
-        $results->free();
-
-        // Convert charset
-        $data = $this->_fromDriverCharset($data);
-
-        // Populate the perms array
-        $this->_loadPermissions($data);
-
-        return new $this->_shareObject($data);
-    }
-
-    /**
-     * Helper function to load the permissions data into the share data
-     *
-     * @param array $data  Array of share attributes
-     */
-    function _loadPermissions(&$data)
-    {
-        $this->_getShareUsers($data);
-        $this->_getShareGroups($data);
-        $this->_getSharePerms($data);
-    }
-
-    function _getSharePerms(&$data)
-    {
-        $data['perm']['type'] = 'matrix';
-        $data['perm']['default'] = isset($data['perm_default']) ? (int)$data['perm_default'] : 0;
-        $data['perm']['guest'] = isset($data['perm_guest']) ? (int)$data['perm_guest'] : 0;
-        $data['perm']['creator'] = isset($data['perm_creator']) ? (int)$data['perm_creator'] : 0;
-        unset($data['perm_creator'], $data['perm_guest'], $data['perm_default']);
-    }
-
-    /**
-     * Returns a Horde_Share_Object_sql object corresponding to the given
-     * unique ID, with the details retrieved appropriately.
-     *
-     * @param integer $cid  The id of the share to retrieve.
-     *
-     * @return Horde_Share_Object_sql  The requested share.
-     */
-    function _getShareById($id)
-    {
-        $params = array($id);
-        $stmt = $this->_db->prepare('SELECT * FROM ' . $this->_table . ' WHERE share_id = ?');
-        if (is_a($stmt, 'PEAR_Error')) {
-            Horde::logMessage($stmt, 'ERR');
-            return $stmt;
-        }
-        $results = $stmt->execute($params);
-        if (is_a($results, 'PEAR_Error')) {
-            Horde::logMessage($results, 'ERR');
-            return $results;
-        }
-        $data = $results->fetchRow(MDB2_FETCHMODE_ASSOC);
-        if (is_a($data, 'PEAR_Error')) {
-            Horde::logMessage($data, 'ERR');
-            return $data;
-        } elseif (empty($data)) {
-            return PEAR::RaiseError(sprintf(_("Share ID %d does not exist."), $id));
-        }
-
-        $stmt->free();
-        $results->free();
-
-        // Convert charset
-        $data = $this->_fromDriverCharset($data);
-
-        // Get permissions
-        $this->_loadPermissions($data);
-
-        return new $this->_shareObject($data);
-    }
-
-    /**
-     * Returns an array of Horde_Share_Object_sql objects corresponding
-     * to the given set of unique IDs, with the details retrieved
-     * appropriately.
-     *
-     * @param array $cids  The array of ids to retrieve.
-     *
-     * @return array  The requested shares.
-     */
-    function _getShares($ids)
-    {
-        $shares = array();
-        $query = 'SELECT * FROM ' . $this->_table . ' WHERE share_id IN (' . implode(', ', $ids) . ')';
-        $result = $this->_db->query($query);
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($result, 'ERR');
-            return $result;
-        } elseif (empty($result)) {
-            return array();
-        }
-
-        $groups = array();
-        $users = array();
-        while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-            $shares[(int)$share['share_id']] = $this->_fromDriverCharset($share);
-            if ($this->_hasUsers($share)) {
-                $users[] = (int)$share['share_id'];
-            }
-            if ($this->_hasGroups($share)) {
-                $groups[] = (int)$share['share_id'];
-            }
-        }
-        $result->free();
-
-        // Get users permissions
-        if (!empty($users)) {
-            $query = 'SELECT share_id, user_uid, perm FROM ' . $this->_table . '_users '
-                    . ' WHERE share_id IN (' . implode(', ', $users) . ')';
-            $result = $this->_db->query($query);
-            if (is_a($result, 'PEAR_Error')) {
-                Horde::logMessage($result, 'ERR');
-                return $result;
-            } elseif (!empty($result)) {
-                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-                    $shares[$share['share_id']]['perm']['users'][$share['user_uid']] = (int)$share['perm'];
-                }
-                $result->free();
-            }
-        }
-
-        // Get groups permissions
-        if (!empty($groups)) {
-            $query = 'SELECT share_id, group_uid, perm FROM ' . $this->_table . '_groups'
-                   . ' WHERE share_id IN (' . implode(', ', $groups) . ')';
-            $result = $this->_db->query($query);
-            if (is_a($result, 'PEAR_Error')) {
-                Horde::logMessage($result, 'ERR');
-                return $result;
-            } elseif (!empty($result)) {
-                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-                    $shares[$share['share_id']]['perm']['groups'][$share['group_uid']] = (int)$share['perm'];
-                }
-                $result->free();
-            }
-        }
-
-        $sharelist = array();
-        foreach ($shares as $id => $data) {
-            $this->_getSharePerms($data);
-            $sharelist[$data['share_name']] = new $this->_shareObject($data);
-        }
-
-        return $sharelist;
-    }
-
-    /**
-     * Lists *all* shares for the current app/share, regardless of
-     * permissions.
-     *
-     * This is for admin functionality and scripting tools, and shouldn't be
-     * called from user-level code!
-     *
-     * @return array  All shares for the current app/share.
-     */
-    function listAllShares()
-    {
-        return $this->_listAllShares();
-    }
-
-    /**
-     * Lists *all* shares for the current app/share, regardless of
-     * permissions.
-     *
-     * @return array  All shares for the current app/share.
-     */
-    function _listAllShares()
-    {
-        $shares = array();
-        $query = 'SELECT * FROM ' . $this->_table . ' ORDER BY share_name ASC';
-        $result = $this->_db->query($query);
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($result, 'ERR');
-            return $result;
-        } elseif (empty($result)) {
-            return array();
-        }
-
-        while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-            $shares[(int)$share['share_id']] = $this->_fromDriverCharset($share);
-        }
-        $result->free();
-
-        // Get users permissions
-        $query = 'SELECT share_id, user_uid, perm FROM ' . $this->_table . '_users';
-        $result = $this->_db->query($query);
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($result, 'ERR');
-            return $result;
-        } elseif (!empty($result)) {
-            while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-                $shares[$share['share_id']]['perm']['users'][$share['user_uid']] = (int)$share['perm'];
-            }
-            $result->free();
-        }
-
-        // Get groups permissions
-        $query = 'SELECT share_id, group_uid, perm FROM ' . $this->_table . '_groups';
-        $result = $this->_db->query($query);
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($result, 'ERR');
-            return $result;
-        } elseif (!empty($result)) {
-            while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-                $shares[$share['share_id']]['perm']['groups'][$share['group_uid']] = (int)$share['perm'];
-            }
-            $result->free();
-        }
-
-        $sharelist = array();
-        foreach ($shares as $id => $data) {
-            $this->_getSharePerms($data);
-            $sharelist[$data['share_name']] = new $this->_shareObject($data);
-            $sharelist[$data['share_name']]->setShareOb($this);
-        }
-
-        return $sharelist;
-    }
-
-    /**
-     * Returns an array of all shares that $userid has access to.
-     *
-     * @param string $userid     The userid of the user to check access for.
-     * @param integer $perm      The level of permissions required.
-     * @param mixed $attributes  Restrict the shares counted to those
-     *                           matching $attributes. An array of
-     *                           attribute/values pairs or a share owner
-     *                           username.
-     *
-     * @return array  The shares the user has access to.
-     * @throws Horde_Exception
-     */
-    function listShares($userid, $perm = Horde_Perms::SHOW, $attributes = null,
-                        $from = 0, $count = 0, $sort_by = null, $direction = 0)
-    {
-        $shares = array();
-        if (is_null($sort_by)) {
-            $sortfield = 's.share_name';
-        } elseif ($sort_by == 'owner' || $sort_by == 'id') {
-            $sortfield = 's.share_' . $sort_by;
-        } else {
-            $sortfield = 's.attribute_' . $sort_by;
-        }
-
-        $query = 'SELECT DISTINCT s.* '
-            . $this->_getShareCriteria($userid, $perm, $attributes)
-            . ' ORDER BY ' . $sortfield
-            . (($direction == 0) ? ' ASC' : ' DESC');
-        if ($from > 0 || $count > 0) {
-            $this->_db->setLimit($count, $from);
-        }
-
-        // Fix field names for sqlite. MDB2 tries to handle this with
-        // MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES, but it doesn't stick.
-        if ($this->_db->phptype == 'sqlite') {
-            $connection = $this->_db->getConnection();
-            @sqlite_query('PRAGMA full_column_names=0', $connection);
-            @sqlite_query('PRAGMA short_column_names=1', $connection);
-        }
-
-        Horde::logMessage(sprintf("SQL Query by Horde_Share_sql::listShares: %s", $query), 'DEBUG');
-        $result = $this->_db->query($query);
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($result, 'ERR');
-            return $result;
-        } elseif (empty($result)) {
-            return array();
-        }
-
-        $users = array();
-        $groups = array();
-        while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-            $shares[(int)$share['share_id']] = $this->_fromDriverCharset($share);
-            if ($this->_hasUsers($share)) {
-                $users[] = (int)$share['share_id'];
-            }
-            if ($this->_hasGroups($share)) {
-                $groups[] = (int)$share['share_id'];
-            }
-        }
-        $result->free();
-
-        // Get users permissions
-        if (!empty($users)) {
-            $query = 'SELECT share_id, user_uid, perm FROM ' . $this->_table
-                 . '_users WHERE share_id IN (' . implode(', ', $users)
-                 . ')';
-            $result = $this->_db->query($query);
-            if (is_a($result, 'PEAR_Error')) {
-                Horde::logMessage($result, 'ERR');
-                return $result;
-            } elseif (!empty($result)) {
-                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-                    $shares[$share['share_id']]['perm']['users'][$share['user_uid']] = (int)$share['perm'];
-                }
-                $result->free();
-            }
-        }
-
-        // Get groups permissions
-        if (!empty($groups)) {
-            $query = 'SELECT share_id, group_uid, perm FROM ' . $this->_table
-                     . '_groups WHERE share_id IN (' . implode(', ', $groups)
-                     . ')';
-            $result = $this->_db->query($query);
-            if (is_a($result, 'PEAR_Error')) {
-                Horde::logMessage($result, 'ERR');
-                return $result;
-            } elseif (!empty($result)) {
-                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-                    $shares[$share['share_id']]['perm']['groups'][$share['group_uid']] = (int)$share['perm'];
-                }
-                $result->free();
-            }
-        }
-
-        $sharelist = array();
-        foreach ($shares as $id => $data) {
-            $this->_getSharePerms($data);
-            $sharelist[$data['share_name']] = new $this->_shareObject($data);
-            $sharelist[$data['share_name']]->setShareOb($this);
-        }
-        unset($shares);
-
-        try {
-            return Horde::callHook('share_list', array($userid, $perm, $attributes, $sharelist));
-        } catch (Horde_Exception_HookNotSet $e) {}
-
-        return $sharelist;
-    }
-
-    /**
-     * Returns an array of all system shares.
-     *
-     * @return array  All system shares.
-     */
-    function listSystemShares()
-    {
-        // Fix field names for sqlite. MDB2 tries to handle this with
-        // MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES, but it doesn't stick.
-        if ($this->_db->phptype == 'sqlite') {
-            $connection = $this->_db->getConnection();
-            @sqlite_query('PRAGMA full_column_names=0', $connection);
-            @sqlite_query('PRAGMA short_column_names=1', $connection);
-        }
-
-        $query = 'SELECT * FROM ' . $this->_table . ' WHERE share_owner IS NULL';
-        Horde::logMessage('SQL Query by Horde_Share_sql::listSystemShares: ' . $query, 'DEBUG');
-        $result = $this->_db->query($query);
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($result, 'ERR');
-            return $result;
-        } elseif (empty($result)) {
-            return array();
-        }
-
-        $sharelist = array();
-        while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-            $data = $this->_fromDriverCharset($share);
-            $this->_getSharePerms($data);
-            $sharelist[$data['share_name']] = new $this->_shareObject($data);
-            $sharelist[$data['share_name']]->setShareOb($this);
-        }
-        $result->free();
-
-        return $sharelist;
-    }
-
-    /**
-     * Returns the number of shares that $userid has access to.
-     *
-     * @param string $userid     The userid of the user to check access for.
-     * @param integer $perm      The level of permissions required.
-     * @param mixed $attributes  Restrict the shares counted to those
-     *                           matching $attributes. An array of
-     *                           attribute/values pairs or a share owner
-     *                           username.
-     *
-     * @return integer  The number of shares
-     */
-    function _countShares($userid, $perm = Horde_Perms::SHOW,
-                          $attributes = null)
-    {
-        $query = $this->_getShareCriteria($userid, $perm, $attributes);
-        $query = 'SELECT COUNT(DISTINCT s.share_id) ' . $query;
-        Horde::logMessage(sprintf("SQL Query by Horde_Share_sql::_countShares: %s", $query), 'DEBUG');
-        return $this->_db->queryOne($query);
-    }
-
-    /**
-     * Returns a new share object.
-     *
-     * @param string $name  The share's name.
-     *
-     * @return Horde_Share_Object_sql  A new share object.
-     */
-    function _newShare($name)
-    {
-        return new $this->_shareObject(array('share_name' => $name));
-    }
-
-    /**
-     * Adds a share to the shares system.
-     *
-     * The share must first be created with
-     * Horde_Share_sql::_newShare(), and have any initial details added
-     * to it, before this function is called.
-     *
-     * @param Horde_Share_Object_sql $share  The new share object.
-     */
-    function _addShare($share)
-    {
-        return $share->save();
-    }
-
-    /**
-     * Removes a share from the shares system permanently.
-     *
-     * @param Horde_Share_Object_sql $share  The share to remove.
-     */
-    function _removeShare($share)
-    {
-        $params = array($share->getId());
-        $tables = array($this->_table,
-                        $this->_table . '_users',
-                        $this->_table . '_groups');
-        foreach ($tables as $table) {
-
-            /* Remove the share entry */
-            $stmt = $this->_write_db->prepare('DELETE FROM ' . $table . ' WHERE share_id = ?', null, MDB2_PREPARE_MANIP);
-            if (is_a($stmt, 'PEAR_Error')) {
-                Horde::logMessage($stmt, 'ERR');
-                return $stmt;
-            }
-            $result = $stmt->execute($params);
-            if (is_a($result, 'PEAR_Error')) {
-                Horde::logMessage($result, 'ERR');
-                return $result;
-            }
-            $stmt->free();
-        }
-
-        return true;
-    }
-
-    /**
-     * Checks if a share exists in the system.
-     *
-     * @param string $share  The share to check.
-     *
-     * @return boolean  True if the share exists.
-     */
-    function _exists($share)
-    {
-        $stmt = $this->_db->prepare('SELECT 1 FROM ' . $this->_table
-                . ' WHERE share_name = ?');
-
-        if (is_a($stmt, 'PEAR_Error')) {
-            Horde::logMessage($stmt, 'ERR');
-            return $stmt;
-        }
-        $result = $stmt->execute(array($share));
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($result, 'ERR');
-            return $result;
-        }
-
-        $exists = (bool)$result->fetchOne();
-        $stmt->free();
-        $result->free();
-
-        return $exists;
-    }
-
-    /**
-     * Returns an array of criteria for querying shares.
-     * @access protected
-     *
-     * @param string  $userid      The userid of the user to check access for.
-     * @param integer $perm        The level of permissions required.
-     * @param mixed   $attributes  Restrict the shares returned to those who
-     *                             have these attribute values.
-     *
-     * @return string  The criteria string for fetching this user's shares.
-     */
-    function _getShareCriteria($userid, $perm = Horde_Perms::SHOW,
-                               $attributes = null)
-    {
-        $query = ' FROM ' . $this->_table . ' s ';
-        $where = '';
-
-        if (!empty($userid)) {
-            // (owner == $userid)
-            $where .= 's.share_owner = ' . $this->_write_db->quote($userid);
-
-            // (name == perm_creator and val & $perm)
-            $where .= ' OR (' . Horde_SQL::buildClause($this->_db, 's.perm_creator', '&', $perm) . ')';
-
-            // (name == perm_creator and val & $perm)
-            $where .= ' OR (' . Horde_SQL::buildClause($this->_db, 's.perm_default',  '&', $perm) . ')';
-
-            // (name == perm_users and key == $userid and val & $perm)
-            $query .= ' LEFT JOIN ' . $this->_table . '_users u ON u.share_id = s.share_id';
-             $where .= ' OR ( u.user_uid = ' .  $this->_write_db->quote($userid)
-            . ' AND (' . Horde_SQL::buildClause($this->_db, 'u.perm', '&', $perm) . '))';
-
-            // If the user has any group memberships, check for those also.
-            require_once 'Horde/Group.php';
-            $group = Group::singleton();
-            $groups = $group->getGroupMemberships($userid, true);
-            if (!is_a($groups, 'PEAR_Error') && $groups) {
-                // (name == perm_groups and key in ($groups) and val & $perm)
-                $ids = array_keys($groups);
-                $group_ids = array();
-                foreach ($ids as $id) {
-                    $group_ids[] = $this->_db->quote((string)$id);
-                }
-                $query .= ' LEFT JOIN ' . $this->_table . '_groups g ON g.share_id = s.share_id';
-                $where .= ' OR (g.group_uid IN (' . implode(',', $group_ids) . ')'
-                    . ' AND (' . Horde_SQL::buildClause($this->_db, 'g.perm', '&', $perm) . '))';
-            } elseif (is_a($groups, 'PEAR_Error')) {
-                Horde::logMessage($groups, 'ERR');
-            }
-        } else {
-            $where = '(' . Horde_SQL::buildClause($this->_db, 's.perm_guest', '&', $perm) . ')';
-        }
-
-        $attributes = $this->_toDriverKeys($attributes);
-        $attributes = $this->_toDriverCharset($attributes);
-
-        if (is_array($attributes)) {
-            // Build attribute/key filter.
-            $where = ' (' . $where . ') ';
-            foreach ($attributes as $key => $value) {
-                $where .= ' AND ' . $key . ' = ' . $this->_db->quote($value);
-            }
-        } elseif (!empty($attributes)) {
-            // Restrict to shares owned by the user specified in the
-            // $attributes string.
-            $where = ' (' . $where . ') AND s.share_owner = ' . $this->_db->quote($attributes);
-        }
-
-        return $query . ' WHERE ' . $where;
-    }
-
-    /**
-     * Resets the current database name so that MDB2 is always selecting the
-     * database before sending a query.
-     */
-    function _selectDB($db, $scope, $message, $is_manip = null)
-    {
-        if ($scope == 'query') {
-            $db->connected_database_name = '';
-        }
-    }
-
-    /**
-     * Attempts to open a connection to the sql server.
-     *
-     * @return boolean  True on success.
-     * @throws Horde_Exception
-     */
-    function _connect()
-    {
-        $this->_params = $GLOBALS['conf']['sql'];
-        if (!isset($this->_params['database'])) {
-            $this->_params['database'] = '';
-        }
-        if (!isset($this->_params['username'])) {
-            $_params['username'] = '';
-        }
-        if (!isset($this->_params['hostspec'])) {
-            $this->_params['hostspec'] = '';
-        }
-
-        /* Connect to the sql server using the supplied parameters. */
-        $params = $this->_params;
-        unset($params['charset']);
-        $this->_write_db = MDB2::factory($params);
-        if (is_a($this->_write_db, 'PEAR_Error')) {
-            throw new Horde_Exception_Prior($this->_write_db);
-        }
-
-        /* Attach debug handler. */
-        $this->_write_db->setOption('debug', true);
-        $this->_write_db->setOption('debug_handler', array($this, '_selectDB'));
-        $this->_write_db->setOption('seqcol_name', 'id');
-
-        /* Set DB portability options. */
-        switch ($this->_write_db->phptype) {
-        case 'mssql':
-            $this->_write_db->setOption('field_case', CASE_LOWER);
-            $this->_write_db->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_RTRIM | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
-            break;
-
-        case 'pgsql':
-            /* The debug handler breaks PostgreSQL. In most cases it shouldn't
-             * be necessary, but this may mean we simply can't support use of
-             * multiple Postgres databases right now. See
-             * http://bugs.horde.org/ticket/7825 */
-            $this->_write_db->setOption('debug', false);
-            // Fall through
-
-        default:
-            $this->_write_db->setOption('field_case', CASE_LOWER);
-            $this->_write_db->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
-        }
-
-        /* Check if we need to set up the read DB connection seperately. */
-        if (!empty($this->_params['splitread'])) {
-            $params = array_merge($params, $this->_params['read']);
-            unset($params['charset']);
-            $this->_db = MDB2::singleton($params);
-            if (is_a($this->_db, 'PEAR_Error')) {
-                throw new Horde_Exception_Prior($this->_db);
-            }
-
-            $this->_db->setOption('seqcol_name', 'id');
-            /* Set DB portability options. */
-            switch ($this->_db->phptype) {
-            case 'mssql':
-                $this->_db->setOption('field_case', CASE_LOWER);
-                $this->_db->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_RTRIM | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
-                break;
-
-            case 'pgsql':
-                /* The debug handler breaks PostgreSQL. In most cases it shouldn't
-                 * be necessary, but this may mean we simply can't support use of
-                 * multiple Postgres databases right now. See
-                 * http://bugs.horde.org/ticket/7825 */
-                $this->_write_db->setOption('debug', false);
-                // Fall through
-
-            default:
-                $this->_db->setOption('field_case', CASE_LOWER);
-                $this->_db->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
-            }
-        } else {
-            /* Default to the same DB handle as the writer for reading too */
-            $this->_db = $this->_write_db;
-        }
-
-        return true;
-    }
-
-    /**
-     * Utility function to convert from the SQL server's charset.
-     */
-    function _fromDriverCharset($data)
-    {
-        foreach ($data as $key => $value) {
-            if (substr($key, 0, 9) == 'attribute') {
-                $data[$key] = Horde_String::convertCharset(
-                    $data[$key], $this->_params['charset']);
-            }
-        }
-
-        return $data;
-    }
-
-    /**
-     * Utility function to convert TO the SQL server's charset.
-     */
-    function _toDriverCharset($data)
-    {
-        if (!is_array($data)) {
-            return $data;
-        }
-
-        foreach ($data as $key => $value) {
-            if (substr($key, 0, 9) == 'attribute') {
-                $data[$key] = Horde_String::convertCharset(
-                    $data[$key], Horde_Nls::getCharset(), $this->_params['charset']);
-            }
-        }
-
-        return $data;
-    }
-
-    /**
-     * Convert an array keyed on client keys to an array keyed on the driver
-     * keys.
-     *
-     * @param array  $data  The client code keyed array.
-     *
-     * @return array  The driver keyed array.
-     */
-    function _toDriverKeys($data)
-    {
-        if (!is_array($data)) {
-            return $data;
-        }
-
-        $driver_keys = array();
-        foreach ($data as $key => $value) {
-            if ($key == 'owner') {
-                $driver_keys['share_owner'] = $value;
-            } else {
-                $driver_keys['attribute_' . $key] = $value;
-            }
-        }
-
-        return $driver_keys;
-    }
-
-}
-
-/**
- * Extension of the Horde_Share_Object class for storing share information in
- * the sql driver.
- *
- * @author  Duck <duck@obala.net>
- * @package Horde_Share
- */
-class Horde_Share_Object_sql extends Horde_Share_Object {
-
-    /**
-     * The actual storage object that holds the data.
-     *
-     * @var mixed
-     */
-    var $data = array();
-
-    /**
-     * Constructor.
-     *
-     * @param array $data Share data array.
-     */
-    function Horde_Share_Object_sql($data)
-    {
-        if (!isset($data['perm']) || !is_array($data['perm'])) {
-            $this->data['perm'] = array(
-                'users' => array(),
-                'type' => 'matrix',
-                'default' => isset($data['perm_default'])
-                    ? (int)$data['perm_default'] : 0,
-                'guest' => isset($data['perm_guest'])
-                    ? (int)$data['perm_guest'] : 0,
-                'creator' => isset($data['perm_creator'])
-                    ? (int)$data['perm_creator'] : 0,
-                'groups' => array());
-
-            unset($data['perm_creator'], $data['perm_guest'],
-                  $data['perm_default']);
-        }
-        $this->data = array_merge($data, $this->data);
-    }
-
-    /**
-     * Sets an attribute value in this object.
-     *
-     * @param string $attribute  The attribute to set.
-     * @param mixed $value       The value for $attribute.
-     *
-     * @return mixed  True if setting the attribute did succeed, a PEAR_Error
-     *                otherwise.
-     */
-    function _set($attribute, $value)
-    {
-        if ($attribute == 'owner') {
-            return $this->data['share_owner'] = $value;
-        } else {
-            return $this->data['attribute_' . $attribute] = $value;
-        }
-    }
-
-    /**
-     * Returns one of the attributes of the object, or null if it isn't
-     * defined.
-     *
-     * @param string $attribute  The attribute to retrieve.
-     *
-     * @return mixed  The value of the attribute, or an empty string.
-     */
-    function _get($attribute)
-    {
-        if ($attribute == 'owner') {
-            return $this->data['share_owner'];
-        } elseif (isset($this->data['attribute_' . $attribute])) {
-            return $this->data['attribute_' . $attribute];
-        }
-    }
-
-    /**
-     * Returns the ID of this share.
-     *
-     * @return string  The share's ID.
-     */
-    function _getId()
-    {
-        return isset($this->data['share_id']) ? $this->data['share_id'] : null;
-    }
-
-    /**
-     * Returns the name of this share.
-     *
-     * @return string  The share's name.
-     */
-    function _getName()
-    {
-        return $this->data['share_name'];
-    }
-
-    /**
-     * Saves the current attribute values.
-     */
-    function _save()
-    {
-        $db = $this->_shareOb->getWriteDb();
-        $table = $this->_shareOb->getTable();
-
-        $fields = array();
-        $params = array();
-
-        foreach ($this->_shareOb->_toDriverCharset($this->data) as $key => $value) {
-            if ($key != 'share_id' && $key != 'perm' && $key != 'share_flags') {
-                $fields[] = $key;
-                $params[] = $value;
-            }
-        }
-
-        $fields[] = 'perm_creator';
-        $params[] = isset($this->data['perm']['creator']) ? (int)$this->data['perm']['creator'] : 0;
-
-        $fields[] = 'perm_default';
-        $params[] = isset($this->data['perm']['default']) ? (int)$this->data['perm']['default'] : 0;
-
-        $fields[] = 'perm_guest';
-        $params[] = isset($this->data['perm']['guest']) ? (int)$this->data['perm']['guest'] : 0;
-
-        $fields[] = 'share_flags';
-        $flags = 0;
-        if (!empty($this->data['perm']['users'])) {
-            $flags |= HORDE_SHARE_SQL_FLAG_USERS;
-        }
-        if (!empty($this->data['perm']['groups'])) {
-            $flags |= HORDE_SHARE_SQL_FLAG_GROUPS;
-        }
-        $params[] = $flags;
-
-        if (empty($this->data['share_id'])) {
-            $share_id = $db->nextId($table);
-            if (is_a($share_id, 'PEAR_Error')) {
-                Horde::logMessage($share_id, 'ERR');
-                return $share_id;
-            }
-
-            $this->data['share_id'] = $share_id;
-            $fields[] = 'share_id';
-            $params[] = $this->data['share_id'];
-
-            $query = 'INSERT INTO ' . $table . ' (' . implode(', ', $fields) . ') VALUES (?' . str_repeat(', ?', count($fields) - 1) . ')';
-        } else {
-            $query = 'UPDATE ' . $table . ' SET ' . implode(' = ?, ', $fields) . ' = ? WHERE share_id = ?';
-            $params[] = $this->data['share_id'];
-        }
-        $stmt = $db->prepare($query, null, MDB2_PREPARE_MANIP);
-        if (is_a($stmt, 'PEAR_Error')) {
-            Horde::logMessage($stmt, 'ERR');
-            return $stmt;
-        }
-        $result = $stmt->execute($params);
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($result, 'ERR');
-            return $result;
-        }
-        $stmt->free();
-
-        // Update the share's user permissions
-        $stmt = $db->prepare('DELETE FROM ' . $table . '_users WHERE share_id = ?', null, MDB2_PREPARE_MANIP);
-        if (is_a($stmt, 'PEAR_Error')) {
-            Horde::logMessage($stmt, 'ERR');
-            return $stmt;
-        }
-        $result = $stmt->execute(array($this->data['share_id']));
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($result, 'ERR');
-            return $result;
-        }
-        $stmt->free();
-
-        if (!empty($this->data['perm']['users'])) {
-            $data = array();
-            foreach ($this->data['perm']['users'] as $user => $perm) {
-                $stmt = $db->prepare('INSERT INTO ' . $table . '_users (share_id, user_uid, perm) VALUES (?, ?, ?)', null, MDB2_PREPARE_MANIP);
-                if (is_a($stmt, 'PEAR_Error')) {
-                    Horde::logMessage($stmt, 'ERR');
-                    return $stmt;
-                }
-                $result = $stmt->execute(array($this->data['share_id'], $user, $perm));
-                if (is_a($result, 'PEAR_Error')) {
-                    Horde::logMessage($result, 'ERR');
-                    return $result;
-                }
-                $stmt->free();
-            }
-        }
-
-        // Update the share's group permissions
-        $stmt = $db->prepare('DELETE FROM ' . $table . '_groups WHERE share_id = ?', null, MDB2_PREPARE_MANIP);
-        if (is_a($stmt, 'PEAR_Error')) {
-            Horde::logMessage($stmt, 'ERR');
-            return $stmt;
-        }
-        $result = $stmt->execute(array($this->data['share_id']));
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($result, 'ERR');
-            return $result;
-        }
-        $stmt->free();
-
-        if (!empty($this->data['perm']['groups'])) {
-            $data = array();
-            foreach ($this->data['perm']['groups'] as $group => $perm) {
-                $stmt = $db->prepare('INSERT INTO ' . $table . '_groups (share_id, group_uid, perm) VALUES (?, ?, ?)', null, MDB2_PREPARE_MANIP);
-                if (is_a($stmt, 'PEAR_Error')) {
-                    Horde::logMessage($stmt, 'ERR');
-                    return $stmt;
-                }
-                $result = $stmt->execute(array($this->data['share_id'], $group, $perm));
-                if (is_a($result, 'PEAR_Error')) {
-                    Horde::logMessage($result, 'ERR');
-                    return $result;
-                }
-                $stmt->free();
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Checks to see if a user has a given permission.
-     *
-     * @param string $userid       The userid of the user.
-     * @param integer $permission  A Horde_Perms::* constant to test for.
-     * @param string $creator      The creator of the event.
-     *
-     * @return boolean  Whether or not $userid has $permission.
-     */
-    function hasPermission($userid, $permission, $creator = null)
-    {
-        if ($userid == $this->data['share_owner']) {
-            return true;
-        }
-
-        return $GLOBALS['injector']->getInstance('Horde_Perms')->hasPermission($this->getPermission(), $userid, $permission, $creator);
-    }
-
-    /**
-     * Sets the permission of this share.
-     *
-     * @param Horde_Perms_Permission $perm  Permission object.
-     * @param boolean $update               Should the share be saved
-     *                                      after this operation?
-     *
-     * @return boolean  True if no error occured, PEAR_Error otherwise
-     */
-    function setPermission($perm, $update = true)
-    {
-        $this->data['perm'] = $perm->getData();
-        if ($update) {
-            return $this->save();
-        }
-        return true;
-    }
-
-    /**
-     * Returns the permission of this share.
-     *
-     * @return Horde_Perms_Permission  Permission object that represents the
-     *                                 permissions on this share.
-     */
-    function getPermission()
-    {
-        $perm = new Horde_Perms_Permission($this->getName());
-        $perm->data = isset($this->data['perm'])
-            ? $this->data['perm']
-            : array();
-
-        return $perm;
-    }
-
-}
diff --git a/framework/Share/Share/sql_hierarchical.php b/framework/Share/Share/sql_hierarchical.php
deleted file mode 100644 (file)
index 937ea0e..0000000
+++ /dev/null
@@ -1,753 +0,0 @@
-<?php
-/**
- * Implementation of Horde_Share class for shared objects that are hierarchical
- * in nature.
- *
- * @author  Duck <duck@obala.net>
- * @author  Michael J. Rubinsky <mrubinsk@horde.org>
- * @package Horde_Share
- */
-class Horde_Share_sql_hierarchical extends Horde_Share_sql {
-
-    /**
-     * The Horde_Share_Object subclass to instantiate objects as
-     *
-     * @var string
-     */
-    var $_shareObject = 'Horde_Share_Object_sql_hierarchical';
-
-    /**
-     * Override new share creation so we can allow for shares with empty
-     * share_names.
-     *
-     */
-    function &newShare($name = '')
-    {
-        $share = &$this->_newShare();
-        $share->setShareOb($this);
-        $share->set('owner', Horde_Auth::getAuth());
-        return $share;
-    }
-
-    /**
-     * Returns a new share object.
-     *
-     * @param string $name  The share's name.
-     *
-     * @return Horde_Share_Object_sql  A new share object.
-     */
-    function &_newShare()
-    {
-        $share = new $this->_shareObject();
-        return $share;
-    }
-
-    /**
-     * Returns an array of all shares that $userid has access to.
-     *
-     * @param string $userid      The userid of the user to check access for.
-     * @param integer $perm       The level of permissions required.
-     * @param mixed $attributes   Restrict the shares counted to those
-     *                            matching $attributes. An array of
-     *                            attribute/values pairs or a share owner
-     *                            username.
-     * @param integer $from       The share to start listing from.
-     * @param integer $count      The number of shares to return.
-     * @param string $sort_by     The field to sort by
-     * @param integer $direction  The sort direction
-     * @param mixed $parent       Either a share_id, Horde_Share_Object or null.
-     * @param boolean $alllevels  List all levels or just the direct children
-     *                            of $parent?
-     *
-     * @return mixed  The shares the user has access to || PEAR_Error
-     */
-    function &listShares($userid, $perm = Horde_Perms::SHOW, $attributes = null,
-                         $from = 0,  $count = 0, $sort_by = null,
-                         $direction = 0, $parent = null,
-                         $allLevels = true, $ignorePerms = false)
-    {
-        $shares = array();
-        if (is_null($sort_by)) {
-            $sortfield = 's.share_id';
-        } elseif ($sort_by == 'owner' || $sort_by == 'id') {
-            $sortfield = 's.share_' . $sort_by;
-        } else {
-            $sortfield = 's.attribute_' . $sort_by;
-        }
-
-        $query = 'SELECT DISTINCT s.* '
-                 . $this->_getShareCriteria($userid, $perm, $attributes,
-                                            $parent, $allLevels, $ignorePerms)
-                 . ' ORDER BY ' . $sortfield
-                 . (($direction == 0) ? ' ASC' : ' DESC');
-        if ($from > 0 || $count > 0) {
-            $this->_db->setLimit($count, $from);
-        }
-
-        Horde::logMessage('Query By Horde_Share_sql_hierarchical: ' . $query, 'DEBUG');
-        $result = $this->_db->query($query);
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($result, 'ERR');
-            return $result;
-        } elseif (empty($result)) {
-            return array();
-        }
-
-        $users = array();
-        $groups = array();
-        while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-            $shares[(int)$share['share_id']] = $this->_fromDriverCharset($share);
-            if ($this->_hasUsers($share)) {
-                $users[] = (int)$share['share_id'];
-            }
-            if ($this->_hasGroups($share)) {
-                $groups[] = (int)$share['share_id'];
-            }
-        }
-        $result->free();
-
-        // Get users permissions
-        if (!empty($users)) {
-            $query = 'SELECT share_id, user_uid, perm FROM ' . $this->_table
-                     . '_users WHERE share_id IN (' . implode(', ', $users)
-                     . ')';
-            $result = $this->_db->query($query);
-            if (is_a($result, 'PEAR_Error')) {
-                Horde::logMessage($result, 'ERR');
-                return $result;
-            } elseif (!empty($result)) {
-                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-                    $shares[$share['share_id']]['perm']['users'][$share['user_uid']] = (int)$share['perm'];
-                }
-                $result->free();
-            }
-        }
-
-        // Get groups permissions
-        if (!empty($groups)) {
-            $query = 'SELECT share_id, group_uid, perm FROM ' . $this->_table
-                     . '_groups WHERE share_id IN (' . implode(', ', $groups)
-                     . ')';
-            $result = $this->_db->query($query);
-            if (is_a($result, 'PEAR_Error')) {
-                Horde::logMessage($result, 'ERR');
-                return $result;
-            } elseif (!empty($result)) {
-                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-                    $shares[$share['share_id']]['perm']['groups'][$share['group_uid']] = (int)$share['perm'];
-                }
-                $result->free();
-            }
-        }
-
-        $sharelist = array();
-        foreach ($shares as $id => $data) {
-            $this->_getSharePerms($data);
-            $sharelist[$id] = new $this->_shareObject($data);
-            $sharelist[$id]->setShareOb($this);
-        }
-        unset($shares);
-
-        try {
-            return Horde::callHook('share_list', array($userid, $perm, $attributes, $sharelist));
-        } catch (Horde_Exception_HookNotSet $e) {}
-
-        return $sharelist;
-    }
-
-    /**
-     * Returns an array of criteria for querying shares.
-     * @access protected
-     *
-     * @param string $userid      The userid of the user to check access for.
-     * @param integer $perm       The level of permissions required.
-     * @param mixed $attributes   Restrict the shares returned to those who
-     *                            have these attribute values.
-     * @param mixed $parent       The share to start searching in.
-     *                            (A Horde_Share_Object, share_id or null)
-     * @param boolean $allLevels  Return all levels, or just the direct
-     *                            children of $parent? Defaults to all levels.
-     *
-     * @return string  The criteria string for fetching this user's shares.
-     */
-    function _getShareCriteria($userid, $perm = Horde_Perms::SHOW, $attributes = null,
-                               $parent = null, $allLevels = true,
-                               $ignorePerms = false)
-    {
-        static $criteria;
-
-        if (is_a($parent, 'Horde_Share_Object')) {
-            $parent_id = $parent->getId();
-        } else {
-            $parent_id = $parent;
-        }
-        $key = $userid . $perm . $parent_id . $allLevels
-               . (is_array($attributes) ? serialize($attributes) : $attributes);
-        if (isset($criteria[$key])) {
-            return $criteria[$key];
-        }
-
-        $query = ' FROM ' . $this->_table . ' s ';
-        $where = '';
-
-        if (!$ignorePerms) {
-            if (empty($userid)) {
-                $where = '(' . Horde_SQL::buildClause($this->_db, 's.perm_guest', '&', $perm) . ')';
-            } else {
-                // (owner == $userid)
-                $where = 's.share_owner = ' . $this->_db->quote($userid);
-
-                // (name == perm_creator and val & $perm)
-                $where .= ' OR (' . Horde_SQL::buildClause($this->_db, 's.perm_creator', '&', $perm) . ')';
-
-                // (name == perm_creator and val & $perm)
-                $where .= ' OR (' . Horde_SQL::buildClause($this->_db, 's.perm_default',  '&', $perm) . ')';
-
-                // (name == perm_users and key == $userid and val & $perm)
-                $query .= ' LEFT JOIN ' . $this->_table . '_users AS u ON u.share_id = s.share_id';
-                $where .= ' OR ( u.user_uid = ' .  $this->_write_db->quote($userid)
-                . ' AND (' . Horde_SQL::buildClause($this->_db, 'u.perm', '&', $perm) . '))';
-
-                // If the user has any group memberships, check for those also.
-                require_once 'Horde/Group.php';
-                $group = &Group::singleton();
-                $groups = $group->getGroupMemberships($userid, true);
-                if (!is_a($groups, 'PEAR_Error') && $groups) {
-                    // (name == perm_groups and key in ($groups) and val & $perm)
-                    $ids = array_keys($groups);
-                    $group_ids = array();
-                    foreach ($ids as $id) {
-                        $group_ids[] = $this->_db->quote((string)$id);
-                    }
-                    $query .= ' LEFT JOIN ' . $this->_table . '_groups AS g ON g.share_id = s.share_id';
-                    $where .= ' OR (g.group_uid IN (' . implode(',', $group_ids) . ')'
-                        . ' AND (' . Horde_SQL::buildClause($this->_db, 'g.perm', '&', $perm) . '))';
-                }
-            }
-        }
-
-        /* Convert to driver's keys */
-        $attributes = $this->_toDriverKeys($attributes);
-
-        /* ...and to driver charset */
-        $attributes = $this->_toDriverCharset($attributes);
-
-        if (is_array($attributes)) {
-            // Build attribute/key filter.
-            if (!empty($where)) {
-                $where = ' (' . $where . ') ';
-            }
-            foreach ($attributes as $key => $value) {
-                $where .= ' AND ' . $key;
-                if (is_array($value)) {
-                    $where .= ' ' . $value[0]. ' ' . $this->_db->quote($value[1]);
-                } else {
-                    $where .= ' = ' . $this->_db->quote($value);
-                }
-            }
-        } elseif (!empty($attributes)) {
-            // Restrict to shares owned by the user specified
-            $where = (!empty($where) ? ' (' . $where . ') AND ' : ' ') . 's.share_owner = ' . $this->_db->quote($attributes);
-        }
-
-        // See if we need to filter by parent or get the parent object
-        if ($parent != null) {
-            if (!is_a($parent, 'Horde_Share_Object')) {
-                $parent = $this->getShareById($parent);
-                if (is_a($parent, 'PEAR_Error')) {
-                    return $parent;
-                }
-            }
-
-            // Need to append the parent's share id to the list of parents in
-            // order to search the share_parents field.
-            $parents = $parent->get('parents') . ':' . $parent->getId();
-            if ($allLevels) {
-                $where_parent = '(share_parents = ' . $this->_db->quote($parents)
-                        . ' OR share_parents LIKE ' . $this->_db->quote($parents . ':%') . ')';
-            } else {
-                $where_parent = 's.share_parents = ' . $this->_db->quote($parents);
-            }
-        } elseif (!$allLevels) {
-            // No parents, and we only want the root.
-            $where_parent = "(s.share_parents = '' OR s.share_parents IS NULL)";
-        }
-
-        if (empty($where_parent)) {
-            $criteria[$key] = $query . ' WHERE ' . $where;
-        } else {
-            if (!empty($where)) {
-                $criteria[$key] = $query . ' WHERE (' . $where . ') AND ' . $where_parent;
-            } else {
-                $criteria[$key] = $query . ' WHERE ' . $where_parent;
-            }
-        }
-
-        return $criteria[$key];
-    }
-
-    /**
-     * Return a list of users who have shares with the given permissions
-     * for the current user.
-     *
-     * @param integer $perm       The level of permissions required.
-     * @param mixed  $parent      The parent share to start looking in.
-     *                            (Horde_Share_Object, share_id, or null)
-     * @param boolean $allLevels  Return all levels, or just the direct
-     *                            children of $parent? Defaults to all levels.
-     * @param integer $from       The user to start listing at.
-     * @param integer $count      The number of users to return.
-     *
-     * @return array  List of users.
-     */
-    function listOwners($perm = Horde_Perms::SHOW, $parent = null, $allLevels = true,
-                        $from = 0, $count = 0)
-    {
-        $sql = 'SELECT DISTINCT(s.share_owner) '
-                . $this->_getShareCriteria(Horde_Auth::getAuth(), $perm, null,
-                                           $parent, $allLevels);
-
-        if ($count) {
-            $this->_db->setLimit($count, $from);
-        }
-
-        $allowners = $this->_db->queryCol($sql);
-        if (is_a($allowners, 'PEAR_Error')) {
-             Horde::logMessage($allowners, 'ERR');
-             return $allowners;
-        }
-
-        $owners = array();
-        foreach ($allowners as $owner) {
-            if ($this->countShares(Horde_Auth::getAuth(), $perm, $owner, $parent,
-                                   $allLevels)) {
-
-                $owners[] = $owner;
-            }
-        }
-
-        return $owners;
-    }
-
-    /**
-     * Count the number of users who have shares with the given permissions
-     * for the current user.
-     *
-     * @param integer $perm       The level of permissions required.
-     * @param mixed $parent       The parent share to start looking in.
-     *                            (Horde_Share_Object, share_id, or null).
-     * @param boolean $allLevels  Return all levels, or just the direct
-     *                            children of $parent?
-     *
-     * @return integer  Number of users.
-     */
-    function countOwners($perm = Horde_Perms::SHOW, $parent = null, $allLevels = true)
-    {
-        $sql = 'SELECT COUNT(DISTINCT(s.share_owner)) '
-               . $this->_getShareCriteria(Horde_Auth::getAuth(), $perm, null, $parent,
-                                          $allLevels);
-
-        return $this->_db->queryOne($sql);
-    }
-
-    /**
-     * Returns a share's direct parent object.
-     *
-     * @param Horde_Share_Object $share  The share to get parent for.
-     *
-     * @return Horde_Share_Object The parent share, if it exists.
-     */
-    function getParent($child)
-    {
-        $parents = $child->get('parents');
-
-        // No parents, this is at the root.
-        if (empty($parents)) {
-            return null;
-        }
-        $parents = explode(':', $parents);
-        return $this->getShareById(array_pop($parents));
-    }
-
-    /**
-     * Returns a Horde_Share_Object object corresponding to the given unique
-     * ID, with the details retrieved appropriately.
-     *
-     * @param string $cid  The id of the share to retrieve.
-     *
-     * @return Horde_Share_Object  The requested share.
-     */
-    function &getShareById($cid)
-    {
-        if (!isset($this->_cache[$cid])) {
-            $share = &$this->_getShareById($cid);
-            if (is_a($share, 'PEAR_Error')) {
-                return $share;
-            }
-            $share->setShareOb($this);
-            $this->_cache[$cid] = &$share;
-        }
-
-        return $this->_cache[$cid];
-    }
-
-    /**
-     * Returns an array of Horde_Share_Object objects corresponding to the
-     * given set of unique IDs, with the details retrieved appropriately.
-     *
-     * @param array $cids  The array of ids to retrieve.
-     *
-     * @return array  The requested shares keyed by share_id.
-     */
-    function &getShares($cids)
-    {
-        $all_shares = array();
-        $missing_ids = array();
-        foreach ($cids as $cid) {
-            if (isset($this->_cache[$cid])) {
-                $all_shares[] = &$this->_cache[$cid];
-            } else {
-                $missing_ids[] = $cid;
-            }
-        }
-
-        if (count($missing_ids)) {
-            $shares = &$this->_getShares($missing_ids);
-            if (is_a($shares, 'PEAR_Error')) {
-                return $shares;
-            }
-
-            foreach (array_keys($shares) as $key) {
-                $this->_cache[$key] = &$shares[$key];
-                $this->_cache[$key]->setShareOb($this);
-                $all_shares[$key] = &$this->_cache[$key];
-            }
-        }
-
-        return $all_shares;
-    }
-
-   /**
-     * Removes a share from the shares system permanently. This will recursively
-     * delete all child shares as well.
-     *
-     * @param Horde_Share_Object $share  The share to remove.
-     * @throws Horde_Exception
-     */
-    function removeShare(&$share)
-    {
-        if (!is_a($share, 'Horde_Share_Object')) {
-            return PEAR::raiseError('Shares must be Horde_Share_Object objects or extend that class.');
-        }
-
-        try {
-            Horde::callHook('share_remove', array($share));
-        } catch (Horde_Exception_HookNotSet $e) {}
-
-        /* Get the list of all $share's children */
-        $children = $share->getChildren(null, true);
-
-        /* Remove share from the caches. */
-        $id = $share->getId();
-        $this->_cache = array();
-        $this->_listCache = array();
-
-        foreach ($children as $child) {
-            $result = $this->_removeShare($child);
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
-            }
-        }
-
-        return $this->_removeShare($share);
-    }
-
-    /**
-     * Returns an array of Horde_Share_Object_sql objects corresponding
-     * to the given set of unique IDs, with the details retrieved
-     * appropriately.
-     *
-     * @param array $cids  The array of ids to retrieve.
-     *
-     * @return array  The requested shares keyed by share_id.
-     */
-    function &_getShares($ids)
-    {
-        $shares = array();
-        $query = 'SELECT * FROM ' . $this->_table . ' WHERE share_id IN (' . implode(', ', $ids) . ')';
-        $result = $this->_db->query($query);
-        if (is_a($result, 'PEAR_Error')) {
-            Horde::logMessage($result, 'ERR');
-            return $result;
-        } elseif (empty($result)) {
-            return array();
-        }
-
-        $groups = array();
-        $users = array();
-        while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-            $shares[(int)$share['share_id']] = $this->_fromDriverCharset($share);
-            if ($this->_hasUsers($share)) {
-                $users[] = (int)$share['share_id'];
-            }
-            if ($this->_hasGroups($share)) {
-                $groups[] = (int)$share['share_id'];
-            }
-        }
-        $result->free();
-
-        // Get users permissions
-        if (!empty($users)) {
-            $query = 'SELECT share_id, user_uid, perm FROM ' . $this->_table . '_users '
-                    . ' WHERE share_id IN (' . implode(', ', $users) . ')';
-            $result = $this->_db->query($query);
-            if (is_a($result, 'PEAR_Error')) {
-                Horde::logMessage($result, 'ERR');
-                return $result;
-            } elseif (!empty($result)) {
-                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-                    $shares[$share['share_id']]['perm']['users'][$share['user_uid']] = (int)$share['perm'];
-                }
-                $result->free();
-            }
-        }
-
-        // Get groups permissions
-        if (!empty($groups)) {
-            $query = 'SELECT share_id, group_uid, perm FROM ' . $this->_table . '_groups'
-                   . ' WHERE share_id IN (' . implode(', ', $groups) . ')';
-            $result = $this->_db->query($query);
-            if (is_a($result, 'PEAR_Error')) {
-                Horde::logMessage($result, 'ERR');
-                return $result;
-            } elseif (!empty($result)) {
-                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
-                    $shares[$share['share_id']]['perm']['groups'][$share['group_uid']] = (int)$share['perm'];
-                }
-                $result->free();
-            }
-        }
-
-        $sharelist = array();
-        foreach ($shares as $id => $data) {
-            $sharelist[$id] = new $this->_shareObject($data);
-        }
-
-        return $sharelist;
-    }
-
-    /**
-     * Override the Horde_Share base class to avoid any confusion
-     *
-     */
-    function getShare($name)
-    {
-        return PEAR::raiseError(_("Share names are not supported in this driver"));
-    }
-
-    /**
-     * Returns the count of all shares that $userid has access to.
-     *
-     * @param string  $userid      The userid of the user to check access for.
-     * @param integer $perm        The level of permissions required.
-     * @param mixed   $attributes  Restrict the shares counted to those
-     *                             matching $attributes. An array of
-     *                             attribute/values pairs or a share owner
-     *                             username.
-     * @param mixed  $parent      The share to start searching from
-     *                            (Horde_Share_Object, share_id, or null)
-     * @param boolean $allLevels  Return all levels, or just the direct
-     *                            children of $parent?
-     *
-     * @return integer  Number of shares the user has access to.
-     */
-    function countShares($userid, $perm = Horde_Perms::SHOW, $attributes = null,
-                         $parent = null, $allLevels = true)
-    {
-        $query = 'SELECT COUNT(DISTINCT s.share_id) '
-                 . $this->_getShareCriteria($userid, $perm, $attributes,
-                                            $parent, $allLevels);
-
-        return $this->_db->queryOne($query);
-    }
-
-}
-
-/**
- * Class for storing Share information.
- */
-class Horde_Share_Object_sql_hierarchical extends Horde_Share_Object_sql {
-
-    /**
-     * Constructor. This is here primarily to make calling the parent
-     * constructor(s) from any subclasses cleaner.
-     *
-     * @param unknown_type $data
-     * @return Horde_Share_Object_sql_hierarchical
-     */
-    function Horde_Share_Object_sql_hierarchical($data)
-    {
-        if (!isset($data['share_parents'])) {
-            $data['share_parents'] = null;
-        }
-        parent::Horde_Share_Object_sql($data);
-    }
-
-    function inheritPermissions()
-    {
-        //FIXME: Not called from anywhere yet anyway.
-    }
-
-    /**
-     * Return a count of the number of children this share has
-     *
-     * @param integer $perm  A Horde_Perms::* constant
-     * @param boolean $allLevels  Count grandchildren or just children
-     *
-     * @return mixed  The number of child shares || PEAR_Error
-     */
-    function countChildren($perm = Horde_Perms::SHOW, $allLevels = true)
-    {
-        return $this->_shareOb->countShares(Horde_Auth::getAuth(), $perm, null, $this, $allLevels);
-    }
-
-    /**
-     * Get all children of this share.
-     *
-     * @param int $perm           Horde_Perms::* constant. If NULL will return
-     *                            all shares regardless of permissions.
-     * @param boolean $allLevels  Return all levels.
-     *
-     * @return mixed  An array of Horde_Share_Object objects || PEAR_Error
-     */
-    function getChildren($perm = Horde_Perms::SHOW, $allLevels = true)
-    {
-        return $this->_shareOb->listShares(Horde_Auth::getAuth(), $perm, null, 0, 0,
-             null, 1, $this, $allLevels, is_null($perm));
-
-    }
-
-    /**
-     * Returns a child's direct parent
-     *
-     * @return mixed  The direct parent Horde_Share_Object or PEAR_Error
-     */
-    function getParent()
-    {
-        return $this->_shareOb->getParent($this);
-    }
-
-    /**
-     * Get all of this share's parents.
-     *
-     * @return array()  An array of Horde_Share_Objects
-     */
-    function getParents()
-    {
-        $parents = array();
-        $share = $this->getParent();
-        while (is_a($share, 'Horde_Share_Object')) {
-            $parents[] = $share;
-            $share = $share->getParent();
-        }
-        return array_reverse($parents);
-    }
-
-    /**
-     * Set the parent object for this share.
-     *
-     * @param mixed $parent    A Horde_Share object or share id for the parent.
-     *
-     * @return mixed  true || PEAR_Error
-     */
-    function setParent($parent)
-    {
-        if (!is_null($parent) && !is_a($parent, 'Horde_Share_Object')) {
-            $parent = $this->_shareOb->getShareById($parent);
-            if (is_a($parent, 'PEAR_Error')) {
-                Horde::logMessage($parent, 'ERR');
-                return $parent;
-            }
-        }
-
-        /* If we are an existing share, check for any children */
-        if ($this->getId()) {
-            $children = $this->_shareOb->listShares(
-                Horde_Auth::getAuth(), Horde_Perms::EDIT, null, 0, 0, null, 0,
-                $this->getId());
-        } else {
-            $children = array();
-        }
-
-        /* Can't set a child share as a parent */
-        if (!empty($parent) && in_array($parent->getId(), array_keys($children))) {
-            return PEAR::raiseError('Cannot set an existing child as the parent');
-        }
-
-        if (!is_null($parent)) {
-            $parent_string = $parent->get('parents') . ':' . $parent->getId();
-        } else {
-            $parent_string = null;
-        }
-        $this->data['share_parents'] = $parent_string;
-        $query = $this->_shareOb->_write_db->prepare('UPDATE ' . $this->_shareOb->_table . ' SET share_parents = ? WHERE share_id = ?', null, MDB2_PREPARE_MANIP);
-        $result = $query->execute(array($this->data['share_parents'], $this->getId()));
-        $query->free();
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        /* Now we can reset the children's parent */
-        foreach($children as $child) {
-            $child->setParent($this);
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns the permission of this share.
-     *
-     * @return Horde_Perms_Permission  Permission object that represents the
-     *                               permissions on this share.
-     */
-    function &getPermission()
-    {
-        $perm = new Horde_Perms_Permission('');
-        $perm->data = isset($this->data['perm'])
-            ? $this->data['perm']
-            : array();
-
-        return $perm;
-    }
-
-    /**
-     * Returns one of the attributes of the object, or null if it isn't
-     * defined.
-     *
-     * @param string $attribute  The attribute to retrieve.
-     *
-     * @return mixed  The value of the attribute, or an empty string.
-     */
-    function _get($attribute)
-    {
-        if ($attribute == 'owner' || $attribute == 'parents') {
-            return $this->data['share_' . $attribute];
-        } elseif (isset($this->data['attribute_' . $attribute])) {
-            return $this->data['attribute_' . $attribute];
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Hierarchical shares do not have share names.
-     *
-     * @return unknown
-     */
-    function _getName()
-    {
-        return '';
-    }
-
-}
diff --git a/framework/Share/lib/Horde/Share.php b/framework/Share/lib/Horde/Share.php
new file mode 100644 (file)
index 0000000..d838fb5
--- /dev/null
@@ -0,0 +1,533 @@
+<?php
+/**
+ * Horde_Share:: provides an interface to all shares a user might have.  Its
+ * methods take care of any site-specific restrictions configured in in the
+ * application's prefs.php and conf.php files.
+ *
+ * Copyright 2002-2010 The Horde Project (http://www.horde.org/)
+ * Copyright 2002-2007 Infoteck Internet <webmaster@infoteck.qc.ca>
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Joel Vandal <joel@scopserv.com>
+ * @author  Mike Cochrame <mike@graftonhall.co.nz>
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @author  Jan Schneider <jan@horde.org>
+ * @author  Gunnar Wrobel <wrobel@pardus.de>
+ * @package Horde_Share
+ */
+class Horde_Share
+{
+    /**
+     * The application we're managing shares for.
+     *
+     * @var string
+     */
+    protected $_app;
+
+    /**
+     * The root of the Share tree.
+     *
+     * @var mixed
+     */
+    protected $_root = null;
+
+    /**
+     * A cache of all shares that have been retrieved, so we don't hit the
+     * backend again and again for them.
+     *
+     * @var array
+     */
+    protected $_cache = array();
+
+    /**
+     * Id-name-map of already cached share objects.
+     *
+     * @var array
+     */
+    protected $_shareMap = array();
+
+    /**
+     * Cache used for listShares().
+     *
+     * @var array
+     */
+    protected $_listcache = array();
+
+    /**
+     * A list of objects that we're currently sorting, for reference during the
+     * sorting algorithm.
+     *
+     * @var array
+     */
+    protected $_sortList;
+
+    /**
+     * The Horde_Share_Object subclass to instantiate objects as
+     *
+     * @var string
+     */
+    protected $_shareObject;
+
+
+    /**
+     * Attempts to return a reference to a concrete Horde_Share instance.
+     *
+     * It will only create a new instance if no Horde_Share instance currently
+     * exists.
+     *
+     * @param string $app     The application that the shares relates to.
+     * @param string $driver  Type of concrete Share subclass to return,
+     *                        based on storage driver ($driver). The code is
+     *                        dynamically included.
+     *
+     * @return Horde_Share  The concrete Share reference, or false on an error.
+     */
+    public static function singleton($app, $driver = null)
+    {
+        static $shares = array();
+
+        // FIXME: This is a temporary solution until the configuration value
+        // actually exists and all apps call this code in the correct fashion.
+        $driver = basename($driver);
+        if (empty($driver)) {
+            if (!empty($GLOBALS['conf']['share']['driver'])) {
+                $driver = $GLOBALS['conf']['share']['driver'];
+            } else {
+                $driver = 'datatree';
+            }
+        }
+
+        $class = 'Horde_Share_' . $driver;
+        if (!class_exists($class)) {
+            include dirname(__FILE__) . '/Share/' . $driver . '.php';
+        }
+
+        $signature = $app . '_' . $driver;
+        if (!isset($shares[$signature]) &&
+            !empty($GLOBALS['conf']['share']['cache'])) {
+            $session = new Horde_SessionObjects();
+            $shares[$signature] = $session->query('horde_share_' . $app . '_' . $driver . '1');
+        }
+
+        if (empty($shares[$signature])) {
+            if (class_exists($class)) {
+                $shares[$signature] = new $class($app);
+            } else {
+                $result = PEAR::raiseError(sprintf(_("\"%s\" share driver not found."), $driver));
+                return $result;
+            }
+        }
+
+        if (!empty($GLOBALS['conf']['share']['cache'])) {
+            register_shutdown_function(array($shares[$signature], 'shutdown'));
+        }
+
+        return $shares[$signature];
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param string $app  The application that the shares belong to.
+     */
+    public function __construct($app)
+    {
+        $this->_app = $app;
+        $this->__wakeup();
+    }
+
+    /**
+     * Initializes the object.
+     *
+     * @throws Horde_Exception
+     */
+    public function __wakeup()
+    {
+        try {
+            Horde::callHook('share_init', array($this, $this->_app));
+        } catch (Horde_Exception_HookNotSet $e) {}
+    }
+
+    /**
+     * Returns the properties that need to be serialized.
+     *
+     * @return array  List of serializable properties.
+     */
+    public function __sleep()
+    {
+        $properties = get_object_vars($this);
+        unset($properties['_sortList']);
+        $properties = array_keys($properties);
+        return $properties;
+    }
+
+    /**
+     * Stores the object in the session cache.
+     */
+    public function shutdown()
+    {
+        $driver = str_replace('horde_share_', '', Horde_String::lower(get_class($this)));
+        $session = new Horde_SessionObjects();
+        $session->overwrite('horde_share_' . $this->_app . '_' . $driver, $this, false);
+    }
+
+    /**
+     * Returns the application we're managing shares for.
+     *
+     * @return string  The application this share belongs to.
+     */
+    public function getApp()
+    {
+        return $this->_app;
+    }
+
+    /**
+     * Returns a Horde_Share_Object object corresponding to the given share
+     * name, with the details retrieved appropriately.
+     *
+     * @param string $name  The name of the share to retrieve.
+     *
+     * @return Horde_Share_Object  The requested share.
+     */
+    public function getShare($name)
+    {
+        if (isset($this->_cache[$name])) {
+            return $this->_cache[$name];
+        }
+
+        $share = $this->_getShare($name);
+        $share->setShareOb($this);
+        $this->_shareMap[$share->getId()] = $name;
+        $this->_cache[$name] = $share;
+
+        return $share;
+    }
+
+    /**
+     * Returns a Horde_Share_Object object corresponding to the given unique
+     * ID, with the details retrieved appropriately.
+     *
+     * @param string $cid  The id of the share to retrieve.
+     *
+     * @return Horde_Share_Object  The requested share.
+     */
+    public function getShareById($cid)
+    {
+        if (!isset($this->_shareMap[$cid])) {
+            $share = $this->_getShareById($cid);
+            $share->setShareOb($this);
+            $name = $share->getName();
+            $this->_cache[$name] = $share;
+            $this->_shareMap[$cid] = $name;
+        }
+
+        return $this->_cache[$this->_shareMap[$cid]];
+    }
+
+    /**
+     * Returns an array of Horde_Share_Object objects corresponding to the
+     * given set of unique IDs, with the details retrieved appropriately.
+     *
+     * @param array $cids  The array of ids to retrieve.
+     *
+     * @return array  The requested shares.
+     */
+    public function getShares($cids)
+    {
+        $all_shares = array();
+        $missing_ids = array();
+        foreach ($cids as $cid) {
+            if (isset($this->_shareMap[$cid])) {
+                $all_shares[$this->_shareMap[$cid]] = $this->_cache[$this->_shareMap[$cid]];
+            } else {
+                $missing_ids[] = $cid;
+            }
+        }
+
+        if (count($missing_ids)) {
+            $shares = $this->_getShares($missing_ids);
+            foreach (array_keys($shares) as $key) {
+                $this->_cache[$key] = $shares[$key];
+                $this->_cache[$key]->setShareOb($this);
+                $this->_shareMap[$shares[$key]->getId()] = $key;
+                $all_shares[$key] = $this->_cache[$key];
+            }
+        }
+
+        return $all_shares;
+    }
+
+    /**
+     * Lists *all* shares for the current app/share, regardless of
+     * permissions.
+     *
+     * This is for admin functionality and scripting tools, and shouldn't be
+     * called from user-level code!
+     *
+     * @return array  All shares for the current app/share.
+     */
+    public function listAllShares()
+    {
+        $shares = $this->_listAllShares();
+        $this->_sortList = $shares;
+        uasort($shares, array($this, '_sortShares'));
+        $this->_sortList = null;
+
+        return $shares;
+    }
+
+    /**
+     * Returns an array of all shares that $userid has access to.
+     *
+     * @param string $userid     The userid of the user to check access for.
+     * @param integer $perm      The level of permissions required.
+     * @param mixed $attributes  Restrict the shares counted to those
+     *                           matching $attributes. An array of
+     *                           attribute/values pairs or a share owner
+     *                           username.
+     *
+     * @return array  The shares the user has access to.
+     */
+    public function listShares($userid, $perm = Horde_Perms::SHOW, $attributes = null,
+                        $from = 0, $count = 0, $sort_by = null, $direction = 0)
+    {
+        $shares = $this->_listShares($userid, $perm, $attributes, $from,
+                                     $count, $sort_by, $direction);
+        if (!count($shares)) {
+            return $shares;
+        }
+
+        $shares = $this->getShares($shares);
+        if (is_null($sort_by)) {
+            $this->_sortList = $shares;
+            uasort($shares, array($this, '_sortShares'));
+            $this->_sortList = null;
+        }
+
+        try {
+            return Horde::callHook('share_list', array($userid, $perm, $attributes, $shares));
+        } catch (Horde_Exception_HookNotSet $e) {}
+
+        return $shares;
+    }
+
+    /**
+     * Returns an array of all system shares.
+     *
+     * @return array  All system shares.
+     */
+    public function listSystemShares()
+    {
+        return array();
+    }
+
+    /**
+     * Returns the number of shares that $userid has access to.
+     *
+     * @param string $userid     The userid of the user to check access for.
+     * @param integer $perm      The level of permissions required.
+     * @param mixed $attributes  Restrict the shares counted to those
+     *                           matching $attributes. An array of
+     *                           attribute/values pairs or a share owner
+     *                           username.
+     *
+     * @return integer  The number of shares
+     */
+    public function countShares($userid, $perm = Horde_Perms::SHOW, $attributes = null)
+    {
+        return $this->_countShares($userid, $perm, $attributes);
+    }
+
+    /**
+     * Returns a new share object.
+     *
+     * @param string $name  The share's name.
+     *
+     * @return Horde_Share_Object  A new share object.
+     * @throws Horde_Share_Exception
+     */
+    public function newShare($name)
+    {
+        if (empty($name)) {
+            throw new Horde_Share_Exception('Share names must be non-empty');
+        }
+        $share = $this->_newShare($name);
+        $share->setShareOb($this);
+        $share->set('owner', Horde_Auth::getAuth());
+
+        return $share;
+    }
+
+    /**
+     * Adds a share to the shares system.
+     *
+     * The share must first be created with Horde_Share::newShare(), and have
+     * any initial details added to it, before this function is called.
+     *
+     * @param Horde_Share_Object $share  The new share object.
+     *
+     * @return boolean
+     * @throws Horde_Share_Exception
+     */
+    public function addShare($share)
+    {
+        if (!is_a($share, 'Horde_Share_Object')) {
+            throw new Horde_Share_Exception('Shares must be Horde_Share_Object objects or extend that class.');
+        }
+
+        try {
+            Horde::callHook('share_add', array($share));
+        } catch (Horde_Exception_HookNotSet $e) {}
+
+        $result = $this->_addShare($share);
+
+        /* Store new share in the caches. */
+        $id = $share->getId();
+        $name = $share->getName();
+        $this->_cache[$name] = $share;
+        $this->_shareMap[$id] = $name;
+
+        /* Reset caches that depend on unknown criteria. */
+        $this->_listCache = array();
+
+        return $result;
+    }
+
+    /**
+     * Removes a share from the shares system permanently.
+     *
+     * @param Horde_Share_Object $share  The share to remove.
+     *
+     * @throws Horde_Share_Exception
+     */
+    public function removeShare($share)
+    {
+        if (!is_a($share, 'Horde_Share_Object')) {
+            throw new Horde_Share_Exception('Shares must be Horde_Share_Object objects or extend that class.');
+        }
+
+        try {
+            Horde::callHook('share_remove', array($share));
+        } catch (Horde_Exception_HookNotSet $e) {}
+
+        /* Remove share from the caches. */
+        $id = $share->getId();
+        unset($this->_shareMap[$id]);
+        unset($this->_cache[$share->getName()]);
+
+        /* Reset caches that depend on unknown criteria. */
+        $this->_listCache = array();
+
+        return $this->_removeShare($share);
+    }
+
+    /**
+     * Checks if a share exists in the system.
+     *
+     * @param string $share  The share to check.
+     *
+     * @return boolean  True if the share exists.
+     */
+    public function exists($share)
+    {
+        if (isset($this->_cache[$share])) {
+            return true;
+        }
+
+        return $this->_exists($share);
+    }
+
+    /**
+     * Finds out what rights the given user has to this object.
+     *
+     * @see Horde_Perms::getPermissions
+     *
+     * @param mixed $share  The share that should be checked for the users
+     *                      permissions.
+     * @param string $user  The user to check for.
+     *
+     * @return mixed  A bitmask of permissions, a permission value, or an array
+     *                of permission values the user has, depending on the
+     *                permission type and whether the permission value is
+     *                ambiguous. False if there is no such permsission.
+     */
+    public function getPermissions($share, $user = null)
+    {
+        if (!is_a($share, 'Horde_Share_Object')) {
+            $share = $this->getShare($share);
+        }
+
+        $perm = $share->getPermission();
+        return $GLOBALS['injector']->getInstance('Horde_Perms')->getPermissions($perm, $user);
+    }
+
+    /**
+     * Returns the Identity for a particular share owner.
+     *
+     * @deprecated
+     *
+     * @param mixed $share  The share to fetch the Identity for - either the
+     *                      string name, or the Horde_Share_Object object.
+     *
+     * @return Identity  An Identity instance.
+     */
+    public function getIdentityByShare($share)
+    {
+        if (!is_a($share, 'Horde_Share_Object')) {
+            $share = $this->getShare($share);
+        }
+
+        // TODO: Need to inject this into this method instead of using injector
+        return $GLOBALS['injector']->getInstance('Horde_Prefs_Identity')->getIdentity($share->get('owner'));
+    }
+
+    /**
+     * Set the class type to use for creating share objects.
+     *
+     * @var string $classname  The classname to use.
+     */
+    public function setShareClass($classname)
+    {
+        $this->_shareObject = $classname;
+    }
+
+    /**
+     * Utility function to be used with uasort() for sorting arrays of
+     * Horde_Share objects.
+     *
+     * Example:
+     * <code>
+     * uasort($list, array('Horde_Share', '_sortShares'));
+     * </code>
+     *
+     * @access protected
+     */
+    protected function _sortShares($a, $b)
+    {
+        $aParts = explode(':', $a->getName());
+        $bParts = explode(':', $b->getName());
+
+        $min = min(count($aParts), count($bParts));
+        $idA = '';
+        $idB = '';
+        for ($i = 0; $i < $min; $i++) {
+            if ($idA) {
+                $idA .= ':';
+                $idB .= ':';
+            }
+            $idA .= $aParts[$i];
+            $idB .= $bParts[$i];
+
+            if ($idA != $idB) {
+                $curA = isset($this->_sortList[$idA]) ? $this->_sortList[$idA]->get('name') : '';
+                $curB = isset($this->_sortList[$idB]) ? $this->_sortList[$idB]->get('name') : '';
+                return strnatcasecmp($curA, $curB);
+            }
+        }
+
+        return count($aParts) > count($bParts);
+    }
+
+}
diff --git a/framework/Share/lib/Horde/Share/Datatree.php b/framework/Share/lib/Horde/Share/Datatree.php
new file mode 100644 (file)
index 0000000..ccebe21
--- /dev/null
@@ -0,0 +1,348 @@
+<?php
+
+require_once 'Horde/DataTree.php';
+
+/**
+ * Horde_Share_datatree:: provides the datatree backend for the horde share
+ * driver.
+ *
+ * Copyright 2002-2010 The Horde Project (http://www.horde.org/)
+ * Copyright 2002-2007 Infoteck Internet <webmaster@infoteck.qc.ca>
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Joel Vandal <joel@scopserv.com>
+ * @author  Mike Cochrame <mike@graftonhall.co.nz>
+ * @author  Chuck Hagenbuch <chuck@horde.org>
+ * @author  Jan Schneider <jan@horde.org>
+ * @author  Gunnar Wrobel <wrobel@pardus.de>
+ * @package Horde_Share
+ */
+class Horde_Share_Datatree extends Horde_Share
+{
+    /**
+     * The Horde_Share_Object subclass to instantiate objects as
+     *
+     * @var string
+     */
+    protected $_shareObject = 'Horde_Share_Object_Datatree';
+
+    /**
+     * Pointer to a DataTree instance to manage/store shares
+     *
+     * @var DataTree
+     */
+    protected $_datatree;
+
+    /**
+     * Initializes the object.
+     *
+     * @throws Horde_Exception
+     */
+    public function __wakeup()
+    {
+        // TODO: Remove GLOBAL config access
+        if (empty($GLOBALS['conf']['datatree']['driver'])) {
+            throw new Horde_Exception('You must configure a DataTree backend to use Shares.');
+        }
+
+        $driver = $GLOBALS['conf']['datatree']['driver'];
+        $this->_datatree = &DataTree::singleton(
+            $driver,
+            array_merge(Horde::getDriverConfig('datatree', $driver),
+                        array('group' => 'horde.shares.' . $this->_app))
+        );
+
+        foreach (array_keys($this->_cache) as $name) {
+            if (!is_a($this->_datatree, 'PEAR_Error')) {
+                $this->_cache[$name]->setShareOb($this);
+                $this->_cache[$name]->datatreeObject->setDataTree($this->_datatree);
+            }
+        }
+
+        parent::__wakeup();
+    }
+
+    /**
+     * Returns a Horde_Share_Object_datatree object corresponding to the given
+     * share name, with the details retrieved appropriately.
+     *
+     * @param string $name  The name of the share to retrieve.
+     *
+     * @return Horde_Share_Object_datatree  The requested share.
+     * @throws Horde_Share_Exception
+     */
+    public function _getShare($name)
+    {
+        $datatreeObject = $this->_datatree->getObject($name, 'DataTreeObject_Share');
+        if ($datatreeObject instanceof PEAR_Error) {
+            throw new Horde_Share_Exception($datatreeObject->getMessage());
+        }
+        $share = new $this->_shareObject($datatreeObject);
+
+        return $share;
+    }
+
+    /**
+     * Returns a Horde_Share_Object_datatree object corresponding to the given
+     * unique ID, with the details retrieved appropriately.
+     *
+     * @param string $cid  The id of the share to retrieve.
+     *
+     * @return Horde_Share_Object_datatree  The requested share.
+     * @throws Horde_Share_Exception
+     */
+    protected function _getShareById($id)
+    {
+        $datatreeObject = $this->_datatree->getObjectById($id, 'DataTreeObject_Share');
+        if (is_a($datatreeObject, 'PEAR_Error')) {
+            throw new Horde_Share_Exception($datatreeObject->getMessage());
+        }
+        $share = new $this->_shareObject($datatreeObject);
+
+        return $share;
+    }
+
+    /**
+     * Returns an array of Horde_Share_Object_datatree objects corresponding
+     * to the given set of unique IDs, with the details retrieved
+     * appropriately.
+     *
+     * @param array $cids  The array of ids to retrieve.
+     *
+     * @return array  The requested shares.
+     * @throws Horde_Share_Exception
+     */
+    protected function _getShares($ids)
+    {
+        $shares = array();
+        $objects = &$this->_datatree->getObjects($ids, 'DataTreeObject_Share');
+        if (is_a($objects, 'PEAR_Error')) {
+            throw new Horde_Share_Exception($objects->getMessage());
+        }
+        foreach (array_keys($objects) as $key) {
+            if (is_a($objects[$key], 'PEAR_Error')) {
+                return $objects[$key];
+            }
+            $shares[$key] = new $this->_shareObject($objects[$key]);
+        }
+        return $shares;
+    }
+
+    /**
+     * Lists *all* shares for the current app/share, regardless of
+     * permissions.
+     *
+     * @return array  All shares for the current app/share.
+     * @throws Horde_Share_Exception
+     */
+    function _listAllShares()
+    {
+        $sharelist = $this->_datatree->get(DATATREE_FORMAT_FLAT, DATATREE_ROOT, true);
+        if ($sharelist instanceof PEAR_Error) {
+            throw new Horde_Share_Exception($sharelist->getMessage());
+        }
+        if (!count($sharelist)) {
+            return $sharelist;
+        }
+        unset($sharelist[DATATREE_ROOT]);
+
+        return $this->getShares(array_keys($sharelist));
+    }
+
+    /**
+     * Returns an array of all shares that $userid has access to.
+     *
+     * @param string $userid     The userid of the user to check access for.
+     * @param integer $perm      The level of permissions required.
+     * @param mixed $attributes  Restrict the shares counted to those
+     *                           matching $attributes. An array of
+     *                           attribute/values pairs or a share owner
+     *                           username.
+     *
+     * @return array  The shares the user has access to.
+     * @throws Horde_Share_Exception
+     */
+    function _listShares($userid, $perm = Horde_Perms::SHOW,
+                         $attributes = null, $from = 0, $count = 0,
+                         $sort_by = null, $direction = 0)
+    {
+        $key = serialize(array($userid, $perm, $attributes));
+        if (empty($this->_listCache[$key])) {
+            $criteria = $this->getShareCriteria($userid, $perm, $attributes);
+            $sharelist = $this->_datatree->getByAttributes($criteria,
+                                                           DATATREE_ROOT,
+                                                           true, 'id', $from,
+                                                           $count, $sort_by,
+                                                           null, $direction);
+            if ($sharelist instanceof PEAR_Error) {
+                throw new Horde_Share_Exception($sharelist->getMessage());
+            }
+            $this->_listCache[$key] = array_keys($sharelist);
+        }
+
+        return $this->_listCache[$key];
+    }
+
+    /**
+     * Returns the number of shares that $userid has access to.
+     *
+     * @param string $userid     The userid of the user to check access for.
+     * @param integer $perm      The level of permissions required.
+     * @param mixed $attributes  Restrict the shares counted to those
+     *                           matching $attributes. An array of
+     *                           attribute/values pairs or a share owner
+     *                           username.
+     *
+     * @return integer  The number of shares
+     */
+    protected function _countShares($userid, $perm = Horde_Perms::SHOW,
+                                    $attributes = null)
+    {
+        $criteria = $this->getShareCriteria($userid, $perm, $attributes);
+        return $this->_datatree->countByAttributes($criteria, DATATREE_ROOT, true, 'id');
+    }
+
+    /**
+     * Returns a new share object.
+     *
+     * @param string $name  The share's name.
+     *
+     * @return Horde_Share_Object_datatree  A new share object.
+     */
+    protected function &_newShare($name)
+    {
+        $datatreeObject = new Horde_Share_Object_DataTree_Share($name);
+        $datatreeObject->setDataTree($this->_datatree);
+        $share = new $this->_shareObject($datatreeObject);
+
+        return $share;
+    }
+
+    /**
+     * Adds a share to the shares system.
+     *
+     * The share must first be created with
+     * Horde_Share_datatreee::_newShare(), and have any initial details added
+     * to it, before this function is called.
+     *
+     * @param Horde_Share_Object_datatree $share  The new share object.
+     */
+    protected function _addShare(&$share)
+    {
+        return $this->_datatree->add($share->datatreeObject);
+    }
+
+    /**
+     * Removes a share from the shares system permanently.
+     *
+     * @param Horde_Share_Object_datatree $share  The share to remove.
+     */
+    protected function _removeShare(&$share)
+    {
+        return $this->_datatree->remove($share->datatreeObject);
+    }
+
+    /**
+     * Checks if a share exists in the system.
+     *
+     * @param string $share  The share to check.
+     *
+     * @return boolean  True if the share exists.
+     */
+    protected function _exists($share)
+    {
+        return $this->_datatree->exists($share);
+    }
+
+    /**
+     * Returns an array of criteria for querying shares.
+     * @access protected
+     *
+     * @param string  $userid      The userid of the user to check access for.
+     * @param integer $perm        The level of permissions required.
+     * @param mixed   $attributes  Restrict the shares returned to those who
+     *                             have these attribute values.
+     *
+     * @return array  The criteria tree for fetching this user's shares.
+     */
+    public function getShareCriteria($userid, $perm = Horde_Perms::SHOW,
+                                         $attributes = null)
+    {
+        if (!empty($userid)) {
+            $criteria = array(
+                'OR' => array(
+                    // (owner == $userid)
+                    array(
+                        'AND' => array(
+                            array('field' => 'name', 'op' => '=', 'test' => 'owner'),
+                            array('field' => 'value', 'op' => '=', 'test' => $userid))),
+
+                    // (name == perm_users and key == $userid and val & $perm)
+                    array(
+                        'AND' => array(
+                            array('field' => 'name', 'op' => '=', 'test' => 'perm_users'),
+                            array('field' => 'key', 'op' => '=', 'test' => $userid),
+                            array('field' => 'value', 'op' => '&', 'test' => $perm))),
+
+                    // (name == perm_creator and val & $perm)
+                    array(
+                        'AND' => array(
+                            array('field' => 'name', 'op' => '=', 'test' => 'perm_creator'),
+                            array('field' => 'value', 'op' => '&', 'test' => $perm))),
+
+                    // (name == perm_default and val & $perm)
+                    array(
+                        'AND' => array(
+                            array('field' => 'name', 'op' => '=', 'test' => 'perm_default'),
+                            array('field' => 'value', 'op' => '&', 'test' => $perm)))));
+
+            // If the user has any group memberships, check for those also.
+            // @TODO: inject
+            require_once 'Horde/Group.php';
+            $group = &Group::singleton();
+            $groups = $group->getGroupMemberships($userid, true);
+            if (!($groups instanceof PEAR_Error) && $groups) {
+                // (name == perm_groups and key in ($groups) and val & $perm)
+                $criteria['OR'][] = array(
+                    'AND' => array(
+                        array('field' => 'name', 'op' => '=', 'test' => 'perm_groups'),
+                        array('field' => 'key', 'op' => 'IN', 'test' => array_keys($groups)),
+                        array('field' => 'value', 'op' => '&', 'test' => $perm)));
+            }
+        } else {
+            $criteria = array(
+                'AND' => array(
+                     array('field' => 'name', 'op' => '=', 'test' => 'perm_guest'),
+                     array('field' => 'value', 'op' => '&', 'test' => $perm)));
+        }
+
+        if (is_array($attributes)) {
+            // Build attribute/key filter.
+            foreach ($attributes as $key => $value) {
+                $criteria = array(
+                    'AND' => array(
+                        $criteria,
+                        array(
+                            'JOIN' => array(
+                                'AND' => array(
+                                    array('field' => 'name', 'op' => '=', 'test' => $key),
+                                    array('field' => 'value', 'op' => '=', 'test' => $value))))));
+            }
+        } elseif (!is_null($attributes)) {
+            // Restrict to shares owned by the user specified in the
+            // $attributes string.
+            $criteria = array(
+                'AND' => array(
+                    $criteria,
+                    array(
+                        'JOIN' => array(
+                            array('field' => 'name', 'op' => '=', 'test' => 'owner'),
+                            array('field' => 'value', 'op' => '=', 'test' => $attributes)))));
+        }
+
+        return $criteria;
+    }
+
+}
diff --git a/framework/Share/lib/Horde/Share/Exception.php b/framework/Share/lib/Horde/Share/Exception.php
new file mode 100644 (file)
index 0000000..88d2525
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+/**
+ * Horde_Share_Exception
+ *
+ *
+ */
+class Horde_Share_Exception extends Exception
+{
+}
\ No newline at end of file
diff --git a/framework/Share/lib/Horde/Share/Kolab.php b/framework/Share/lib/Horde/Share/Kolab.php
new file mode 100644 (file)
index 0000000..a2cf09a
--- /dev/null
@@ -0,0 +1,334 @@
+<?php
+/**
+ * @package Horde_Share
+ */
+
+/**
+ * Horde_Share_kolab:: provides the kolab backend for the horde share driver.
+ *
+ * Copyright 2004-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Stuart Binge <omicron@mighty.co.za>
+ * @author  Gunnar Wrobel <wrobel@pardus.de>
+ * @package Horde_Share
+ */
+class Horde_Share_kolab extends Horde_Share
+{
+    /**
+     * Our Kolab folder list handler
+     *
+     * @var Kolab_List
+     */
+    protected $_list;
+
+    /**
+     * The share type
+     *
+     * @var string
+     */
+    protected $_type;
+
+    /**
+     * A marker for the validity of the list cache
+     *
+     * @var int
+     */
+    protected $_listCacheValidity;
+
+    /**
+     * The session handler.
+     *
+     * @var Horde_Kolab_Session
+     */
+    protected $_session;
+
+    /**
+     * Initializes the object.
+     *
+     * @throws Horde_Exception
+     */
+    public function __wakeup()
+    {
+        if (empty($GLOBALS['conf']['kolab']['enabled'])) {
+            throw new Horde_Exception('You must enable the kolab settings to use the Kolab Share driver.');
+        }
+
+        $this->_type = $this->_getFolderType($this->_app);
+        if ($this->_type instanceof PEAR_Error) {
+            return $this->_type;
+        }
+
+        $this->_list = $GLOBALS['injector']->getInstance('Horde_Kolab_Storage');
+
+        parent::__wakeup();
+    }
+
+    private function _getFolderType($app)
+    {
+        switch ($app) {
+        case 'mnemo':
+            return 'note';
+        case 'kronolith':
+            return 'event';
+        case 'turba':
+            return 'contact';
+        case 'nag':
+            return 'task';
+        default:
+            throw new Horde_Share_Exception(sprintf(_("The Horde/Kolab integration engine does not support \"%s\""), $app));
+        }
+    }
+
+    /**
+     * Returns the properties that need to be serialized.
+     *
+     * @return array  List of serializable properties.
+     */
+    public function __sleep()
+    {
+        $properties = get_object_vars($this);
+        unset($properties['_sortList'], $properties['_list']);
+        $properties = array_keys($properties);
+        return $properties;
+    }
+
+    /**
+     * Returns a Horde_Share_Object_kolab object of the request folder.
+     *
+     * @param string $object  The share to fetch.
+     *
+     * @return Horde_Share_Object_kolab  The share object.
+     * @throws Horde_Share_Exception
+     */
+    protected function _getShare($object)
+    {
+        if (empty($object)) {
+            throw new Horde_Share_Exception('No object requested.');
+        }
+
+        /** Get the corresponding folder for this share ID */
+        $folder = $this->_list->getByShare($object, $this->_type);
+
+        /** Does the folder exist? */
+        if (!$folder->exists()) {
+            throw new Horde_Share_Exception(sprintf(_("Share \"%s\" does not exist."), $object));
+        }
+
+        /** Create the object from the folder */
+        $share = new Horde_Share_Object_Kolab($object, $this->_type);
+        $share->setFolder($folder);
+
+        return $share;
+    }
+
+    /**
+     * Returns a Horde_Share_Object_kolab object of the requested folder.
+     *
+     * @param string $id  The id of the share to fetch.
+     *
+     * @return Horde_Share_Object_kolab  The share object.
+     */
+    protected function &_getShareById($id)
+    {
+        return $this->_getShare($id);
+    }
+
+    /**
+     * Returns an array of Horde_Share_Object_kolab objects corresponding to
+     * the requested folders.
+     *
+     * @param string $ids  The ids of the shares to fetch.
+     *
+     * @return array  An array of Horde_Share_Object_kolab objects.
+     */
+    protected function &_getShares($ids)
+    {
+        $objects = array();
+        foreach ($ids as $id) {
+            $result = &$this->_getShare($id);
+            if ($result instanceof PEAR_Error) {
+                return $result;
+            }
+            $objects[$id] = &$result;
+        }
+        return $objects;
+    }
+
+    /**
+     * Lists *all* shares for the current app/share, regardless of
+     * permissions.
+     *
+     * Currently not implemented in this class.
+     *
+     * @return array  All shares for the current app/share.
+     */
+    protected function &_listAllShares()
+    {
+        $shares = array();
+        return $shares;
+    }
+
+    /**
+     * Returns an array of all shares that $userid has access to.
+     *
+     * @param string $userid     The userid of the user to check access for.
+     * @param integer $perm      The level of permissions required.
+     * @param mixed $attributes  Restrict the shares counted to those
+     *                           matching $attributes. An array of
+     *                           attribute/values pairs or a share owner
+     *                           username.
+     *
+     * @return array  The shares the user has access to.
+     */
+    protected function _listShares($userid, $perm = Horde_Perms::SHOW,
+                          $attributes = null, $from = 0, $count = 0,
+                          $sort_by = null, $direction = 0)
+    {
+        $key = serialize(array($this->_type, $userid, $perm, $attributes));
+        if ($this->_list === false) {
+            $this->_listCache[$key] = array();
+        } else if (empty($this->_listCache[$key])
+            || $this->_list->validity != $this->_listCacheValidity) {
+            $sharelist = $this->_list->getByType($this->_type);
+            if ($sharelist instanceof PEAR_Error) {
+                throw new Horde_Share_Exception($sharelist->getMessage());
+            }
+
+            $shares = array();
+            foreach ($sharelist as $folder) {
+                $id = $folder->getShareId();
+                $share = &$this->getShare($id);
+                $keep = true;
+                if (!$share->hasPermission($userid, $perm)) {
+                    $keep = false;
+                }
+                if (isset($attributes) && $keep) {
+                    if (is_array($attributes)) {
+                        foreach ($attributes as $key => $value) {
+                            if (!$share->get($key) == $value) {
+                                $keep = false;
+                                break;
+                            }
+                        }
+                    } elseif (!$share->get('owner') == $attributes) {
+                        $keep = false;
+                    }
+                }
+                if ($keep) {
+                    $shares[] = $id;
+                }
+            }
+            $this->_listCache[$key] = $shares;
+            $this->_listCacheValidity = $this->_list->validity;
+        }
+
+        return $this->_listCache[$key];
+    }
+
+    /**
+     * Returns the number of shares that $userid has access to.
+     *
+     * @param string $userid     The userid of the user to check access for.
+     * @param integer $perm      The level of permissions required.
+     * @param mixed $attributes  Restrict the shares counted to those
+     *                           matching $attributes. An array of
+     *                           attribute/values pairs or a share owner
+     *                           username.
+     *
+     * @return integer  The number of shares
+     */
+    protected function _countShares($userid, $perm = Horde_Perms::SHOW,
+                          $attributes = null)
+    {
+        $shares = $this->_listShares($userid, $perm, $attributes);
+        return count($shares);
+    }
+
+    /**
+     * Returns a new share object.
+     *
+     * @param string $name  The share's name.
+     *
+     * @return Horde_Share_Object_kolab  A new share object.
+     */
+    protected function &_newShare($name)
+    {
+        $storageObject = new Horde_Share_Object_Kolab($name, $this->_type);
+        return $storageObject;
+    }
+
+    /**
+     * Adds a share to the shares system.
+     *
+     * The share must first be created with Horde_Share_kolab::_newShare(),
+     * and have any initial details added to it, before this function is
+     * called.
+     *
+     * @param Horde_Share_Object_kolab $share  The new share object.
+     */
+    protected function _addShare(&$share)
+    {
+        return $share->save();
+    }
+
+    /**
+     * Removes a share from the shares system permanently.
+     *
+     * @param Horde_Share_Object_Kolab $share  The share to remove.
+     */
+    protected function _removeShare(Horde_Share_Object $share)
+    {
+        $share_id = $share->getName();
+        $result = $share->delete();
+    }
+
+    /**
+     * Checks if a share exists in the system.
+     *
+     * @param string $share  The share to check.
+     *
+     * @return boolean  True if the share exists.
+     */
+    protected function _exists($object)
+    {
+        if (empty($object)) {
+            return false;
+        }
+
+        /** Get the corresponding folder for this share ID */
+        $folder = $this->_list->getByShare($object, $this->_type);
+        if ($folder instanceof PEAR_Error) {
+            throw new Horde_Share_Exception($folder->getMessage());
+        }
+
+        return $folder->exists();
+    }
+
+    /**
+     * Create a default share for the current app
+     *
+     * @return string The share ID of the new default share.
+     */
+    public function getDefaultShare()
+    {
+        $default = $this->_list->getDefault($this->_type);
+        if ($default instanceof PEAR_Error) {
+            throw new Horde_Share_Exception($default->getMessage());
+        }
+        if ($default !== false) {
+            return $this->getShare($default->getShareId());
+        }
+
+        /** Okay, no default folder yet */
+        $share = $this->newShare(Horde_Auth::getAuth());
+
+        /** The value does not matter here as the share will rewrite it */
+        $share->set('name', '');
+        $result = $this->addShare($share);
+
+        return $share;
+    }
+}
diff --git a/framework/Share/lib/Horde/Share/Object.php b/framework/Share/lib/Horde/Share/Object.php
new file mode 100644 (file)
index 0000000..d9e94fe
--- /dev/null
@@ -0,0 +1,367 @@
+<?php
+/**
+ * Abstract class for storing Share information.
+ *
+ * This class should be extended for the more specific drivers.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @author  Jan Schneider <jan@horde.org>
+ * @author  Gunnar Wrobel <wrobel@pardus.de>
+ * @package Horde_Share
+ */
+class Horde_Share_Object
+{
+    /**
+     * The Horde_Share object which this share came from - needed for updating
+     * data in the backend to make changes stick, etc.
+     *
+     * @var Horde_Share
+     */
+    protected $_shareOb;
+
+    /**
+     * Returns the properties that need to be serialized.
+     *
+     * @return array  List of serializable properties.
+     */
+    public function __sleep()
+    {
+        $properties = get_object_vars($this);
+        unset($properties['_shareOb']);
+        $properties = array_keys($properties);
+        return $properties;
+    }
+
+    /**
+     * Associates a Share object with this share.
+     *
+     * @param Horde_Share $shareOb  The Share object.
+     */
+    public function setShareOb($shareOb)
+    {
+        if (!($shareOb instanceof Horde_Share)) {
+            throw new Horde_Share_Exception('This object needs a Horde_Share instance as storage handler!');
+        }
+        $this->_shareOb = $shareOb;
+    }
+
+    /**
+     * Sets an attribute value in this object.
+     *
+     * @param string $attribute  The attribute to set.
+     * @param mixed $value       The value for $attribute.
+     *
+     * @return mixed  True if setting the attribute did succeed, a PEAR_Error
+     *                otherwise.
+     */
+    public function set($attribute, $value)
+    {
+        return $this->_set($attribute, $value);
+    }
+
+    /**
+     * Returns an attribute value from this object.
+     *
+     * @param string $attribute  The attribute to return.
+     *
+     * @return mixed  The value for $attribute.
+     */
+    public function get($attribute)
+    {
+        return $this->_get($attribute);
+    }
+
+    /**
+     * Returns the ID of this share.
+     *
+     * @return string  The share's ID.
+     */
+    public function getId()
+    {
+        return $this->_getId();
+    }
+
+    /**
+     * Returns the name of this share.
+     *
+     * @return string  The share's name.
+     */
+    public function getName()
+    {
+        return $this->_getName();
+    }
+
+    /**
+     * Saves the current attribute values.
+     *
+     * @return boolean
+     * @throws Horde_Exception
+     */
+    public function save()
+    {
+        try {
+            Horde::callHook('share_modify', array($this));
+        } catch (Horde_Exception_HookNotSet $e) {}
+
+        return $this->_save();
+    }
+
+    /**
+     * Gives a user a certain privilege for this share.
+     *
+     * @param string $userid       The userid of the user.
+     * @param integer $permission  A Horde_Perms::* constant.
+     */
+    public function addUserPermission($userid, $permission)
+    {
+        $perm = $this->getPermission();
+        $perm->addUserPermission($userid, $permission, false);
+        $this->setPermission($perm);
+    }
+
+    /**
+     * Removes a certain privilege for a user from this share.
+     *
+     * @param string $userid       The userid of the user.
+     * @param integer $permission  A Horde_Perms::* constant.
+     */
+    public function removeUserPermission($userid, $permission)
+    {
+        $perm = $this->getPermission();
+        $perm->removeUserPermission($userid, $permission, false);
+        $this->setPermission($perm);
+    }
+
+    /**
+     * Gives a group certain privileges for this share.
+     *
+     * @param string $group        The group to add permissions for.
+     * @param integer $permission  A Horde_Perms::* constant.
+     */
+    public function addGroupPermission($group, $permission)
+    {
+        $perm = $this->getPermission();
+        $perm->addGroupPermission($group, $permission, false);
+        $this->setPermission($perm);
+    }
+
+    /**
+     * Removes a certain privilege from a group.
+     *
+     * @param string $group         The group to remove permissions from.
+     * @param constant $permission  A Horde_Perms::* constant.
+     */
+    public function removeGroupPermission($group, $permission)
+    {
+        $perm = $this->getPermission();
+        $perm->removeGroupPermission($group, $permission, false);
+        $this->setPermission($perm);
+    }
+
+    /**
+     * Removes a user from this share.
+     *
+     * @param string $userid  The userid of the user to remove.
+     */
+    public function removeUser($userid)
+    {
+        /* Remove all $userid's permissions. */
+        $perm = $this->getPermission();
+        $perm->removeUserPermission($userid, Horde_Perms::SHOW, false);
+        $perm->removeUserPermission($userid, Horde_Perms::READ, false);
+        $perm->removeUserPermission($userid, Horde_Perms::EDIT, false);
+        $perm->removeUserPermission($userid, Horde_Perms::DELETE, false);
+
+        return $this->setPermission($perm);
+    }
+
+    /**
+     * Removes a group from this share.
+     *
+     * @param integer $groupId  The group to remove.
+     */
+    public function removeGroup($groupId)
+    {
+        /* Remove all $groupId's permissions. */
+        $perm = $this->getPermission();
+        $perm->removeGroupPermission($groupId, Horde_Perms::SHOW, false);
+        $perm->removeGroupPermission($groupId, Horde_Perms::READ, false);
+        $perm->removeGroupPermission($groupId, Horde_Perms::EDIT, false);
+        $perm->removeGroupPermission($groupId, Horde_Perms::DELETE, false);
+
+        return $this->setPermission($perm);
+    }
+
+    /**
+     * Returns an array containing all the userids of the users with access to
+     * this share.
+     *
+     * @param integer $perm_level  List only users with this permission level.
+     *                             Defaults to all users.
+     *
+     * @return array  The users with access to this share.
+     */
+    public function listUsers($perm_level = null)
+    {
+        $perm = $this->getPermission();
+        $results = array_keys($perm->getUserPermissions($perm_level));
+        // Always return the share's owner.
+        if ($this->get('owner')) {
+            array_push($results, $this->get('owner'));
+        }
+        return $results;
+    }
+
+    /**
+     * Returns an array containing all the groupids of the groups with access
+     * to this share.
+     *
+     * @param integer $perm_level  List only users with this permission level.
+     *                             Defaults to all users.
+     *
+     * @return array  The IDs of the groups with access to this share.
+     */
+    public function listGroups($perm_level = null)
+    {
+        $perm = $this->getPermission();
+        return array_keys($perm->getGroupPermissions($perm_level));
+    }
+
+    /**
+     * Locks an item from this share, or the entire share if no item defined.
+     *
+     * @param string $item_uid  A uid of an item from this share.
+     *
+     * @return mixed   A lock ID on success, PEAR_Error on failure, false if:
+     *                  - The share is already locked
+     *                  - The item is already locked
+     *                  - A share lock was requested and an item is already
+     *                    locked in the share
+     * @throws Horde_Share_Exception
+     */
+    public function lock($item_uid = null)
+    {
+        try {
+            // TODO: Pass this in as a parameter, don't use the injector directly in
+            //       a library
+            $locks = $GLOBALS['injector']->getInstance('Horde_Lock');
+        } catch (Horde_Lock_Exception $e) {
+            Horde::logMessage($e, 'ERR');
+            throw new Horde_Share_Exception($e);
+        }
+
+        $shareid = $this->getId();
+
+        // Default parameters.
+        $locktype = Horde_Lock::TYPE_EXCLUSIVE;
+        $timeout = 600;
+        $itemscope = $this->_shareOb->getApp() . ':' . $shareid;
+
+        if (!empty($item_uid)) {
+            // Check if the share is locked. Share locks are placed at app
+            // scope.
+            try {
+                $result = $locks->getLocks($this->_shareOb->getApp(), $shareid, $locktype);
+            } catch (Horde_Lock_Exception $e) {
+                throw new Horde_Share_Exception($e);
+            }
+            if (!empty($result)) {
+                // Lock found.
+                return false;
+            }
+            // Try to place the item lock at app:shareid scope.
+            return $locks->setLock(Horde_Auth::getAuth(), $itemscope, $item_uid,
+                                   $timeout, $locktype);
+        } else {
+            // Share lock requested. Check for locked items.
+            try {
+                $result = $locks->getLocks($itemscope, null, $locktype);
+            } catch (Horde_Lock_Exception $e) {
+                throw new Horde_Share_Exception($e);
+            }
+            if (!empty($result)) {
+                // Lock found.
+                return false;
+            }
+            // Try to place the share lock
+            return $locks->setLock(Horde_Auth::getAuth(), $this->_shareOb->getApp(),
+                                   $shareid, $timeout, $locktype);
+        }
+    }
+
+    /**
+     * Removes the lock for a lock ID.
+     *
+     * @param string $lockid  The lock ID as generated by a previous call
+     *                        to lock().
+     *
+     * @return mixed  True on success, PEAR_Error on failure.
+     */
+    public function unlock($lockid)
+    {
+        try {
+            // TODO: pass in method, do not use injector in library
+            $locks = $GLOBALS['injector']->getInstance('Horde_Lock');
+        } catch (Horde_Lock_Exception $e) {
+            Horde::logMessage($e, 'ERR');
+            throw new Horde_Share_Exception($e);
+        }
+
+        return $locks->clearLock($lockid);
+    }
+
+    /**
+     * Checks for existing locks.
+     *
+     * First this checks for share locks and if none exists, checks for item
+     * locks (if item_uid defined).  It will return the first lock found.
+     *
+     * @param string $item_uid  A uid of an item from this share.
+     *
+     * @return mixed   Hash with the found lock information in 'lock' and the
+     *                 lock type ('share' or 'item') in 'type', or an empty
+     *                 array if there are no locks, or a PEAR_Error on failure.
+     */
+    public function checkLocks($item_uid = null)
+    {
+        // TODO: Inject lock object, do not use injector in library
+        try {
+            $locks = $GLOBALS['injector']->getInstance('Horde_Lock');
+        } catch (Horde_Lock_Exception $e) {
+            Horde::logMessage($e, 'ERR');
+            throw new Horde_Share_Exception($e);
+        }
+
+        $shareid = $this->getId();
+        $locktype = Horde_Lock::TYPE_EXCLUSIVE;
+
+        // Check for share locks
+        try {
+            $result = $locks->getLocks($this->_shareOb->getApp(), $shareid, $locktype);
+        } catch (Horde_Lock_Exception $e) {
+            Horde::logMessage($e, 'ERR');
+            throw new Horde_Share_Exception($e);
+        }
+
+        if (empty($result) && !empty($item_uid)) {
+            // Check for item locks
+            $locktargettype = 'item';
+            try {
+                $result = $locks->getLocks($this->_shareOb->getApp() . ':' . $shareid, $item_uid, $locktype);
+            } catch (Horde_Lock_Exception $e) {
+                Horde::logMessage($e, 'ERR');
+                throw new Horde_Share_Exception($e->getMessage());
+            }
+        } else {
+            $locktargettype = 'share';
+        }
+
+        if (empty($result)) {
+            return array();
+        }
+
+        return array('type' => $locktargettype,
+                     'lock' => reset($result));
+    }
+
+}
diff --git a/framework/Share/lib/Horde/Share/Object/Datatree.php b/framework/Share/lib/Horde/Share/Object/Datatree.php
new file mode 100644 (file)
index 0000000..6b21f27
--- /dev/null
@@ -0,0 +1,138 @@
+<?php
+/**
+ * Extension of the Horde_Share_Object class for storing share information in
+ * the DataTree driver.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @author  Jan Schneider <jan@horde.org>
+ * @author  Gunnar Wrobel <wrobel@pardus.de>
+ * @package Horde_Share
+ */
+class Horde_Share_Object_Datatree extends Horde_Share_Object
+{
+    /**
+     * The actual storage object that holds the data.
+     *
+     * @var mixed
+     */
+    public $datatreeObject;
+
+    /**
+     * Constructor.
+     *
+     * @param DataTreeObject_Share $datatreeObject  A DataTreeObject_Share
+     *                                              instance.
+     */
+    public function __construct(Horde_Share_Object_Datatree_Share $datatreeObject)
+    {
+        $this->datatreeObject = $datatreeObject;
+    }
+
+    /**
+     * Sets an attribute value in this object.
+     *
+     * @param string $attribute  The attribute to set.
+     * @param mixed $value       The value for $attribute.
+     *
+     * @return mixed  True if setting the attribute did succeed, a PEAR_Error
+     *                otherwise.
+     */
+    public function _set($attribute, $value)
+    {
+        return $this->datatreeObject->set($attribute, $value);
+    }
+
+    /**
+     * Returns one of the attributes of the object, or null if it isn't
+     * defined.
+     *
+     * @param string $attribute  The attribute to retrieve.
+     *
+     * @return mixed  The value of the attribute, or an empty string.
+     */
+    public function _get($attribute)
+    {
+        return $this->datatreeObject->get($attribute);
+    }
+
+    /**
+     * Returns the ID of this share.
+     *
+     * @return string  The share's ID.
+     */
+    protected function _getId()
+    {
+        return $this->datatreeObject->getId();
+    }
+
+    /**
+     * Returns the name of this share.
+     *
+     * @return string  The share's name.
+     */
+    protected function _getName()
+    {
+        return $this->datatreeObject->getName();
+    }
+
+    /**
+     * Saves the current attribute values.
+     */
+    protected function _save()
+    {
+        return $this->datatreeObject->save();
+    }
+
+    /**
+     * Checks to see if a user has a given permission.
+     *
+     * @param string $userid       The userid of the user.
+     * @param integer $permission  A Horde_Perms::* constant to test for.
+     * @param string $creator      The creator of the event.
+     *
+     * @return boolean  Whether or not $userid has $permission.
+     */
+    public function hasPermission($userid, $permission, $creator = null)
+    {
+        if ($userid && $userid == $this->datatreeObject->get('owner')) {
+            return true;
+        }
+        // TODO: inject
+        return $GLOBALS['injector']->getInstance('Horde_Perms')->hasPermission($this->getPermission(), $userid, $permission, $creator);
+    }
+
+    /**
+     * Sets the permission of this share.
+     *
+     * @param Horde_Perms_Permission $perm  Permission object.
+     * @param boolean $update         Should the share be saved
+     *                                after this operation?
+     *
+     * @return boolean  True if no error occured, PEAR_Error otherwise
+     */
+    public function setPermission(&$perm, $update = true)
+    {
+        $this->datatreeObject->data['perm'] = $perm->getData();
+        if ($update) {
+            return $this->datatreeObject->save();
+        }
+        return true;
+    }
+
+    /**
+     * Returns the permission of this share.
+     *
+     * @return Horde_Perms_Permission  Permission object that represents the
+     *                           permissions on this share
+     */
+    function getPermission()
+    {
+        $perm = new Horde_Perms_Permission($this->datatreeObject->getName());
+        $perm->data = isset($this->datatreeObject->data['perm'])
+            ? $this->datatreeObject->data['perm']
+            : array();
+
+        return $perm;
+    }
+
+}
diff --git a/framework/Share/lib/Horde/Share/Object/Datatree/Share.php b/framework/Share/lib/Horde/Share/Object/Datatree/Share.php
new file mode 100644 (file)
index 0000000..83f6299
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+
+/**
+ * Extension of the DataTreeObject class for storing Share information in the
+ * DataTree driver. If you want to store specialized Share information, you
+ * should extend this class instead of extending DataTreeObject directly.
+ *
+ * @author  Mike Cochrane <mike@graftonhall.co.nz>
+ * @author  Jan Schneider <jan@horde.org>
+ * @package Horde_Share
+ */
+class Horde_Share_Object_DataTree_Share extends DataTreeObject
+{
+    /**
+     * Returns the properties that need to be serialized.
+     *
+     * @return array  List of serializable properties.
+     */
+    public function __sleep()
+    {
+        $properties = get_object_vars($this);
+        unset($properties['datatree']);
+        $properties = array_keys($properties);
+        return $properties;
+    }
+
+    /**
+     * Maps this object's attributes from the data array into a format that we
+     * can store in the attributes storage backend.
+     *
+     * @access protected
+     *
+     * @param boolean $permsonly  Only process permissions? Lets subclasses
+     *                            override part of this method while handling
+     *                            their additional attributes seperately.
+     *
+     * @return array  The attributes array.
+     */
+    protected function _toAttributes($permsonly = false)
+    {
+        // Default to no attributes.
+        $attributes = array();
+
+        foreach ($this->data as $key => $value) {
+            if ($key == 'perm') {
+                foreach ($value as $type => $perms) {
+                    if (is_array($perms)) {
+                        foreach ($perms as $member => $perm) {
+                            $attributes[] = array('name' => 'perm_' . $type,
+                                                  'key' => $member,
+                                                  'value' => $perm);
+                        }
+                    } else {
+                        $attributes[] = array('name' => 'perm_' . $type,
+                                              'key' => '',
+                                              'value' => $perms);
+                    }
+                }
+            } elseif (!$permsonly) {
+                $attributes[] = array('name' => $key,
+                                      'key' => '',
+                                      'value' => $value);
+            }
+        }
+
+        return $attributes;
+    }
+
+    /**
+     * Takes in a list of attributes from the backend and maps it to our
+     * internal data array.
+     *
+     * @access protected
+     *
+     * @param array $attributes   The list of attributes from the backend
+     *                            (attribute name, key, and value).
+     * @param boolean $permsonly  Only process permissions? Lets subclasses
+     *                            override part of this method while handling
+     *                            their additional attributes seperately.
+     */
+    protected function _fromAttributes($attributes, $permsonly = false)
+    {
+        // Initialize data array.
+        $this->data['perm'] = array();
+
+        foreach ($attributes as $attr) {
+            if (substr($attr['name'], 0, 4) == 'perm') {
+                if (!empty($attr['key'])) {
+                    $this->data['perm'][substr($attr['name'], 5)][$attr['key']] = $attr['value'];
+                } else {
+                    $this->data['perm'][substr($attr['name'], 5)] = $attr['value'];
+                }
+            } elseif (!$permsonly) {
+                $this->data[$attr['name']] = $attr['value'];
+            }
+        }
+    }
+
+}
diff --git a/framework/Share/lib/Horde/Share/Object/Kolab.php b/framework/Share/lib/Horde/Share/Object/Kolab.php
new file mode 100644 (file)
index 0000000..eb4490f
--- /dev/null
@@ -0,0 +1,360 @@
+<?php
+/**
+ * Extension of the Horde_Share_Object class for handling Kolab share
+ * information.
+ *
+ * @author  Stuart Binge <omicron@mighty.co.za>
+ * @author  Gunnar Wrobel <wrobel@pardus.de>
+ * @package Horde_Share
+ */
+class Horde_Share_Object_Kolab extends Horde_Share_Object
+{
+    /**
+     * The Kolab folder this share is based on.
+     *
+     * @var Kolab_Folder
+     */
+    protected $_folder;
+
+    /**
+     * The Kolab folder name.
+     *
+     * @var string
+     */
+    protected $_folder_name;
+
+    /**
+     * A cache for the share attributes.
+     *
+     * @var array
+     */
+    protected $_data;
+
+    /**
+     * Our Kolab folder list handler
+     *
+     * @var Kolab_List
+     */
+    protected $_list;
+
+    /**
+     * The session handler.
+     *
+     * @var Horde_Kolab_Session
+     */
+    private $_session;
+
+    /**
+     * Constructor.
+     *
+     * Sets the folder name.
+     *
+     * @param string $id  The share id.
+     */
+    public function __construct($id, $type)
+    {
+        // We actually ignore the random id string that all horde apps provide
+        // as initial name and wait for a set('name', 'xyz') call. But we want
+        // to know if we should create a default share.
+        if ($id == Horde_Auth::getAuth()) {
+            $this->_data['default'] = true;
+        } else {
+            $this->_data['default'] = false;
+        }
+        $this->_type = $type;
+        $this->__wakeup();
+    }
+
+    /**
+     * Associates a Share object with this share.
+     *
+     * @param Horde_Share $shareOb  The Share object.
+     */
+    public function setShareOb($shareOb)
+    {
+        /** Ignore the parent as we don't need it */
+    }
+
+    /**
+     * Initializes the object.
+     */
+    public function __wakeup()
+    {
+        $this->_list = $GLOBALS['injector']->getInstance('Horde_Kolab_Storage');
+        if (isset($this->_folder_name)) {
+            $this->_folder = $this->_list->getFolder($this->_folder_name);
+        }
+    }
+
+    /**
+     * Returns the properties that need to be serialized.
+     *
+     * @return array  List of serializable properties.
+     */
+    public function __sleep()
+    {
+        $properties = get_object_vars($this);
+        unset($properties['_shareOb'], $properties['_list'],
+              $properties['_folder']);
+        $properties = array_keys($properties);
+        return $properties;
+    }
+
+    /**
+     * Returns the default share name for the current application.
+     *
+     * @return string  The default share name.
+     */
+    public function getDefaultShareName()
+    {
+        switch ($this->_type) {
+        case 'contact':
+            return _("Contacts");
+        case 'note':
+            return _("Notes");
+        case 'event':
+            return _("Calendar");
+        case 'task':
+            return _("Tasks");
+        case 'filter':
+            return _("Filters");
+        case 'h-prefs':
+            return _("Preferences");
+        }
+    }
+
+    /**
+     * Sets the folder for this storage object.
+     *
+     * @param string $folder  Name of the Kolab folder.
+     * @param array  $perms  The permissions of the folder if they are known.
+     */
+    public function setFolder(&$folder)
+    {
+        if (!isset($this->_folder)) {
+            $this->_folder = &$folder;
+            $this->_folder_name = $folder->name;
+        } else {
+           throw new Horde_Share_Exception(_("The share has already been initialized!"));
+        }
+    }
+
+    /**
+     * Returns the ID of this share.
+     *
+     * @return string  The share's ID.
+     */
+    protected function _getId()
+    {
+        return $this->_folder->getShareId();
+    }
+
+    /**
+     * Returns the name of this share.
+     *
+     * @return string  The share's name.
+     */
+    protected function _getName()
+    {
+        return $this->_folder->getShareId();
+    }
+
+    /**
+     * Returns an attribute value from this object.
+     *
+     * @param string $attribute  The attribute to return.
+     *
+     * @return mixed  The value for $attribute.
+     */
+    protected function _get($attribute)
+    {
+        if (isset($this->_data[$attribute])) {
+            return $this->_data[$attribute];
+        }
+
+        if (!isset($this->_folder)) {
+            return $this->_folderError();
+        }
+
+        switch ($attribute) {
+        case 'owner':
+            $this->_data['owner'] = $this->_folder->getOwner();
+            break;
+
+        case 'name':
+            $this->_data['name'] = $this->_folder->getTitle();
+            break;
+
+        case 'type':
+            $this->_data['type'] = $this->_folder->getType();
+            break;
+
+        case 'params':
+            $params = @unserialize($this->_folder->getAttribute('params'));
+            $default = array('source' => 'kolab',
+                             'default' => $this->get('default'),
+                             'name' => $this->get('name'));
+            $type = $this->get('type');
+            if (!($type instanceof PEAR_Error) && $type == 'event') {
+                $default = array_merge($default, array(
+                                           'fbrelevance' => $this->_folder->getFbrelevance(),
+                                           'xfbaccess'   => $this->_folder->getXfbaccess()
+                                       ));
+            }
+            if ($params instanceof PEAR_Error || $params == '') {
+                $params = $default;
+            }
+            $this->_data['params'] = serialize(array_merge($default, $params));
+            break;
+
+        case 'default':
+            $this->_data['default'] = $this->_folder->isDefault();
+            break;
+
+        default:
+            $annotation = $this->_folder->getAttribute($attribute);
+            if ($annotation instanceof PEAR_Error || empty($annotation)) {
+                $annotation = '';
+            }
+            $this->_data[$attribute] = $annotation;
+            break;
+        }
+
+        return $this->_data[$attribute];
+    }
+
+    /**
+     * Sets an attribute value in this object.
+     *
+     * @param string $attribute  The attribute to set.
+     * @param mixed $value       The value for $attribute.
+     *
+     * @return mixed  True if setting the attribute did succeed, a PEAR_Error
+     *                otherwise.
+     */
+    protected function _set($attribute, $value)
+    {
+        switch ($attribute) {
+        case 'name':
+            /* On folder creation of default shares we wish to ignore
+             * the names provided by the Horde applications. We use
+             * the Kolab default names. */
+            if (!isset($this->_folder)) {
+                if ($this->get('default')) {
+                    $value = $this->getDefaultShareName();
+                }
+                $this->setFolder($this->_list->getNewFolder());
+            }
+            $this->_folder->setName($value);
+            $this->_data['name'] = $this->_folder->getTitle();
+            break;
+
+        case 'params':
+            $value = unserialize($value);
+            if (isset($value['default'])) {
+                $this->_data['default'] = $value['default'];
+                unset($value['default']);
+            }
+            $value = serialize($value);
+
+        default:
+            $this->_data[$attribute] = $value;
+        }
+    }
+
+    /**
+     * Saves the current attribute values.
+     */
+    protected function _save()
+    {
+        if (!isset($this->_folder)) {
+            return $this->_folderError();
+        }
+
+        $data = $this->_data;
+        /** The name is handled immediately when set */
+        unset($data['name']);
+        $data['type'] = $this->_type;
+
+        $result = $this->_folder->save($data);
+        if ($result instanceof PEAR_Error) {
+            return $result;
+        }
+
+        /** The name may have changed */
+        $this->_data['name'] = $this->_folder->getTitle();
+        $this->_folder_name = $this->_folder->name;
+        return true;
+    }
+
+    /**
+     * Delete this share.
+     *
+     * @return boolean|PEAR_Error  True on success.
+     */
+    public function delete()
+    {
+        if (!isset($this->_folder)) {
+            return $this->_folderError();
+        }
+        return $this->_folder->delete();
+    }
+
+    /**
+     * Checks to see if a user has a given permission.
+     *
+     * @param string $userid       The userid of the user.
+     * @param integer $permission  A Horde_Perms::* constant to test for.
+     * @param string $creator      The creator of the shared object.
+     *
+     * @return boolean|PEAR_Error  Whether or not $userid has $permission.
+     */
+    public function hasPermission($userid, $permission, $creator = null)
+    {
+        if (!isset($this->_folder)) {
+            return $this->_folderError();
+        }
+        return $this->_folder->hasPermission($userid, $permission, $creator);
+    }
+
+    /**
+     * Returns the permissions from this storage object.
+     *
+     * @return Horde_Perms_Permission_Kolab|PEAR_Error  The permissions on the share.
+     */
+    public function getPermission()
+    {
+        if (!isset($this->_folder)) {
+            return $this->_folderError();
+        }
+        return $this->_folder->getPermission();
+    }
+
+    /**
+     * Sets the permissions on the share.
+     *
+     * @param Horde_Perms_Permission_Kolab $perms Permission object to folder on the
+     *                                     object.
+     * @param boolean $update              Save the updated information?
+     *
+     * @return boolean|PEAR_Error  True on success.
+     */
+    public function setPermission($perms, $update = true)
+    {
+        if (!isset($this->_folder)) {
+            return $this->_folderError();
+        }
+        return $this->_folder->setPermission($perms, $update);
+    }
+
+    /**
+     * Return a standard error in case the share has not been
+     * correctly initialized.
+     *
+     * @throws Horde_Share_Exception
+     */
+    protected function _folderError()
+    {
+        throw new Horde_Share_Exception(_("The Kolab share object has not been initialized yet!"));
+    }
+}
diff --git a/framework/Share/lib/Horde/Share/Object/Sql.php b/framework/Share/lib/Horde/Share/Object/Sql.php
new file mode 100644 (file)
index 0000000..2553e3e
--- /dev/null
@@ -0,0 +1,283 @@
+<?php
+/**
+ * Extension of the Horde_Share_Object class for storing share information in
+ * the sql driver.
+ *
+ * @author  Duck <duck@obala.net>
+ * @package Horde_Share
+ */
+class Horde_Share_Object_Sql extends Horde_Share_Object
+{
+    /**
+     * The actual storage object that holds the data.
+     *
+     * @TODO: Check visibility - should be protected/private
+     * @var mixed
+     */
+    public $data = array();
+
+    /**
+     * Constructor.
+     *
+     * @param array $data Share data array.
+     */
+    public function __construct($data)
+    {
+        if (!isset($data['perm']) || !is_array($data['perm'])) {
+            $this->data['perm'] = array(
+                'users' => array(),
+                'type' => 'matrix',
+                'default' => isset($data['perm_default'])
+                    ? (int)$data['perm_default'] : 0,
+                'guest' => isset($data['perm_guest'])
+                    ? (int)$data['perm_guest'] : 0,
+                'creator' => isset($data['perm_creator'])
+                    ? (int)$data['perm_creator'] : 0,
+                'groups' => array());
+
+            unset($data['perm_creator'], $data['perm_guest'],
+                  $data['perm_default']);
+        }
+        $this->data = array_merge($data, $this->data);
+    }
+
+    /**
+     * Sets an attribute value in this object.
+     *
+     * @param string $attribute  The attribute to set.
+     * @param mixed $value       The value for $attribute.
+     *
+     * @return mixed  True if setting the attribute did succeed, a PEAR_Error
+     *                otherwise.
+     */
+    public function _set($attribute, $value)
+    {
+        if ($attribute == 'owner') {
+            return $this->data['share_owner'] = $value;
+        } else {
+            return $this->data['attribute_' . $attribute] = $value;
+        }
+    }
+
+    /**
+     * Returns one of the attributes of the object, or null if it isn't
+     * defined.
+     *
+     * @param string $attribute  The attribute to retrieve.
+     *
+     * @return mixed  The value of the attribute, or an empty string.
+     */
+    protected function _get($attribute)
+    {
+        if ($attribute == 'owner') {
+            return $this->data['share_owner'];
+        } elseif (isset($this->data['attribute_' . $attribute])) {
+            return $this->data['attribute_' . $attribute];
+        }
+    }
+
+    /**
+     * Returns the ID of this share.
+     *
+     * @return string  The share's ID.
+     */
+    protected function _getId()
+    {
+        return isset($this->data['share_id']) ? $this->data['share_id'] : null;
+    }
+
+    /**
+     * Returns the name of this share.
+     *
+     * @return string  The share's name.
+     */
+    protected function _getName()
+    {
+        return $this->data['share_name'];
+    }
+
+    /**
+     * Saves the current attribute values.
+     */
+    protected function _save()
+    {
+        $db = $this->_shareOb->getWriteDb();
+        $table = $this->_shareOb->getTable();
+
+        $fields = array();
+        $params = array();
+
+        foreach ($this->_shareOb->_toDriverCharset($this->data) as $key => $value) {
+            if ($key != 'share_id' && $key != 'perm' && $key != 'share_flags') {
+                $fields[] = $key;
+                $params[] = $value;
+            }
+        }
+
+        $fields[] = 'perm_creator';
+        $params[] = isset($this->data['perm']['creator']) ? (int)$this->data['perm']['creator'] : 0;
+
+        $fields[] = 'perm_default';
+        $params[] = isset($this->data['perm']['default']) ? (int)$this->data['perm']['default'] : 0;
+
+        $fields[] = 'perm_guest';
+        $params[] = isset($this->data['perm']['guest']) ? (int)$this->data['perm']['guest'] : 0;
+
+        $fields[] = 'share_flags';
+        $flags = 0;
+        if (!empty($this->data['perm']['users'])) {
+            $flags |= Horde_Share_Sql::SQL_FLAG_USERS;
+        }
+        if (!empty($this->data['perm']['groups'])) {
+            $flags |= Horde_Share_Sql::SQL_FLAG_GROUPS;
+        }
+        $params[] = $flags;
+
+        if (empty($this->data['share_id'])) {
+            $share_id = $db->nextId($table);
+            if ($share_id instanceof PEAR_Error) {
+                Horde::logMessage($share_id, 'ERR');
+                throw new Horde_Share_Exception($share_id->getMessage());
+            }
+
+            $this->data['share_id'] = $share_id;
+            $fields[] = 'share_id';
+            $params[] = $this->data['share_id'];
+
+            $query = 'INSERT INTO ' . $table . ' (' . implode(', ', $fields) . ') VALUES (?' . str_repeat(', ?', count($fields) - 1) . ')';
+        } else {
+            $query = 'UPDATE ' . $table . ' SET ' . implode(' = ?, ', $fields) . ' = ? WHERE share_id = ?';
+            $params[] = $this->data['share_id'];
+        }
+        $stmt = $db->prepare($query, null, MDB2_PREPARE_MANIP);
+        if ($stmt instanceof PEAR_Error) {
+            Horde::logMessage($stmt, 'ERR');
+            throw new Horde_Share_Exception($stmt->getMessage());
+        }
+        $result = $stmt->execute($params);
+        if ($result instanceof PEAR_Error) {
+            Horde::logMessage($result, 'ERR');
+            throw new Horde_Share_Exception($result->getMessage());
+        }
+        $stmt->free();
+
+        // Update the share's user permissions
+        $stmt = $db->prepare('DELETE FROM ' . $table . '_users WHERE share_id = ?', null, MDB2_PREPARE_MANIP);
+        if ($stmt instanceof PEAR_Error) {
+            Horde::logMessage($stmt, 'ERR');
+            throw new Horde_Share_Exception($stmt->getMessage());
+        }
+        $result = $stmt->execute(array($this->data['share_id']));
+        if ($result instanceof PEAR_Error) {
+            Horde::logMessage($result, 'ERR');
+            throw new Horde_Share_Exception($result->getMessage());
+        }
+        $stmt->free();
+
+        if (!empty($this->data['perm']['users'])) {
+            $data = array();
+            foreach ($this->data['perm']['users'] as $user => $perm) {
+                $stmt = $db->prepare('INSERT INTO ' . $table . '_users (share_id, user_uid, perm) VALUES (?, ?, ?)', null, MDB2_PREPARE_MANIP);
+                if ($stmt instanceof PEAR_Error) {
+                    Horde::logMessage($stmt, 'ERR');
+                    throw new Horde_Share_Exception($stmt->getMessage());
+                }
+                $result = $stmt->execute(array($this->data['share_id'], $user, $perm));
+                if ($result instanceof PEAR_Error) {
+                    Horde::logMessage($result, 'ERR');
+                    throw new Horde_Share_Exception($result->getMessage());
+                }
+                $stmt->free();
+            }
+        }
+
+        // Update the share's group permissions
+        $stmt = $db->prepare('DELETE FROM ' . $table . '_groups WHERE share_id = ?', null, MDB2_PREPARE_MANIP);
+        if ($stmt instanceof PEAR_Error) {
+            Horde::logMessage($stmt, 'ERR');
+            throw new Horde_Share_Exception($stmt->getMessage());
+        }
+        $result = $stmt->execute(array($this->data['share_id']));
+        if ($result instanceof PEAR_Error) {
+            Horde::logMessage($result, 'ERR');
+            throw new Horde_Share_Exception($result->getMessage());
+        }
+        $stmt->free();
+
+        if (!empty($this->data['perm']['groups'])) {
+            $data = array();
+            foreach ($this->data['perm']['groups'] as $group => $perm) {
+                $stmt = $db->prepare('INSERT INTO ' . $table . '_groups (share_id, group_uid, perm) VALUES (?, ?, ?)', null, MDB2_PREPARE_MANIP);
+                if ($stmt instanceof PEAR_Error) {
+                    Horde::logMessage($stmt, 'ERR');
+                    throw new Horde_Share_Exception($stmt->getMessage());
+                }
+                $result = $stmt->execute(array($this->data['share_id'], $group, $perm));
+                if ($result instanceof PEAR_Error) {
+                    Horde::logMessage($result, 'ERR');
+                    throw new Horde_Share_Exception($result->getMessage());
+                }
+                $stmt->free();
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks to see if a user has a given permission.
+     *
+     * @param string $userid       The userid of the user.
+     * @param integer $permission  A Horde_Perms::* constant to test for.
+     * @param string $creator      The creator of the event.
+     *
+     * @return boolean  Whether or not $userid has $permission.
+     */
+    public function hasPermission($userid, $permission, $creator = null)
+    {
+        if ($userid == $this->data['share_owner']) {
+            return true;
+        }
+        // @TODO: inject perms, don't use the injector here
+        return $GLOBALS['injector']->getInstance('Horde_Perms')->hasPermission($this->getPermission(), $userid, $permission, $creator);
+    }
+
+    /**
+     * Sets the permission of this share.
+     *
+     * @param Horde_Perms_Permission $perm  Permission object.
+     * @param boolean $update               Should the share be saved
+     *                                      after this operation?
+     *
+     * @TODO: Look at storing the Perm object itself, instead of the data
+     *        (Make it easier to inject the perm object instead of instantiating
+     *        it in the library).
+     *
+     * @return boolean  True if no error occured, PEAR_Error otherwise
+     */
+    public function setPermission($perm, $update = true)
+    {
+        $this->data['perm'] = $perm->getData();
+        if ($update) {
+            return $this->save();
+        }
+        return true;
+    }
+
+    /**
+     * Returns the permission of this share.
+     *
+     * @return Horde_Perms_Permission  Permission object that represents the
+     *                                 permissions on this share.
+     */
+    public function getPermission()
+    {
+        $perm = new Horde_Perms_Permission($this->getName());
+        $perm->data = isset($this->data['perm'])
+            ? $this->data['perm']
+            : array();
+
+        return $perm;
+    }
+
+}
diff --git a/framework/Share/lib/Horde/Share/Object/Sql/Hierarchical.php b/framework/Share/lib/Horde/Share/Object/Sql/Hierarchical.php
new file mode 100644 (file)
index 0000000..4fb21f0
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+/**
+ * Extension of the Horde_Share_Object class for storing share information in
+ * the sql driver.
+ *
+ * @author  Duck <duck@obala.net>
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ *
+ * @package Horde_Share
+ */
+class Horde_Share_Object_Sql_Hierarchical extends Horde_Share_Object_Sql
+{
+    /**
+     * Constructor. This is here primarily to make calling the parent
+     * constructor(s) from any subclasses cleaner.
+     *
+     * @param unknown_type $data
+     * @return Horde_Share_Object_sql_hierarchical
+     */
+    public function __construct($data)
+    {
+        if (!isset($data['share_parents'])) {
+            $data['share_parents'] = null;
+        }
+        parent::__construct($data);
+    }
+
+    public function inheritPermissions()
+    {
+        throw new Horde_Share_Exception('Not implemented.');
+    }
+
+    /**
+     * Return a count of the number of children this share has
+     *
+     * @param integer $perm  A Horde_Perms::* constant
+     * @param boolean $allLevels  Count grandchildren or just children
+     *
+     * @return mixed  The number of child shares || PEAR_Error
+     */
+    public function countChildren($perm = Horde_Perms::SHOW, $allLevels = true)
+    {
+        return $this->_shareOb->countShares(Horde_Auth::getAuth(), $perm, null, $this, $allLevels);
+    }
+
+    /**
+     * Get all children of this share.
+     *
+     * @param int $perm           Horde_Perms::* constant. If NULL will return
+     *                            all shares regardless of permissions.
+     * @param boolean $allLevels  Return all levels.
+     *
+     * @return mixed  An array of Horde_Share_Object objects || PEAR_Error
+     */
+    public function getChildren($perm = Horde_Perms::SHOW, $allLevels = true)
+    {
+        return $this->_shareOb->listShares(Horde_Auth::getAuth(), $perm, null, 0, 0,
+             null, 1, $this, $allLevels, is_null($perm));
+
+    }
+
+    /**
+     * Returns a child's direct parent
+     *
+     * @return mixed  The direct parent Horde_Share_Object or PEAR_Error
+     */
+    public function getParent()
+    {
+        return $this->_shareOb->getParent($this);
+    }
+
+    /**
+     * Get all of this share's parents.
+     *
+     * @return array()  An array of Horde_Share_Objects
+     */
+    public function getParents()
+    {
+        $parents = array();
+        $share = $this->getParent();
+        while (is_a($share, 'Horde_Share_Object')) {
+            $parents[] = $share;
+            $share = $share->getParent();
+        }
+        return array_reverse($parents);
+    }
+
+    /**
+     * Set the parent object for this share.
+     *
+     * @param mixed $parent    A Horde_Share object or share id for the parent.
+     *
+     * @return mixed  true || PEAR_Error
+     */
+    public function setParent($parent)
+    {
+        if (!is_null($parent) && !is_a($parent, 'Horde_Share_Object')) {
+            $parent = $this->_shareOb->getShareById($parent);
+            if ($parent instanceof PEAR_Error) {
+                Horde::logMessage($parent, 'ERR');
+                throw new Horde_Share_Exception($parent->getMessage());
+            }
+        }
+
+        /* If we are an existing share, check for any children */
+        if ($this->getId()) {
+            $children = $this->_shareOb->listShares(
+                Horde_Auth::getAuth(), Horde_Perms::EDIT, null, 0, 0, null, 0,
+                $this->getId());
+        } else {
+            $children = array();
+        }
+
+        /* Can't set a child share as a parent */
+        if (!empty($parent) && in_array($parent->getId(), array_keys($children))) {
+            throw new Horde_Share_Exception('Cannot set an existing child as the parent');
+        }
+
+        if (!is_null($parent)) {
+            $parent_string = $parent->get('parents') . ':' . $parent->getId();
+        } else {
+            $parent_string = null;
+        }
+        $this->data['share_parents'] = $parent_string;
+        $query = $this->_shareOb->_write_db->prepare('UPDATE ' . $this->_shareOb->_table . ' SET share_parents = ? WHERE share_id = ?', null, MDB2_PREPARE_MANIP);
+        $result = $query->execute(array($this->data['share_parents'], $this->getId()));
+        $query->free();
+        if ($result instanceof PEAR_Error) {
+            throw new Horde_Share_Exception($result->getMessage());
+        }
+
+        /* Now we can reset the children's parent */
+        foreach($children as $child) {
+            $child->setParent($this);
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns the permission of this share.
+     *
+     * @return Horde_Perms_Permission  Permission object that represents the
+     *                               permissions on this share.
+     */
+    public function getPermission()
+    {
+        $perm = new Horde_Perms_Permission('');
+        $perm->data = isset($this->data['perm'])
+            ? $this->data['perm']
+            : array();
+
+        return $perm;
+    }
+
+    /**
+     * Returns one of the attributes of the object, or null if it isn't
+     * defined.
+     *
+     * @param string $attribute  The attribute to retrieve.
+     *
+     * @return mixed  The value of the attribute, or an empty string.
+     */
+    protected function _get($attribute)
+    {
+        if ($attribute == 'owner' || $attribute == 'parents') {
+            return $this->data['share_' . $attribute];
+        } elseif (isset($this->data['attribute_' . $attribute])) {
+            return $this->data['attribute_' . $attribute];
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Hierarchical shares do not have share names.
+     *
+     * @return unknown
+     */
+    protected function _getName()
+    {
+        return '';
+    }
+
+}
+
diff --git a/framework/Share/lib/Horde/Share/Sql.php b/framework/Share/lib/Horde/Share/Sql.php
new file mode 100644 (file)
index 0000000..95df8d1
--- /dev/null
@@ -0,0 +1,943 @@
+<?php
+/**
+ * Horde_Share_sql:: provides the sql backend for the horde share
+ * driver.
+ *
+ * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author  Duck <duck@obala.net>
+ * @package Horde_Share
+ */
+
+/**
+ * @package Horde_Share
+ */
+class Horde_Share_Sql extends Horde_Share
+{
+    /* Share has user perms */
+    const SQL_FLAG_USERS = 1;
+
+    /* Share has group perms */
+    const SQL_FLAG_GROUPS = 2;
+
+    /**
+     * Handle for the current database connection.
+     * @TODO: port to Horde_Db
+     *
+     * @var MDB2
+     */
+    protected $_db;
+
+    /**
+     * Handle for the current database connection, used for writing. Defaults
+     * to the same handle as $db if a separate write database is not required.
+     *
+     * @var MDB2
+     */
+    protected $_write_db;
+
+    /**
+     * SQL connection parameters
+     */
+    protected $_params = array();
+
+    /**
+     * Main share table for the current scope.
+     *
+     * @var string
+     */
+    protected $_table;
+
+    /**
+     * The Horde_Share_Object subclass to instantiate objects as
+     *
+     * @var string
+     */
+    protected $_shareObject = 'Horde_Share_Object_Sql';
+
+    /**
+     * Initializes the object.
+     */
+    public function __wakeup()
+    {
+        $this->_table = $this->_app . '_shares';
+        $this->_connect();
+
+        foreach (array_keys($this->_cache) as $name) {
+            $this->_cache[$name]->setShareOb($this);
+        }
+
+        parent::__wakeup();
+    }
+
+    /**
+     * Returns the properties that need to be serialized.
+     *
+     * @return array  List of serializable properties.
+     */
+    public function __sleep()
+    {
+        $properties = get_object_vars($this);
+        unset($properties['_sortList'],
+              $properties['_db'],
+              $properties['_write_db']);
+        return array_keys($properties);
+    }
+
+    /**
+     * Get storage table
+     */
+    public function getTable()
+    {
+        return $this->_table;
+    }
+
+    /**
+     * Refetence to write db
+     */
+    public function getWriteDb()
+    {
+        return $this->_write_db;
+    }
+
+    public function getReadDb()
+    {
+        return $this->_db;
+    }
+
+    /**
+     * Finds out if the share has user set
+     */
+    public function _hasUsers($share)
+    {
+        return $share['share_flags'] & self::SQL_FLAG_USERS;
+    }
+
+    /**
+     * Finds out if the share has user set
+     */
+    public function _hasGroups($share)
+    {
+        return $share['share_flags'] & self::SQL_FLAG_GROUPS;
+    }
+
+    /**
+     * Get users permissions
+     *
+     * @param array $share Share data array
+     *
+     * @throws Horde_Share_Exception
+     */
+    protected function _getShareUsers(&$share)
+    {
+        if ($this->_hasUsers($share)) {
+            $stmt = $this->_db->prepare('SELECT user_uid, perm FROM ' . $this->_table . '_users WHERE share_id = ?');
+            if ($stmt instanceof PEAR_Error) {
+                Horde::logMessage($stmt, 'ERR');
+                throw new Horde_Share_Exception($stmt->getMessage());
+            }
+            $result = $stmt->execute(array($share['share_id']));
+            if ( $result instanceof PEAR_Error) {
+                Horde::logMessage($result, 'ERR');
+                throw new Horde_Share_Exception($result->getMessage());
+            } elseif (!empty($result)) {
+                while ($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+                    $share['perm']['users'][$row['user_uid']] = (int)$row['perm'];
+                }
+            }
+            $stmt->free();
+            $result->free();
+        }
+    }
+
+    /**
+     * Get groups permissions
+     *
+     * @param array $share Share data array
+     * @throws Horde_Share_Exception
+     */
+    protected function _getShareGroups(&$share)
+    {
+        if ($this->_hasGroups($share)) {
+            // Get groups permissions
+            $stmt = $this->_db->prepare('SELECT group_uid, perm FROM ' . $this->_table . '_groups WHERE share_id = ?');
+            if ($stmt instanceof PEAR_Error) {
+                Horde::logMessage($stmt, 'ERR');
+                throw new Horde_Share_Exception($stmt->getMessage());
+            }
+            $result = $stmt->execute(array($share['share_id']));
+            if ($result instanceof PEAR_Error) {
+                Horde::logMessage($result, 'ERR');
+                throw new Horde_Share_Exception($result->getMessage());
+            } elseif (!empty($result)) {
+                while ($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+                    $share['perm']['groups'][$row['group_uid']] = (int)$row['perm'];
+                }
+            }
+            $stmt->free();
+            $result->free();
+        }
+    }
+
+    /**
+     * Returns a Horde_Share_Object_sql object corresponding to the given
+     * share name, with the details retrieved appropriately.
+     *
+     * @param string $name  The name of the share to retrieve.
+     *
+     * @return Horde_Share_Object_sql  The requested share.
+     */
+    protected function _getShare($name)
+    {
+        $stmt = $this->_db->prepare('SELECT * FROM ' . $this->_table . ' WHERE share_name = ?');
+        if ($stmt instanceof PEAR_Error) {
+            Horde::logMessage($stmt, 'ERR');
+            throw new Horde_Share_Exception($stmt->getMessage());
+        }
+        $results = $stmt->execute(array($name));
+        if ($results instanceof PEAR_Error) {
+            Horde::logMessage($results, 'ERR');
+            throw new Horde_Share_Exception($results->getMessage());
+        }
+        $data = $results->fetchRow(MDB2_FETCHMODE_ASSOC);
+        if ($data instanceof PEAR_Error) {
+            Horde::logMessage($data, 'ERR');
+            throw new Horde_Share_Exception($data->getMessage());
+        } elseif (empty($data)) {
+            throw new Horde_Share_Exception(sprintf(_("Share \"%s\" does not exist."), $name));
+        }
+        $stmt->free();
+        $results->free();
+
+        // Convert charset
+        $data = $this->_fromDriverCharset($data);
+
+        // Populate the perms array
+        $this->_loadPermissions($data);
+
+        return new $this->_shareObject($data);
+    }
+
+    /**
+     * Helper function to load the permissions data into the share data
+     *
+     * @param array $data  Array of share attributes
+     */
+    protected function _loadPermissions(&$data)
+    {
+        $this->_getShareUsers($data);
+        $this->_getShareGroups($data);
+        $this->_getSharePerms($data);
+    }
+
+    protected function _getSharePerms(&$data)
+    {
+        $data['perm']['type'] = 'matrix';
+        $data['perm']['default'] = isset($data['perm_default']) ? (int)$data['perm_default'] : 0;
+        $data['perm']['guest'] = isset($data['perm_guest']) ? (int)$data['perm_guest'] : 0;
+        $data['perm']['creator'] = isset($data['perm_creator']) ? (int)$data['perm_creator'] : 0;
+        unset($data['perm_creator'], $data['perm_guest'], $data['perm_default']);
+    }
+
+    /**
+     * Returns a Horde_Share_Object_sql object corresponding to the given
+     * unique ID, with the details retrieved appropriately.
+     *
+     * @param integer $cid  The id of the share to retrieve.
+     *
+     * @return Horde_Share_Object_sql  The requested share.
+     * @throws Horde_Share_Exception
+     */
+    protected function _getShareById($id)
+    {
+        $params = array($id);
+        $stmt = $this->_db->prepare('SELECT * FROM ' . $this->_table . ' WHERE share_id = ?');
+        if ($stmt instanceof PEAR_Error) {
+            Horde::logMessage($stmt, 'ERR');
+            throw new Horde_Share_Exception($stmt->getMessage());
+        }
+        $results = $stmt->execute($params);
+        if ($results instanceof PEAR_Error) {
+            Horde::logMessage($results, 'ERR');
+            throw new Horde_Share_Exception($results->getMessage());
+        }
+        $data = $results->fetchRow(MDB2_FETCHMODE_ASSOC);
+        if ($data instanceof PEAR_Error) {
+            Horde::logMessage($data, 'ERR');
+            throw new Horde_Share_Exception($data->getMessage());
+        } elseif (empty($data)) {
+            throw new Horde_Share_Exception(sprintf(_("Share ID %d does not exist."), $id));
+        }
+
+        $stmt->free();
+        $results->free();
+
+        // Convert charset
+        $data = $this->_fromDriverCharset($data);
+
+        // Get permissions
+        $this->_loadPermissions($data);
+
+        return new $this->_shareObject($data);
+    }
+
+    /**
+     * Returns an array of Horde_Share_Object_sql objects corresponding
+     * to the given set of unique IDs, with the details retrieved
+     * appropriately.
+     *
+     * @param array $cids  The array of ids to retrieve.
+     *
+     * @return array  The requested shares.
+     */
+    protected function _getShares($ids)
+    {
+        $shares = array();
+        $query = 'SELECT * FROM ' . $this->_table . ' WHERE share_id IN (' . implode(', ', $ids) . ')';
+        $result = $this->_db->query($query);
+        if ($result instanceof PEAR_Error) {
+            Horde::logMessage($result, 'ERR');
+            throw new Horde_Share_Exception($result->getMessage());
+        } elseif (empty($result)) {
+            return array();
+        }
+
+        $groups = array();
+        $users = array();
+        while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            $shares[(int)$share['share_id']] = $this->_fromDriverCharset($share);
+            if ($this->_hasUsers($share)) {
+                $users[] = (int)$share['share_id'];
+            }
+            if ($this->_hasGroups($share)) {
+                $groups[] = (int)$share['share_id'];
+            }
+        }
+        $result->free();
+
+        // Get users permissions
+        if (!empty($users)) {
+            $query = 'SELECT share_id, user_uid, perm FROM ' . $this->_table . '_users '
+                    . ' WHERE share_id IN (' . implode(', ', $users) . ')';
+            $result = $this->_db->query($query);
+            if ($result instanceof PEAR_Error) {
+                Horde::logMessage($result, 'ERR');
+                throw new Horde_Share_Exception($result->getMessage());
+            } elseif (!empty($result)) {
+                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+                    $shares[$share['share_id']]['perm']['users'][$share['user_uid']] = (int)$share['perm'];
+                }
+                $result->free();
+            }
+        }
+
+        // Get groups permissions
+        if (!empty($groups)) {
+            $query = 'SELECT share_id, group_uid, perm FROM ' . $this->_table . '_groups'
+                   . ' WHERE share_id IN (' . implode(', ', $groups) . ')';
+            $result = $this->_db->query($query);
+            if ($result instanceof PEAR_Error) {
+                Horde::logMessage($result, 'ERR');
+                throw new Horde_Share_Exception($result->getMessage());
+            } elseif (!empty($result)) {
+                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+                    $shares[$share['share_id']]['perm']['groups'][$share['group_uid']] = (int)$share['perm'];
+                }
+                $result->free();
+            }
+        }
+
+        $sharelist = array();
+        foreach ($shares as $id => $data) {
+            $this->_getSharePerms($data);
+            $sharelist[$data['share_name']] = new $this->_shareObject($data);
+        }
+
+        return $sharelist;
+    }
+
+    /**
+     * Lists *all* shares for the current app/share, regardless of
+     * permissions.
+     *
+     * This is for admin functionality and scripting tools, and shouldn't be
+     * called from user-level code!
+     *
+     * @return array  All shares for the current app/share.
+     * @throws Horde_Share_Exception
+     */
+    public function listAllShares()
+    {
+        return $this->_listAllShares();
+    }
+
+    /**
+     * Lists *all* shares for the current app/share, regardless of
+     * permissions.
+     *
+     * @return array  All shares for the current app/share.
+     */
+    protected function _listAllShares()
+    {
+        $shares = array();
+        $query = 'SELECT * FROM ' . $this->_table . ' ORDER BY share_name ASC';
+        $result = $this->_db->query($query);
+        if ($result instanceof PEAR_Error) {
+            Horde::logMessage($result, 'ERR');
+            throw new Horde_Share_Exception($result->getMessage());
+        } elseif (empty($result)) {
+            return array();
+        }
+
+        while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            $shares[(int)$share['share_id']] = $this->_fromDriverCharset($share);
+        }
+        $result->free();
+
+        // Get users permissions
+        $query = 'SELECT share_id, user_uid, perm FROM ' . $this->_table . '_users';
+        $result = $this->_db->query($query);
+        if ($result instanceof PEAR_Error) {
+            Horde::logMessage($result, 'ERR');
+            return $result;
+        } elseif (!empty($result)) {
+            while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+                $shares[$share['share_id']]['perm']['users'][$share['user_uid']] = (int)$share['perm'];
+            }
+            $result->free();
+        }
+
+        // Get groups permissions
+        $query = 'SELECT share_id, group_uid, perm FROM ' . $this->_table . '_groups';
+        $result = $this->_db->query($query);
+        if ($result instanceof PEAR_Error) {
+            Horde::logMessage($result, 'ERR');
+            throw new Horde_Share_Exception($result->getMessage());
+        } elseif (!empty($result)) {
+            while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+                $shares[$share['share_id']]['perm']['groups'][$share['group_uid']] = (int)$share['perm'];
+            }
+            $result->free();
+        }
+
+        $sharelist = array();
+        foreach ($shares as $id => $data) {
+            $this->_getSharePerms($data);
+            $sharelist[$data['share_name']] = new $this->_shareObject($data);
+            $sharelist[$data['share_name']]->setShareOb($this);
+        }
+
+        return $sharelist;
+    }
+
+    /**
+     * Returns an array of all shares that $userid has access to.
+     *
+     * @param string $userid     The userid of the user to check access for.
+     * @param integer $perm      The level of permissions required.
+     * @param mixed $attributes  Restrict the shares counted to those
+     *                           matching $attributes. An array of
+     *                           attribute/values pairs or a share owner
+     *                           username.
+     *
+     * @return array  The shares the user has access to.
+     * @throws Horde_Share_Exception
+     */
+    public function listShares($userid, $perm = Horde_Perms::SHOW, $attributes = null,
+                        $from = 0, $count = 0, $sort_by = null, $direction = 0)
+    {
+        $shares = array();
+        if (is_null($sort_by)) {
+            $sortfield = 's.share_name';
+        } elseif ($sort_by == 'owner' || $sort_by == 'id') {
+            $sortfield = 's.share_' . $sort_by;
+        } else {
+            $sortfield = 's.attribute_' . $sort_by;
+        }
+
+        $query = 'SELECT DISTINCT s.* '
+            . $this->getShareCriteria($userid, $perm, $attributes)
+            . ' ORDER BY ' . $sortfield
+            . (($direction == 0) ? ' ASC' : ' DESC');
+        if ($from > 0 || $count > 0) {
+            $this->_db->setLimit($count, $from);
+        }
+
+        // Fix field names for sqlite. MDB2 tries to handle this with
+        // MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES, but it doesn't stick.
+        if ($this->_db->phptype == 'sqlite') {
+            $connection = $this->_db->getConnection();
+            @sqlite_query('PRAGMA full_column_names=0', $connection);
+            @sqlite_query('PRAGMA short_column_names=1', $connection);
+        }
+
+        Horde::logMessage(sprintf("SQL Query by Horde_Share_sql::listShares: %s", $query), 'DEBUG');
+        $result = $this->_db->query($query);
+        if ($result instanceof PEAR_Error) {
+            Horde::logMessage($result, 'ERR');
+            throw new Horde_Share_Exception($result->getMessage());
+        } elseif (empty($result)) {
+            return array();
+        }
+
+        $users = array();
+        $groups = array();
+        while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            $shares[(int)$share['share_id']] = $this->_fromDriverCharset($share);
+            if ($this->_hasUsers($share)) {
+                $users[] = (int)$share['share_id'];
+            }
+            if ($this->_hasGroups($share)) {
+                $groups[] = (int)$share['share_id'];
+            }
+        }
+        $result->free();
+
+        // Get users permissions
+        if (!empty($users)) {
+            $query = 'SELECT share_id, user_uid, perm FROM ' . $this->_table
+                 . '_users WHERE share_id IN (' . implode(', ', $users)
+                 . ')';
+            $result = $this->_db->query($query);
+            if ($result instanceof PEAR_Error) {
+                Horde::logMessage($result, 'ERR');
+                throw new Horde_Share_Exception($result->getMessage());
+            } elseif (!empty($result)) {
+                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+                    $shares[$share['share_id']]['perm']['users'][$share['user_uid']] = (int)$share['perm'];
+                }
+                $result->free();
+            }
+        }
+
+        // Get groups permissions
+        if (!empty($groups)) {
+            $query = 'SELECT share_id, group_uid, perm FROM ' . $this->_table
+                     . '_groups WHERE share_id IN (' . implode(', ', $groups)
+                     . ')';
+            $result = $this->_db->query($query);
+            if ($result instanceof PEAR_Error) {
+                Horde::logMessage($result, 'ERR');
+                throw new Horde_Share_Exception($result->getMessage());
+            } elseif (!empty($result)) {
+                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+                    $shares[$share['share_id']]['perm']['groups'][$share['group_uid']] = (int)$share['perm'];
+                }
+                $result->free();
+            }
+        }
+
+        $sharelist = array();
+        foreach ($shares as $id => $data) {
+            $this->_getSharePerms($data);
+            $sharelist[$data['share_name']] = new $this->_shareObject($data);
+            $sharelist[$data['share_name']]->setShareOb($this);
+        }
+        unset($shares);
+
+        try {
+            return Horde::callHook('share_list', array($userid, $perm, $attributes, $sharelist));
+        } catch (Horde_Exception_HookNotSet $e) {}
+
+        return $sharelist;
+    }
+
+    /**
+     * Returns an array of all system shares.
+     *
+     * @return array  All system shares.
+     * @throws Horde_Share_Exception
+     */
+    public function listSystemShares()
+    {
+        // Fix field names for sqlite. MDB2 tries to handle this with
+        // MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES, but it doesn't stick.
+        if ($this->_db->phptype == 'sqlite') {
+            $connection = $this->_db->getConnection();
+            @sqlite_query('PRAGMA full_column_names=0', $connection);
+            @sqlite_query('PRAGMA short_column_names=1', $connection);
+        }
+
+        $query = 'SELECT * FROM ' . $this->_table . ' WHERE share_owner IS NULL';
+        Horde::logMessage('SQL Query by Horde_Share_sql::listSystemShares: ' . $query, 'DEBUG');
+        $result = $this->_db->query($query);
+        if ($result instanceof PEAR_Error) {
+            Horde::logMessage($result, 'ERR');
+            throw new Horde_Share_Exception($result->getMessage());;
+        } elseif (empty($result)) {
+            return array();
+        }
+
+        $sharelist = array();
+        while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            $data = $this->_fromDriverCharset($share);
+            $this->_getSharePerms($data);
+            $sharelist[$data['share_name']] = new $this->_shareObject($data);
+            $sharelist[$data['share_name']]->setShareOb($this);
+        }
+        $result->free();
+
+        return $sharelist;
+    }
+
+    /**
+     * Returns the number of shares that $userid has access to.
+     *
+     * @param string $userid     The userid of the user to check access for.
+     * @param integer $perm      The level of permissions required.
+     * @param mixed $attributes  Restrict the shares counted to those
+     *                           matching $attributes. An array of
+     *                           attribute/values pairs or a share owner
+     *                           username.
+     *
+     * @return integer  The number of shares
+     */
+    protected function _countShares($userid, $perm = Horde_Perms::SHOW,
+                                    $attributes = null)
+    {
+        $query = $this->getShareCriteria($userid, $perm, $attributes);
+        $query = 'SELECT COUNT(DISTINCT s.share_id) ' . $query;
+        Horde::logMessage(sprintf("SQL Query by Horde_Share_sql::_countShares: %s", $query), 'DEBUG');
+        return $this->_db->queryOne($query);
+    }
+
+    /**
+     * Returns a new share object.
+     *
+     * @param string $name  The share's name.
+     *
+     * @return Horde_Share_Object_sql  A new share object.
+     */
+    protected function _newShare($name)
+    {
+        return new $this->_shareObject(array('share_name' => $name));
+    }
+
+    /**
+     * Adds a share to the shares system.
+     *
+     * The share must first be created with
+     * Horde_Share_sql::_newShare(), and have any initial details added
+     * to it, before this function is called.
+     *
+     * @param Horde_Share_Object_sql $share  The new share object.
+     */
+    protected function _addShare($share)
+    {
+        return $share->save();
+    }
+
+    /**
+     * Removes a share from the shares system permanently.
+     *
+     * @param Horde_Share_Object_sql $share  The share to remove.
+     *
+     * @return boolean
+     * @throws Horde_Share_Exception
+     */
+    protected function _removeShare($share)
+    {
+        $params = array($share->getId());
+        $tables = array($this->_table,
+                        $this->_table . '_users',
+                        $this->_table . '_groups');
+        foreach ($tables as $table) {
+
+            /* Remove the share entry */
+            $stmt = $this->_write_db->prepare('DELETE FROM ' . $table . ' WHERE share_id = ?', null, MDB2_PREPARE_MANIP);
+            if ($stmt instanceof PEAR_Error) {
+                Horde::logMessage($stmt, 'ERR');
+                throw new Horde_Share_Exception($stmt->getMessage());
+            }
+            $result = $stmt->execute($params);
+            if ($result instanceof PEAR_Error) {
+                Horde::logMessage($result, 'ERR');
+                throw new Horde_Share_Exception($result->getMessage());
+            }
+            $stmt->free();
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks if a share exists in the system.
+     *
+     * @param string $share  The share to check.
+     *
+     * @return boolean  True if the share exists.
+     * @throws Horde_Share_Exception
+     */
+    protected function _exists($share)
+    {
+        $stmt = $this->_db->prepare('SELECT 1 FROM ' . $this->_table
+                . ' WHERE share_name = ?');
+
+        if ($stmt instanceof PEAR_Error) {
+            Horde::logMessage($stmt, 'ERR');
+            throw new Horde_Share_Exception($stmt->getMessage());
+        }
+        $result = $stmt->execute(array($share));
+        if ($result instanceof PEAR_Error) {
+            Horde::logMessage($result, 'ERR');
+            throw new Horde_Share_Exception($result->getMessage());
+        }
+
+        $exists = (bool)$result->fetchOne();
+        $stmt->free();
+        $result->free();
+
+        return $exists;
+    }
+
+    /**
+     * Returns an array of criteria for querying shares.
+     * @access protected
+     *
+     * @param string  $userid      The userid of the user to check access for.
+     * @param integer $perm        The level of permissions required.
+     * @param mixed   $attributes  Restrict the shares returned to those who
+     *                             have these attribute values.
+     *
+     * @return string  The criteria string for fetching this user's shares.
+     */
+    public function getShareCriteria($userid, $perm = Horde_Perms::SHOW,
+                               $attributes = null)
+    {
+        $query = ' FROM ' . $this->_table . ' s ';
+        $where = '';
+
+        if (!empty($userid)) {
+            // (owner == $userid)
+            $where .= 's.share_owner = ' . $this->_write_db->quote($userid);
+
+            // (name == perm_creator and val & $perm)
+            $where .= ' OR (' . Horde_SQL::buildClause($this->_db, 's.perm_creator', '&', $perm) . ')';
+
+            // (name == perm_creator and val & $perm)
+            $where .= ' OR (' . Horde_SQL::buildClause($this->_db, 's.perm_default',  '&', $perm) . ')';
+
+            // (name == perm_users and key == $userid and val & $perm)
+            $query .= ' LEFT JOIN ' . $this->_table . '_users u ON u.share_id = s.share_id';
+             $where .= ' OR ( u.user_uid = ' .  $this->_write_db->quote($userid)
+            . ' AND (' . Horde_SQL::buildClause($this->_db, 'u.perm', '&', $perm) . '))';
+
+            // If the user has any group memberships, check for those also.
+            // @TODO: Inject the group driver
+            require_once 'Horde/Group.php';
+            $group = Group::singleton();
+            $groups = $group->getGroupMemberships($userid, true);
+            if (!is_a($groups, 'PEAR_Error') && $groups) {
+                // (name == perm_groups and key in ($groups) and val & $perm)
+                $ids = array_keys($groups);
+                $group_ids = array();
+                foreach ($ids as $id) {
+                    $group_ids[] = $this->_db->quote((string)$id);
+                }
+                $query .= ' LEFT JOIN ' . $this->_table . '_groups g ON g.share_id = s.share_id';
+                $where .= ' OR (g.group_uid IN (' . implode(',', $group_ids) . ')'
+                    . ' AND (' . Horde_SQL::buildClause($this->_db, 'g.perm', '&', $perm) . '))';
+            } elseif ($groups instanceof PEAR_Error) {
+                Horde::logMessage($groups, 'ERR');
+            }
+        } else {
+            $where = '(' . Horde_SQL::buildClause($this->_db, 's.perm_guest', '&', $perm) . ')';
+        }
+
+        $attributes = $this->_toDriverKeys($attributes);
+        $attributes = $this->_toDriverCharset($attributes);
+
+        if (is_array($attributes)) {
+            // Build attribute/key filter.
+            $where = ' (' . $where . ') ';
+            foreach ($attributes as $key => $value) {
+                $where .= ' AND ' . $key . ' = ' . $this->_db->quote($value);
+            }
+        } elseif (!empty($attributes)) {
+            // Restrict to shares owned by the user specified in the
+            // $attributes string.
+            $where = ' (' . $where . ') AND s.share_owner = ' . $this->_db->quote($attributes);
+        }
+
+        return $query . ' WHERE ' . $where;
+    }
+
+    /**
+     * Resets the current database name so that MDB2 is always selecting the
+     * database before sending a query.
+     *
+     * @TODO: This needs to be public since it's used as a callback in MDB2.
+     *        Remove when refactored to use Horde_Db
+     */
+    public function _selectDB($db, $scope, $message, $is_manip = null)
+    {
+        if ($scope == 'query') {
+            $db->connected_database_name = '';
+        }
+    }
+
+    /**
+     * Set the SQL table name to use for the current scope's share storage.
+     *
+     * @var string $table  The table name
+     */
+    public function setShareTable($table) {
+        $this->_table = $table;
+    }
+
+    /**
+     * Attempts to open a connection to the sql server.
+     *
+     * @return boolean  True on success.
+     * @throws Horde_Share_Exception
+     */
+    protected function _connect()
+    {
+        $this->_params = $GLOBALS['conf']['sql'];
+        if (!isset($this->_params['database'])) {
+            $this->_params['database'] = '';
+        }
+        if (!isset($this->_params['username'])) {
+            $_params['username'] = '';
+        }
+        if (!isset($this->_params['hostspec'])) {
+            $this->_params['hostspec'] = '';
+        }
+
+        /* Connect to the sql server using the supplied parameters. */
+        $params = $this->_params;
+        unset($params['charset']);
+        $this->_write_db = MDB2::factory($params);
+        if ($this->_write_db instanceof PEAR_Error) {
+            throw new Horde_Share_Excetion($this->_write_db->getMessage());
+        }
+
+        /* Attach debug handler. */
+        $this->_write_db->setOption('debug', true);
+        $this->_write_db->setOption('debug_handler', array($this, '_selectDB'));
+        $this->_write_db->setOption('seqcol_name', 'id');
+
+        /* Set DB portability options. */
+        switch ($this->_write_db->phptype) {
+        case 'mssql':
+            $this->_write_db->setOption('field_case', CASE_LOWER);
+            $this->_write_db->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_RTRIM | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
+            break;
+
+        case 'pgsql':
+            /* The debug handler breaks PostgreSQL. In most cases it shouldn't
+             * be necessary, but this may mean we simply can't support use of
+             * multiple Postgres databases right now. See
+             * http://bugs.horde.org/ticket/7825 */
+            $this->_write_db->setOption('debug', false);
+            // Fall through
+
+        default:
+            $this->_write_db->setOption('field_case', CASE_LOWER);
+            $this->_write_db->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
+        }
+
+        /* Check if we need to set up the read DB connection seperately. */
+        if (!empty($this->_params['splitread'])) {
+            $params = array_merge($params, $this->_params['read']);
+            unset($params['charset']);
+            $this->_db = MDB2::singleton($params);
+            if ($this->_db instanceof PEAR_Error) {
+                throw new Horde_Share_Exception($this->_db);
+            }
+
+            $this->_db->setOption('seqcol_name', 'id');
+            /* Set DB portability options. */
+            switch ($this->_db->phptype) {
+            case 'mssql':
+                $this->_db->setOption('field_case', CASE_LOWER);
+                $this->_db->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_RTRIM | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
+                break;
+
+            case 'pgsql':
+                /* The debug handler breaks PostgreSQL. In most cases it shouldn't
+                 * be necessary, but this may mean we simply can't support use of
+                 * multiple Postgres databases right now. See
+                 * http://bugs.horde.org/ticket/7825 */
+                $this->_write_db->setOption('debug', false);
+                // Fall through
+
+            default:
+                $this->_db->setOption('field_case', CASE_LOWER);
+                $this->_db->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
+            }
+        } else {
+            /* Default to the same DB handle as the writer for reading too */
+            $this->_db = $this->_write_db;
+        }
+
+        return true;
+    }
+
+    /**
+     * Utility function to convert from the SQL server's charset.
+     */
+    protected function _fromDriverCharset($data)
+    {
+        foreach ($data as $key => $value) {
+            if (substr($key, 0, 9) == 'attribute') {
+                $data[$key] = Horde_String::convertCharset(
+                    $data[$key], $this->_params['charset']);
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Utility function to convert TO the SQL server's charset.
+     *
+     * @TODO: This needs to be public since it's called by the share object.
+     * Look at making this outright public or maybe moving it to the object.
+     */
+    public function _toDriverCharset($data)
+    {
+        if (!is_array($data)) {
+            return $data;
+        }
+
+        foreach ($data as $key => $value) {
+            if (substr($key, 0, 9) == 'attribute') {
+                $data[$key] = Horde_String::convertCharset(
+                    $data[$key], Horde_Nls::getCharset(), $this->_params['charset']);
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Convert an array keyed on client keys to an array keyed on the driver
+     * keys.
+     *
+     * @param array  $data  The client code keyed array.
+     *
+     * @return array  The driver keyed array.
+     */
+    protected function _toDriverKeys($data)
+    {
+        if (!is_array($data)) {
+            return $data;
+        }
+
+        $driver_keys = array();
+        foreach ($data as $key => $value) {
+            if ($key == 'owner') {
+                $driver_keys['share_owner'] = $value;
+            } else {
+                $driver_keys['attribute_' . $key] = $value;
+            }
+        }
+
+        return $driver_keys;
+    }
+
+}
diff --git a/framework/Share/lib/Horde/Share/Sql/Hierarchical.php b/framework/Share/lib/Horde/Share/Sql/Hierarchical.php
new file mode 100644 (file)
index 0000000..007af4b
--- /dev/null
@@ -0,0 +1,577 @@
+<?php
+/**
+ * Implementation of Horde_Share class for shared objects that are hierarchical
+ * in nature.
+ *
+ * @author  Duck <duck@obala.net>
+ * @author  Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Horde_Share
+ */
+class Horde_Share_Sql_Hierarchical extends Horde_Share_Sql
+{    
+    /**
+     * The Horde_Share_Object subclass to instantiate objects as
+     *
+     * @var string
+     */
+    protected $_shareObject = 'Horde_Share_Object_Sql_Hierarchical';
+
+    /**
+     * Override new share creation so we can allow for shares with empty
+     * share_names.
+     *
+     */
+    public function newShare($name = '')
+    {
+        $share = $this->_newShare();
+        $share->setShareOb($this);
+        //@TODO: inject the Auth object
+        $share->set('owner', Horde_Auth::getAuth());
+
+        return $share;
+    }
+
+    /**
+     * Returns a new share object.
+     *
+     * @param string $name  The share's name.
+     *
+     * @return Horde_Share_Object_sql  A new share object.
+     */
+    protected function _newShare()
+    {
+        $share = new $this->_shareObject();
+        return $share;
+    }
+
+    /**
+     * Returns an array of all shares that $userid has access to.
+     *
+     * @param string $userid      The userid of the user to check access for.
+     * @param integer $perm       The level of permissions required.
+     * @param mixed $attributes   Restrict the shares counted to those
+     *                            matching $attributes. An array of
+     *                            attribute/values pairs or a share owner
+     *                            username.
+     * @param integer $from       The share to start listing from.
+     * @param integer $count      The number of shares to return.
+     * @param string $sort_by     The field to sort by
+     * @param integer $direction  The sort direction
+     * @param mixed $parent       Either a share_id, Horde_Share_Object or null.
+     * @param boolean $alllevels  List all levels or just the direct children
+     *                            of $parent?
+     *
+     * @return mixed  The shares the user has access to
+     * @throws Horde_Share_Exception
+     */
+    public function &listShares($userid, $perm = Horde_Perms::SHOW, $attributes = null,
+                                $from = 0,  $count = 0, $sort_by = null,
+                                $direction = 0, $parent = null,
+                                $allLevels = true, $ignorePerms = false)
+    {
+        $shares = array();
+        if (is_null($sort_by)) {
+            $sortfield = 's.share_id';
+        } elseif ($sort_by == 'owner' || $sort_by == 'id') {
+            $sortfield = 's.share_' . $sort_by;
+        } else {
+            $sortfield = 's.attribute_' . $sort_by;
+        }
+
+        $query = 'SELECT DISTINCT s.* '
+                 . $this->getShareCriteria($userid, $perm, $attributes,
+                                            $parent, $allLevels, $ignorePerms)
+                 . ' ORDER BY ' . $sortfield
+                 . (($direction == 0) ? ' ASC' : ' DESC');
+        if ($from > 0 || $count > 0) {
+            $this->_db->setLimit($count, $from);
+        }
+
+        Horde::logMessage('Query By Horde_Share_sql_hierarchical: ' . $query, 'DEBUG');
+        $result = $this->_db->query($query);
+        if ($result instanceof PEAR_Error) {
+            Horde::logMessage($result, 'ERR');
+            throw new Horde_Share_Exception($result->getMessage());
+        } elseif (empty($result)) {
+            return array();
+        }
+
+        $users = array();
+        $groups = array();
+        while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            $shares[(int)$share['share_id']] = $this->_fromDriverCharset($share);
+            if ($this->_hasUsers($share)) {
+                $users[] = (int)$share['share_id'];
+            }
+            if ($this->_hasGroups($share)) {
+                $groups[] = (int)$share['share_id'];
+            }
+        }
+        $result->free();
+
+        // Get users permissions
+        if (!empty($users)) {
+            $query = 'SELECT share_id, user_uid, perm FROM ' . $this->_table
+                     . '_users WHERE share_id IN (' . implode(', ', $users)
+                     . ')';
+            $result = $this->_db->query($query);
+            if ($result instanceof PEAR_Error) {
+                Horde::logMessage($result, 'ERR');
+                throw new Horde_Share_Exception($result->getMessage());
+            } elseif (!empty($result)) {
+                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+                    $shares[$share['share_id']]['perm']['users'][$share['user_uid']] = (int)$share['perm'];
+                }
+                $result->free();
+            }
+        }
+
+        // Get groups permissions
+        if (!empty($groups)) {
+            $query = 'SELECT share_id, group_uid, perm FROM ' . $this->_table
+                     . '_groups WHERE share_id IN (' . implode(', ', $groups)
+                     . ')';
+            $result = $this->_db->query($query);
+            if ($result instanceof PEAR_Error) {
+                Horde::logMessage($result, 'ERR');
+                throw new Horde_Share_Exception($result->getMessage());
+            } elseif (!empty($result)) {
+                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+                    $shares[$share['share_id']]['perm']['groups'][$share['group_uid']] = (int)$share['perm'];
+                }
+                $result->free();
+            }
+        }
+
+        $sharelist = array();
+        foreach ($shares as $id => $data) {
+            $this->_getSharePerms($data);
+            $sharelist[$id] = new $this->_shareObject($data);
+            $sharelist[$id]->setShareOb($this);
+        }
+        unset($shares);
+
+        try {
+            return Horde::callHook('share_list', array($userid, $perm, $attributes, $sharelist));
+        } catch (Horde_Exception_HookNotSet $e) {}
+
+        return $sharelist;
+    }
+
+    /**
+     * Returns an array of criteria for querying shares.
+     * @access protected
+     *
+     * @param string $userid      The userid of the user to check access for.
+     * @param integer $perm       The level of permissions required.
+     * @param mixed $attributes   Restrict the shares returned to those who
+     *                            have these attribute values.
+     * @param mixed $parent       The share to start searching in.
+     *                            (A Horde_Share_Object, share_id or null)
+     * @param boolean $allLevels  Return all levels, or just the direct
+     *                            children of $parent? Defaults to all levels.
+     *
+     * @return string  The criteria string for fetching this user's shares.
+     * @throws Horde_Share_Exception
+     */
+    public function getShareCriteria($userid, $perm = Horde_Perms::SHOW, $attributes = null,
+                                         $parent = null, $allLevels = true,
+                                         $ignorePerms = false)
+    {
+        static $criteria;
+
+        if ($parent instanceof Horde_Share_Object) {
+            $parent_id = $parent->getId();
+        } else {
+            $parent_id = $parent;
+        }
+        $key = $userid . $perm . $parent_id . $allLevels
+               . (is_array($attributes) ? serialize($attributes) : $attributes);
+        if (isset($criteria[$key])) {
+            return $criteria[$key];
+        }
+
+        $query = ' FROM ' . $this->_table . ' s ';
+        $where = '';
+
+        if (!$ignorePerms) {
+            if (empty($userid)) {
+                $where = '(' . Horde_SQL::buildClause($this->_db, 's.perm_guest', '&', $perm) . ')';
+            } else {
+                // (owner == $userid)
+                $where = 's.share_owner = ' . $this->_db->quote($userid);
+
+                // (name == perm_creator and val & $perm)
+                $where .= ' OR (' . Horde_SQL::buildClause($this->_db, 's.perm_creator', '&', $perm) . ')';
+
+                // (name == perm_creator and val & $perm)
+                $where .= ' OR (' . Horde_SQL::buildClause($this->_db, 's.perm_default',  '&', $perm) . ')';
+
+                // (name == perm_users and key == $userid and val & $perm)
+                $query .= ' LEFT JOIN ' . $this->_table . '_users AS u ON u.share_id = s.share_id';
+                $where .= ' OR ( u.user_uid = ' .  $this->_write_db->quote($userid)
+                . ' AND (' . Horde_SQL::buildClause($this->_db, 'u.perm', '&', $perm) . '))';
+
+                // If the user has any group memberships, check for those also.
+                // @TODO: Inject the group driver
+                require_once 'Horde/Group.php';
+                $group = &Group::singleton();
+                $groups = $group->getGroupMemberships($userid, true);
+                if (!($groups instanceof PEAR_Error) && $groups) {
+                    // (name == perm_groups and key in ($groups) and val & $perm)
+                    $ids = array_keys($groups);
+                    $group_ids = array();
+                    foreach ($ids as $id) {
+                        $group_ids[] = $this->_db->quote((string)$id);
+                    }
+                    $query .= ' LEFT JOIN ' . $this->_table . '_groups AS g ON g.share_id = s.share_id';
+                    $where .= ' OR (g.group_uid IN (' . implode(',', $group_ids) . ')'
+                        . ' AND (' . Horde_SQL::buildClause($this->_db, 'g.perm', '&', $perm) . '))';
+                }
+            }
+        }
+
+        /* Convert to driver's keys */
+        $attributes = $this->_toDriverKeys($attributes);
+
+        /* ...and to driver charset */
+        $attributes = $this->_toDriverCharset($attributes);
+
+        if (is_array($attributes)) {
+            // Build attribute/key filter.
+            if (!empty($where)) {
+                $where = ' (' . $where . ') ';
+            }
+            foreach ($attributes as $key => $value) {
+                $where .= ' AND ' . $key;
+                if (is_array($value)) {
+                    $where .= ' ' . $value[0]. ' ' . $this->_db->quote($value[1]);
+                } else {
+                    $where .= ' = ' . $this->_db->quote($value);
+                }
+            }
+        } elseif (!empty($attributes)) {
+            // Restrict to shares owned by the user specified
+            $where = (!empty($where) ? ' (' . $where . ') AND ' : ' ') . 's.share_owner = ' . $this->_db->quote($attributes);
+        }
+
+        // See if we need to filter by parent or get the parent object
+        if ($parent != null) {
+            if (!($parent instanceof Horde_Share_Object)) {
+                $parent = $this->getShareById($parent);
+                if ($parent instanceof PEAR_Error) {
+                    throw new Horde_Share_Exception($parent->getMessage());
+                }
+            }
+
+            // Need to append the parent's share id to the list of parents in
+            // order to search the share_parents field.
+            $parents = $parent->get('parents') . ':' . $parent->getId();
+            if ($allLevels) {
+                $where_parent = '(share_parents = ' . $this->_db->quote($parents)
+                        . ' OR share_parents LIKE ' . $this->_db->quote($parents . ':%') . ')';
+            } else {
+                $where_parent = 's.share_parents = ' . $this->_db->quote($parents);
+            }
+        } elseif (!$allLevels) {
+            // No parents, and we only want the root.
+            $where_parent = "(s.share_parents = '' OR s.share_parents IS NULL)";
+        }
+
+        if (empty($where_parent)) {
+            $criteria[$key] = $query . ' WHERE ' . $where;
+        } else {
+            if (!empty($where)) {
+                $criteria[$key] = $query . ' WHERE (' . $where . ') AND ' . $where_parent;
+            } else {
+                $criteria[$key] = $query . ' WHERE ' . $where_parent;
+            }
+        }
+
+        return $criteria[$key];
+    }
+
+    /**
+     * Return a list of users who have shares with the given permissions
+     * for the current user.
+     *
+     * @param integer $perm       The level of permissions required.
+     * @param mixed  $parent      The parent share to start looking in.
+     *                            (Horde_Share_Object, share_id, or null)
+     * @param boolean $allLevels  Return all levels, or just the direct
+     *                            children of $parent? Defaults to all levels.
+     * @param integer $from       The user to start listing at.
+     * @param integer $count      The number of users to return.
+     *
+     * @return array  List of users.
+     */
+    public function listOwners($perm = Horde_Perms::SHOW, $parent = null, $allLevels = true,
+                               $from = 0, $count = 0)
+    {
+        $sql = 'SELECT DISTINCT(s.share_owner) '
+                . $this->getShareCriteria(Horde_Auth::getAuth(), $perm, null,
+                                           $parent, $allLevels);
+
+        if ($count) {
+            $this->_db->setLimit($count, $from);
+        }
+
+        $allowners = $this->_db->queryCol($sql);
+        if ($allowners instanceof PEAR_Error) {
+             Horde::logMessage($allowners, 'ERR');
+             throw new Horde_Share_Exception($allowners->getMessage());
+        }
+
+        $owners = array();
+        foreach ($allowners as $owner) {
+            if ($this->countShares(Horde_Auth::getAuth(), $perm, $owner, $parent,
+                                   $allLevels)) {
+
+                $owners[] = $owner;
+            }
+        }
+
+        return $owners;
+    }
+
+    /**
+     * Count the number of users who have shares with the given permissions
+     * for the current user.
+     *
+     * @param integer $perm       The level of permissions required.
+     * @param mixed $parent       The parent share to start looking in.
+     *                            (Horde_Share_Object, share_id, or null).
+     * @param boolean $allLevels  Return all levels, or just the direct
+     *                            children of $parent?
+     *
+     * @return integer  Number of users.
+     */
+    public function countOwners($perm = Horde_Perms::SHOW, $parent = null, $allLevels = true)
+    {
+        $sql = 'SELECT COUNT(DISTINCT(s.share_owner)) '
+               . $this->getShareCriteria(Horde_Auth::getAuth(), $perm, null, $parent,
+                                          $allLevels);
+
+        return $this->_db->queryOne($sql);
+    }
+
+    /**
+     * Returns a share's direct parent object.
+     *
+     * @param Horde_Share_Object $share  The share to get parent for.
+     *
+     * @return Horde_Share_Object The parent share, if it exists.
+     */
+    public function getParent($child)
+    {
+        $parents = $child->get('parents');
+
+        // No parents, this is at the root.
+        if (empty($parents)) {
+            return null;
+        }
+        $parents = explode(':', $parents);
+        return $this->getShareById(array_pop($parents));
+    }
+
+    /**
+     * Returns a Horde_Share_Object object corresponding to the given unique
+     * ID, with the details retrieved appropriately.
+     *
+     * @param string $cid  The id of the share to retrieve.
+     *
+     * @return Horde_Share_Object  The requested share.
+     */
+    public function getShareById($cid)
+    {
+        if (!isset($this->_cache[$cid])) {
+            $share = &$this->_getShareById($cid);
+            if ($share instanceof PEAR_Error) {
+                throw new Horde_Share_Exception($share->getMessage());
+            }
+            $share->setShareOb($this);
+            $this->_cache[$cid] = &$share;
+        }
+
+        return $this->_cache[$cid];
+    }
+
+    /**
+     * Returns an array of Horde_Share_Object objects corresponding to the
+     * given set of unique IDs, with the details retrieved appropriately.
+     *
+     * @param array $cids  The array of ids to retrieve.
+     *
+     * @return array  The requested shares keyed by share_id.
+     * @throws Horde_Share_Exception
+     */
+    public function getShares($cids)
+    {
+        $all_shares = array();
+        $missing_ids = array();
+        foreach ($cids as $cid) {
+            if (isset($this->_cache[$cid])) {
+                $all_shares[] = &$this->_cache[$cid];
+            } else {
+                $missing_ids[] = $cid;
+            }
+        }
+
+        if (count($missing_ids)) {
+            $shares = &$this->_getShares($missing_ids);
+            if ($shares instanceof PEAR_Error) {
+                throw new Horde_Share_Exception($shares->getMessage());
+            }
+
+            foreach (array_keys($shares) as $key) {
+                $this->_cache[$key] = &$shares[$key];
+                $this->_cache[$key]->setShareOb($this);
+                $all_shares[$key] = &$this->_cache[$key];
+            }
+        }
+
+        return $all_shares;
+    }
+
+   /**
+     * Removes a share from the shares system permanently. This will recursively
+     * delete all child shares as well.
+     *
+     * @param Horde_Share_Object $share  The share to remove.
+     * @throws Horde_Exception
+     */
+    public function removeShare(Horde_Share_Object $share)
+    {
+        try {
+            Horde::callHook('share_remove', array($share));
+        } catch (Horde_Share_Exception_HookNotSet $e) {}
+
+        /* Get the list of all $share's children */
+        $children = $share->getChildren(null, true);
+
+        /* Remove share from the caches. */
+        $id = $share->getId();
+        $this->_cache = array();
+        $this->_listCache = array();
+
+        foreach ($children as $child) {
+            $result = $this->_removeShare($child);
+            if ($result instanceof PEAR_Error) {
+                throw new Horde_Share_Exception($result->getMessage());
+            }
+        }
+
+        return $this->_removeShare($share);
+    }
+
+    /**
+     * Returns an array of Horde_Share_Object_sql objects corresponding
+     * to the given set of unique IDs, with the details retrieved
+     * appropriately.
+     *
+     * @param array $cids  The array of ids to retrieve.
+     *
+     * @return array  The requested shares keyed by share_id.
+     */
+    protected function _getShares($ids)
+    {
+        $shares = array();
+        $query = 'SELECT * FROM ' . $this->_table . ' WHERE share_id IN (' . implode(', ', $ids) . ')';
+        $result = $this->_db->query($query);
+        if ($result instanceof PEAR_Error) {
+            Horde::logMessage($result, 'ERR');
+            throw new Horde_Share_Exception($result->getMessage());
+        } elseif (empty($result)) {
+            return array();
+        }
+
+        $groups = array();
+        $users = array();
+        while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+            $shares[(int)$share['share_id']] = $this->_fromDriverCharset($share);
+            if ($this->_hasUsers($share)) {
+                $users[] = (int)$share['share_id'];
+            }
+            if ($this->_hasGroups($share)) {
+                $groups[] = (int)$share['share_id'];
+            }
+        }
+        $result->free();
+
+        // Get users permissions
+        if (!empty($users)) {
+            $query = 'SELECT share_id, user_uid, perm FROM ' . $this->_table . '_users '
+                    . ' WHERE share_id IN (' . implode(', ', $users) . ')';
+            $result = $this->_db->query($query);
+            if ($result instanceof PEAR_Error) {
+                Horde::logMessage($result, 'ERR');
+                throw new Horde_Share_Exception($result->getMessage());
+            } elseif (!empty($result)) {
+                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+                    $shares[$share['share_id']]['perm']['users'][$share['user_uid']] = (int)$share['perm'];
+                }
+                $result->free();
+            }
+        }
+
+        // Get groups permissions
+        if (!empty($groups)) {
+            $query = 'SELECT share_id, group_uid, perm FROM ' . $this->_table . '_groups'
+                   . ' WHERE share_id IN (' . implode(', ', $groups) . ')';
+            $result = $this->_db->query($query);
+            if ($result instanceof PEAR_Error) {
+                Horde::logMessage($result, 'ERR');
+                throw new Horde_Share_Exception($result->getMessage());
+            } elseif (!empty($result)) {
+                while ($share = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+                    $shares[$share['share_id']]['perm']['groups'][$share['group_uid']] = (int)$share['perm'];
+                }
+                $result->free();
+            }
+        }
+
+        $sharelist = array();
+        foreach ($shares as $id => $data) {
+            $sharelist[$id] = new $this->_shareObject($data);
+        }
+
+        return $sharelist;
+    }
+
+    /**
+     * Override the Horde_Share base class to avoid any confusion
+     *
+     * @throws Horde_Share_Exception
+     */
+    public function getShare($name)
+    {
+        throw new Horde_Share_Exception(_("Share names are not supported in this driver"));
+    }
+
+    /**
+     * Returns the count of all shares that $userid has access to.
+     *
+     * @param string  $userid      The userid of the user to check access for.
+     * @param integer $perm        The level of permissions required.
+     * @param mixed   $attributes  Restrict the shares counted to those
+     *                             matching $attributes. An array of
+     *                             attribute/values pairs or a share owner
+     *                             username.
+     * @param mixed  $parent      The share to start searching from
+     *                            (Horde_Share_Object, share_id, or null)
+     * @param boolean $allLevels  Return all levels, or just the direct
+     *                            children of $parent?
+     *
+     * @return integer  Number of shares the user has access to.
+     */
+    public function countShares($userid, $perm = Horde_Perms::SHOW, $attributes = null,
+                         $parent = null, $allLevels = true)
+    {
+        $query = 'SELECT COUNT(DISTINCT s.share_id) '
+                 . $this->getShareCriteria($userid, $perm, $attributes,
+                                            $parent, $allLevels);
+
+        return $this->_db->queryOne($query);
+    }
+
+}
index 6c08176..e9d891f 100644 (file)
@@ -1,56 +1,78 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
-http://pear.php.net/dtd/tasks-1.0.xsd
-http://pear.php.net/dtd/package-2.0
-http://pear.php.net/dtd/package-2.0.xsd">
+<package packagerversion="1.9.0" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
  <name>Horde_Share</name>
  <channel>pear.horde.org</channel>
  <summary>Horde Shared Permissions System</summary>
  <description>Horde_Share:: This class provides an interface to all shared
-resources a user owns or has access to.
- </description>
+resources a user owns or has access to.</description>
  <lead>
   <name>Chuck Hagenbuch</name>
   <user>chuck</user>
   <email>chuck@horde.org</email>
   <active>yes</active>
  </lead>
- <date>2006-05-08</date>
- <time>23:21:27</time>
+ <date>2010-05-17</date>
+ <time>13:26:30</time>
  <version>
-  <release>0.0.3</release>
-  <api>0.0.3</api>
+  <release>0.0.4</release>
+  <api>0.0.4</api>
  </version>
  <stability>
   <release>beta</release>
   <api>beta</api>
  </stability>
  <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
- <notes>* Converted to package.xml 2.0 for pear.horde.org.
-* Split into a driver pattern.
-* Simplified the API greatly, removing unused functions.
-* Add a method for counting the number of shares at a certain access level.
-* Add native SQL driver (duck@obala.net, Request #6109).
-* Implemented extended free/busy access concept in the Kolab driver.
+ <notes>
+* Converted to Horde 4 coding standards
  </notes>
  <contents>
-  <dir name="/">
-   <file baseinstalldir="/Horde" name="Share.php" role="php"/>
-   <dir name="Share">
-     <file baseinstalldir="/Horde" name="datatree.php" role="php"/>
-     <file baseinstalldir="/Horde" name="kolab.php" role="php"/>
-     <file baseinstalldir="/Horde" name="sql.php" role="php"/>
-     <file baseinstalldir="/Horde" name="sql_hierarchical.php" role="php"/>
-   </dir>
+  <dir baseinstalldir="/" name="/">
+   <dir name="lib">
+    <dir name="Horde">
+     <dir name="Share">
+      <dir name="Object">
+       <dir name="Datatree">
+        <file baseinstalldir="/" name="Share.php" role="php" />
+       </dir> <!-- /lib/Horde/Share/Object/Datatree -->
+       <dir name="Sql">
+        <file baseinstalldir="/" name="Hierarchical.php" role="php" />
+       </dir> <!-- /lib/Horde/Share/Object/Sql -->
+       <file baseinstalldir="/" name="Datatree.php" role="php" />
+       <file baseinstalldir="/" name="Kolab.php" role="php" />
+       <file baseinstalldir="/" name="Sql.php" role="php" />
+      </dir> <!-- /lib/Horde/Share/Object -->
+      <dir name="Sql">
+       <file baseinstalldir="/" name="Hierarchical.php" role="php" />
+      </dir> <!-- /lib/Horde/Share/Sql -->
+      <file baseinstalldir="/" name="Datatree.php" role="php" />
+      <file baseinstalldir="/" name="Exception.php" role="php" />
+      <file baseinstalldir="/" name="Kolab.php" role="php" />
+      <file baseinstalldir="/" name="Object.php" role="php" />
+      <file baseinstalldir="/" name="Sql.php" role="php" />
+     </dir> <!-- /lib/Horde/Share -->
+     <file baseinstalldir="/" name="Share.php" role="php" />
+    </dir> <!-- /lib/Horde -->
+   </dir> <!-- /lib -->
+   <dir name="tests">
+    <dir name="Horde">
+     <dir name="Share">
+      <file baseinstalldir="/" name="AllTests.php" role="php" />
+      <file baseinstalldir="/" name="KolabScenarioTest.php" role="php" />
+     </dir> <!-- /tests/Horde/Share -->
+    </dir> <!-- /tests/Horde -->
+    <file baseinstalldir="/" name="kolab_createdefault.phpt" role="data" />
+    <file baseinstalldir="/" name="kolab_list.phpt" role="data" />
+    <file baseinstalldir="/" name="kolab_simple.phpt" role="data" />
+   </dir> <!-- /tests -->
   </dir> <!-- / -->
  </contents>
  <dependencies>
   <required>
    <php>
-    <min>4.0.0</min>
+    <min>5.2.0</min>
    </php>
    <pearinstaller>
-    <min>1.4.0b1</min>
+    <min>1.5.0</min>
    </pearinstaller>
    <package>
     <name>Perms</name>
@@ -62,10 +84,40 @@ resources a user owns or has access to.
    </package>
   </required>
  </dependencies>
- <phprelease />
+ <phprelease>
+  <filelist>
+   <install as="Horde/Share.php" name="lib/Horde/Share.php" />
+   <install as="Horde/Share/Datatree.php" name="lib/Horde/Share/Datatree.php" />
+   <install as="Horde/Share/Exception.php" name="lib/Horde/Share/Exception.php" />
+   <install as="Horde/Share/Kolab.php" name="lib/Horde/Share/Kolab.php" />
+   <install as="Horde/Share/Object.php" name="lib/Horde/Share/Object.php" />
+   <install as="Horde/Share/Sql.php" name="lib/Horde/Share/Sql.php" />
+   <install as="Horde/Share/Object/Datatree.php" name="lib/Horde/Share/Object/Datatree.php" />
+   <install as="Horde/Share/Object/Kolab.php" name="lib/Horde/Share/Object/Kolab.php" />
+   <install as="Horde/Share/Object/Sql.php" name="lib/Horde/Share/Object/Sql.php" />
+   <install as="Horde/Share/Object/Datatree/Share.php" name="lib/Horde/Share/Object/Datatree/Share.php" />
+   <install as="Horde/Share/Object/Sql/Hierarchical.php" name="lib/Horde/Share/Object/Sql/Hierarchical.php" />
+   <install as="Horde/Share/Sql/Hierarchical.php" name="lib/Horde/Share/Sql/Hierarchical.php" />
+  </filelist>
+ </phprelease>
  <changelog>
   <release>
    <version>
+    <release>0.0.1</release>
+    <api>0.0.1</api>
+   </version>
+   <stability>
+    <release>alpha</release>
+    <api>alpha</api>
+   </stability>
+   <date>2003-07-05</date>
+   <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+   <notes>
+Initial release as a PEAR package
+   </notes>
+  </release>
+  <release>
+   <version>
     <release>0.0.2</release>
     <api>0.0.2</api>
    </version>
@@ -75,7 +127,8 @@ resources a user owns or has access to.
    </stability>
    <date>2004-10-29</date>
    <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
-   <notes>- Added countShares().
+   <notes>
+- Added countShares().
 - listShares() and countShares() now treat the $owner argument
   as an optional username restricting the returned shares, not
   a modifier to the initial $userid. Now $userid is the user
@@ -86,16 +139,37 @@ resources a user owns or has access to.
   </release>
   <release>
    <version>
-    <release>0.0.1</release>
-    <api>0.0.1</api>
+    <release>0.0.3</release>
+    <api>0.0.3</api>
    </version>
    <stability>
-    <release>alpha</release>
-    <api>alpha</api>
+    <release>beta</release>
+    <api>beta</api>
    </stability>
-   <date>2003-07-05</date>
+   <date>2006-05-08</date>
+   <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+   <notes>
+* Converted to package.xml 2.0 for pear.horde.org.
+    * Split into a driver pattern.
+    * Simplified the API greatly, removing unused functions.
+    * Add a method for counting the number of shares at a certain access level.
+    * Add native SQL driver (duck@obala.net, Request #6109).
+    * Implemented extended free/busy access concept in the Kolab driver.
+   </notes>
+  </release>
+  <release>
+   <version>
+    <release>0.0.4</release>
+    <api>0.0.4</api>
+   </version>
+   <stability>
+    <release>beta</release>
+    <api>beta</api>
+   </stability>
+   <date>2010-05-17</date>
    <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
-   <notes>Initial release as a PEAR package
+   <notes>
+* Converted to Horde 4 coding standards
    </notes>
   </release>
  </changelog>
index 33ee6c1..903463e 100644 (file)
@@ -40,21 +40,23 @@ $reload = false;
 $actionID = Horde_Util::getFormData('actionID', 'edit');
 switch ($actionID) {
 case 'edit':
-    $share = &$shares->getShareById(Horde_Util::getFormData('cid'));
-    if (!$share instanceof PEAR_Error) {
+    try {
+        $share = &$shares->getShareById(Horde_Util::getFormData('cid'));
         $form = 'edit.inc';
         $perm = &$share->getPermission();
-    } elseif (($category = Horde_Util::getFormData('share')) !== null) {
-        $share = &$shares->getShare($category);
-        if (!$share instanceof PEAR_Error) {
-            $form = 'edit.inc';
-            $perm = &$share->getPermission();
+    } catch (Horde_Share_Exception $e) {
+        if (($category = Horde_Util::getFormData('share')) !== null) {
+            try {
+                $share = $shares->getShare($category);
+                $form = 'edit.inc';
+                $perm = &$share->getPermission();
+            } catch (Horde_Share_Exception $e) {
+                $notification->push($e->getMessage(), 'horde.error');
+            }
         }
     }
 
-    if ($share instanceof PEAR_Error) {
-        $notification->push($share, 'horde.error');
-    } elseif (!Horde_Auth::getAuth() ||
+    if (!Horde_Auth::getAuth() ||
               (isset($share) &&
                !Horde_Auth::isAdmin() &&
                Horde_Auth::getAuth() != $share->get('owner'))) {
@@ -63,10 +65,13 @@ case 'edit':
     break;
 
 case 'editform':
-    $share = &$shares->getShareById(Horde_Util::getFormData('cid'));
-    if ($share instanceof PEAR_Error) {
+    try {
+        $share = &$shares->getShareById(Horde_Util::getFormData('cid'));
+    } catch (Horde_Share_Exception $e) {
         $notification->push(_("Attempt to edit a non-existent share."), 'horde.error');
-    } else {
+    }
+
+    if (!empty($share)) {
         if (!Horde_Auth::getAuth() ||
             (!Horde_Auth::isAdmin() &&
              Horde_Auth::getAuth() != $share->get('owner'))) {
index d161cff..6f65299 100644 (file)
@@ -139,8 +139,7 @@ if ($exception = Horde_Util::getFormData('del_exception')) {
                 // has permissions to do so.
                 try {
                     $sourceShare = Kronolith::getInternalCalendar($source);
-                    if (!($share instanceof PEAR_Error) &&
-                        $sourceShare->hasPermission(Horde_Auth::getAuth(), Horde_Perms::DELETE) &&
+                    if ($sourceShare->hasPermission(Horde_Auth::getAuth(), Horde_Perms::DELETE) &&
                         (($user == Horde_Auth::getAuth() &&
                           $share->hasPermission(Horde_Auth::getAuth(), Horde_Perms::EDIT)) ||
                          ($user != Horde_Auth::getAuth() &&
index c02f707..b39deec 100644 (file)
@@ -412,9 +412,6 @@ class Kronolith_Api extends Horde_Registry_Api
                 // Remove share and all groups/permissions.
                 $share = $GLOBALS['kronolith_shares']->getShare($calendarId);
                 $result = $GLOBALS['kronolith_shares']->removeShare($share);
-                if ($result instanceof PEAR_Error) {
-                    throw new Kronolith_Exception($result);
-                }
             } catch (Exception $e) {
                 throw new Kronolith_Exception(sprintf(_("Unable to delete calendar \"%s\": %s"), $calendarId, $e->getMessage()));
             }
index 1b282aa..0490f10 100644 (file)
@@ -588,9 +588,10 @@ class Kronolith_Driver_Sql extends Kronolith_Driver
             $tagger->replaceTags($event->uid, $event->tags, $event->creator, 'event');
 
             /* Add tags again, but as the share owner (replaceTags removes ALL tags). */
-            $cal = $GLOBALS['kronolith_shares']->getShare($event->calendar);
-            if ($cal instanceof PEAR_Error) {
-                throw new Kronolith_Exception($cal);
+            try {
+                $cal = $GLOBALS['kronolith_shares']->getShare($event->calendar);
+            } catch (Horde_Share_Exception $e) {
+                throw new Kronolith_Exception($e);
             }
             $tagger->tag($event->uid, $event->tags, $cal->get('owner'), 'event');
 
@@ -656,8 +657,12 @@ class Kronolith_Driver_Sql extends Kronolith_Driver
 
         /* Add tags again, but as the share owner (replaceTags removes ALL
          * tags). */
-        $cal = $GLOBALS['kronolith_shares']->getShare($event->calendar);
-        $this->handleError($cal);
+        try {
+            $cal = $GLOBALS['kronolith_shares']->getShare($event->calendar);
+        } catch (Horde_Share_Exception $e) {
+            Horde::logMessage($e->getMessage(), 'ERR');
+            throw new Kronolith_Exception($e);
+        }
         $tagger->tag($event->uid, $event->tags, $cal->get('owner'), 'event');
 
         /* Update Geolocation */
@@ -970,8 +975,12 @@ class Kronolith_Driver_Sql extends Kronolith_Driver
             throw new Horde_Exception_PermissionDenied();
         }
 
-        $shares = $GLOBALS['kronolith_shares']->listShares($user, Horde_Perms::EDIT);
-        $this->handleError($shares);
+        try {
+            $shares = $GLOBALS['kronolith_shares']->listShares($user, Horde_Perms::EDIT);
+        } catch (Horde_Share_Exception $e) {
+            Horde::logMessage($shares, 'ERR');
+            throw new Kronolith_Exception($shares);
+        }
 
         foreach (array_keys($shares) as $calendar) {
             $ids = Kronolith::listEventIds(null, null, $calendar);
index 3425f0d..8f0f4c1 100644 (file)
@@ -32,8 +32,10 @@ class Kronolith_FreeBusy
         }
 
         /* Fetch the appropriate share and check permissions. */
-        $share = $kronolith_shares->getShare($calendar[0]);
-        if ($share instanceof PEAR_Error) {
+        try {
+            $share = $kronolith_shares->getShare($calendar[0]);
+            $owner = $share->get('owner');
+        } catch (Horde_Share_Exception $e) {
             // Might be a Kronolith_Resource
             try {
                 $resource = Kronolith_Resource::isResourceCalendar($calendar[0]);
@@ -41,8 +43,6 @@ class Kronolith_FreeBusy
             } catch (Horde_Exception $e) {
                 return $returnObj ? $share : '';
             }
-        } else {
-            $owner = $share->get('owner');
         }
 
         /* Default the start date to today. */
index 3a3289b..62d994b 100644 (file)
@@ -27,17 +27,21 @@ $reload = false;
 $actionID = Horde_Util::getFormData('actionID', 'edit');
 switch ($actionID) {
 case 'edit':
-    $share = $shares->getShareById(Horde_Util::getFormData('cid'));
-    if (!($share instanceof PEAR_Error)) {
+    try {
+        $share = $shares->getShareById(Horde_Util::getFormData('cid'));
         $perm = $share->getPermission();
-    } elseif (($category = Horde_Util::getFormData('share')) !== null) {
-        try {
-            $share = $shares->getShare($category);
+    } catch (Horde_Share_Exception $e) {
+        if (($category = Horde_Util::getFormData('share')) !== null) {
+            try {
+                $share = $shares->getShare($category);
+                $perm = $share->getPermission();
+            } catch (Exception $e) {
+                $notification->push($e, 'horde.error');
+            }
             $perm = $share->getPermission();
-        } catch (Exception $e) {
-            $notification->push($e, 'horde.error');
         }
     }
+    
     if (!Horde_Auth::getAuth() ||
         (isset($share) &&
          !Horde_Auth::isAdmin() &&
@@ -47,10 +51,8 @@ case 'edit':
     break;
 
 case 'editform':
-    $share = $shares->getShareById(Horde_Util::getFormData('cid'));
-    if ($share instanceof PEAR_Error) {
-        $notification->push(_("Attempt to edit a non-existent share."), 'horde.error');
-    } else {
+    try {
+        $share = $shares->getShareById(Horde_Util::getFormData('cid'));
         if (!Horde_Auth::getAuth() ||
             (!Horde_Auth::isAdmin() &&
              Horde_Auth::getAuth() != $share->get('owner'))) {
@@ -71,11 +73,14 @@ case 'editform':
             $notification->push($e, 'horde.error');
         }
         $perm = $share->getPermission();
+    } catch (Horde_Share_Exception $e) {
+        $notification->push(_("Attempt to edit a non-existent share."), 'horde.error');
     }
+
     break;
 }
 
-if ($share instanceof PEAR_Error) {
+if (empty($share)) {
     $title = _("Edit Permissions");
 } else {
     $title = sprintf(_("Edit Permissions for %s"), $share->get('name'));
index b17dc9e..07c503a 100644 (file)
@@ -11,9 +11,6 @@
  * @package Whups
  */
 
-/** Horde_Share */
-require_once 'Horde/Share.php';
-
 /** Horde_Form_Action */
 require_once 'Horde/Form/Action.php';