Add Horde_Lock to git.
authorMichael J. Rubinsky <mrubinsk@horde.org>
Mon, 28 Sep 2009 17:56:29 +0000 (13:56 -0400)
committerMichael J. Rubinsky <mrubinsk@horde.org>
Mon, 28 Sep 2009 17:57:03 +0000 (13:57 -0400)
framework/Lock/lib/Horde/Lock.php [new file with mode: 0644]
framework/Lock/lib/Horde/Lock/Sql.php [new file with mode: 0644]
framework/Lock/package.xml [new file with mode: 0644]
framework/Rpc/lib/Horde/Rpc/Webdav.php

diff --git a/framework/Lock/lib/Horde/Lock.php b/framework/Lock/lib/Horde/Lock.php
new file mode 100644 (file)
index 0000000..1387de0
--- /dev/null
@@ -0,0 +1,236 @@
+<?php
+/**
+ * The Horde_Lock class provides an API to create, store, check and expire locks
+ * based on a given resource URI.
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * $Horde: framework/Lock/Lock.php,v 1.12 2009/03/05 04:20:32 slusarz Exp $
+ *
+ * @author  Ben Klang <ben@alkaloid.net>
+ * @since   Horde 3.2
+ * @package Horde_Lock
+ */
+class Horde_Lock
+{
+    const TYPE_EXCLUSIVE = 1;
+    const TYPE_SHARED = 2;
+
+    /**
+     * Local copy of driver parameters
+     * @var $_params
+     */
+    protected $_params;
+
+    /**
+     * Horde_Lock constructor
+     *
+     * @param array $params  Parameters for the specific Horde_Lock driver
+     *
+     * @return Horde_Lock    Instance of Horde_Lock on success or PEAR_Error
+     */
+    public function __construct($params)
+    {
+        $this->_params = $params;
+        return $this;
+    }
+
+    /**
+     * Return an array of information about the requested lock.
+     *
+     * @param string $lockid   Lock ID to look up
+     *
+     * @return mixed           Array of lock information on success; PEAR_Error
+     *                         on failure.
+     */
+    public function getLockInfo($lockid)
+    {
+        return PEAR::raiseError(_("No lock driver configured!"));
+    }
+
+    /**
+     * Return a list of valid locks with the option to limit the results
+     * by principal, scope and/or type.
+     *
+     * @param string $scope      The scope of the lock.  Typically the name of
+     *                           the application requesting the lock or some
+     *                           other identifier used to group locks together.
+     * @param string $principal  Principal for which to check for locks
+     * @param int $type          Only return locks of the given type.
+     *                           Defaults to null, or all locks
+     *
+     * @return mixed  Array of locks with the ID as the key and the lock details
+     *                as the value. If there are no current locks this will
+     *                return an empty array. On failure a PEAR_Error object
+     *                will be returned.
+     */
+    public function getLocks($scope = null, $principal = null, $type = null)
+    {
+        return PEAR::raiseError(_("No lock driver configured!"));
+    }
+
+    /**
+     * Extend the valid lifetime of a valid lock to now + $extend.
+     *
+     * @param string $lockid  Lock ID to reset.  Must be a valid, non-expired
+     *                        lock.
+     * @param int $extend     Extend lock this many seconds from now.
+     *
+     * @return mixed          True on success; PEAR_Error on failure.
+     */
+    public function resetLock($lockid, $extend)
+    {
+        return PEAR::raiseError(_("No lock driver configured!"));
+    }
+
+    /**
+     * Sets a lock on the requested principal and returns the generated lock ID.
+     * NOTE: No security checks are done in the Horde_Lock API.  It is expected
+     * that the calling application has done all necessary security checks
+     * before requesting a lock be granted.
+     *
+     * @param string $requestor  User ID of the lock requestor.
+     * @param string $scope      The scope of the lock.  Typically the name of
+     *                           the application requesting the lock or some
+     *                           other identifier used to group locks together.
+     * @param string $principal  A principal on which a lock should be granted.
+     *                           The format can be any string but is suggested
+     *                           to be in URI form.
+     * @param int $lifetime      Time (in seconds) for which the lock will be
+     *                           considered valid.
+     * @param string exclusive   One of HORDE_LOCK_TYPE_SHARED or
+     *                           HORDE_LOCK_TYPE_EXCLUSIVE.
+     *                           - An exclusive lock will be enforced strictly
+     *                             and must be interpreted to mean that the
+     *                             resource can not be modified.  Only one
+     *                             exclusive lock per principal is allowed.
+     *                           - A shared lock is one that notifies other
+     *                             potential lock requestors that the resource
+     *                             is in use.  This lock can be overridden
+     *                             (cleared or replaced with a subsequent
+     *                             call to setLock()) by other users.  Multiple
+     *                             users may request (and will be granted) a
+     *                             shared lock on a given principal.  All locks
+     *                             will be considered valid until they are
+     *                             cleared or expire.
+     *
+     * @return mixed   A string lock ID on success; PEAR_Error on failure.
+     */
+    public function setLock($requestor, $scope, $principal,
+                     $lifetime = 1, $exclusive = HORDE_LOCK_TYPE_SHARED)
+    {
+        return PEAR::raiseError(_("No lock driver configured!"));
+    }
+
+    /**
+     * Removes a lock given the lock ID.
+     * NOTE: No security checks are done in the Horde_Lock API.  It is expected
+     * that the calling application has done all necessary security checks
+     * before requesting a lock be cleared.
+     *
+     * @param string $lockid  The lock ID as generated by a previous call
+     *                        to setLock()
+     *
+     * @return mixed   True on success; PEAR_Error on failure.
+     */
+    public function clearLock($lockid)
+    {
+        return PEAR::raiseError(_("No lock driver configured!"));
+    }
+
+    /**
+     * Attempts to return a concrete Horde_Lock instance based on $driver.
+     *
+     * @param mixed $driver  The type of concrete Horde_Lock subclass to return.
+     *                       This is based on the storage driver ($driver).
+     *                       The code is dynamically included. If $driver is an
+     *                       array, then we will look in $driver[0]/lib/Lock/
+     *                       for the subclass implementation named
+     *                       $driver[1].php.
+     * @param array $params  A hash containing any additional configuration or
+     *                       connection parameters a subclass might need.
+     *
+     * @return Horde_Lock    The newly created concrete Lock instance, or a
+     *                       PEAR_Error object on error.
+     */
+    public function factory($driver, $params = null)
+    {
+        if (is_array($driver)) {
+            $app = $driver[0];
+            $driver = $driver[1];
+        }
+
+        $driver = basename($driver);
+        if (empty($driver) || ($driver == 'none')) {
+            return new Horde_Lock();
+        }
+
+        if (is_null($params)) {
+            $params = Horde::getDriverConfig('lock', $driver);
+        }
+
+        $class = 'Horde_Lock_' . $driver;
+        $include_error = '';
+        if (!class_exists($class)) {
+            $oldTrackErrors = ini_set('track_errors', 1);
+            if (!empty($app)) {
+                include $GLOBALS['registry']->get('fileroot', $app) . '/lib/Horde_Lock/' . $driver . '.php';
+            } else {
+                include 'Horde/Lock/' . $driver . '.php';
+            }
+            if (isset($php_errormsg)) {
+                $include_error = $php_errormsg;
+            }
+            ini_set('track_errors', $oldTrackErrors);
+        }
+
+        if (class_exists($class)) {
+            $lock = new $class($params);
+        } else {
+            $lock = PEAR::raiseError('Horde_Lock Driver (' . $class . ') not found' . ($include_error ? ': ' . $include_error : '') . '.');
+        }
+
+        return $lock;
+    }
+
+    /**
+     * Attempts to return a reference to a concrete Horde_Lock instance based on
+     * $driver. It will only create a new instance if no Horde_Lock instance
+     * with the same parameters currently exists.
+     *
+     * This should be used if multiple authentication sources (and, thus,
+     * multiple Horde_Lock instances) are required.
+     *
+     * This method must be invoked as: $var = &Horde_Lock::singleton()
+     *
+     * @param string $driver  The type of concrete Horde_Lock subclass to
+     *                        return.
+     *                        This is based on the storage driver ($driver).
+     *                        The code is dynamically included.
+     * @param array $params   A hash containing any additional configuration or
+     *                        connection parameters a subclass might need.
+     *
+     * @return Horde_Lock     The concrete Horde_Lock reference or PEAR_Error
+     *                        on failure.
+     */
+    public function &singleton($driver, $params = null)
+    {
+        static $instances = array();
+
+        if (is_null($params)) {
+            $params = Horde::getDriverConfig('lock',
+                is_array($driver) ? $driver[1] : $driver);
+        }
+
+        $signature = serialize(array($driver, $params));
+        if (empty($instances[$signature])) {
+            $instances[$signature] = Horde_Lock::factory($driver, $params);
+        }
+
+        return $instances[$signature];
+    }
+
+}
diff --git a/framework/Lock/lib/Horde/Lock/Sql.php b/framework/Lock/lib/Horde/Lock/Sql.php
new file mode 100644 (file)
index 0000000..046fb4f
--- /dev/null
@@ -0,0 +1,458 @@
+<?php
+/**
+ * The Horde_Lock_sql driver implements a storage backend for the Horde_Lock API
+ *
+ * Required parameters:<pre>
+ *   'phptype'      The database type (ie. 'pgsql', 'mysql', etc.).</pre>
+ *
+ * Required by some database implementations:<pre>
+ *   'database'     The name of the database.
+ *   'hostspec'     The hostname of the database server.
+ *   'username'     The username with which to connect to the database.
+ *   'password'     The password associated with 'username'.
+ *   'options'      Additional options to pass to the database.
+ *   'tty'          The TTY on which to connect to the database.
+ *   'port'         The port on which to connect to the database.</pre>
+ *
+ * Optional parameters:<pre>
+ *   'table'               The name of the lock table in 'database'.
+ *                         Defaults to 'horde_locks'.
+ *
+ * Optional values when using separate reading and writing servers, for example
+ * in replication settings:<pre>
+ *   'splitread'   Boolean, whether to implement the separation or not.
+ *   'read'        Array containing the parameters which are different for
+ *                 the read database connection, currently supported
+ *                 only 'hostspec' and 'port' parameters.</pre>
+ *
+ * The table structure for the locks is as follows:
+ * <pre>
+ * CREATE TABLE horde_locks (
+ *     lock_id                  VARCHAR(36) NOT NULL,
+ *     lock_owner               VARCHAR(32) NOT NULL,
+ *     lock_scope               VARCHAR(32) NOT NULL,
+ *     lock_principal           VARCHAR(255) NOT NULL,
+ *     lock_origin_timestamp    BIGINT NOT NULL,
+ *     lock_update_timestamp    BIGINT NOT NULL,
+ *     lock_expiry_timestamp    BIGINT NOT NULL,
+ *     lock_type                TINYINT NOT NULL,
+ *
+ *     PRIMARY KEY (lock_id)
+ * );
+ * </pre>
+ *
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did
+ * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
+ *
+ * $Horde: framework/Lock/Lock/sql.php,v 1.19 2009/06/09 23:23:40 slusarz Exp $
+ *
+ * @author  Ben Klang <ben@alkaloid.net>
+ * @since   Horde 3.2
+ * @package Horde_Lock
+ */
+class Horde_Lock_Sql extends Horde_Lock
+{
+    /**
+     * Handle for the current database connection.
+     *
+     * @var DB
+     */
+    private $_db;
+
+    /**
+     * Handle for the current database connection, used for writing. Defaults
+     * to the same handle as $_db if a separate write database isn't required.
+     *
+     * @var DB
+     */
+    private $_write_db;
+
+    /**
+     * Boolean indicating whether or not we're connected to the SQL server.
+     *
+     * @var boolean
+     */
+    private $_connected = false;
+
+    /**
+     * Constructs a new Horde_Lock_sql object.
+     *
+     * @param array $params  A hash containing configuration parameters.
+     */
+    public function __construct($params = array())
+    {
+        $options = array(
+            'database' => '',
+            'username' => '',
+            'password' => '',
+            'hostspec' => '',
+            'table' => '',
+        );
+        $this->_params = array_merge($options, $params);
+        if (empty($this->_params['table'])) {
+            $this->_params['table'] = 'horde_locks';
+        }
+
+        /* Only do garbage collection if asked for, and then only 0.1% of the
+         * time we create an object. */
+        if (rand(0, 999) == 0) {
+            register_shutdown_function(array(&$this, '_doGC'));
+        }
+
+        parent::__construct($this->_params);
+    }
+
+    /**
+     * Return an array of information about the requested lock.
+     *
+     * @param string $lockid   Lock ID to look up
+     *
+     * @return mixed           Array of lock information on success; false if no
+     *                         valid lock exists; PEAR_Error on failure.
+     */
+    public function getLockInfo($lockid)
+    {
+       if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return PEAR::raiseError(_("Internal database error.  Details have been logged for the administrator."));
+        }
+
+        $now = time();
+        $sql = 'SELECT lock_id, lock_owner, lock_scope, lock_principal, ' .
+               'lock_origin_timestamp, lock_update_timestamp, ' .
+               'lock_expiry_timestamp, lock_type FROM ' . $this->_params['table'] .
+               ' WHERE lock_id = ? AND lock_expiry_timestamp >= ?';
+        $values = array($lockid, $now);
+
+        Horde::logMessage('SQL Query by Horde_Lock_sql::getLockInfo(): ' . $sql,
+                          __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        $result = $this->_db->query($sql, $values);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        $locks = array();
+        $row = $result->fetchRow(DB_FETCHMODE_ASSOC);
+        if (is_a($row, 'PEAR_Error')) {
+            return false;
+        }
+
+        $result->free();
+        return $row;
+    }
+
+    /**
+     * Return a list of valid locks with the option to limit the results
+     * by principal, scope and/or type.
+     *
+     * @param string $scope      The scope of the lock.  Typically the name of
+     *                           the application requesting the lock or some
+     *                           other identifier used to group locks together.
+     * @param string $principal  Principal for which to check for locks
+     * @param int $type          Only return locks of the given type.
+     *                           Defaults to null, or all locks
+     *
+     * @return mixed  Array of locks with the ID as the key and the lock details
+     *                as the value. If there are no current locks this will
+     *                return an empty array. On failure a PEAR_Error object
+     *                will be returned.
+     */
+    public function getLocks($scope = null, $principal = null, $type = null)
+    {
+        if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return PEAR::raiseError(_("Internal database error.  Details have been logged for the administrator."));
+        }
+
+        $now = time();
+        $sql = 'SELECT lock_id, lock_owner, lock_scope, lock_principal, ' .
+               'lock_origin_timestamp, lock_update_timestamp, ' .
+               'lock_expiry_timestamp, lock_type FROM ' .
+               $this->_params['table'] . ' WHERE lock_expiry_timestamp >= ?';
+        $values = array($now);
+
+        // Check to see if we need to filter the results
+        if (!empty($principal)) {
+            $sql .= ' AND lock_principal = ?';
+            $values[] = $principal;
+        }
+        if(!empty($scope)) {
+            $sql .= ' AND lock_scope = ?';
+            $values[] = $scope;
+        }
+        if (!empty($type)) {
+            $sql .= ' AND lock_type = ?';
+            $values[] = $type;
+        }
+
+        Horde::logMessage('SQL Query by Horde_Lock_sql::getLocks(): ' . $sql,
+                          __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        $result = $this->_db->query($sql, $values);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        $locks = array();
+        $row = $result->fetchRow(DB_FETCHMODE_ASSOC);
+        while ($row && !is_a($row, 'PEAR_Error')) {
+            $locks[$row['lock_id']] = $row;
+            /* Advance to the new row in the result set. */
+            $row = $result->fetchRow(DB_FETCHMODE_ASSOC);
+        }
+        $result->free();
+        return $locks;
+    }
+
+    /**
+     * Extend the valid lifetime of a valid lock to now + $newtimeout.
+     *
+     * @param string $lockid  Lock ID to reset.  Must be a valid, non-expired
+     *                        lock.
+     * @param int $extend     Extend lock this many seconds from now.
+     *
+     * @return mixed          True on success; false on expired lock;
+     *                        PEAR_Error on failure.
+     */
+    public function resetLock($lockid, $extend)
+    {
+       if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return PEAR::raiseError(_("Internal database error.  Details have been logged for the administrator."));
+        }
+
+        $now = time();
+
+        $lockinfo = $this->getLockInfo($lockid);
+        if ($lockinfo === true) {
+            $expiry = $now + $extend;
+            $sql = 'UPDATE ' . $this->_params['table'] . ' SET ' .
+                   'lock_update_timestamp = ?, lock_expiry_timestamp = ? ' .
+                   'WHERE lock_id = ?';
+            $values = array($now, $expiry, $lockid);
+
+            Horde::logMessage('SQL Query by Horde_Lock_sql::resetLock(): ' .
+                              $sql, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+            $result = $this->_write_db->query($sql, $values);
+            if (is_a($result, 'PEAR_Error')) {
+                return $result;
+            }
+
+            Horde::logMessage(sprintf('Lock %s reset successfully.', $lockid), __FILE__, __LINE__, PEAR_LOG_INFO);
+            return true;
+        } elseif (is_a($lockinfo, 'PEAR_Error')) {
+            return $lockinfo;
+        } else {
+            // $lockinfo is false indicating the lock is no longer valid.
+            return false;
+        }
+    }
+
+    /**
+     * Sets a lock on the requested principal and returns the generated lock ID.
+     * NOTE: No security checks are done in the Horde_Lock API.  It is expected
+     * that the calling application has done all necessary security checks
+     * before requesting a lock be granted.
+     *
+     * @param string $requestor  User ID of the lock requestor.
+     * @param string $scope      The scope of the lock.  Typically the name of
+     *                           the application requesting the lock or some
+     *                           other identifier used to group locks together.
+     * @param string $principal  A principal on which a lock should be granted.
+     *                           The format can be any string but is suggested
+     *                           to be in URI form.
+     * @param int $lifetime      Time (in seconds) for which the lock will be
+     *                           considered valid.
+     * @param string type        One of HORDE_LOCK_TYPE_SHARED or
+     *                           HORDE_LOCK_TYPE_EXCLUSIVE.
+     *                           - An exclusive lock will be enforced strictly
+     *                             and must be interpreted to mean that the
+     *                             resource can not be modified.  Only one
+     *                             exclusive lock per principal is allowed.
+     *                           - A shared lock is one that notifies other
+     *                             potential lock requestors that the resource
+     *                             is in use.  This lock can be overridden
+     *                             (cleared or replaced with a subsequent
+     *                             call to setLock()) by other users.  Multiple
+     *                             users may request (and will be granted) a
+     *                             shared lock on a given principal.  All locks
+     *                             will be considered valid until they are
+     *                             cleared or expire.
+     *
+     * @return mixed   A string lock ID on success; PEAR_Error on failure.
+     */
+    public function setLock($requestor, $scope, $principal,
+                     $lifetime = 1, $type = HORDE_LOCK_TYPE_SHARED)
+    {
+       if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return PEAR::raiseError(_("Internal database error.  Details have been logged for the administrator."));
+        }
+
+        $oldlocks = $this->getLocks($scope, $principal,  HORDE_LOCK_TYPE_EXCLUSIVE);
+        if (is_a($oldlocks, 'PEAR_Error')) {
+            return $oldlocks;
+        }
+
+        if (count($oldlocks) != 0) {
+            // An exclusive lock exists.  Deny the new request.
+            Horde::logMessage(sprintf('Lock requested for %s denied due to existing exclusive lock.', $principal), __FILE__, __LINE__, PEAR_LOG_NOTICE);
+            return false;
+        }
+
+        $lockid = (string)new Horde_Support_Uuid();
+
+        $now = time();
+        $expiration = $now + $lifetime;
+        $sql = 'INSERT INTO ' . $this->_params['table'] . ' (lock_id, lock_owner, lock_scope, lock_principal, lock_origin_timestamp, lock_update_timestamp, lock_expiry_timestamp, lock_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?)';
+        $values = array($lockid, $requestor, $scope, $principal, $now, $now,
+                        $expiration, $type);
+
+        Horde::logMessage('SQL Query by Horde_Lock_sql::setLock(): ' . $sql,
+                          __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        $result = $this->_write_db->query($sql, $values);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        Horde::logMessage(sprintf('Lock %s set successfully by %s in scope %s on "%s"', $lockid, $requestor, $scope, $principal), __FILE__, __LINE__, PEAR_LOG_INFO);
+        return $lockid;
+    }
+
+    /**
+     * Removes a lock given the lock ID.
+     * NOTE: No security checks are done in the Horde_Lock API.  It is expected
+     * that the calling application has done all necessary security checks
+     * before requesting a lock be cleared.
+     *
+     * @param string $lockid  The lock ID as generated by a previous call
+     *                        to setLock()
+     *
+     * @return mixed   True on success; PEAR_Error on failure.
+     */
+    public function clearLock($lockid)
+    {
+        if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return PEAR::raiseError(_("Internal database error.  Details have been logged for the administrator."));
+        }
+
+        if (empty($lockid)) {
+            return PEAR::raiseError(_("Must supply a valid lock ID."));
+        }
+
+        // Since we're trying to clear the lock we don't care
+        // whether it is still valid or not.  Unconditionally
+        // remove it.
+        $sql = 'DELETE FROM ' . $this->_params['table'] . ' WHERE lock_id = ?';
+        $values = array($lockid);
+
+        Horde::logMessage('SQL Query by Horde_Lock_sql::clearLock(): ' . $sql,
+                          __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        $result = $this->_write_db->query($sql, $values);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        Horde::logMessage(sprintf('Lock %s cleared successfully.', $lockid), __FILE__, __LINE__, PEAR_LOG_INFO);
+        return true;
+    }
+
+    /**
+     * Opens a connection to the SQL server.
+     *
+     * @return boolean  True on success, a PEAR_Error object on failure.
+     */
+    private function _connect()
+    {
+        if ($this->_connected) {
+            return true;
+        }
+
+        $result = Horde_Util::assertDriverConfig($this->_params, array('phptype'),
+                                           'Lock SQL', array('driver' => 'lock'));
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $result;
+        }
+
+        require_once 'DB.php';
+        $this->_write_db = &DB::connect(
+            $this->_params,
+            array('persistent' => !empty($this->_params['persistent']),
+                  'ssl' => !empty($this->_params['ssl']))
+        );
+        if (is_a($this->_write_db, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return $this->_write_db;
+        }
+
+        // Set DB portability options.
+        $portability = DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS;
+        if ($this->_write_db->phptype) {
+            $portability |= DB_PORTABILITY_RTRIM;
+        }
+        $this->_write_db->setOption('portability', $portability);
+
+        /* Check if we need to set up the read DB connection
+         * seperately. */
+        if (!empty($this->_params['splitread'])) {
+            $params = array_merge($this->_params, $this->_params['read']);
+            $this->_db = &DB::connect(
+                $params,
+                array('persistent' => !empty($params['persistent']),
+                      'ssl' => !empty($params['ssl']))
+            );
+            if (is_a($this->_db, 'PEAR_Error')) {
+                Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+                return $this->_db;
+            }
+
+            // Set DB portability options.
+            $portability = DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS;
+            if ($this->_db->phptype) {
+                $portability |= DB_PORTABILITY_RTRIM;
+            }
+            $this->_db->setOption('portability', $portability);
+        } else {
+            /* Default to the same DB handle for read. */
+            $this->_db = $this->_write_db;
+        }
+
+        $this->_connected = true;
+        return true;
+    }
+
+    /**
+     * Do garbage collection needed for the driver.
+     *
+     * @access private
+     */
+    private function _doGC()
+    {
+        if (is_a(($result = $this->_connect()), 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return false;
+        }
+
+        $now = time();
+
+        $query = 'DELETE FROM ' . $this->_params['table'] . ' WHERE ' .
+                 'lock_expiry_timestamp < ? AND lock_expiry_timestamp != 0';
+        $values = array($now);
+
+        Horde::logMessage('SQL Query by Horde_Lock_sql::_doGC(): ' .  $sql,
+                          __FILE__, __LINE__, PEAR_LOG_DEBUG);
+        $result = $this->_write_db->query($query, $values);
+        if (is_a($result, 'PEAR_Error')) {
+            Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+            return false;
+        }
+
+        Horde::logMessage(sprintf('Lock garbage collection cleared %d locks.', $this->_write_db->affectedRows()), __FILE__, __LINE__, PEAR_LOG_INFO);
+        return true;
+    }
+
+}
diff --git a/framework/Lock/package.xml b/framework/Lock/package.xml
new file mode 100644 (file)
index 0000000..6933712
--- /dev/null
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+http://pear.php.net/dtd/tasks-1.0.xsd
+http://pear.php.net/dtd/package-2.0
+http://pear.php.net/dtd/package-2.0.xsd">
+ <name>Horde_Lock</name>
+ <channel>pear.horde.org</channel>
+ <summary>Horde Resource Locking System</summary>
+ <description>The Lock:: class provides the Horde resource locking system.
+ </description>
+ <lead>
+  <name>Ben Klang</name>
+  <user>bklang</user>
+  <email>ben@alkaloid.net</email>
+  <active>yes</active>
+ </lead>
+ <date>2008-05-05</date>
+ <time>22:54:46</time>
+ <version>
+  <release>0.0.1</release>
+  <api>0.0.1</api>
+ </version>
+ <stability>
+  <release>alpha</release>
+  <api>alpha</api>
+ </stability>
+ <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+ <notes>* Initial release
+ </notes>
+ <contents>
+ <dir name="/">
+   <dir name="lib">
+    <dir name="Horde">
+     <dir name="Lock">
+      <file name="Sql.php" role="php" />
+     </dir> <!-- /lib/Horde/Lock -->
+     <file name="Lock.php" role="php" />
+    </dir> <!-- /lib/Horde -->
+  </dir> <!-- /lib -->
+ </dir> <!-- / -->
+ </contents>
+ <dependencies>
+  <required>
+   <php>
+    <min>4.0.0</min>
+   </php>
+   <pearinstaller>
+    <min>1.4.0b1</min>
+   </pearinstaller>
+   <package>
+    <name>Horde_Framework</name>
+    <channel>pear.horde.org</channel>
+   </package>
+   <package>
+    <name>Support</name>
+    <channel>pear.horde.org</channel>
+   </package>
+   <package>
+    <name>Util</name>
+    <channel>pear.horde.org</channel>
+   </package>
+   <extension>
+    <name>gettext</name>
+   </extension>
+  </required>
+  <optional>
+   <package>
+    <name>Horde_Tree</name>
+    <channel>pear.horde.org</channel>
+   </package>
+  </optional>
+ </dependencies>
+ <phprelease>
+  <filelist>
+   <install name="lib/Horde/Lock/Sql.php" as="Horde/Lock/Sql.php" />
+   <install name="lib/Horde/Lock.php" as="Horde/Lock.php" />
+  </filelist>
+ </phprelease>
+</package>
index 910d2ba..ceb07e7 100644 (file)
@@ -743,9 +743,9 @@ class Horde_Rpc_Webdav extends Horde_Rpc
             return 500;
         }
 
-        $locktype = HORDE_LOCK_TYPE_SHARED;
+        $locktype = Horde_Lock::TYPE_SHARED;
         if ($params['scope'] == 'exclusive') {
-            $locktype = HORDE_LOCK_TYPE_EXCLUSIVE;
+            $locktype = Horde_Lock::TYPE_EXCLUSIVE;
         }
 
         $lockid = $locks->setLock(Horde_Auth::getAuth(), 'webdav', $params['path'],
@@ -833,7 +833,7 @@ class Horde_Rpc_Webdav extends Horde_Rpc
 
         // Format the array keys for HTTP_WebDAV_Server
         $ret = array();
-        if ($lock['lock_type'] == HORDE_LOCK_TYPE_EXCLUSIVE) {
+        if ($lock['lock_type'] == Horde_Lock::TYPE_EXCLUSIVE) {
             $ret['scope'] = 'exclusive';
         } else {
             $ret['scope'] = 'shared';