summaryrefslogtreecommitdiff
path: root/lib/Kolab
diff options
context:
space:
mode:
authorThomas Bruederli <bruederli@kolabsys.com>2013-11-11 11:10:28 (GMT)
committerThomas Bruederli <bruederli@kolabsys.com>2014-01-08 13:02:23 (GMT)
commitb060ac26c9c4a9ead08f8423ce3b8c7dc7cbc673 (patch)
tree2ba7105630e1602d3a177fec0f5758c77fdff1c6 /lib/Kolab
parente636dd31dca8701914dda591c2bab76bc7fcab84 (diff)
downloadiRony-b060ac26c9c4a9ead08f8423ce3b8c7dc7cbc673.tar.gz
Add adapter for a per-user file-based locking
Diffstat (limited to 'lib/Kolab')
-rw-r--r--lib/Kolab/DAV/Locks/File.php201
1 files changed, 201 insertions, 0 deletions
diff --git a/lib/Kolab/DAV/Locks/File.php b/lib/Kolab/DAV/Locks/File.php
new file mode 100644
index 0000000..0d8d17b
--- /dev/null
+++ b/lib/Kolab/DAV/Locks/File.php
@@ -0,0 +1,201 @@
+<?php
+
+/**
+ * File-based Lock manager for the Kolab WebDAV service.
+ *
+ * @author Thomas Bruederli <bruederli@kolabsys.com>
+ *
+ * Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace Kolab\DAV\Locks;
+
+use Kolab\DAV\Auth\HTTPBasic;
+use Sabre\DAV\Locks\LockInfo;
+use Sabre\DAV\Locks\Backend\AbstractBackend;
+
+/**
+ * The Lock manager that maintains a lock file per user in the local file system.
+ */
+class File extends AbstractBackend
+{
+ /**
+ * The storage file prefix
+ *
+ * @var string
+ */
+ private $basePath;
+
+ /**
+ * Constructor
+ *
+ * @param string $basePath base path to store lock files
+ */
+ public function __construct($basePath)
+ {
+ $this->basePath = $basePath;
+ }
+
+ /**
+ * Returns a list of Sabre\DAV\Locks\LockInfo objects
+ *
+ * This method should return all the locks for a particular uri, including
+ * locks that might be set on a parent uri.
+ *
+ * If returnChildLocks is set to true, this method should also look for
+ * any locks in the subtree of the uri for locks.
+ *
+ * @param string $uri
+ * @param bool $returnChildLocks
+ * @return array
+ */
+ public function getLocks($uri, $returnChildLocks)
+ {
+ console(__METHOD__, $uri, $returnChildLocks);
+
+ $newLocks = array();
+ $locks = $this->getData();
+
+ foreach ($locks as $lock) {
+ if ($lock->uri === $uri ||
+ // deep locks on parents
+ ($lock->depth != 0 && strpos($uri, $lock->uri . '/') === 0) ||
+ // locks on children
+ ($returnChildLocks && (strpos($lock->uri, $uri . '/') === 0)) ) {
+ $newLocks[] = $lock;
+ }
+ }
+
+ // Checking if we can remove any of these locks
+ foreach ($newLocks as $k => $lock) {
+ if (time() > $lock->timeout + $lock->created)
+ unset($newLocks[$k]);
+ }
+
+ return $newLocks;
+ }
+
+ /**
+ * Locks a uri
+ *
+ * @param string $uri
+ * @param LockInfo $lockInfo
+ * @return bool
+ */
+ public function lock($uri, LockInfo $lockInfo)
+ {
+ console(__METHOD__, $uri, $lockInfo);
+
+ // We're making the lock timeout 30 minutes
+ $lockInfo->timeout = 1800;
+ $lockInfo->created = time();
+ $lockInfo->uri = $uri;
+ $lockInfo->owner = trim($lockInfo->owner);
+
+ $locks = $this->getData();
+ $now = time();
+
+ foreach ($locks as $k => $lock) {
+ if (($lock->token == $lockInfo->token) || ($now > $lock->timeout + $lock->created)) {
+ unset($locks[$k]);
+ }
+ }
+ $locks[] = $lockInfo;
+ $this->putData($locks);
+
+ return true;
+ }
+
+ /**
+ * Removes a lock from a uri
+ *
+ * @param string $uri
+ * @param LockInfo $lockInfo
+ * @return bool
+ */
+ public function unlock($uri, LockInfo $lockInfo)
+ {
+ console(__METHOD__, $uri, $lockInfo);
+
+ $locks = $this->getData();
+ foreach ($locks as $k => $lock) {
+ if ($lock->token == $lockInfo->token) {
+ unset($locks[$k]);
+ $this->putData($locks);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Loads the lockdata from the filesystem.
+ *
+ * @return array
+ */
+ protected function getData()
+ {
+ $locksFile = $this->getLocksFile();
+ if (!file_exists($locksFile))
+ return array();
+
+ // opening up the file, and creating a shared lock
+ $handle = fopen($locksFile, 'r');
+ flock($handle, LOCK_SH);
+
+ // Reading data until the eof
+ $data = stream_get_contents($handle);
+
+ // We're all good
+ fclose($handle);
+
+ // Unserializing and checking if the resource file contains data for this file
+ $data = unserialize($data);
+ return $data ?: array();
+
+ }
+
+ /**
+ * Compose a full path to the lock file of the current user
+ */
+ protected function getLocksFile()
+ {
+ return $this->basePath . '_' . str_replace('@', '_', HTTPBasic::$current_user);
+ }
+
+ /**
+ * Saves the lockdata
+ *
+ * @param array $newData
+ * @return void
+ */
+ protected function putData(array $newData)
+ {
+ // opening up the file, and creating an exclusive lock
+ $handle = fopen($this->getLocksFile(), 'a+');
+ flock($handle, LOCK_EX);
+
+ // We can only truncate and rewind once the lock is acquired.
+ ftruncate($handle, 0);
+ rewind($handle);
+
+ fwrite($handle, serialize($newData));
+ fclose($handle);
+ }
+
+}
+