1 <?php
2 
3 namespace Sabre\DAV\Locks\Backend;
4 
5 use Sabre\DAV\Locks\LockInfo;
6 
7 /**
8  * This Locks backend stores all locking information in a single file.
9  *
10  * Note that this is not nearly as robust as a database. If you are considering
11  * using this backend, keep in mind that the PDO backend can work with SqLite,
12  * which is designed to be a good file-based database.
13  *
14  * It literally solves the problem this class solves as well, but much better.
15  *
16  * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/).
17  * @author Evert Pot (http://evertpot.com/)
18  * @license http://sabre.io/license/ Modified BSD License
19  */
20 class File extends AbstractBackend {
21 
22     /**
23      * The storage file
24      *
25      * @var string
26      */
27     private $locksFile;
28 
29     /**
30      * Constructor
31      *
32      * @param string $locksFile path to file
33      */
34     function __construct($locksFile) {
35 
36         $this->locksFile = $locksFile;
37 
38     }
39 
40     /**
41      * Returns a list of Sabre\DAV\Locks\LockInfo objects
42      *
43      * This method should return all the locks for a particular uri, including
44      * locks that might be set on a parent uri.
45      *
46      * If returnChildLocks is set to true, this method should also look for
47      * any locks in the subtree of the uri for locks.
48      *
49      * @param string $uri
50      * @param bool $returnChildLocks
51      * @return array
52      */
53     function getLocks($uri, $returnChildLocks) {
54 
55         $newLocks = [];
56 
57         $locks = $this->getData();
58 
59         foreach ($locks as $lock) {
60 
61             if ($lock->uri === $uri ||
62                 //deep locks on parents
63                 ($lock->depth != 0 && strpos($uri, $lock->uri . '/') === 0) ||
64 
65                 // locks on children
66                 ($returnChildLocks && (strpos($lock->uri, $uri . '/') === 0))) {
67 
68                 $newLocks[] = $lock;
69 
70             }
71 
72         }
73 
74         // Checking if we can remove any of these locks
75         foreach ($newLocks as $k => $lock) {
76             if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]);
77         }
78         return $newLocks;
79 
80     }
81 
82     /**
83      * Locks a uri
84      *
85      * @param string $uri
86      * @param LockInfo $lockInfo
87      * @return bool
88      */
89     function lock($uri, LockInfo $lockInfo) {
90 
91         // We're making the lock timeout 30 minutes
92         $lockInfo->timeout = 1800;
93         $lockInfo->created = time();
94         $lockInfo->uri = $uri;
95 
96         $locks = $this->getData();
97 
98         foreach ($locks as $k => $lock) {
99             if (
100                 ($lock->token == $lockInfo->token) ||
101                 (time() > $lock->timeout + $lock->created)
102             ) {
103                 unset($locks[$k]);
104             }
105         }
106         $locks[] = $lockInfo;
107         $this->putData($locks);
108         return true;
109 
110     }
111 
112     /**
113      * Removes a lock from a uri
114      *
115      * @param string $uri
116      * @param LockInfo $lockInfo
117      * @return bool
118      */
119     function unlock($uri, LockInfo $lockInfo) {
120 
121         $locks = $this->getData();
122         foreach ($locks as $k => $lock) {
123 
124             if ($lock->token == $lockInfo->token) {
125 
126                 unset($locks[$k]);
127                 $this->putData($locks);
128                 return true;
129 
130             }
131         }
132         return false;
133 
134     }
135 
136     /**
137      * Loads the lockdata from the filesystem.
138      *
139      * @return array
140      */
141     protected function getData() {
142 
143         if (!file_exists($this->locksFile)) return [];
144 
145         // opening up the file, and creating a shared lock
146         $handle = fopen($this->locksFile, 'r');
147         flock($handle, LOCK_SH);
148 
149         // Reading data until the eof
150         $data = stream_get_contents($handle);
151 
152         // We're all good
153         flock($handle, LOCK_UN);
154         fclose($handle);
155 
156         // Unserializing and checking if the resource file contains data for this file
157         $data = unserialize($data);
158         if (!$data) return [];
159         return $data;
160 
161     }
162 
163     /**
164      * Saves the lockdata
165      *
166      * @param array $newData
167      * @return void
168      */
169     protected function putData(array $newData) {
170 
171         // opening up the file, and creating an exclusive lock
172         $handle = fopen($this->locksFile, 'a+');
173         flock($handle, LOCK_EX);
174 
175         // We can only truncate and rewind once the lock is acquired.
176         ftruncate($handle, 0);
177         rewind($handle);
178 
179         fwrite($handle, serialize($newData));
180         flock($handle, LOCK_UN);
181         fclose($handle);
182 
183     }
184 
185 }
186