1<?php
2
3namespace SabreForRainLoop\DAV\Locks\Backend;
4
5use SabreForRainLoop\DAV\Locks\LockInfo;
6
7/**
8 * This Lock Backend stores all its data in the filesystem in separate file per
9 * node.
10 *
11 * This Lock Manager is now deprecated. It has a bug that allows parent
12 * collections to be deletes when children deeper in the tree are locked.
13 *
14 * This also means that using this backend means you will not pass the Neon
15 * Litmus test.
16 *
17 * You are recommended to use either the PDO or the File backend instead.
18 *
19 * @deprecated
20 * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
21 * @author Evert Pot (http://evertpot.com/)
22 * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
23 */
24class FS extends AbstractBackend {
25
26    /**
27     * The default data directory
28     *
29     * @var string
30     */
31    private $dataDir;
32
33    public function __construct($dataDir) {
34
35        $this->dataDir = $dataDir;
36
37    }
38
39    protected function getFileNameForUri($uri) {
40
41        return $this->dataDir . '/sabredav_' . md5($uri) . '.locks';
42
43    }
44
45
46    /**
47     * Returns a list of SabreForRainLoop\DAV\Locks\LockInfo objects
48     *
49     * This method should return all the locks for a particular uri, including
50     * locks that might be set on a parent uri.
51     *
52     * If returnChildLocks is set to true, this method should also look for
53     * any locks in the subtree of the uri for locks.
54     *
55     * @param string $uri
56     * @param bool $returnChildLocks
57     * @return array
58     */
59    public function getLocks($uri, $returnChildLocks) {
60
61        $lockList = array();
62        $currentPath = '';
63
64        foreach(explode('/',$uri) as $uriPart) {
65
66            // weird algorithm that can probably be improved, but we're traversing the path top down
67            if ($currentPath) $currentPath.='/';
68            $currentPath.=$uriPart;
69
70            $uriLocks = $this->getData($currentPath);
71
72            foreach($uriLocks as $uriLock) {
73
74                // Unless we're on the leaf of the uri-tree we should ignore locks with depth 0
75                if($uri==$currentPath || $uriLock->depth!=0) {
76                    $uriLock->uri = $currentPath;
77                    $lockList[] = $uriLock;
78                }
79
80            }
81
82        }
83
84        // Checking if we can remove any of these locks
85        foreach($lockList as $k=>$lock) {
86            if (time() > $lock->timeout + $lock->created) unset($lockList[$k]);
87        }
88        return $lockList;
89
90    }
91
92    /**
93     * Locks a uri
94     *
95     * @param string $uri
96     * @param LockInfo $lockInfo
97     * @return bool
98     */
99    public function lock($uri, LockInfo $lockInfo) {
100
101        // We're making the lock timeout 30 minutes
102        $lockInfo->timeout = 1800;
103        $lockInfo->created = time();
104
105        $locks = $this->getLocks($uri,false);
106        foreach($locks as $k=>$lock) {
107            if ($lock->token == $lockInfo->token) unset($locks[$k]);
108        }
109        $locks[] = $lockInfo;
110        $this->putData($uri,$locks);
111        return true;
112
113    }
114
115    /**
116     * Removes a lock from a uri
117     *
118     * @param string $uri
119     * @param LockInfo $lockInfo
120     * @return bool
121     */
122    public function unlock($uri, LockInfo $lockInfo) {
123
124        $locks = $this->getLocks($uri,false);
125        foreach($locks as $k=>$lock) {
126
127            if ($lock->token == $lockInfo->token) {
128
129                unset($locks[$k]);
130                $this->putData($uri,$locks);
131                return true;
132
133            }
134        }
135        return false;
136
137    }
138
139    /**
140     * Returns the stored data for a uri
141     *
142     * @param string $uri
143     * @return array
144     */
145    protected function getData($uri) {
146
147        $path = $this->getFilenameForUri($uri);
148        if (!file_exists($path)) return array();
149
150        // opening up the file, and creating a shared lock
151        $handle = fopen($path,'r');
152        flock($handle,LOCK_SH);
153        $data = '';
154
155        // Reading data until the eof
156        while(!feof($handle)) {
157            $data.=fread($handle,8192);
158        }
159
160        // We're all good
161        fclose($handle);
162
163        // Unserializing and checking if the resource file contains data for this file
164        $data = unserialize($data);
165        if (!$data) return array();
166        return $data;
167
168    }
169
170    /**
171     * Updates the lock information
172     *
173     * @param string $uri
174     * @param array $newData
175     * @return void
176     */
177    protected function putData($uri,array $newData) {
178
179        $path = $this->getFileNameForUri($uri);
180
181        // opening up the file, and creating a shared lock
182        $handle = fopen($path,'a+');
183        flock($handle,LOCK_EX);
184        ftruncate($handle,0);
185        rewind($handle);
186
187        fwrite($handle,serialize($newData));
188        fclose($handle);
189
190    }
191
192}
193
194