Abstract prefs storage code into Horde_Prefs_Storage.
authorMichael M Slusarz <slusarz@curecanti.org>
Fri, 15 Oct 2010 22:02:55 +0000 (16:02 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Wed, 20 Oct 2010 16:07:14 +0000 (10:07 -0600)
Jan and I talked about using a stack system for retrieving prefs data
from storage.  After discussion, determined that cache storage won't
work in the stack system for several reasons.  However, it may be
possible to implement otherwise.  For example, in Horde, there would be
2 stack drivers.  The first is a read-only driver that returns the
default values from the prefs.php files.  The second would be the
persistent storage driver.

28 files changed:
framework/Core/lib/Horde/Core/Factory/Prefs.php
framework/Core/lib/Horde/Core/Prefs/Cache/Session.php [deleted file]
framework/Core/lib/Horde/Core/Prefs/Session.php [deleted file]
framework/Core/lib/Horde/Core/Prefs/Storage/Session.php [new file with mode: 0644]
framework/Core/lib/Horde/Registry.php
framework/Core/package.xml
framework/Prefs/lib/Horde/Prefs.php
framework/Prefs/lib/Horde/Prefs/Cache.php [deleted file]
framework/Prefs/lib/Horde/Prefs/Cache/Null.php [deleted file]
framework/Prefs/lib/Horde/Prefs/Cache/Session.php [deleted file]
framework/Prefs/lib/Horde/Prefs/File.php [deleted file]
framework/Prefs/lib/Horde/Prefs/Imsp.php [deleted file]
framework/Prefs/lib/Horde/Prefs/Kolab.php [deleted file]
framework/Prefs/lib/Horde/Prefs/KolabImap.php [deleted file]
framework/Prefs/lib/Horde/Prefs/Ldap.php [deleted file]
framework/Prefs/lib/Horde/Prefs/Session.php [deleted file]
framework/Prefs/lib/Horde/Prefs/Sql.php [deleted file]
framework/Prefs/lib/Horde/Prefs/Storage.php [new file with mode: 0644]
framework/Prefs/lib/Horde/Prefs/Storage/File.php [new file with mode: 0644]
framework/Prefs/lib/Horde/Prefs/Storage/Imsp.php [new file with mode: 0644]
framework/Prefs/lib/Horde/Prefs/Storage/Kolab.php [new file with mode: 0644]
framework/Prefs/lib/Horde/Prefs/Storage/KolabImap.php [new file with mode: 0644]
framework/Prefs/lib/Horde/Prefs/Storage/Ldap.php [new file with mode: 0644]
framework/Prefs/lib/Horde/Prefs/Storage/Null.php [new file with mode: 0644]
framework/Prefs/lib/Horde/Prefs/Storage/Session.php [new file with mode: 0644]
framework/Prefs/lib/Horde/Prefs/Storage/Sql.php [new file with mode: 0644]
framework/Prefs/package.xml
framework/Prefs/test/Horde/Prefs/bug_2838.phpt [deleted file]

index b9bce66..320f7b2 100644 (file)
@@ -63,7 +63,8 @@ class Horde_Core_Factory_Prefs
      * Return the Horde_Prefs:: instance.
      *
      * @param string $scope  The scope for this set of preferences.
-     * @param array $opts    See Horde_Prefs::factory(). Additional options:
+     * @param array $opts    See Horde_Prefs::__construct(). Additional
+     *                       options:
      * <pre>
      * 'session' - (boolean) Use the session driver.
      *             DEFAULT: false
@@ -75,7 +76,7 @@ class Horde_Core_Factory_Prefs
     {
         if (empty($GLOBALS['conf']['prefs']['driver']) ||
             !empty($opts['session'])) {
-            $driver = 'Horde_Core_Prefs_Session';
+            $driver = 'Horde_Core_Prefs_Storage_Session';
             $params = array();
             unset($opts['session']);
         } else {
@@ -84,7 +85,7 @@ class Horde_Core_Factory_Prefs
         }
 
         $opts = array_merge(array(
-            'cache' => 'Horde_Core_Prefs_Cache_Session',
+            'cache' => 'Horde_Core_Prefs_Storage_Session',
             'charset' => 'UTF-8',
             'logger' => $this->_injector->getInstance('Horde_Log_Logger'),
             'password' => '',
@@ -122,7 +123,7 @@ class Horde_Core_Factory_Prefs
             }
 
             try {
-                $this->_instances[$sig] = Horde_Prefs::factory($driver, $scope, $opts, $params);
+                $this->_instances[$sig] = new Horde_Prefs($driver, $scope, $opts, $params);
             } catch (Horde_Prefs_Exception $e) {
                 if (!$GLOBALS['session']['horde:no_prefs']) {
                     $GLOBALS['session']['horde:no_prefs'] = true;
@@ -130,7 +131,7 @@ class Horde_Core_Factory_Prefs
                         $GLOBALS['notification']->push($this->_coreDict->t("The preferences backend is currently unavailable and your preferences have not been loaded. You may continue to use the system with default preferences."));
                     }
                 }
-                $this->_instances[$sig] = Horde_Prefs::factory('Horde_Core_Prefs_Session', $scope);
+                $this->_instances[$sig] = new Horde_Prefs('Horde_Core_Prefs_Storage_Session', $scope, $opts);
             }
         }
 
diff --git a/framework/Core/lib/Horde/Core/Prefs/Cache/Session.php b/framework/Core/lib/Horde/Core/Prefs/Cache/Session.php
deleted file mode 100644 (file)
index 2ae9f26..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-/**
- * Session storage cache driver (using Horde_Session) for the preferences
- * system.
- *
- * Copyright 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   Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
- * @package  Core
- */
-class Horde_Core_Prefs_Cache_Session extends Horde_Prefs_Cache
-{
-    /**
-     * Session key.
-     *
-     * @var string
-     */
-    protected $_key;
-
-    /**
-     */
-    public function __construct($user)
-    {
-        parent::__construct($user);
-
-        $this->_key = 'horde:prefs_' . $this->_user . '/';
-    }
-
-    /**
-     */
-    public function get($scope)
-    {
-        global $session;
-
-        return isset($session[$this->_key . $scope])
-            ? $session[$this->_key . $scope]
-            : false;
-    }
-
-    /**
-     */
-    public function update($scope, $prefs)
-    {
-        if (($cached = $this->get($scope)) === false) {
-            $cached = array();
-        }
-        $cached = array_merge($cached, $prefs);
-        $GLOBALS['session'][$this->_key . $scope] = $cached;
-    }
-
-    /**
-     */
-    public function clear($scope = null, $pref = null)
-    {
-        global $session;
-
-        if (is_null($scope)) {
-            unset($session[$this->_key]);
-        } elseif (is_null($pref)) {
-            unset($session[$this->_key . $scope]);
-        } elseif ((($cached = $this->get($scope)) !== false) &&
-                  isset($cached[$pref])) {
-            unset($cached[$pref]);
-            $session[$this->_key . $scope] = $cached;
-        }
-    }
-
-}
diff --git a/framework/Core/lib/Horde/Core/Prefs/Session.php b/framework/Core/lib/Horde/Core/Prefs/Session.php
deleted file mode 100644 (file)
index d5a2898..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-/**
- * Preferences storage implementation using Horde_Session.
- *
- * Copyright 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   Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
- * @package  Core
- */
-class Horde_Core_Prefs_Session extends Horde_Prefs
-{
-    /**
-     * Retrieves the requested set of preferences from the current session.
-     *
-     * @param string $scope  Scope specifier.
-     */
-    protected function _retrieve($scope)
-    {
-        global $session;
-
-        if (isset($session['horde:prefs_session/' . $scope])) {
-            $this->_scopes[$scope] = $session['horde:prefs_session/' . $scope];
-        }
-    }
-
-    /**
-     * Stores preferences in the current session.
-     */
-    public function store()
-    {
-        // Copy the current preferences into the session variable.
-        foreach ($this->_scopes as $scope => $prefs) {
-            foreach (array_keys($prefs) as $pref_name) {
-                // Clean the pref since it was just saved.
-                $prefs[$pref_name]['m'] &= ~Horde_Prefs::DIRTY;
-            }
-
-            $session['horde:prefs_session/' . $scope] = $prefs;
-        }
-    }
-
-    /**
-     * Perform cleanup operations.
-     *
-     * @param boolean $all  Cleanup all Horde preferences.
-     */
-    public function cleanup($all = false)
-    {
-        global $session;
-
-        // Perform a Horde-wide cleanup?
-        if ($all) {
-            unset($session['horde:prefs_session/']);
-        } else {
-            unset($session['horde:prefs_session/' . $this->_scope]);
-        }
-
-        parent::cleanup($all);
-    }
-
-}
diff --git a/framework/Core/lib/Horde/Core/Prefs/Storage/Session.php b/framework/Core/lib/Horde/Core/Prefs/Storage/Session.php
new file mode 100644 (file)
index 0000000..406053b
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Preferences session storage implementation using Horde_Session.
+ *
+ * Copyright 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   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Core
+ */
+class Horde_Core_Prefs_Storage_Session extends Horde_Prefs_Storage
+{
+    const SESS_KEY = 'horde:prefs_session/';
+
+    /**
+     */
+    public function get($scope)
+    {
+        global $session;
+
+        return isset($session[self::SESS_KEY . $scope])
+            ? $session[self::SESS_KEY . $scope]
+            : false;
+    }
+
+    /**
+     */
+    public function store($prefs)
+    {
+        foreach ($prefs as $scope => $vals) {
+            if (($old_vals = $this->get($scope)) === false) {
+                $old_vals = array();
+            }
+            $GLOBALS['session'][self::SESS_KEY . $scope] = array_merge($old_vals, $vals);
+        }
+    }
+
+    /**
+     */
+    public function remove($scope = null, $pref = null)
+    {
+        global $session;
+
+        if (is_null($scope)) {
+            unset($session[self::SESS_KEY]);
+        } elseif (is_null($pref)) {
+            unset($session[self::SESS_KEY . $this->_scope]);
+        } elseif ((($vals = $this->get($scope)) !== false) &&
+                  isset($vals[$pref])) {
+            unset($vals[$pref]);
+            $session[self::SESS_KEY . $scope] = $vals;
+        }
+    }
+
+}
index e61dabe..9da47e4 100644 (file)
@@ -1344,10 +1344,10 @@ class Horde_Registry
                 'user' => $this->getAuth()
             );
         } else {
-            /* If there is no logged in user, return an empty Horde_Prefs::
+            /* If there is no logged in user, return an empty Horde_Prefs
              * object with just default preferences. */
             $opts = array(
-                'cache' => 'Horde_Prefs_Cache_Null',
+                'cache' => null,
                 'session' => true
             );
         }
index f781d4a..59554f7 100644 (file)
@@ -184,14 +184,13 @@ Application Framework.</description>
        <file name="Ui.php" role="php" />
       </dir> <!-- /lib/Horde/Core/Perms -->
       <dir name="Prefs">
-       <dir name="Cache">
+       <dir name="Storage">
         <file name="Session.php" role="php" />
        </dir> <!-- /lib/Horde/Core/Prefs/Cache -->
        <dir name="Ui">
         <file name="Widgets.php" role="php" />
        </dir> <!-- /lib/Horde/Core/Prefs/Ui -->
        <file name="Identity.php" role="php" />
-       <file name="Session.php" role="php" />
        <file name="Ui.php" role="php" />
       </dir> <!-- /lib/Horde/Core/Prefs -->
       <dir name="Text">
@@ -482,9 +481,8 @@ Application Framework.</description>
    <install as="Horde/Core/Notification/Hordelog.php" name="lib/Horde/Core/Notification/Hordelog.php" />
    <install as="Horde/Core/Notification/Status.php" name="lib/Horde/Core/Notification/Status.php" />
    <install as="Horde/Core/Perms/Ui.php" name="lib/Horde/Core/Perms/Ui.php" />
-   <install as="Horde/Core/Prefs/Cache/Session.php" name="lib/Horde/Core/Prefs/Cache/Session.php" />
    <install as="Horde/Core/Prefs/Identity.php" name="lib/Horde/Core/Prefs/Identity.php" />
-   <install as="Horde/Core/Prefs/Session.php" name="lib/Horde/Core/Prefs/Session.php" />
+   <install as="Horde/Core/Prefs/Storage/Session.php" name="lib/Horde/Core/Prefs/Storage/Session.php" />
    <install as="Horde/Core/Prefs/Ui.php" name="lib/Horde/Core/Prefs/Ui.php" />
    <install as="Horde/Core/Prefs/Ui/Widgets.php" name="lib/Horde/Core/Prefs/Ui/Widgets.php" />
    <install as="Horde/Core/Text/Filter/Bbcode.php" name="lib/Horde/Core/Text/Filter/Bbcode.php" />
index 4494457..ad8379e 100644 (file)
@@ -19,43 +19,37 @@ class Horde_Prefs implements ArrayAccess
     /** Preference is administratively locked. */
     const LOCKED = 1;
 
-    /** Preference value has been changed. */
-    const DIRTY = 4;
-
     /** Preference value is the application default.
      *  DEFAULT is a reserved PHP constant. */
-    const PREFS_DEFAULT = 8;
+    const PREFS_DEFAULT = 2;
 
     /**
-     * Connection parameters.
+     * Caching object.
      *
-     * @var array
+     * @var Horde_Prefs_Storage
      */
-    protected $_params = array();
+    protected $_cache;
 
     /**
-     * String containing the name of the current scope. This is used
-     * to differentiate between sets of preferences.  By default, preferences
-     * belong to the "global" (Horde) scope.
+     * Translation provider.
      *
-     * @var string
+     * @var Horde_Translation
      */
-    protected $_scope = 'horde';
+    protected $_dict;
 
     /**
-     * Preferences list.  Stored by scope name.  Each preference has the
-     * following format:
-     * <pre>
-     * [pref_name] => array(
-     *     [d] => (string) Default value
-     *     [m] => (integer) Pref mask
-     *     [v] => (string) Current pref value
-     * )
-     * </pre>
+     * List of dirty prefs.
      *
      * @var array
      */
-    protected $_scopes = array();
+    protected $_dirty = array();
+
+    /**
+     * Hash holding preferences with hook functions defined.
+     *
+     * @var array
+     */
+    protected $_hooks = array();
 
     /**
      * General library options.
@@ -63,46 +57,50 @@ class Horde_Prefs implements ArrayAccess
      * @var array
      */
     protected $_opts = array(
-        'cache' => 'Horde_Prefs_Cache_Null',
+        'cache' => null,
         'logger' => null,
         'password' => '',
         'sizecallback' => null,
+        'storage' => null,
         'user' => ''
     );
 
     /**
-     * Caching object.
+     * String containing the name of the current scope. This is used
+     * to differentiate between sets of preferences.  By default, preferences
+     * belong to the "global" (Horde) scope.
      *
-     * @var Horde_Prefs_Cache
+     * @var string
      */
-    protected $_cache;
+    protected $_scope = 'horde';
 
     /**
-     * Hash holding preferences with hook functions defined.
+     * Preferences list.  Stored by scope name.  Each preference has the
+     * following format:
+     * <pre>
+     * [pref_name] => array(
+     *     [d] => (string) Default value
+     *     [m] => (integer) Pref mask
+     *     [v] => (string) Current pref value
+     * )
+     * </pre>
      *
      * @var array
      */
-    protected $_hooks = array();
-
-    /**
-     * Translation provider.
-     *
-     * @var Horde_Translation
-     */
-    protected $_dict;
+    protected $_scopes = array();
 
     /**
-     * List of dirty prefs.
+     * The storage driver.
      *
-     * @var array
+     * @var Horde_Prefs_Storage
      */
-    protected $_dirty = array();
+    protected $_storage;
 
     /**
-     * Attempts to return a concrete instance based on $driver.
+     * Constructor.
      *
-     * @param string $driver  Either a driver name, or the full class name to
-     *                        use (class must extend Horde_Auth_Base).
+     * @param string $driver  THe storage driver name. Either a driver name,
+     *                        or the full class name to use.
      * @param string $scope   The scope for this set of preferences.
      * @param array $opts     Additional confguration options:
      * <pre>
@@ -129,56 +127,57 @@ class Horde_Prefs implements ArrayAccess
      * @param array $params   A hash containing any additional configuration
      *                        or connection parameters a subclass might need.
      *
-     * @return Horde_Prefs  The newly created concrete instance.
-     * @throws Horde_Prefs_Exception
+     * @throws InvalidArgumentException
      */
-    static public function factory($driver, $scope, array $opts = array(),
-                                   array $params = array())
+    public function __construct($driver, $scope, array $opts,
+                                array $params = array())
     {
-        /* Base drivers (in Auth/ directory). */
-        $class = __CLASS__ . '_' . $driver;
-        if (!class_exists($class)) {
-            /* Explicit class name, */
-            $class = $driver;
-            if (!class_exists($class)) {
-                throw new Horde_Prefs_Exception(__CLASS__ . ': class definition not found - ' . $class);
-            }
+        if (!isset($opts['charset'])) {
+            throw new InvalidArgumentException(__CLASS__ . ': Missing charset parameter.');
         }
 
-        $prefs = new $class($scope, $opts, $params);
-        $prefs->retrieve($scope);
+        $this->_opts = array_merge($this->_opts, $opts);
+
+        $this->_cache = $this->_getStorage($this->_opts['cache']);
+        $this->_dict = isset($this->_opts['translation'])
+            ? $this->_opts['translation']
+            : new Horde_Translation_Gettext('Horde_Prefs', dirname(__FILE__) . '/../../locale');
+        $this->_scope = $scope;
+        $this->_storage = $this->_getStorage($driver, $params);
+
+        register_shutdown_function(array($this, 'store'));
 
-        return $prefs;
+        $this->retrieve($scope);
     }
 
     /**
-     * Constructor.
+     * Instantiate storage driver.
      *
-     * @param string $scope  The scope for this set of preferences.
-     * @param array $opts    See factory() for list of options.
-     * @param array $params  A hash containing any additional configuration
-     *                       or connection parameters a subclass might need.
+     * @param string $driver  Storage driver name.
+     * @param array $params   Storage driver parameters.
      *
-     * @throws InvalidArgumentException
+     * @return Horde_Prefs_Storage  The storage object.
+     * @throws Horde_Prefs_Exception
      */
-    protected function __construct($scope, $opts, $params)
+    protected function _getStorage($driver, $params = array())
     {
-        foreach (array('charset') as $val) {
-            if (!isset($opts[$val])) {
-                throw new InvalidArgumentException('Missing ' . $val . ' parameter.');
+        if (is_null($driver)) {
+            $class = __CLASS__ . '_Storage_Null';
+        } else {
+            /* Built-in drivers (in Storage/ directory). */
+            $class = __CLASS__ . '_Storage_' . $driver;
+            if (!class_exists($class)) {
+                /* Explicit class name, */
+                $class = $driver;
+                if (!class_exists($class)) {
+                    throw new Horde_Prefs_Exception(__CLASS__ . ': class definition not found - ' . $class);
+                }
             }
         }
 
-        $this->_opts = array_merge($this->_opts, $opts);
-        $this->_params = $params;
-        $this->_scope = $scope;
-
-        $this->_cache = new $this->_opts['cache']($this->getUser());
-        $this->_dict = isset($this->_opts['translation'])
-            ? $this->_opts['translation']
-            : new Horde_Translation_Gettext('Horde_Prefs', dirname(__FILE__) . '/../../locale');
+        $params['user'] = $this->getUser();
 
-        register_shutdown_function(array($this, 'store'));
+        return new $class($params);
     }
 
     /**
@@ -228,13 +227,23 @@ class Horde_Prefs implements ArrayAccess
      */
     public function remove($pref)
     {
-        // FIXME not updated yet - not removed from backend.
         $scope = $this->_getPrefScope($pref);
         unset(
             $this->_dirty[$scope][$pref],
             $this->_scopes[$scope][$pref]
         );
-        $this->_cache->clear($scope, $pref);
+
+        try {
+            $this->_storage->remove($scope, $pref);
+        } catch (Horde_Prefs_Exception $e) {
+            // TODO: logging
+        }
+
+        try {
+            $this->_cache->remove($scope, $pref);
+        } catch (Horde_Prefs_Exception $e) {
+            // TODO: logging
+        }
     }
 
     /**
@@ -249,7 +258,7 @@ class Horde_Prefs implements ArrayAccess
      *
      * @return boolean  True if the value was successfully set, false on a
      *                  failure.
-     * @throws Horde_Exception
+     * @throws Horde_Prefs_Exception
      */
     public function setValue($pref, $val, $convert = true)
     {
@@ -260,11 +269,13 @@ class Horde_Prefs implements ArrayAccess
             return false;
         }
 
-        $result = $this->_setValue($pref, $val, true, $convert);
+        $result = $this->_setValue($pref, $val, $convert);
 
         if ($result && $this->isDirty($pref)) {
-            $this->_cache->update($scope, array(
-                $pref => $this->_scopes[$scope][$pref]
+            $this->_cache->store(array(
+                $scope => array(
+                    $pref => $this->_scopes[$scope][$pref]
+                )
             ));
 
             /* If this preference has a change hook, call it now. */
@@ -291,8 +302,6 @@ class Horde_Prefs implements ArrayAccess
      *
      * @param string $pref      The name of the preference to modify.
      * @param string $val       The new value for this preference.
-     * @param boolean $dirty    True if we should mark the new value as
-     *                          dirty (changed).
      * @param boolean $convert  If true the preference value gets converted
      *                          from the current charset to the backend's
      *                          charset.
@@ -300,7 +309,7 @@ class Horde_Prefs implements ArrayAccess
      * @return boolean  True if the value was successfully set, false on a
      *                  failure.
      */
-    protected function _setValue($pref, $val, $dirty = true, $convert = true)
+    protected function _setValue($pref, $val, $convert = true)
     {
         if ($convert) {
             $val = $this->convertToDriver($val);
@@ -326,14 +335,16 @@ class Horde_Prefs implements ArrayAccess
 
         // Assign the new value, unset the "default" bit, and set the
         // "dirty" bit.
-        if (empty($this->_scopes[$scope][$pref]['m'])) {
-            $this->_scopes[$scope][$pref]['m'] = 0;
+        $ptr = &$this->_scopes[$scope][$pref];
+        if (empty($ptr['m'])) {
+            $ptr['m'] = 0;
         }
-        $this->_scopes[$scope][$pref]['v'] = $val;
-        $this->setDefault($pref, false);
-        if ($dirty) {
-            $this->setDirty($pref, true);
+        if (!isset($ptr['d'])) {
+            $ptr['d'] = $ptr['v'];
         }
+        $ptr['v'] = $val;
+        $this->setDefault($pref, false);
+        $this->setDirty($pref, true);
 
         if ($this->_opts['logger']) {
             $this->_opts['logger']->log(__CLASS__ . ': Storing preference value (' . $pref . ')', 'DEBUG');
@@ -405,8 +416,8 @@ class Horde_Prefs implements ArrayAccess
     /**
      * Modifies the "dirty" bit for the given preference.
      *
-     * @param string $pref      The name of the preference to modify.
-     * @param boolean $bool     The new boolean value for the "dirty" bit.
+     * @param string $pref   The name of the preference to modify.
+     * @param boolean $bool  The new boolean value for the "dirty" bit.
      */
     public function setDirty($pref, $bool)
     {
@@ -417,8 +428,6 @@ class Horde_Prefs implements ArrayAccess
         } else {
             unset($this->_dirty[$scope][$pref]);
         }
-
-        $this->_setMask($pref, $bool, self::DIRTY);
     }
 
     /**
@@ -430,7 +439,8 @@ class Horde_Prefs implements ArrayAccess
      */
     public function isDirty($pref)
     {
-        return $this->_getMask($pref, self::DIRTY);
+        $scope = $this->_getPrefScope($pref);
+        return isset($this->_dirty[$scope][$pref]);
     }
 
     /**
@@ -455,14 +465,13 @@ class Horde_Prefs implements ArrayAccess
     {
         $scope = $this->_getPrefScope($pref);
 
-        return empty($this->_scopes[$scope][$pref]['d'])
-            ? ''
-            : $this->_scopes[$scope][$pref]['d'];
+        return isset($this->_scopes[$scope][$pref]['d'])
+            ? $this->_scopes[$scope][$pref]['d']
+            : '';
     }
 
     /**
-     * Determines if the current preference value is the default
-     * value from prefs.php or a user defined value
+     * Determines if the current preference value is the default value.
      *
      * @param string $pref  The name of the preference to check.
      *
@@ -527,7 +536,7 @@ class Horde_Prefs implements ArrayAccess
     }
 
     /**
-     * Retrieves preferences for the current scope + the 'horde' scope.
+     * Retrieves preferences for the current scope.
      *
      * @param string $scope  Optional scope specifier - if not present the
      *                       current scope will be used.
@@ -559,40 +568,56 @@ class Horde_Prefs implements ArrayAccess
         // Basic initialization so _something_ is always set.
         $this->_scopes[$scope] = array();
 
-        // Always set defaults to pick up new default values, etc.
         $this->_setDefaults($scope);
 
         // Now check the prefs cache for existing values.
-        if (($cached = $this->_cache->get($scope)) !== false) {
-            $this->_scopes[$scope] = $cached;
-            return;
+        try {
+            if (($cached = $this->_cache->get($scope)) !== false) {
+                $this->_scopes[$scope] = $cached;
+                return;
+            }
+        } catch (Horde_Prefs_Exception $e) {}
+
+        if (($prefs = $this->_storage->get($scope)) !== false) {
+            foreach ($prefs as $name => $val) {
+                if ($this->isDefault($name)) {
+                    $this->_scopes[$scope][$name]['d'] = $this->_scopes[$scope][$name]['v'];
+
+                } elseif (!isset($this->_scopes[$scope][$name])) {
+                    $this->_scopes[$scope][$name] = array(
+                        'm' => 0
+                    );
+                }
+                $this->_scopes[$scope][$name]['v'] = $val;
+                $this->setDefault($name, false);
+            }
         }
 
-        $this->_retrieve($scope);
         $this->_callHooks($scope);
 
         /* Update the cache. */
-        $this->_cache->update($scope, $this->_scopes[$scope]);
+        $this->_cache->store(array($scope => $this->_scopes[$scope]));
     }
 
     /**
      * This function will be run at the end of every request as a shutdown
      * function (registered by the constructor).  All prefs with the
-     * dirty bit set will be saved to the storage backend at this time; thus,
-     * there is no need to manually call $prefs->store() every time a
-     * preference is changed.
+     * dirty bit set will be saved to the storage backend.
      */
     public function store()
     {
-    }
+        if (!empty($this->_dirty)) {
+            try {
+                $this->_storage->store($this->_dirty);
 
-    /**
-     * TODO
-     *
-     * @throws Horde_Exception
-     */
-    protected function _retrieve()
-    {
+                /* Clear the dirty flag. */
+                foreach ($this->_dirty as $k => $v) {
+                    foreach (array_keys($v) as $name) {
+                        $this->setDirty($name, false);
+                    }
+                }
+            } catch (Horde_Prefs_Exception $e) {}
+        }
     }
 
     /**
@@ -609,22 +634,21 @@ class Horde_Prefs implements ArrayAccess
             $this->_dirty = $this->_scopes = array();
 
             /* Destroy the contents of the preferences cache. */
-            $this->_cache->clear();
+            try {
+                $this->_cache->remove();
+            } catch (Horde_Prefs_Exception $e) {}
         } else {
+            $scope = $this->getScope();
+
+            $this->_dirty[$scope] = $this->_scopes[$scope] = array();
             /* Remove this scope from the preferences cache. */
-            $this->_cache->clear($this->getScope());
+            try {
+                $this->_cache->remove($scope);
+            } catch (Horde_Prefs_Exception $e) {}
         }
     }
 
     /**
-     * Clears all preferences from the backend.
-     */
-    public function clear()
-    {
-        $this->cleanup(true);
-    }
-
-    /**
      * Converts a value from the driver's charset to the specified charset.
      *
      * @param mixed $value  A value to convert.
@@ -678,16 +702,12 @@ class Horde_Prefs implements ArrayAccess
 
             $name = str_replace('.', '_', $name);
 
-            $mask = 0;
-            $mask &= ~self::DIRTY;
-            $mask |= self::PREFS_DEFAULT;
-
+            $mask = self::PREFS_DEFAULT;
             if (!empty($pref['locked'])) {
                 $mask |= self::LOCKED;
             }
 
             $this->_scopes[$scope][$name] = array(
-                'd' => $pref['value'],
                 'm' => $mask,
                 'v' => $pref['value']
             );
@@ -703,8 +723,6 @@ class Horde_Prefs implements ArrayAccess
      * preferences that have hooks to the result of the hook.
      *
      * @param string $scope  The preferences scope to call hooks for.
-     *
-     * @throws Horde_Exception
      */
     protected function _callHooks($scope)
     {
diff --git a/framework/Prefs/lib/Horde/Prefs/Cache.php b/framework/Prefs/lib/Horde/Prefs/Cache.php
deleted file mode 100644 (file)
index 469eea2..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-/**
- * Cache driver for the preferences system.
- *
- * Copyright 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   Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
- * @package  Prefs
- */
-abstract class Horde_Prefs_Cache
-{
-    /**
-     * The username.
-     *
-     * @var string
-     */
-    protected $_user;
-
-    /**
-     * Constructor.
-     *
-     * @param string $user  The current username.
-     */
-    public function __construct($user)
-    {
-        $this->_user = $user;
-    }
-
-    /**
-     * Gets a cache entry.
-     *
-     * @param string $scope  The scope of the prefs to get.
-     *
-     * @return mixed  Prefs array on success, false on failure.
-     */
-    abstract public function get($scope);
-
-    /**
-     * Updates a cache entry.
-     *
-     * @param string $scope  The scope of the prefs being updated.
-     * @param array $prefs   The preferences to update.
-     */
-    abstract public function update($scope, $prefs);
-
-    /**
-     * Clear cache entries.
-     *
-     * @param string $scope  The scope of the prefs to clear. If null, clears
-     *                       entire cache.
-     * @param string $scope  The pref to clear. If null, clears the entire
-     *                       scope.
-     */
-    abstract public function clear($scope = null, $pref = null);
-
-}
diff --git a/framework/Prefs/lib/Horde/Prefs/Cache/Null.php b/framework/Prefs/lib/Horde/Prefs/Cache/Null.php
deleted file mode 100644 (file)
index 9737ba2..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-/**
- * Null cache driver for the preferences system.
- *
- * Copyright 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   Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
- * @package  Prefs
- */
-class Horde_Prefs_Cache_Null extends Horde_Prefs_Cache
-{
-    /**
-     */
-    public function get($scope)
-    {
-        return false;
-    }
-
-    /**
-     */
-    public function update($scope, $prefs)
-    {
-    }
-
-    /**
-     */
-    public function clear($scope = null, $pref = null)
-    {
-    }
-
-}
diff --git a/framework/Prefs/lib/Horde/Prefs/Cache/Session.php b/framework/Prefs/lib/Horde/Prefs/Cache/Session.php
deleted file mode 100644 (file)
index 1315c90..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-/**
- * Session storage cache driver for the preferences system.
- *
- * Copyright 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   Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
- * @package  Prefs
- */
-class Horde_Prefs_Cache_Session extends Horde_Prefs_Cache
-{
-    /**
-     * Session key.
-     *
-     * @var string
-     */
-    protected $_key;
-
-    /**
-     */
-    public function __construct($user)
-    {
-        parent::__construct($user);
-
-        $this->_key = 'horde_prefs_' . $this->_user;
-    }
-
-    /**
-     */
-    public function get($scope)
-    {
-        return isset($_SESSION[$this->_key][$scope])
-            ? $_SESSION[$this->_key][$scope]
-            : false;
-    }
-
-    /**
-     */
-    public function update($scope, $prefs)
-    {
-        $_SESSION[$this->_key][$scope] = isset($_SESSION[$this->_key][$scope])
-            ? array_merge($_SESSION[$this->_key][$scope], $prefs)
-            : array();
-    }
-
-    /**
-     */
-    public function clear($scope = null, $pref = null)
-    {
-        if (is_null($scope)) {
-            unset($_SESSION[$this->_key]);
-        } elseif (is_null($pref)) {
-            unset($_SESSION[$this->_key][$scope]);
-        } else {
-            unset($_SESSION[$this->_key][$scope][$pref]);
-        }
-    }
-
-}
diff --git a/framework/Prefs/lib/Horde/Prefs/File.php b/framework/Prefs/lib/Horde/Prefs/File.php
deleted file mode 100644 (file)
index 5a58004..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-<?php
-/**
- * Preferences storage implementation using files in a directory
- *
- * 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   Thomas Jarosch <thomas.jarosch@intra2net.com>
- * @category Horde
- * @package  Prefs
- */
-class Horde_Prefs_File extends Horde_Prefs_Base
-{
-    /**
-     * Current version number of the data format
-     *
-     * @var int
-     */
-    protected $_version = 2;
-
-    /**
-     * Directory to store the preferences
-     *
-     * @var string
-     */
-    protected $_dirname;
-
-    /**
-     * Full path to the current preference file
-     *
-     * @var string
-     */
-    protected $_fullpath;
-
-    /**
-     * Cached unserialized data of all scopes
-     *
-     * @var array
-     */
-    protected $_fileCache = null;
-
-    /**
-     * Constructor.
-     *
-     * @param string $scope  The scope for this set of preferences.
-     * @param array $opts    See factory() for list of options.
-     * @param array $params  Additional parameters:
-     * <pre>
-     * 'directory' - (string) [REQUIRED] Preference storage directory.
-     * </pre>
-     *
-     * @throws InvalidArgumentException
-     */
-    public function __construct($scope, $opts, $params);
-    {
-        parent::__construct($scope, $opts, $params);
-
-        // Sanity check for directory
-        if (empty($params['directory']) || !is_dir($params['directory'])) {
-            throw new InvalidArgumentException('Preference storage directory is not available.');
-        }
-        if (!is_writable($params['directory'])) {
-            throw new InvalidArgumentException(sprintf('Directory %s is not writeable.', $params['directory']));
-        }
-
-        $this->_dirname = $params['directory'];
-        $this->_fullpath = $this->_dirname . '/' . basename($user) . '.prefs';
-    }
-
-    /**
-     * Retrieves the requested set of preferences from the current session.
-     *
-     * @param string $scope  Scope specifier.
-     *
-     * @throws Horde_Prefs_Exception
-     */
-    protected function _retrieve($scope)
-    {
-        if (is_null($this->_dirname)) {
-            return;
-        }
-
-        if (is_null($this->_fileCache)) {
-            // Try to read
-            $this->_fileCache = $this->_readCache();
-            if (is_null($this->_fileCache)) {
-                return;
-            }
-
-            // Check version number. We can call format transformations hooks
-            // in the future.
-            if (!is_array($this->_fileCache) ||
-                !array_key_exists('__file_version', $this->_fileCache) ||
-                !($this->_fileCache['__file_version'] == $this->_version)) {
-                if ($this->_fileCache['__file_version'] == 1) {
-                    $this->transformV1V2();
-                } else {
-                    throw new Horde_Prefs_Exception(sprintf('Wrong version number found: %s (should be %d)', $this->_fileCache['__file_version'], $this->_version));
-                }
-            }
-        }
-
-        // Check if the scope exists
-        if (empty($scope) || !array_key_exists($scope, $this->_fileCache)) {
-            return;
-        }
-
-        // Merge config values
-        foreach ($this->_fileCache[$scope] as $name => $val) {
-            if (isset($this->_scopes[$scope][$name])) {
-                $this->_scopes[$scope][$name]['v'] = $val;
-                $this->_scopes[$scope][$name]['m'] &= ~self::PREFS_DEFAULT;
-            } else {
-                // This is a shared preference.
-                $this->_scopes[$scope][$name] = array('v' => $val,
-                                                      'm' => 0,
-                                                      'd' => null);
-            }
-        }
-    }
-
-    /**
-     * Read data from disk.
-     *
-     * @return mixed  Data array on success or null on error.
-     */
-    protected function _readCache()
-    {
-        return file_exists($this->_fullpath)
-            ? unserialize(file_get_contents($this->_fullpath))
-            : null;
-    }
-
-    /**
-     * Transforms the broken version 1 format into version 2.
-     */
-    public function transformV1V2()
-    {
-        $version2 = array('__file_version' => 2);
-        foreach ($this->_fileCache as $scope => $prefs) {
-            if ($scope != '__file_version') {
-                foreach ($prefs as $name => $pref) {
-                    /* Default values should not have been stored by the
-                     * driver. They are being set via the prefs.php files. */
-                    if (!($pref['m'] & self::PREFS_DEFAULT)) {
-                        $version2[$scope][$name] = $pref['v'];
-                    }
-                }
-            }
-        }
-        $this->_fileCache = $version2;
-    }
-
-    /**
-     * Write data to disk
-     *
-     * @return boolean  True on success.
-     */
-    protected function _writeCache()
-    {
-        $tmp_file = Horde_Util::getTempFile('PrefsFile', true, $this->_dirname);
-
-        $data = serialize($this->_fileCache);
-
-        if (file_put_contents($tmp_file, $data) === false) {
-            return false;
-        }
-
-        return @rename($tmp_file, $this->_fullpath);
-    }
-
-    /**
-     * Stores preferences in the current session.
-     *
-     * @throws Horde_Prefs_Exception
-     */
-    public function store()
-    {
-        if (is_null($this->_dirname) || empty($this->_dirty)) {
-            return;
-        }
-
-        // Read in all existing preferences, if any.
-        $this->_retrieve('');
-        if (!is_array($this->_fileCache)) {
-            $this->_fileCache = array('__file_version' => $this->_version);
-        }
-
-        // Update all values from dirty scope
-        foreach ($this->_dirty as $scope => $prefs) {
-            foreach ($prefs as $name => $pref) {
-                // Don't store locked preferences.
-                if (!($this->_scopes[$scope][$name]['m'] & self::LOCKED)) {
-                    $this->_fileCache[$scope][$name] = $pref['v'];
-
-                    // Clean the pref since it was just saved.
-                    $this->_scopes[$scope][$name]['m'] &= ~self::DIRTY;
-                }
-            }
-        }
-
-        if ($this->_writeCache() == false) {
-            throw new Horde_Prefs_Exception(sprintf('Write of preferences to %s failed', $this->_filename));
-        }
-    }
-
-}
diff --git a/framework/Prefs/lib/Horde/Prefs/Imsp.php b/framework/Prefs/lib/Horde/Prefs/Imsp.php
deleted file mode 100644 (file)
index e3d5efc..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-<?php
-/**
- * Preference storage implementation for an IMSP server.
- *
- * 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   Michael Rubinsky <mrubinsk@horde.org>
- * @category Horde
- * @package  Prefs
- */
-class Horde_Prefs_Imsp extends Horde_Prefs_Base
-{
-    /**
-     * Handle for the IMSP server connection.
-     *
-     * @var Net_IMSP
-     */
-    protected $_imsp;
-
-    /**
-     * Boolean indicating whether or not we're connected to the IMSP server.
-     *
-     * @var boolean
-     */
-    protected $_connected = false;
-
-    /**
-     * Retrieves the requested set of preferences from the IMSP server.
-     *
-     * @param string $scope  Scope specifier.
-     *
-     * @throws Horde_Prefs_Exception
-     */
-    protected function _retrieve($scope)
-    {
-        $this->_connect();
-
-        $prefs = $this->_imsp->get($scope . '.*');
-        if ($prefs instanceof PEAR_Error) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log($prefs, 'ERR');
-            }
-            return;
-        }
-
-        foreach ($prefs as $name => $val) {
-            $name = str_replace($scope . '.', '', $name);
-            if ($val != '-') {
-                if (isset($this->_scopes[$scope][$name])) {
-                    $this->_scopes[$scope][$name]['v'] = $val;
-                    $this->_scopes[$scope][$name]['m'] &= ~self::PREFS_DEFAULT;
-                } else {
-                    // This is a shared preference.
-                    $this->_scopes[$scope][$name] = array('v' => $val,
-                                                          'm' => 0,
-                                                          'd' => null);
-                }
-            }
-        }
-    }
-
-    /**
-     * Stores all dirty prefs to IMSP server.
-     */
-    public function store()
-    {
-        // Get the list of preferences that have changed. If there are
-        // none, no need to hit the backend.
-        if (empty($this->_dirty)) {
-            return;
-        }
-
-        $this->_connect();
-
-        foreach ($this->_dirty as $scope => $prefs) {
-            $updated = array();
-
-            foreach ($prefs as $name => $pref) {
-                // Don't store locked preferences.
-                if ($this->_scopes[$scope][$name]['m'] & self::LOCKED) {
-                    continue;
-                }
-
-                $value = $pref['v'];
-                if (empty($value)) {
-                    $value = '-';
-                }
-
-                $result = $this->_imsp->set($scope . '.' . $name, $value);
-                if ($result instanceof PEAR_Error) {
-                    if ($this->_opts['logger']) {
-                        $this->_opts['logger']->log($result, 'ERR');
-                    }
-                    return;
-                }
-
-                // Clean the pref since it was just saved.
-                $this->_scopes[$scope][$name]['m'] &= ~self::DIRTY;
-
-                $updated[$name] = $this->_scopes[$scope][$name];
-            }
-
-            // Update the cache for this scope.
-            $this->_cache->update($scope, $updated);
-        }
-    }
-
-    /**
-     * Attempts to set up a connection to the IMSP server.
-     *
-     * @throws Horde_Prefs_Exception
-     */
-    protected function _connect()
-    {
-        if ($this->_connected) {
-            return;
-        }
-
-        $this->_params['username'] = preg_match('/(^.*)@/', $this->getUser(), $matches)
-            ? $matches[1]
-            : $this->getUser();
-        $this->_params['password'] = $this->_opts['password'];
-
-        if (isset($this->_params['socket'])) {
-            $this->_params['socket'] = $params['socket'] . 'imsp_' . $this->_params['username'] . '.sck';
-        }
-
-        $this->_imsp = Net_IMSP::factory('Options', $this->_params);
-        $result = $this->_imsp->init();
-        if ($result instanceof PEAR_Error) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log($result, 'ERR');
-            }
-            throw new Horde_Prefs_Exception($result);
-        }
-
-        // TODO
-        //$this->_imsp->setLogger($GLOBALS['conf']['log']);
-        $this->_connected = true;
-    }
-
-}
diff --git a/framework/Prefs/lib/Horde/Prefs/Kolab.php b/framework/Prefs/lib/Horde/Prefs/Kolab.php
deleted file mode 100644 (file)
index 2d4cae3..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-/**
- * Kolab implementation of the Horde preference system. Derives from the
- * Prefs_ldap LDAP authentication object, and simply provides parameters to it
- * based on the global Kolab configuration.
- *
- * Copyright 2004-2007 Stuart Binge <s.binge@codefusion.co.za>
- *
- * 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 <s.binge@codefusion.co.za>
- * @category Horde
- * @package  Prefs
- */
-class Horde_Prefs_Kolab extends Horde_Prefs_Ldap
-{
-   /**
-     * Constructor.
-     *
-     * @param string $scope  The scope for this set of preferences.
-     * @param array $opts    See factory() for list of options.
-     * @param array $params  A hash containing any additional configuration
-     *                       or connection parameters a subclass might need.
-     */
-    protected function __construct($scope, $opts, $params)
-    {
-        require_once 'Horde/Kolab.php';
-        $params = array(
-            'hostspec' => Kolab::getServer('ldap'),
-            'port' => $GLOBALS['conf']['kolab']['ldap']['port'],
-            'version' => '3',
-            'basedn' => $GLOBALS['conf']['kolab']['ldap']['basedn'],
-            'writedn' => 'user',
-            'searchdn' => $GLOBALS['conf']['kolab']['ldap']['phpdn'],
-            'searchpw' => $GLOBALS['conf']['kolab']['ldap']['phppw'],
-            'uid' => 'mail'
-        );
-
-        parent::__construct($scope, $opts, $params);
-    }
-
-}
diff --git a/framework/Prefs/lib/Horde/Prefs/KolabImap.php b/framework/Prefs/lib/Horde/Prefs/KolabImap.php
deleted file mode 100644 (file)
index 3e0a5eb..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-<?php
-/**
- * Preferences storage implementation for a Kolab IMAP server.
- *
- * Copyright 2007-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   Gunnar Wrobel <p@rdus.de>
- * @category Horde
- * @package  Prefs
- */
-class Horde_Prefs_KolabImap extends Horde_Prefs_Base
-{
-    /**
-     * ID of the config default share
-     *
-     * @var string
-     */
-    protected $_share;
-
-    /**
-     * Handle for the current Kolab connection.
-     *
-     * @var Kolab
-     */
-    protected $_connection;
-
-    /**
-     * Opens a connection to the Kolab server.
-     *
-     * @throws Horde_Prefs_Exception
-     */
-    protected function _connect()
-    {
-        if (isset($this->_connection)) {
-            return;
-        }
-
-        $shares = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Share')->create('h-prefs');
-        $default = $shares->getDefaultShare();
-        if ($default instanceof PEAR_Error) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log($default, 'ERR');
-            }
-            throw new Horde_Prefs_Exception($default);
-        }
-        $this->_share = $default->getName();
-
-        require_once 'Horde/Kolab.php';
-        $connection = new Kolab('h-prefs');
-        if ($connection instanceof PEAR_Error) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log($connection, 'ERR');
-            }
-            throw new Horde_Prefs_Exception($connection);
-        }
-
-        $result = $this->_connection->open($this->_share, 1);
-        if ($result instanceof PEAR_Error) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log($result, 'ERR');
-            }
-            throw new Horde_Prefs_Exception($result);
-        }
-
-        $this->_connection = $connection;
-    }
-
-    /**
-     * Retrieves the requested set of preferences from the user's config folder.
-     *
-     * @param string $scope  Scope specifier.
-     *
-     * @throws Horde_Prefs_Exception
-     */
-    protected function _retrieve($scope)
-    {
-        $this->_connect();
-
-        try {
-            $pref = $this->_getPref($scope);
-        } catch (Horde_Prefs_Exception $e) {
-            return;
-        }
-
-        if (is_null($pref)) {
-            /* No preferences saved yet */
-            return;
-        }
-
-        foreach ($pref['pref'] as $prefstr) {
-            // If the string doesn't contain a colon delimiter, skip it.
-            if (strpos($prefstr, ':') === false) {
-                continue;
-            }
-
-            // Split the string into its name:value components.
-            list($name, $val) = explode(':', $prefstr, 2);
-            if (isset($this->_scopes[$scope][$name])) {
-                $this->_scopes[$scope][$name]['v'] = base64_decode($val);
-                $this->_scopes[$scope][$name]['m'] &= ~self::PREFS_DEFAULT;
-            } else {
-                // This is a shared preference.
-                $this->_scopes[$scope][$name] = array('v' => base64_decode($val),
-                                                      'm' => 0,
-                                                      'd' => null);
-            }
-        }
-    }
-
-    /**
-     * Retrieves the requested preference from the user's config folder.
-     *
-     * @param string $scope  Scope specifier.
-     *
-     * @return array  The preference value.
-     * @throws Horde_Prefs_Exception
-     */
-    protected function _getPref($scope)
-    {
-        $this->_connect();
-
-        $prefs = $this->_connection->getObjects();
-        if ($prefs instanceof PEAR_Error) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log($prefs, 'ERR');
-            }
-            throw new Horde_Prefs_Exception($prefs);
-        }
-
-        foreach ($prefs as $pref) {
-            if ($pref['application'] == $scope) {
-                return $pref;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Stores preferences to the Kolab server.
-     *
-     * @throws Horde_Prefs_Exception
-     */
-    public function store()
-    {
-        // Get the list of preferences that have changed. If there are
-        // none, no need to hit the backend.
-        if (empty($this->_dirty)) {
-            return;
-        }
-
-        $this->_connect();
-
-        // Build a hash of the preferences and their values that need
-        // to be stored on the IMAP server. Because we have to update
-        // all of the values of a multi-value entry wholesale, we
-        // can't just pick out the dirty preferences; we must update
-        // every scope that has dirty preferences.
-        foreach (array_keys($this->_dirty) as $scope) {
-            $new_values = array();
-            foreach ($this->_scopes[$scope] as $name => $pref) {
-                // Don't store locked preferences.
-                if (!($pref['m'] & self::LOCKED)) {
-                    $new_values[] = $name . ':' . base64_encode($pref['v']);
-                }
-            }
-
-            try {
-                $pref = $this->_getPref($scope);
-            } catch (Horde_Prefs_Exception $e) {
-                return;
-            }
-
-            if (is_null($pref)) {
-                $old_uid = null;
-                $prefs_uid = $this->_connection->_storage->generateUID();
-            } else {
-                $old_uid = $pref['uid'];
-                $prefs_uid = $pref['uid'];
-            }
-
-            $object = array(
-                'uid' => $prefs_uid,
-                'application' => $scope,
-                'pref' => $new_values
-            );
-
-            $result = $this->_connection->_storage->save($object, $old_uid);
-            if ($result instanceof PEAR_Error) {
-                if ($this->_opts['logger']) {
-                    $this->_opts['logger']->log($result, 'ERR');
-                }
-                return;
-            }
-        }
-
-        // Clean the preferences since they were just saved.
-        foreach ($this->_dirty as $scope => $prefs) {
-            foreach ($prefs as $name => $pref) {
-                $this->_scopes[$scope][$name]['m'] &= ~self::DIRTY;
-            }
-
-            // Update the cache for this scope.
-            $this->_cache->update($scope, $prefs);
-        }
-    }
-
-    /**
-     * Clears all preferences from the kolab_imap backend.
-     */
-    public function clear()
-    {
-        return $this->_connection->deleteAll();
-    }
-
-}
diff --git a/framework/Prefs/lib/Horde/Prefs/Ldap.php b/framework/Prefs/lib/Horde/Prefs/Ldap.php
deleted file mode 100644 (file)
index b2cf3d3..0000000
+++ /dev/null
@@ -1,451 +0,0 @@
-<?php
-/**
- * Preferences storage implementation for PHP's LDAP extension.
- *
- * Copyright 1999-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   Jon Parise <jon@horde.org>
- * @author   Ben Klang <ben@alkaloid.net>
- * @category Horde
- * @package  Prefs
- */
-class Horde_Prefs_Ldap extends Horde_Prefs
-{
-    /**
-     * Handle for the current LDAP connection.
-     *
-     * @var resource
-     */
-    protected $_connection;
-
-    /**
-     * Boolean indicating whether or not we're connected to the LDAP server.
-     *
-     * @var boolean
-     */
-    protected $_connected = false;
-
-    /**
-     * String holding the user's DN.
-     *
-     * @var string
-     */
-    protected $_dn = '';
-
-    /**
-     * Constructor.
-     *
-     * @param string $scope  The scope for this set of preferences.
-     * @param array $opts    See factory() for list of options.
-     * @param array $params  Additional configuration options:
-     * <pre>
-     * basedn - (string) [REQUIRED] The base DN for the LDAP server.
-     * hostspec - (string) [REQUIRED] The hostname of the LDAP server.
-     * uid - (string) [REQUIRED] The username search key.
-     * writeas - (string) [REQUIRED] One of "user", "admin", or "search"
-     *
-     * Optional parameters:
-     * binddn - (string) The DN of the administrative account to bind for
-     *          write operations.
-     * bindpw - (string) binddn's password for bind authentication.
-     * port - (integer) The port of the LDAP server.
-     *        DEFAULT: 389
-     * searchdn - (string) The DN of a user with search permissions on the
-     *            directory.
-     * searchpw - (string) searchdn's password for binding.
-     * tls - (boolean) Whether to use TLS connections.
-     *       DEFAULT: false
-     * version - (integer) The version of the LDAP protocol to use.
-     *           DEFAULT: NONE (system default will be used)
-     * </pre>
-     */
-    protected function __construct($scope, $opts, $params)
-    {
-        /* If a valid server port has not been specified, set the default. */
-        if (!isset($params['port']) || !is_int($params['port'])) {
-            $params['port'] = 389;
-        }
-
-        parent::__construct($scope, $opts, $params);
-    }
-
-    /**
-     * Opens a connection to the LDAP server.
-     *
-     * @throws Horde_Prefs_Exception
-     */
-    function _connect()
-    {
-        if ($this->_connected) {
-            return;
-        }
-
-        if (!Horde_Util::extensionExists('ldap')) {
-            throw new Horde_Prefs_Exception('Required LDAP extension not found.');
-        }
-
-        Horde::assertDriverConfig($this->_params, 'prefs',
-            array('hostspec', 'basedn', 'uid', 'writeas'),
-            'preferences LDAP');
-
-        /* Connect to the LDAP server anonymously. */
-        $conn = ldap_connect($this->_params['hostspec'], $this->_params['port']);
-        if (!$conn) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log(sprintf('Failed to open an LDAP connection to %s.', $this->_params['hostspec']), 'ERR');
-            }
-            throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.');
-        }
-
-        /* Set the LDAP protocol version. */
-        if (isset($this->_params['version'])) {
-            $result = @ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION,
-                                       $this->_params['version']);
-            if ($result === false) {
-                if ($this->_opts['logger']) {
-                    $this->_opts['logger']->log(sprintf('Set LDAP protocol version to %d failed: [%d] %s', $this->_params['version'], @ldap_errno($conn), @ldap_error($conn)), 'WARN');
-                }
-                throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.');
-            }
-        }
-
-        /* Start TLS if we're using it. */
-        if (!empty($this->_params['tls']) &&
-            !@ldap_start_tls($conn) &&
-            $this->_opts['logger']) {
-            $this->_opts['logger']->log(sprintf('STARTTLS failed: [%d] %s', @ldap_errno($this->_ds), @ldap_error($this->_ds)), 'ERR');
-        }
-
-        /* If necessary, bind to the LDAP server as the user with search
-         * permissions. */
-        if (!empty($this->_params['searchdn'])) {
-            $bind = @ldap_bind($conn, $this->_params['searchdn'],
-                               $this->_params['searchpw']);
-            if ($bind === false) {
-                if ($this->_opts['logger']) {
-                    $this->_opts['logger']->log(sprintf('Bind to server %s:%d with DN %s failed: [%d] %s', $this->_params['hostspec'], $this->_params['port'], $this->_params['searchdn'], @ldap_errno($conn), @ldap_error($conn)), 'ERR');
-                }
-                throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.', @ldap_errno($conn));
-            }
-        }
-
-        /* Register our callback function to handle referrals. */
-        if (function_exists('ldap_set_rebind_proc')) {
-            $result = @ldap_set_rebind_proc($conn, array($this, 'rebindProc'));
-            if ($result === false) {
-                if ($this->_opts['logger']) {
-                    $this->_opts['logger']->log(sprintf('Setting referral callback failed: [%d] %s', @ldap_errno($conn), @ldap_error($conn)), 'WARN');
-                }
-                throw new Horde_Prefs_Exception($this->_dict->t("Internal LDAP error.  Details have been logged for the administrator."), @ldap_errno($conn));
-            }
-        }
-
-        /* Store the connection handle at the instance level. */
-        $this->_connection = $conn;
-
-        /* Search for the user's full DN. */
-        $search = @ldap_search($this->_connection, $this->_params['basedn'],
-                               $this->_params['uid'] . '=' . $this->getUser(), array('dn'));
-        if ($search === false) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log(sprintf('Error while searching the directory for the user\'s DN: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
-            }
-            throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.', @ldap_errno($conn));
-        }
-
-        $result = @ldap_get_entries($this->_connection, $search);
-        if ($result === false) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log(sprintf('Error while retrieving LDAP search results for the user\'s DN: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
-            }
-            throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.', @ldap_errno($this->_connection));
-        }
-
-        if ($result['count'] != 1) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log('Zero or more than one DN returned from search; unable to determine user\'s correct DN.', 'ERR');
-            }
-            throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.');
-        }
-        $this->_dn = $result[0]['dn'];
-
-        // Now we should have the user's DN.  Re-bind as appropriate with write
-        // permissions to be able to store preferences.
-        switch($this->_params['writeas']) {
-        case 'user':
-            $result = @ldap_bind($this->_connection,
-                                 $this->_dn, $this->_opts['password']);
-            break;
-
-        case 'admin':
-            $result = @ldap_bind($this->_connection,
-                                 $this->_params['binddn'],
-                                 $this->_params['bindpw']);
-            break;
-
-        case 'search':
-            // Since we've already bound as the search DN above, no rebinding
-            // is necessary.
-            $result = true;
-            break;
-        }
-
-        if ($result === false) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log(sprintf('Error rebinding for prefs writing: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
-            }
-            throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.', @ldap_errno($this->_connection));
-        }
-
-        // We now have a ready-to-use connection.
-        $this->_connected = true;
-    }
-
-    /**
-     * Callback function for LDAP referrals.  This function is called when an
-     * LDAP operation returns a referral to an alternate server.
-     *
-     * @return integer  1 on error, 0 on success.
-     */
-    public function rebindProc($conn, $who)
-    {
-        /* Strip out the hostname we're being redirected to. */
-        $who = preg_replace(array('|^.*://|', '|:\d*$|'), '', $who);
-
-        /* Make sure the server we're being redirected to is in our list of
-           valid servers. */
-        if (strpos($this->_params['hostspec'], $who) === false) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log(sprintf('Referral target %s for DN %s is not in the authorized server list.', $who, $bind_dn), 'ERR');
-            }
-            return 1;
-        }
-
-        /* Figure out the DN of the authenticating user. */
-        switch($this->_params['writeas']) {
-        case 'user':
-            $bind_dn = $this->_dn;
-            $bind_pw = $this->_opts['password'];
-            break;
-
-        case 'admin':
-            $bind_dn = $this->_params['binddn'];
-            $bind_pw = $this->_params['bindpw'];
-            break;
-
-        case 'search':
-            $bind_dn = $this->_params['searchdn'];
-            $bind_dn = $this->_params['searchpw'];
-            break;
-        }
-
-        /* Bind to the new server. */
-        $bind = @ldap_bind($conn, $bind_dn, $bind_pw);
-        if (($bind === false) && $this->_opts['logger']) {
-            $this->_opts['logger']->log(sprintf('Rebind to server %s:%d with DN %s failed: [%d] %s', $this->_params['hostspec'], $this->_params['port'], $bind_dn, @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
-        }
-
-        return 0;
-    }
-
-    /**
-     * Retrieves the requested set of preferences from the user's LDAP entry.
-     *
-     * @param string $scope  Scope specifier.
-     */
-    function _retrieve($scope)
-    {
-        $this->_connect();
-
-        // Search for the multi-valued field containing the array of
-        // preferences.
-        $search = @ldap_search($this->_connection, $this->_params['basedn'],
-                              $this->_params['uid'] . '=' . $this->getUser(),
-                              array($scope . 'Prefs'));
-        if ($search === false) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log(sprintf('Error while searching for the user\'s prefs: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
-            }
-            return;
-        }
-
-        $result = @ldap_get_entries($this->_connection, $search);
-        if ($result === false) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log(sprintf('Error while retrieving LDAP search results for the user\'s prefs: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
-            }
-            return;
-        }
-
-        // Preferences are stored as colon-separated name:value pairs.
-        // Each pair is stored as its own attribute off of the multi-
-        // value attribute named in: $scope . 'Prefs'
-
-        // ldap_get_entries() converts attribute indexes to lowercase.
-        $field = Horde_String::lower($scope . 'prefs');
-        $prefs = isset($result[0][$field])
-            ? $result[0][$field]
-            : array();
-
-        foreach ($prefs as $prefstr) {
-            // If the string doesn't contain a colon delimiter, skip it.
-            if (strpos($prefstr, ':') === false) {
-                continue;
-            }
-
-            // Split the string into its name:value components.
-            list($name, $val) = explode(':', $prefstr, 2);
-            if (isset($this->_scopes[$scope][$name])) {
-                $this->_scopes[$scope][$name]['v'] = base64_decode($val);
-                $this->_scopes[$scope][$name]['m'] &= ~self::PREFS_DEFAULT;
-            } else {
-                // This is a shared preference.
-                $this->_scopes[$scope][$name] = array('v' => base64_decode($val),
-                                                      'm' => 0,
-                                                      'd' => null);
-            }
-        }
-    }
-
-    /**
-     * Stores preferences to the LDAP server.
-     *
-     * @throws Horde_Prefs_Exception
-     */
-    public function store()
-    {
-        // Get the list of preferences that have changed. If there are
-        // none, no need to hit the backend.
-        if (empty($this->_dirty)) {
-            return;
-        }
-
-        $this->_connect();
-
-        // Build a hash of the preferences and their values that need
-        // to be stored on the LDAP server. Because we have to update
-        // all of the values of a multi-value entry wholesale, we
-        // can't just pick out the dirty preferences; we must update
-        // every scope that has dirty preferences.
-        $new_values = array();
-        foreach (array_keys($this->_dirty) as $scope) {
-            foreach ($this->_scopes[$scope] as $name => $pref) {
-                // Don't store locked preferences.
-                if (!($pref['m'] & self::LOCKED)) {
-                    $new_values[$scope . 'Prefs'][] =
-                        $name . ':' . base64_encode($pref['v']);
-                }
-            }
-        }
-
-        // Entries must have the objectclasses 'top' and 'hordeperson'
-        // to successfully store LDAP prefs. Check for both of them,
-        // and add them if necessary.
-        $search = @ldap_search($this->_connection, $this->_params['basedn'],
-                              $this->_params['uid'] . '=' . $this->getUser(),
-                              array('objectclass'));
-        if ($search === false) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log(sprintf('Error searching the directory for required objectClasses: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
-            }
-            return;
-        }
-
-        $result = @ldap_get_entries($this->_connection, $search);
-        if ($result === false) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log(sprintf('Error retrieving results while checking for required objectClasses: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
-            }
-            return;
-        }
-
-        if ($result['count'] > 0) {
-            $top = false;
-            $hordeperson = false;
-
-            for ($i = 0; $i < $result[0]['objectclass']['count']; $i++) {
-                if ($result[0]['objectclass'][$i] == 'top') {
-                    $top = true;
-                } elseif ($result[0]['objectclass'][$i] == 'hordePerson') {
-                    $hordeperson = true;
-                }
-            }
-
-            // Add any missing objectclasses.
-            if (!$top) {
-                @ldap_mod_add($this->_connection, $this->_dn, array('objectclass' => 'top'));
-            }
-
-            if (!$hordeperson) {
-                @ldap_mod_add($this->_connection, $this->_dn, array('objectclass' => 'hordePerson'));
-            }
-        }
-
-        // Send the hash to the LDAP server.
-        $result = @ldap_mod_replace($this->_connection, $this->_dn,
-                                    $new_values);
-        if ($result === false) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log(sprintf('Unable to modify user\'s objectClass for preferences: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
-            }
-            return;
-        }
-
-        // Clean the preferences since they were just saved.
-        foreach ($this->_dirty as $scope => $prefs) {
-            foreach ($prefs as $name => $pref) {
-                $this->_scopes[$scope][$name]['m'] &= ~self::DIRTY;
-            }
-
-            // Update the cache for this scope.
-            $this->_cache->update($scope, $prefs);
-        }
-    }
-
-    /**
-     * Clears all preferences from the LDAP backend.
-     *
-     * @throws Horde_Prefs_Exception
-     */
-    public function clear()
-    {
-        $this->_connect();
-
-        $attrs = $GLOBALS['registry']->listApps(array('inactive', 'active', 'hidden', 'notoolbar', 'admin'));
-        foreach ($attrs as $key => $val) {
-            $attrs[$key] = $val . 'Prefs';
-        }
-
-        $search = @ldap_read($this->_connection, $this->_dn,
-                            'objectClass=hordePerson', $attrs, 1);
-        if ($search === false) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log(sprintf('Error while getting preferenes from LDAP: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
-            }
-            return;
-        }
-
-        $result = @ldap_get_entries($this->_connection, $search);
-        if ($result === false) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log(sprintf('Error while retrieving results from LDAP: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
-            }
-            return;
-        }
-
-        $attrs = array();
-        for ($i = 0; $i < $result[0]['count']; $i++) {
-            $attrs[$result[0][$i]] = array();
-        }
-        $result = @ldap_mod_del($this->_connection, $this->_dn, $attrs);
-        if (($result === false) && $this->_opts['logger']) {
-            $this->_opts['logger']->log(sprintf('Unable to clear user\'s preferences: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
-        }
-
-        $this->cleanup(true);
-    }
-
-}
diff --git a/framework/Prefs/lib/Horde/Prefs/Session.php b/framework/Prefs/lib/Horde/Prefs/Session.php
deleted file mode 100644 (file)
index 9bc62be..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-/**
- * Preferences storage implementation for PHP's session implementation.
- *
- * Copyright 1999-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   Jon Parise <jon@horde.org>
- * @category Horde
- * @package  Prefs
- */
-class Horde_Prefs_Session extends Horde_Prefs
-{
-    /**
-     * Retrieves the requested set of preferences from the current session.
-     *
-     * @param string $scope  Scope specifier.
-     */
-    protected function _retrieve($scope)
-    {
-        if (isset($_SESSION['horde_prefs'][$scope])) {
-            $this->_scopes[$scope] = $_SESSION['horde_prefs'][$scope];
-        }
-    }
-
-    /**
-     * Stores preferences in the current session.
-     */
-    public function store()
-    {
-        // Create and register the preferences array, if necessary.
-        if (!isset($_SESSION['horde_prefs'])) {
-            $_SESSION['horde_prefs'] = array();
-        }
-
-        // Copy the current preferences into the session variable.
-        foreach ($this->_scopes as $scope => $prefs) {
-            $pref_keys = array_keys($prefs);
-            foreach ($pref_keys as $pref_name) {
-                // Clean the pref since it was just saved.
-                $prefs[$pref_name]['m'] &= ~Horde_Prefs::DIRTY;
-            }
-
-            $_SESSION['horde_prefs'][$scope] = $prefs;
-        }
-    }
-
-    /**
-     * Perform cleanup operations.
-     *
-     * @param boolean $all  Cleanup all Horde preferences.
-     */
-    public function cleanup($all = false)
-    {
-        // Perform a Horde-wide cleanup?
-        if ($all) {
-            unset($_SESSION['horde_prefs']);
-        } else {
-            unset($_SESSION['horde_prefs'][$this->_scope]);
-            $_SESSION['horde_prefs']['_filled'] = false;
-        }
-
-        parent::cleanup($all);
-    }
-
-}
diff --git a/framework/Prefs/lib/Horde/Prefs/Sql.php b/framework/Prefs/lib/Horde/Prefs/Sql.php
deleted file mode 100644 (file)
index d853d9d..0000000
+++ /dev/null
@@ -1,218 +0,0 @@
-<?php
-/**
- * Preferences storage implementation for PHP's PEAR database
- * abstraction layer.
- *
- * The table structure for the Prefs system is in
- * scripts/sql/horde_prefs.sql.
- *
- * Copyright 1999-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   Jon Parise <jon@horde.org>
- * @category Horde
- * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
- * @package  Prefs
- */
-class Horde_Prefs_Sql extends Horde_Prefs
-{
-    /**
-     * Handle for the current database connection.
-     *
-     * @var Horde_Db_Adapter
-     */
-    protected $_db;
-
-    /**
-     * Constructor.
-     *
-     * @param string $scope  The scope for this set of preferences.
-     * @param array $opts    See factory() for list of options.
-     * @param array $params  A hash containing any additional configuration
-     *                       or connection parameters a subclass might need.
-     * <pre>
-     * 'db' - (Horde_Db_Adapter) [REQUIRED] The DB instance.
-     * 'table' - (string) The name of the prefs table.
-     *           DEFAULT: 'horde_prefs'
-     * </pre>
-     *
-     * @throws InvalidArgumentException
-     */
-    protected function __construct($scope, $opts, $params)
-    {
-        if (!isset($params['db'])) {
-            throw new InvalidArgumentException('Missing db parameter.');
-        }
-        $this->_db = $params['db'];
-        unset($params['db']);
-
-        $params = array_merge(array(
-            'table' => 'horde_prefs'
-        ), $params);
-
-        parent::__construct($scope, $opts, $params);
-    }
-
-    /**
-     * Retrieves the requested set of preferences from the user's database
-     * entry.
-     *
-     * @param string $scope  Scope specifier.
-     */
-    protected function _retrieve($scope)
-    {
-        $query = 'SELECT pref_scope, pref_name, pref_value FROM ' .
-            $this->_params['table'] . ' ' .
-            'WHERE pref_uid = ? AND pref_scope = ?';
-        $values = array($this->getUser(), $scope);
-
-        try {
-            $result = $this->_db->selectAll($query, $values);
-        } catch (Horde_Db_Exception $e) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log('No preferences were retrieved.', 'DEBUG');
-            }
-            return;
-        }
-
-        foreach ($result as $row) {
-            $name = trim($row['pref_name']);
-
-            switch ($this->_db->adapterName()) {
-            case 'PDO_PostgreSQL':
-                // TODO: Should be done in DB driver
-                if (is_resource($row['pref_value'])) {
-                    $val = stream_get_contents($row['pref_value']);
-                    fclose($row['pref_value']);
-                    $row['pref_value'] = $val;
-                }
-                $row['pref_value'] = pg_unescape_bytea($row['pref_value']);
-                break;
-            }
-
-            if (isset($this->_scopes[$scope][$name])) {
-                $this->_scopes[$scope][$name]['m'] &= ~self::PREFS_DEFAULT;
-                $this->_scopes[$scope][$name]['v'] = $row['pref_value'];
-            } else {
-                // This is a shared preference.
-                $this->_scopes[$scope][$name] = array(
-                    'd' => null,
-                    'm' => 0,
-                    'v' => $row['pref_value']
-                );
-            }
-        }
-    }
-
-    /**
-     * Stores preferences to the SQL server.
-     *
-     * @throws Horde_Exception
-     */
-    public function store()
-    {
-        // For each preference, check for an existing table row and
-        // update it if it's there, or create a new one if it's not.
-        foreach ($this->_dirty as $scope => $prefs) {
-            $updated = array();
-
-            foreach ($prefs as $name => $pref) {
-                // Don't store locked preferences.
-                if ($this->_scopes[$scope][$name]['m'] & self::LOCKED) {
-                    continue;
-                }
-
-                // Does a row already exist for this preference?
-                $query = 'SELECT 1 FROM ' . $this->_params['table'] .
-                    ' WHERE pref_uid = ? AND pref_name = ?' .
-                    ' AND pref_scope = ?';
-                $values = array($this->getUser(), $name, $scope);
-
-                try {
-                    $check = $this->_db->selectValue($query, $values);
-                } catch (Horde_Db_Exception $e) {
-                    return;
-                }
-
-                $value = strval(isset($pref['v']) ? $pref['v'] : null);
-
-                switch ($this->_db->adapterName()) {
-                case 'PDO_PostgreSQL':
-                    // TODO: Should be done in DB driver
-                    $value = pg_escape_bytea($value);
-                    break;
-                }
-
-                if (empty($check)) {
-                    // Insert a new row.
-                    $query = 'INSERT INTO ' . $this->_params['table'] . ' ' .
-                        '(pref_uid, pref_scope, pref_name, pref_value) VALUES' .
-                        '(?, ?, ?, ?)';
-                    $values = array(
-                        $this->getUser(),
-                        $scope,
-                        $name,
-                        $value
-                    );
-
-                    try {
-                        $this->_db->insert($query, $values);
-                    } catch (Horde_Db_Exception $e) {
-                        return;
-                    }
-                } else {
-                    // Update the existing row.
-                    $query = 'UPDATE ' . $this->_params['table'] .
-                        ' SET pref_value = ?' .
-                        ' WHERE pref_uid = ?' .
-                        ' AND pref_name = ?' .
-                        ' AND pref_scope = ?';
-                    $values = array(
-                        $value,
-                        $this->getUser(),
-                        $name,
-                        $scope
-                    );
-
-                    try {
-                        $this->_db->update($query, $values);
-                    } catch (Horde_Db_Exception $e) {
-                        return;
-                    }
-                }
-
-                // Clean the pref since it was just saved.
-                $this->_scopes[$scope][$name]['m'] &= ~self::DIRTY;
-
-                $updated[$name] = $this->_scopes[$scope][$name];
-            }
-
-            // Update the cache for this scope.
-            $this->_cache->update($scope, $updated);
-        }
-    }
-
-    /**
-     * Clears all preferences from the backend.
-     *
-     * @throws Horde_Exception
-     */
-    public function clear()
-    {
-        // Build the SQL query.
-        $query = 'DELETE FROM ' . $this->_params['table'] .
-            ' WHERE pref_uid = ?';
-        $values = array($this->getUser());
-
-        // Execute the query.
-        try {
-            $this->_db->delete($query, $values);
-        } catch (Horde_Db_Exception $e) {}
-
-        // Cleanup.
-        parent::clear();
-    }
-
-}
diff --git a/framework/Prefs/lib/Horde/Prefs/Storage.php b/framework/Prefs/lib/Horde/Prefs/Storage.php
new file mode 100644 (file)
index 0000000..2655dde
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Storage driver for the preferences system.
+ *
+ * Copyright 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   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Prefs
+ */
+abstract class Horde_Prefs_Storage
+{
+    /**
+     * Configuration parameters.
+     *
+     * @var string
+     */
+    protected $_params = array();
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Configuration parameters.
+     * <pre>
+     * 'user' - (string) The current username.
+     * </pre>
+     */
+    public function __construct(array $params = array())
+    {
+        $this->_params = $params;
+    }
+
+    /**
+     * Retrieves the requested preferences scope from the storage backend.
+     *
+     * @param string $scope  Scope specifier.
+     *
+     * @return mixed  Keys are pref names, values are pref values. Returns
+     *                false if no data is available.
+     * @throws Horde_Db_Exception
+     */
+    abstract public function get($scope);
+
+    /**
+     * Stores preferences in the storage backend.
+     *
+     * @param array $prefs  The preference list.
+     *
+     * @throws Horde_Db_Exception
+     */
+    abstract public function store($prefs);
+
+    /**
+     * Removes preferences from the backend.
+     *
+     * @param string $scope  The scope of the prefs to clear. If null, clears
+     *                       entire cache.
+     * @param string $pref   The pref to clear. If null, clears the entire
+     *                       scope.
+     *
+     * @throws Horde_Db_Exception
+     */
+    abstract public function remove($scope = null, $pref = null);
+
+}
diff --git a/framework/Prefs/lib/Horde/Prefs/Storage/File.php b/framework/Prefs/lib/Horde/Prefs/Storage/File.php
new file mode 100644 (file)
index 0000000..1815f9a
--- /dev/null
@@ -0,0 +1,155 @@
+<?php
+/**
+ * Preferences storage implementation using files in a directory
+ *
+ * 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   Thomas Jarosch <thomas.jarosch@intra2net.com>
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @package  Prefs
+ */
+class Horde_Prefs_Storage_File extends Horde_Prefs_Storage
+{
+    /* Current version number of the data format */
+    const VERSION = 2;
+
+    /**
+     * Cached unserialized data of all scopes.
+     *
+     * @var array
+     */
+    protected $_fileCache = null;
+
+    /**
+     * Full path to the current preference file.
+     *
+     * @var string
+     */
+    protected $_fullpath;
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Configuration parameters:
+     * <pre>
+     * 'directory' - (string) [REQUIRED] Preference storage directory.
+     * </pre>
+     *
+     * @throws InvalidArgumentException
+     */
+    public function __construct(array $params = array())
+    {
+        // Sanity check for directory
+        if (empty($params['directory']) || !is_dir($params['directory'])) {
+            throw new InvalidArgumentException('Preference storage directory is not available.');
+        }
+        if (!is_writable($params['directory'])) {
+            throw new InvalidArgumentException(sprintf('Directory %s is not writeable.', $params['directory']));
+        }
+
+        parent::__construct($scope, $opts, $params);
+
+        $this->_fullpath = $this->params['directory'] . '/' . basename($this->_params['user']) . '.prefs';
+    }
+
+    /**
+     */
+    public function get($scope)
+    {
+        if (!isset($this->_params['directory'])) {
+            return false;
+        }
+
+        if (is_null($this->_fileCache)) {
+            // Try to read
+            if (!file_exists($this->_fullpath)) {
+                return false;
+            }
+            $this->_fileCache = unserialize(file_get_contents($this->_fullpath));
+
+            // Check version number. We can call format transformations hooks
+            // in the future.
+            if (!is_array($this->_fileCache) ||
+                !array_key_exists('__file_version', $this->_fileCache) ||
+                !($this->_fileCache['__file_version'] == self::VERSION)) {
+                if ($this->_fileCache['__file_version'] == 1) {
+                    $this->transformV1V2();
+                } else {
+                    throw new Horde_Prefs_Exception(sprintf('Wrong version number found: %s (should be %d)', $this->_fileCache['__file_version'], self::VERSION));
+                }
+            }
+        }
+
+        // Check if the scope exists
+        if (empty($scope) || !isset($this->_fileCache[$scope])) {
+            return false;
+        }
+
+        $ret = array();
+
+        foreach ($this->_fileCache[$scope] as $name => $val) {
+            $ret[$name] = $val;
+        }
+
+        return $ret;
+    }
+
+    /**
+     */
+    public function store($prefs)
+    {
+        if (!isset($this->_params['directory'])) {
+            return;
+        }
+
+        // Read in all existing preferences, if any.
+        $this->get('');
+        if (!is_array($this->_fileCache)) {
+            $this->_fileCache = array('__file_version' => self::VERSION);
+        }
+
+        foreach ($prefs as $scope => $p) {
+            foreach ($p as $name => $val) {
+                $this->_fileCache[$scope][$name] = $pref['v'];
+            }
+        }
+
+        $tmp_file = Horde_Util::getTempFile('PrefsFile', true, $this->_params['directory']);
+
+        if ((file_put_contents($tmp_file, serialize($this->_fileCache)) === false) ||
+            (@rename($tmp_file, $this->_fullpath) === false)) {
+            throw new Horde_Prefs_Exception(sprintf('Write of preferences to %s failed', $this->_filename));
+        }
+    }
+
+    /**
+     */
+    public function remove($scope = null, $pref = null)
+    {
+        // TODO
+    }
+
+    /* Helper functions. */
+
+    /**
+     * Transforms the broken version 1 format into version 2.
+     */
+    public function transformV1V2()
+    {
+        $version2 = array('__file_version' => 2);
+        foreach ($this->_fileCache as $scope => $prefs) {
+            if ($scope != '__file_version') {
+                foreach ($prefs as $name => $pref) {
+                    $version2[$scope][$name] = $pref['v'];
+                }
+            }
+        }
+
+        $this->_fileCache = $version2;
+    }
+
+}
diff --git a/framework/Prefs/lib/Horde/Prefs/Storage/Imsp.php b/framework/Prefs/lib/Horde/Prefs/Storage/Imsp.php
new file mode 100644 (file)
index 0000000..805cff2
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+/**
+ * Preference storage implementation for an IMSP server.
+ *
+ * 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   Michael Rubinsky <mrubinsk@horde.org>
+ * @category Horde
+ * @package  Prefs
+ */
+class Horde_Prefs_Storage_Imsp extends Horde_Prefs_Storage
+{
+    /**
+     * Boolean indicating whether or not we're connected to the IMSP server.
+     *
+     * @var boolean
+     */
+    protected $_connected = false;
+
+    /**
+     * Handle for the IMSP server connection.
+     *
+     * @var Net_IMSP
+     */
+    protected $_imsp;
+
+    /**
+     */
+    public function get($scope)
+    {
+        $this->_connect();
+
+        $prefs = $this->_imsp->get($scope . '.*');
+        if ($prefs instanceof PEAR_Error) {
+            throw new Horde_Prefs_Exception($prefs);
+        }
+
+        $ret = array();
+
+        foreach ($prefs as $name => $val) {
+            $name = str_replace($scope . '.', '', $name);
+            if ($val != '-') {
+                $ret[$name] = $val;
+            }
+        }
+    }
+
+    /**
+     */
+    public function store($prefs)
+    {
+        $this->_connect();
+
+        foreach ($prefs as $scope => $p) {
+            foreach ($p as $name => $pref) {
+                $value = $pref['v'];
+                if (empty($value)) {
+                    $value = '-';
+                }
+
+                $result = $this->_imsp->set($scope . '.' . $name, $value);
+                if ($result instanceof PEAR_Error) {
+                    throw new Horde_Prefs_Exception($result);
+                }
+            }
+        }
+    }
+
+    /**
+     */
+    public function remove($scope = null, $pref = null)
+    {
+        // TODO
+    }
+
+    /* Helper functions. */
+
+    /**
+     * Attempts to set up a connection to the IMSP server.
+     *
+     * @throws Horde_Prefs_Exception
+     */
+    protected function _connect()
+    {
+        if ($this->_connected) {
+            return;
+        }
+
+        $this->_params['username'] = preg_match('/(^.*)@/', $this->getUser(), $matches)
+            ? $matches[1]
+            : $this->getUser();
+        $this->_params['password'] = $this->_opts['password'];
+
+        if (isset($this->_params['socket'])) {
+            $this->_params['socket'] = $params['socket'] . 'imsp_' . $this->_params['username'] . '.sck';
+        }
+
+        $this->_imsp = Net_IMSP::factory('Options', $this->_params);
+        $result = $this->_imsp->init();
+        if ($result instanceof PEAR_Error) {
+            throw new Horde_Prefs_Exception($result);
+        }
+
+        $this->_connected = true;
+    }
+
+}
diff --git a/framework/Prefs/lib/Horde/Prefs/Storage/Kolab.php b/framework/Prefs/lib/Horde/Prefs/Storage/Kolab.php
new file mode 100644 (file)
index 0000000..8b1fcd6
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Kolab implementation of the Horde preference system. Derives from the
+ * Prefs LDAP authentication object, and simply provides parameters to it
+ * based on the global Kolab configuration.
+ *
+ * Copyright 2004-2007 Stuart Binge <s.binge@codefusion.co.za>
+ *
+ * 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 <s.binge@codefusion.co.za>
+ * @category Horde
+ * @package  Prefs
+ */
+class Horde_Prefs_Storage_Kolab extends Horde_Prefs_Storage_Ldap
+{
+   /**
+     * Constructor.
+     *
+     * @param array $params  Configuration parameters.
+     */
+    public function __construct(array $params = array())
+    {
+        require_once 'Horde/Kolab.php';
+        $params = array(
+            'hostspec' => Kolab::getServer('ldap'),
+            'port' => $GLOBALS['conf']['kolab']['ldap']['port'],
+            'version' => '3',
+            'basedn' => $GLOBALS['conf']['kolab']['ldap']['basedn'],
+            'writedn' => 'user',
+            'searchdn' => $GLOBALS['conf']['kolab']['ldap']['phpdn'],
+            'searchpw' => $GLOBALS['conf']['kolab']['ldap']['phppw'],
+            'uid' => 'mail'
+        );
+
+        parent::__construct($params);
+    }
+
+}
diff --git a/framework/Prefs/lib/Horde/Prefs/Storage/KolabImap.php b/framework/Prefs/lib/Horde/Prefs/Storage/KolabImap.php
new file mode 100644 (file)
index 0000000..cac031e
--- /dev/null
@@ -0,0 +1,167 @@
+<?php
+/**
+ * Preferences storage implementation for a Kolab IMAP server.
+ *
+ * Copyright 2007-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   Gunnar Wrobel <p@rdus.de>
+ * @category Horde
+ * @package  Prefs
+ */
+class Horde_Prefs_Storage_KolabImap extends Horde_Prefs_Storage
+{
+    /**
+     * Handle for the current Kolab connection.
+     *
+     * @var Kolab
+     */
+    protected $_connection;
+
+    /**
+     * ID of the config default share
+     *
+     * @var string
+     */
+    protected $_share;
+
+    /**
+     */
+    public function get($scope)
+    {
+        $this->_connect();
+
+        $pref = $this->_getPref($scope);
+
+        if (is_null($pref)) {
+            /* No preferences saved yet. */
+            return false;
+        }
+
+        $ret = array();
+
+        foreach ($pref['pref'] as $prefstr) {
+            // If the string doesn't contain a colon delimiter, skip it.
+            if (strpos($prefstr, ':') !== false) {
+                // Split the string into its name:value components.
+                list($name, $val) = explode(':', $prefstr, 2);
+                $ret[$name] = base64_decode($val);
+            }
+        }
+    }
+
+    /**
+     */
+    public function store($prefs)
+    {
+        $this->_connect();
+
+        // Build a hash of the preferences and their values that need
+        // to be stored on the IMAP server. Because we have to update
+        // all of the values of a multi-value entry wholesale, we
+        // can't just pick out the dirty preferences; we must update
+        // every scope that has dirty preferences.
+        foreach ($prefs as $scope => $vals) {
+            $new_values = array();
+            foreach ($vals as $name => $pref) {
+                $new_values[] = $name . ':' . base64_encode($pref['v']);
+            }
+
+            $pref = $this->_getPref($scope);
+
+            if (is_null($pref)) {
+                $old_uid = null;
+                $prefs_uid = $this->_connection->_storage->generateUID();
+            } else {
+                $old_uid = $pref['uid'];
+                $prefs_uid = $pref['uid'];
+            }
+
+            $object = array(
+                'uid' => $prefs_uid,
+                'application' => $scope,
+                'pref' => $new_values
+            );
+
+            $result = $this->_connection->_storage->save($object, $old_uid);
+            if ($result instanceof PEAR_Error) {
+                throw new Horde_Prefs_Exception($result);
+            }
+        }
+    }
+
+    /**
+     */
+    public function remove($scope = null, $pref = null)
+    {
+        if (is_null($scope)) {
+            $this->_connection->deleteAll();
+        } else {
+            // TODO
+        }
+    }
+
+    /* Helper functions. */
+
+    /**
+     * Opens a connection to the Kolab server.
+     *
+     * @throws Horde_Prefs_Exception
+     */
+    protected function _connect()
+    {
+        if (isset($this->_connection)) {
+            return;
+        }
+
+        $shares = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Share')->create('h-prefs');
+        $default = $shares->getDefaultShare();
+        if ($default instanceof PEAR_Error) {
+            throw new Horde_Prefs_Exception($default);
+        }
+        $this->_share = $default->getName();
+
+        require_once 'Horde/Kolab.php';
+        $connection = new Kolab('h-prefs');
+        if ($connection instanceof PEAR_Error) {
+            throw new Horde_Prefs_Exception($connection);
+        }
+
+        $result = $this->_connection->open($this->_share, 1);
+        if ($result instanceof PEAR_Error) {
+            throw new Horde_Prefs_Exception($result);
+        }
+
+        $this->_connection = $connection;
+    }
+
+    /**
+     * Retrieves the requested preference from the user's config folder.
+     *
+     * @param string $scope  Scope specifier.
+     *
+     * @return array  The preference value.
+     * @throws Horde_Prefs_Exception
+     */
+    protected function _getPref($scope)
+    {
+        $this->_connect();
+
+        $prefs = $this->_connection->getObjects();
+        if ($prefs instanceof PEAR_Error) {
+            throw new Horde_Prefs_Exception($prefs);
+        }
+
+        foreach ($prefs as $pref) {
+            if ($pref['application'] == $scope) {
+                return $pref;
+            }
+        }
+
+        return null;
+    }
+
+
+}
diff --git a/framework/Prefs/lib/Horde/Prefs/Storage/Ldap.php b/framework/Prefs/lib/Horde/Prefs/Storage/Ldap.php
new file mode 100644 (file)
index 0000000..84598c9
--- /dev/null
@@ -0,0 +1,376 @@
+<?php
+/**
+ * Preferences storage implementation for LDAP servers.
+ *
+ * Copyright 1999-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   Jon Parise <jon@horde.org>
+ * @author   Ben Klang <ben@alkaloid.net>
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @package  Prefs
+ */
+class Horde_Prefs_Storage_Ldap extends Horde_Prefs_Storage
+{
+    /**
+     * Handle for the current LDAP connection.
+     *
+     * @var resource
+     */
+    protected $_connection;
+
+    /**
+     * Boolean indicating whether or not we're connected to the LDAP server.
+     *
+     * @var boolean
+     */
+    protected $_connected = false;
+
+    /**
+     * String holding the user's DN.
+     *
+     * @var string
+     */
+    protected $_dn = '';
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Configuration options:
+     * <pre>
+     * basedn - (string) [REQUIRED] The base DN for the LDAP server.
+     * hostspec - (string) [REQUIRED] The hostname of the LDAP server.
+     * uid - (string) [REQUIRED] The username search key.
+     * writeas - (string) [REQUIRED] One of "user", "admin", or "search"
+     *
+     * Optional parameters:
+     * binddn - (string) The DN of the administrative account to bind for
+     *          write operations.
+     * bindpw - (string) binddn's password for bind authentication.
+     * port - (integer) The port of the LDAP server.
+     *        DEFAULT: 389
+     * searchdn - (string) The DN of a user with search permissions on the
+     *            directory.
+     * searchpw - (string) searchdn's password for binding.
+     * tls - (boolean) Whether to use TLS connections.
+     *       DEFAULT: false
+     * version - (integer) The version of the LDAP protocol to use.
+     *           DEFAULT: NONE (system default will be used)
+     * </pre>
+     */
+    public function __construct(array $params = array())
+    {
+        /* If a valid server port has not been specified, set the default. */
+        if (!isset($params['port']) || !is_integer($params['port'])) {
+            $params['port'] = 389;
+        }
+
+        parent::__construct($params);
+    }
+
+    /**
+     */
+    public function get($scope)
+    {
+        $this->_connect();
+
+        // Search for the multi-valued field containing the array of
+        // preferences.
+        $search = @ldap_search($this->_connection, $this->_params['basedn'],
+                              $this->_params['uid'] . '=' . $this->params['user'],
+                              array($scope . 'Prefs'));
+        if ($search === false) {
+            throw new Horde_Prefs_Exception(sprintf('Error while searching for the user\'s prefs: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
+        }
+
+        $result = @ldap_get_entries($this->_connection, $search);
+        if ($result === false) {
+            throw new Horde_Prefs_Exception(sprintf('Error while retrieving LDAP search results for the user\'s prefs: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
+        }
+
+        // Preferences are stored as colon-separated name:value pairs.
+        // Each pair is stored as its own attribute off of the multi-
+        // value attribute named in: $scope . 'Prefs'
+
+        // ldap_get_entries() converts attribute indexes to lowercase.
+        $field = Horde_String::lower($scope . 'prefs');
+        $prefs = isset($result[0][$field])
+            ? $result[0][$field]
+            : array();
+
+        $ret = array();
+
+        foreach ($prefs as $prefstr) {
+            // If the string doesn't contain a colon delimiter, skip it.
+            if (strpos($prefstr, ':') !== false) {
+                // Split the string into its name:value components.
+                list($name, $val) = explode(':', $prefstr, 2);
+                $ret[$name] = base64_decode($val);
+            }
+        }
+
+        return $ret;
+    }
+
+    /**
+     */
+    public function store($prefs)
+    {
+        $this->_connect();
+
+        // Build a hash of the preferences and their values that need
+        // to be stored on the LDAP server. Because we have to update
+        // all of the values of a multi-value entry wholesale, we
+        // can't just pick out the dirty preferences; we must update
+        // every scope that has dirty preferences.
+        $new_values = array();
+        foreach ($prefs as $scope => $vals) {
+            foreach ($vals as $name => $pref) {
+                $new_values[$scope . 'Prefs'][] = $name . ':' . base64_encode($pref['v']);
+            }
+        }
+
+        // Entries must have the objectclasses 'top' and 'hordeperson'
+        // to successfully store LDAP prefs. Check for both of them,
+        // and add them if necessary.
+        $search = @ldap_search($this->_connection, $this->_params['basedn'],
+                              $this->_params['uid'] . '=' . $this->prefs['user'],
+                              array('objectclass'));
+        if ($search === false) {
+            throw new Horde_Prefs_Exception(sprintf('Error searching the directory for required objectClasses: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
+        }
+
+        $result = @ldap_get_entries($this->_connection, $search);
+        if ($result === false) {
+            throw new Horde_Prefs_Exception(sprintf('Error retrieving results while checking for required objectClasses: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
+        }
+
+        if ($result['count'] > 0) {
+            $top = false;
+            $hordeperson = false;
+
+            for ($i = 0; $i < $result[0]['objectclass']['count']; $i++) {
+                if ($result[0]['objectclass'][$i] == 'top') {
+                    $top = true;
+                } elseif ($result[0]['objectclass'][$i] == 'hordePerson') {
+                    $hordeperson = true;
+                }
+            }
+
+            // Add any missing objectclasses.
+            if (!$top) {
+                @ldap_mod_add($this->_connection, $this->_dn, array('objectclass' => 'top'));
+            }
+
+            if (!$hordeperson) {
+                @ldap_mod_add($this->_connection, $this->_dn, array('objectclass' => 'hordePerson'));
+            }
+        }
+
+        // Send the hash to the LDAP server.
+        $result = @ldap_mod_replace($this->_connection, $this->_dn,
+                                    $new_values);
+        if ($result === false) {
+            throw new Horde_Prefs_Exception(sprintf('Unable to modify user\'s objectClass for preferences: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
+        }
+    }
+
+    /**
+     */
+    public function remove($scope = null, $pref = null)
+    {
+        // TODO: Implement scope/pref-level removal
+        if (!is_null($scope) || !is_null($pref)) {
+            throw new Horde_Prefs_Exception('Removal not supported.');
+        }
+
+        $this->_connect();
+
+        // TODO: Move to horde/Core
+        $attrs = $GLOBALS['registry']->listApps(array('inactive', 'active', 'hidden', 'notoolbar', 'admin'));
+        foreach ($attrs as $key => $val) {
+            $attrs[$key] = $val . 'Prefs';
+        }
+
+        $search = @ldap_read($this->_connection, $this->_dn,
+                            'objectClass=hordePerson', $attrs, 1);
+        if ($search === false) {
+            throw new Horde_Prefs_Exception(sprintf('Error while getting preferenes from LDAP: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
+        }
+
+        $result = @ldap_get_entries($this->_connection, $search);
+        if ($result === false) {
+            throw new Horde_Prefs_Exception(sprintf('Error while retrieving results from LDAP: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
+        }
+
+        $attrs = array();
+        for ($i = 0; $i < $result[0]['count']; $i++) {
+            $attrs[$result[0][$i]] = array();
+        }
+        $result = @ldap_mod_del($this->_connection, $this->_dn, $attrs);
+        if ($result === false) {
+            throw new Horde_Prefs_Exception(sprintf('Unable to clear user\'s preferences: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
+        }
+    }
+
+    /* LDAP Helper functions.
+     * TODO: Use Horde_LDAP */
+
+    /**
+     * Opens a connection to the LDAP server.
+     *
+     * @throws Horde_Prefs_Exception
+     */
+    protected function _connect()
+    {
+        if ($this->_connected) {
+            return;
+        }
+
+        if (!Horde_Util::extensionExists('ldap')) {
+            throw new Horde_Prefs_Exception('Required LDAP extension not found.');
+        }
+
+        Horde::assertDriverConfig($this->_params, 'prefs',
+            array('hostspec', 'basedn', 'uid', 'writeas'),
+            'preferences LDAP');
+
+        /* Connect to the LDAP server anonymously. */
+        $conn = ldap_connect($this->_params['hostspec'], $this->_params['port']);
+        if (!$conn) {
+            throw new Horde_Prefs_Exception(sprintf('Failed to open an LDAP connection to %s.', $this->_params['hostspec']));
+        }
+
+        /* Set the LDAP protocol version. */
+        if (isset($this->_params['version'])) {
+            $result = @ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION,
+                                       $this->_params['version']);
+            if ($result === false) {
+                throw new Horde_Prefs_Exception(sprintf('Set LDAP protocol version to %d failed: [%d] %s', $this->_params['version'], @ldap_errno($conn), @ldap_error($conn)));
+            }
+        }
+
+        /* Start TLS if we're using it. */
+        if (!empty($this->_params['tls'])) {
+            @ldap_start_tls($conn);
+        }
+
+        /* If necessary, bind to the LDAP server as the user with search
+         * permissions. */
+        if (!empty($this->_params['searchdn'])) {
+            $bind = @ldap_bind($conn, $this->_params['searchdn'],
+                               $this->_params['searchpw']);
+            if ($bind === false) {
+                throw new Horde_Prefs_Exception(sprintf('Bind to server %s:%d with DN %s failed: [%d] %s', $this->_params['hostspec'], $this->_params['port'], $this->_params['searchdn'], @ldap_errno($conn), @ldap_error($conn)));
+            }
+        }
+
+        /* Register our callback function to handle referrals. */
+        if (function_exists('ldap_set_rebind_proc')) {
+            $result = @ldap_set_rebind_proc($conn, array($this, 'rebindProc'));
+            if ($result === false) {
+                throw new Horde_Prefs_Exception(sprintf('Setting referral callback failed: [%d] %s', @ldap_errno($conn), @ldap_error($conn)));
+            }
+        }
+
+        /* Store the connection handle at the instance level. */
+        $this->_connection = $conn;
+
+        /* Search for the user's full DN. */
+        $search = @ldap_search($this->_connection, $this->_params['basedn'],
+                               $this->_params['uid'] . '=' . $this->params['user'], array('dn'));
+        if ($search === false) {
+            throw new Horde_Prefs_Exception(sprintf('Error while searching the directory for the user\'s DN: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
+        }
+
+        $result = @ldap_get_entries($this->_connection, $search);
+        if ($result === false) {
+            throw new Horde_Prefs_Exception(sprintf('Error while retrieving LDAP search results for the user\'s DN: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
+        }
+
+        if ($result['count'] != 1) {
+            throw new Horde_Prefs_Exception('Zero or more than one DN returned from search; unable to determine user\'s correct DN.');
+        }
+        $this->_dn = $result[0]['dn'];
+
+        // Now we should have the user's DN.  Re-bind as appropriate with write
+        // permissions to be able to store preferences.
+        switch($this->_params['writeas']) {
+        case 'user':
+            $result = @ldap_bind($this->_connection,
+                                 $this->_dn, $this->_opts['password']);
+            break;
+
+        case 'admin':
+            $result = @ldap_bind($this->_connection,
+                                 $this->_params['binddn'],
+                                 $this->_params['bindpw']);
+            break;
+
+        case 'search':
+            // Since we've already bound as the search DN above, no rebinding
+            // is necessary.
+            $result = true;
+            break;
+        }
+
+        if ($result === false) {
+            throw new Horde_Prefs_Exception(sprintf('Error rebinding for prefs writing: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)));
+        }
+
+        // We now have a ready-to-use connection.
+        $this->_connected = true;
+    }
+
+    /**
+     * Callback function for LDAP referrals.  This function is called when an
+     * LDAP operation returns a referral to an alternate server.
+     *
+     * @return integer  1 on error, 0 on success.
+     */
+    public function rebindProc($conn, $who)
+    {
+        /* Strip out the hostname we're being redirected to. */
+        $who = preg_replace(array('|^.*://|', '|:\d*$|'), '', $who);
+
+        /* Make sure the server we're being redirected to is in our list of
+           valid servers. */
+        if (strpos($this->_params['hostspec'], $who) === false) {
+            if ($this->_opts['logger']) {
+                $this->_opts['logger']->log(sprintf('Referral target %s for DN %s is not in the authorized server list.', $who, $bind_dn), 'ERR');
+            }
+            return 1;
+        }
+
+        /* Figure out the DN of the authenticating user. */
+        switch($this->_params['writeas']) {
+        case 'user':
+            $bind_dn = $this->_dn;
+            $bind_pw = $this->_opts['password'];
+            break;
+
+        case 'admin':
+            $bind_dn = $this->_params['binddn'];
+            $bind_pw = $this->_params['bindpw'];
+            break;
+
+        case 'search':
+            $bind_dn = $this->_params['searchdn'];
+            $bind_dn = $this->_params['searchpw'];
+            break;
+        }
+
+        /* Bind to the new server. */
+        $bind = @ldap_bind($conn, $bind_dn, $bind_pw);
+        if (($bind === false) && $this->_opts['logger']) {
+            $this->_opts['logger']->log(sprintf('Rebind to server %s:%d with DN %s failed: [%d] %s', $this->_params['hostspec'], $this->_params['port'], $bind_dn, @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR');
+        }
+
+        return 0;
+    }
+
+
+}
diff --git a/framework/Prefs/lib/Horde/Prefs/Storage/Null.php b/framework/Prefs/lib/Horde/Prefs/Storage/Null.php
new file mode 100644 (file)
index 0000000..b082fde
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Null storage driver for the preferences system.
+ *
+ * Copyright 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   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Prefs
+ */
+class Horde_Prefs_Storage_Null extends Horde_Prefs_Storage
+{
+    /**
+     */
+    public function get($scope)
+    {
+        return false;
+    }
+
+    /**
+     */
+    public function store($prefs)
+    {
+    }
+
+    /**
+     */
+    public function remove($scope = null, $pref = null)
+    {
+    }
+
+}
diff --git a/framework/Prefs/lib/Horde/Prefs/Storage/Session.php b/framework/Prefs/lib/Horde/Prefs/Storage/Session.php
new file mode 100644 (file)
index 0000000..7a55524
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+/**
+ * Session storage driver for the preferences system.
+ *
+ * Copyright 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   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Prefs
+ */
+class Horde_Prefs_Storage_Session extends Horde_Prefs_Storage
+{
+    /**
+     * Session key.
+     *
+     * @var string
+     */
+    protected $_key;
+
+    /**
+     */
+    public function __construct($user)
+    {
+        parent::__construct($user);
+
+        $this->_key = 'horde_prefs_' . $this->_user;
+    }
+
+    /**
+     */
+    public function get($scope)
+    {
+        return isset($_SESSION[$this->_key][$scope])
+            ? $_SESSION[$this->_key][$scope]
+            : false;
+    }
+
+    /**
+     */
+    public function store($prefs)
+    {
+        foreach ($prefs as $scope => $vals) {
+            $_SESSION[$this->_key][$scope] = isset($_SESSION[$this->_key][$scope])
+                ? array_merge($_SESSION[$this->_key][$scope], $vals)
+                : array();
+        }
+    }
+
+    /**
+     */
+    public function remove($scope = null, $pref = null)
+    {
+        if (is_null($scope)) {
+            unset($_SESSION[$this->_key]);
+        } elseif (is_null($pref)) {
+            unset($_SESSION[$this->_key][$scope]);
+        } else {
+            unset($_SESSION[$this->_key][$scope][$pref]);
+        }
+    }
+
+}
diff --git a/framework/Prefs/lib/Horde/Prefs/Storage/Sql.php b/framework/Prefs/lib/Horde/Prefs/Storage/Sql.php
new file mode 100644 (file)
index 0000000..f3d0b03
--- /dev/null
@@ -0,0 +1,185 @@
+<?php
+/**
+ * Preferences storage implementation for a SQL database.
+ *
+ * Copyright 1999-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   Jon Parise <jon@horde.org>
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Prefs
+ */
+class Horde_Prefs_Storage_Sql extends Horde_Prefs_Storage
+{
+    /**
+     * Handle for the current database connection.
+     *
+     * @var Horde_Db_Adapter
+     */
+    protected $_db;
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Configuration parameters.
+     * <pre>
+     * 'db' - (Horde_Db_Adapter) [REQUIRED] The DB instance.
+     * 'table' - (string) The name of the prefs table.
+     *           DEFAULT: 'horde_prefs'
+     * </pre>
+     *
+     * @throws InvalidArgumentException
+     */
+    public function __construct(array $params = array())
+    {
+        if (!isset($params['db'])) {
+            throw new InvalidArgumentException('Missing db parameter.');
+        }
+        $this->_db = $params['db'];
+        unset($params['db']);
+
+        $params = array_merge(array(
+            'table' => 'horde_prefs'
+        ), $params);
+
+        parent::__construct($params);
+    }
+
+    /**
+     */
+    public function get($scope)
+    {
+        $query = 'SELECT pref_scope, pref_name, pref_value FROM ' .
+            $this->_params['table'] . ' ' .
+            'WHERE pref_uid = ? AND pref_scope = ?';
+        $values = array($this->_params['user'], $scope);
+
+        try {
+            $result = $this->_db->selectAll($query, $values);
+        } catch (Horde_Db_Exception $e) {
+            throw Horde_Prefs_Exception($e);
+        }
+
+        $ret = array();
+
+        foreach ($result as $row) {
+            $name = trim($row['pref_name']);
+
+            switch ($this->_db->adapterName()) {
+            case 'PDO_PostgreSQL':
+                // TODO: Should be done in DB driver
+                if (is_resource($row['pref_value'])) {
+                    $val = stream_get_contents($row['pref_value']);
+                    fclose($row['pref_value']);
+                    $row['pref_value'] = $val;
+                }
+                $row['pref_value'] = pg_unescape_bytea($row['pref_value']);
+                break;
+            }
+
+            $ret[$name] = $row['pref_value'];
+        }
+
+        return $ret;
+    }
+
+    /**
+     */
+    public function store($prefs)
+    {
+        // For each preference, check for an existing table row and
+        // update it if it's there, or create a new one if it's not.
+        foreach ($prefs as $scope => $p) {
+            foreach ($p as $name => $pref) {
+                // Does a row already exist for this preference?
+                $query = 'SELECT 1 FROM ' . $this->_params['table'] .
+                    ' WHERE pref_uid = ? AND pref_name = ?' .
+                    ' AND pref_scope = ?';
+                $values = array($this->_params['user'], $name, $scope);
+
+                try {
+                    $check = $this->_db->selectValue($query, $values);
+                } catch (Horde_Db_Exception $e) {
+                    throw Horde_Prefs_Exception($e);
+                }
+
+                $value = strval(isset($pref['v']) ? $pref['v'] : null);
+
+                switch ($this->_db->adapterName()) {
+                case 'PDO_PostgreSQL':
+                    // TODO: Should be done in DB driver
+                    $value = pg_escape_bytea($value);
+                    break;
+                }
+
+                if (empty($check)) {
+                    // Insert a new row.
+                    $query = 'INSERT INTO ' . $this->_params['table'] . ' ' .
+                        '(pref_uid, pref_scope, pref_name, pref_value) VALUES' .
+                        '(?, ?, ?, ?)';
+                    $values = array(
+                        $this->_params['user'],
+                        $scope,
+                        $name,
+                        $value
+                    );
+
+                    try {
+                        $this->_db->insert($query, $values);
+                    } catch (Horde_Db_Exception $e) {
+                        throw Horde_Prefs_Exception($e);
+                    }
+                } else {
+                    // Update the existing row.
+                    $query = 'UPDATE ' . $this->_params['table'] .
+                        ' SET pref_value = ?' .
+                        ' WHERE pref_uid = ?' .
+                        ' AND pref_name = ?' .
+                        ' AND pref_scope = ?';
+                    $values = array(
+                        $value,
+                        $this->_params['user'],
+                        $name,
+                        $scope
+                    );
+
+                    try {
+                        $this->_db->update($query, $values);
+                    } catch (Horde_Db_Exception $e) {
+                        throw Horde_Prefs_Exception($e);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     */
+    public function remove($scope = null, $pref = null)
+    {
+        $query = 'DELETE FROM ' . $this->_params['table'] .
+                 ' WHERE pref_uid = ?';
+        $values = array($this->_params['user']);
+
+        if (!is_null($scope)) {
+            $query .= ' AND pref_scope = ?';
+            $values[] = $scope;
+
+            if (!is_null($pref)) {
+                $query .= ' AND pref_name = ?';
+                $values[] = $pref;
+            }
+        }
+
+        try {
+            $this->_db->delete($query, $values);
+        } catch (Horde_Db_Exception $e) {
+            throw Horde_Prefs_Exception($e);
+        }
+    }
+
+}
index 0938065..95c4cf2 100644 (file)
@@ -24,7 +24,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
   <api>beta</api>
  </stability>
  <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
- <notes>* Abstract caching code into Horde_Prefs_Cache.
+ <notes>* Abstract storage code into Horde_Prefs_Storage.
  * Add array access API to Horde_Prefs.
  * Remove dependency on horde/Core.
  * Use Horde_Db as backend for Sql driver.
@@ -36,32 +36,24 @@ http://pear.php.net/dtd/package-2.0.xsd">
    <dir name="lib">
     <dir name="Horde">
      <dir name="Prefs">
-      <dir name="Cache">
+      <dir name="Storage">
+       <file name="File.php" role="php" />
+       <file name="Imsp.php" role="php" />
+       <file name="Kolab.php" role="php" />
+       <file name="KolabImap.php" role="php" />
+       <file name="Ldap.php" role="php" />
        <file name="Null.php" role="php" />
        <file name="Session.php" role="php" />
-      </dir> <!-- /lib/Horde/Prefs/Cache -->
-      <file name="Cache.php" role="php" />
+       <file name="Sql.php" role="php" />
+      </dir> <!-- /lib/Horde/Prefs/Storage -->
       <file name="CategoryManager.php" role="php" />
       <file name="Exception.php" role="php" />
-      <file name="File.php" role="php" />
       <file name="Identity.php" role="php" />
-      <file name="Imsp.php" role="php" />
-      <file name="Kolab.php" role="php" />
-      <file name="KolabImap.php" role="php" />
-      <file name="Ldap.php" role="php" />
-      <file name="Session.php" role="php" />
-      <file name="Sql.php" role="php" />
+      <file name="Storage.php" role="php" />
      </dir> <!-- /lib/Horde/Prefs -->
      <file name="Prefs.php" role="php" />
     </dir> <!-- /lib/Horde -->
    </dir> <!-- /lib -->
-   <dir name="test">
-    <dir name="Horde">
-     <dir name="Prefs">
-      <file name="bug_2838.phpt" role="test" />
-     </dir> <!-- /test/Horde/Prefs -->
-    </dir> <!-- /test/Horde -->
-   </dir> <!-- /test -->
   </dir> <!-- / -->
  </contents>
  <dependencies>
@@ -97,19 +89,18 @@ http://pear.php.net/dtd/package-2.0.xsd">
  </dependencies>
  <phprelease>
   <filelist>
-   <install name="lib/Horde/Prefs/Cache/Null.php" as="Horde/Prefs/Cache/Null.php" />
-   <install name="lib/Horde/Prefs/Cache/Session.php" as="Horde/Prefs/Cache/Session.php" />
-   <install name="lib/Horde/Prefs/Cache.php" as="Horde/Prefs/Cache.php" />
    <install name="lib/Horde/Prefs/CategoryManager.php" as="Horde/Prefs/CategoryManager.php" />
    <install name="lib/Horde/Prefs/Exception.php" as="Horde/Prefs/Exception.php" />
-   <install name="lib/Horde/Prefs/File.php" as="Horde/Prefs/File.php" />
    <install name="lib/Horde/Prefs/Identity.php" as="Horde/Prefs/Identity.php" />
-   <install name="lib/Horde/Prefs/Imsp.php" as="Horde/Prefs/Imsp.php" />
-   <install name="lib/Horde/Prefs/Kolab.php" as="Horde/Prefs/Kolab.php" />
-   <install name="lib/Horde/Prefs/KolabImap.php" as="Horde/Prefs/KolabImap.php" />
-   <install name="lib/Horde/Prefs/Ldap.php" as="Horde/Prefs/Ldap.php" />
-   <install name="lib/Horde/Prefs/Session.php" as="Horde/Prefs/Session.php" />
-   <install name="lib/Horde/Prefs/Sql.php" as="Horde/Prefs/Sql.php" />
+   <install name="lib/Horde/Prefs/Storage/File.php" as="Horde/Prefs/Storage/File.php" />
+   <install name="lib/Horde/Prefs/Storage/Imsp.php" as="Horde/Prefs/Storage/Imsp.php" />
+   <install name="lib/Horde/Prefs/Storage/Kolab.php" as="Horde/Prefs/Storage/Kolab.php" />
+   <install name="lib/Horde/Prefs/Storage/KolabImap.php" as="Horde/Prefs/Storage/KolabImap.php" />
+   <install name="lib/Horde/Prefs/Storage/Ldap.php" as="Horde/Prefs/Storage/Ldap.php" />
+   <install name="lib/Horde/Prefs/Storage/Null.php" as="Horde/Prefs/Storage/Null.php" />
+   <install name="lib/Horde/Prefs/Storage/Session.php" as="Horde/Prefs/Storage/Session.php" />
+   <install name="lib/Horde/Prefs/Storage/Sql.php" as="Horde/Prefs/Storage/Sql.php" />
+   <install name="lib/Horde/Prefs/Storage.php" as="Horde/Prefs/Storage.php" />
    <install name="lib/Horde/Prefs.php" as="Horde/Prefs.php" />
   </filelist>
  </phprelease>
diff --git a/framework/Prefs/test/Horde/Prefs/bug_2838.phpt b/framework/Prefs/test/Horde/Prefs/bug_2838.phpt
deleted file mode 100644 (file)
index a8899fa..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
---TEST--
-Test for Bug #2838, overwriting of preferences when multiple scopes are retrieved.
---FILE--
-<?php
-
-$horde_base = '/path/to/horde';
-require_once HORDE_BASE . '/lib/Application.php';
-Horde_Registry::appInit('horde', array('authentication' => 'none'));
-
-$prefs = Horde_Prefs::factory('session', 'horde', 'testuser', 'testpw');
-$prefs->retrieve('imp');
-$prefs->setValue('last_login', 'test');
-echo $prefs->getValue('last_login') . "\n";
-
-$prefs->retrieve('ingo');
-echo $prefs->getValue('last_login') . "\n";
-
-?>
---EXPECT--
-test
-test