1<?php
2
3namespace Sabre\DAV\Locks\Backend;
4
5use Sabre\DAV\Locks\LockInfo;
6
7/**
8 * The Lock manager allows you to handle all file-locks centrally.
9 *
10 * This Lock Manager stores all its data in a database. You must pass a PDO
11 * connection object in the constructor.
12 *
13 * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
14 * @author Evert Pot (http://evertpot.com/)
15 * @license http://sabre.io/license/ Modified BSD License
16 */
17class PDO extends AbstractBackend {
18
19    /**
20     * The PDO tablename this backend uses.
21     *
22     * @var string
23     */
24    public $tableName = 'locks';
25
26    /**
27     * The PDO connection object
28     *
29     * @var pdo
30     */
31    protected $pdo;
32
33    /**
34     * Constructor
35     *
36     * @param \PDO $pdo
37     */
38    function __construct(\PDO $pdo) {
39
40        $this->pdo = $pdo;
41
42    }
43
44    /**
45     * Returns a list of Sabre\DAV\Locks\LockInfo objects
46     *
47     * This method should return all the locks for a particular uri, including
48     * locks that might be set on a parent uri.
49     *
50     * If returnChildLocks is set to true, this method should also look for
51     * any locks in the subtree of the uri for locks.
52     *
53     * @param string $uri
54     * @param bool $returnChildLocks
55     * @return array
56     */
57    function getLocks($uri, $returnChildLocks) {
58
59        // NOTE: the following 10 lines or so could be easily replaced by
60        // pure sql. MySQL's non-standard string concatenation prevents us
61        // from doing this though.
62        $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM ' . $this->tableName . ' WHERE (created > (? - timeout)) AND ((uri = ?)';
63        $params = [time(),$uri];
64
65        // We need to check locks for every part in the uri.
66        $uriParts = explode('/', $uri);
67
68        // We already covered the last part of the uri
69        array_pop($uriParts);
70
71        $currentPath = '';
72
73        foreach ($uriParts as $part) {
74
75            if ($currentPath) $currentPath .= '/';
76            $currentPath .= $part;
77
78            $query .= ' OR (depth!=0 AND uri = ?)';
79            $params[] = $currentPath;
80
81        }
82
83        if ($returnChildLocks) {
84
85            $query .= ' OR (uri LIKE ?)';
86            $params[] = $uri . '/%';
87
88        }
89        $query .= ')';
90
91        $stmt = $this->pdo->prepare($query);
92        $stmt->execute($params);
93        $result = $stmt->fetchAll();
94
95        $lockList = [];
96        foreach ($result as $row) {
97
98            $lockInfo = new LockInfo();
99            $lockInfo->owner = $row['owner'];
100            $lockInfo->token = $row['token'];
101            $lockInfo->timeout = $row['timeout'];
102            $lockInfo->created = $row['created'];
103            $lockInfo->scope = $row['scope'];
104            $lockInfo->depth = $row['depth'];
105            $lockInfo->uri = $row['uri'];
106            $lockList[] = $lockInfo;
107
108        }
109
110        return $lockList;
111
112    }
113
114    /**
115     * Locks a uri
116     *
117     * @param string $uri
118     * @param LockInfo $lockInfo
119     * @return bool
120     */
121    function lock($uri, LockInfo $lockInfo) {
122
123        // We're making the lock timeout 30 minutes
124        $lockInfo->timeout = 30 * 60;
125        $lockInfo->created = time();
126        $lockInfo->uri = $uri;
127
128        $locks = $this->getLocks($uri, false);
129        $exists = false;
130        foreach ($locks as $lock) {
131            if ($lock->token == $lockInfo->token) $exists = true;
132        }
133
134        if ($exists) {
135            $stmt = $this->pdo->prepare('UPDATE ' . $this->tableName . ' SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?');
136            $stmt->execute([
137                $lockInfo->owner,
138                $lockInfo->timeout,
139                $lockInfo->scope,
140                $lockInfo->depth,
141                $uri,
142                $lockInfo->created,
143                $lockInfo->token
144            ]);
145        } else {
146            $stmt = $this->pdo->prepare('INSERT INTO ' . $this->tableName . ' (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)');
147            $stmt->execute([
148                $lockInfo->owner,
149                $lockInfo->timeout,
150                $lockInfo->scope,
151                $lockInfo->depth,
152                $uri,
153                $lockInfo->created,
154                $lockInfo->token
155            ]);
156        }
157
158        return true;
159
160    }
161
162
163
164    /**
165     * Removes a lock from a uri
166     *
167     * @param string $uri
168     * @param LockInfo $lockInfo
169     * @return bool
170     */
171    function unlock($uri, LockInfo $lockInfo) {
172
173        $stmt = $this->pdo->prepare('DELETE FROM ' . $this->tableName . ' WHERE uri = ? AND token = ?');
174        $stmt->execute([$uri, $lockInfo->token]);
175
176        return $stmt->rowCount() === 1;
177
178    }
179
180}
181