Completed
Branch develop (fa72bb)
by
unknown
26:08
created
htdocs/includes/sabre/sabre/dav/lib/DAV/CorePlugin.php 2 patches
Indentation   +853 added lines, -853 removed lines patch added patch discarded remove patch
@@ -18,433 +18,433 @@  discard block
 block discarded – undo
18 18
  */
19 19
 class CorePlugin extends ServerPlugin
20 20
 {
21
-    /**
22
-     * Reference to server object.
23
-     *
24
-     * @var Server
25
-     */
26
-    protected $server;
27
-
28
-    /**
29
-     * Sets up the plugin.
30
-     */
31
-    public function initialize(Server $server)
32
-    {
33
-        $this->server = $server;
34
-        $server->on('method:GET', [$this, 'httpGet']);
35
-        $server->on('method:OPTIONS', [$this, 'httpOptions']);
36
-        $server->on('method:HEAD', [$this, 'httpHead']);
37
-        $server->on('method:DELETE', [$this, 'httpDelete']);
38
-        $server->on('method:PROPFIND', [$this, 'httpPropFind']);
39
-        $server->on('method:PROPPATCH', [$this, 'httpPropPatch']);
40
-        $server->on('method:PUT', [$this, 'httpPut']);
41
-        $server->on('method:MKCOL', [$this, 'httpMkcol']);
42
-        $server->on('method:MOVE', [$this, 'httpMove']);
43
-        $server->on('method:COPY', [$this, 'httpCopy']);
44
-        $server->on('method:REPORT', [$this, 'httpReport']);
45
-
46
-        $server->on('propPatch', [$this, 'propPatchProtectedPropertyCheck'], 90);
47
-        $server->on('propPatch', [$this, 'propPatchNodeUpdate'], 200);
48
-        $server->on('propFind', [$this, 'propFind']);
49
-        $server->on('propFind', [$this, 'propFindNode'], 120);
50
-        $server->on('propFind', [$this, 'propFindLate'], 200);
51
-
52
-        $server->on('exception', [$this, 'exception']);
53
-    }
54
-
55
-    /**
56
-     * Returns a plugin name.
57
-     *
58
-     * Using this name other plugins will be able to access other plugins
59
-     * using DAV\Server::getPlugin
60
-     *
61
-     * @return string
62
-     */
63
-    public function getPluginName()
64
-    {
65
-        return 'core';
66
-    }
67
-
68
-    /**
69
-     * This is the default implementation for the GET method.
70
-     *
71
-     * @return bool
72
-     */
73
-    public function httpGet(RequestInterface $request, ResponseInterface $response)
74
-    {
75
-        $path = $request->getPath();
76
-        $node = $this->server->tree->getNodeForPath($path);
77
-
78
-        if (!$node instanceof IFile) {
79
-            return;
80
-        }
81
-
82
-        if ('HEAD' === $request->getHeader('X-Sabre-Original-Method')) {
83
-            $body = '';
84
-        } else {
85
-            $body = $node->get();
86
-
87
-            // Converting string into stream, if needed.
88
-            if (is_string($body)) {
89
-                $stream = fopen('php://temp', 'r+');
90
-                fwrite($stream, $body);
91
-                rewind($stream);
92
-                $body = $stream;
93
-            }
94
-        }
95
-
96
-        /*
21
+	/**
22
+	 * Reference to server object.
23
+	 *
24
+	 * @var Server
25
+	 */
26
+	protected $server;
27
+
28
+	/**
29
+	 * Sets up the plugin.
30
+	 */
31
+	public function initialize(Server $server)
32
+	{
33
+		$this->server = $server;
34
+		$server->on('method:GET', [$this, 'httpGet']);
35
+		$server->on('method:OPTIONS', [$this, 'httpOptions']);
36
+		$server->on('method:HEAD', [$this, 'httpHead']);
37
+		$server->on('method:DELETE', [$this, 'httpDelete']);
38
+		$server->on('method:PROPFIND', [$this, 'httpPropFind']);
39
+		$server->on('method:PROPPATCH', [$this, 'httpPropPatch']);
40
+		$server->on('method:PUT', [$this, 'httpPut']);
41
+		$server->on('method:MKCOL', [$this, 'httpMkcol']);
42
+		$server->on('method:MOVE', [$this, 'httpMove']);
43
+		$server->on('method:COPY', [$this, 'httpCopy']);
44
+		$server->on('method:REPORT', [$this, 'httpReport']);
45
+
46
+		$server->on('propPatch', [$this, 'propPatchProtectedPropertyCheck'], 90);
47
+		$server->on('propPatch', [$this, 'propPatchNodeUpdate'], 200);
48
+		$server->on('propFind', [$this, 'propFind']);
49
+		$server->on('propFind', [$this, 'propFindNode'], 120);
50
+		$server->on('propFind', [$this, 'propFindLate'], 200);
51
+
52
+		$server->on('exception', [$this, 'exception']);
53
+	}
54
+
55
+	/**
56
+	 * Returns a plugin name.
57
+	 *
58
+	 * Using this name other plugins will be able to access other plugins
59
+	 * using DAV\Server::getPlugin
60
+	 *
61
+	 * @return string
62
+	 */
63
+	public function getPluginName()
64
+	{
65
+		return 'core';
66
+	}
67
+
68
+	/**
69
+	 * This is the default implementation for the GET method.
70
+	 *
71
+	 * @return bool
72
+	 */
73
+	public function httpGet(RequestInterface $request, ResponseInterface $response)
74
+	{
75
+		$path = $request->getPath();
76
+		$node = $this->server->tree->getNodeForPath($path);
77
+
78
+		if (!$node instanceof IFile) {
79
+			return;
80
+		}
81
+
82
+		if ('HEAD' === $request->getHeader('X-Sabre-Original-Method')) {
83
+			$body = '';
84
+		} else {
85
+			$body = $node->get();
86
+
87
+			// Converting string into stream, if needed.
88
+			if (is_string($body)) {
89
+				$stream = fopen('php://temp', 'r+');
90
+				fwrite($stream, $body);
91
+				rewind($stream);
92
+				$body = $stream;
93
+			}
94
+		}
95
+
96
+		/*
97 97
          * TODO: getetag, getlastmodified, getsize should also be used using
98 98
          * this method
99 99
          */
100
-        $httpHeaders = $this->server->getHTTPHeaders($path);
100
+		$httpHeaders = $this->server->getHTTPHeaders($path);
101 101
 
102
-        /* ContentType needs to get a default, because many webservers will otherwise
102
+		/* ContentType needs to get a default, because many webservers will otherwise
103 103
          * default to text/html, and we don't want this for security reasons.
104 104
          */
105
-        if (!isset($httpHeaders['Content-Type'])) {
106
-            $httpHeaders['Content-Type'] = 'application/octet-stream';
107
-        }
108
-
109
-        if (isset($httpHeaders['Content-Length'])) {
110
-            $nodeSize = $httpHeaders['Content-Length'];
111
-
112
-            // Need to unset Content-Length, because we'll handle that during figuring out the range
113
-            unset($httpHeaders['Content-Length']);
114
-        } else {
115
-            $nodeSize = null;
116
-        }
117
-
118
-        $response->addHeaders($httpHeaders);
119
-
120
-        $range = $this->server->getHTTPRange();
121
-        $ifRange = $request->getHeader('If-Range');
122
-        $ignoreRangeHeader = false;
123
-
124
-        // If ifRange is set, and range is specified, we first need to check
125
-        // the precondition.
126
-        if ($nodeSize && $range && $ifRange) {
127
-            // if IfRange is parsable as a date we'll treat it as a DateTime
128
-            // otherwise, we must treat it as an etag.
129
-            try {
130
-                $ifRangeDate = new \DateTime($ifRange);
131
-
132
-                // It's a date. We must check if the entity is modified since
133
-                // the specified date.
134
-                if (!isset($httpHeaders['Last-Modified'])) {
135
-                    $ignoreRangeHeader = true;
136
-                } else {
137
-                    $modified = new \DateTime($httpHeaders['Last-Modified']);
138
-                    if ($modified > $ifRangeDate) {
139
-                        $ignoreRangeHeader = true;
140
-                    }
141
-                }
142
-            } catch (\Exception $e) {
143
-                // It's an entity. We can do a simple comparison.
144
-                if (!isset($httpHeaders['ETag'])) {
145
-                    $ignoreRangeHeader = true;
146
-                } elseif ($httpHeaders['ETag'] !== $ifRange) {
147
-                    $ignoreRangeHeader = true;
148
-                }
149
-            }
150
-        }
151
-
152
-        // We're only going to support HTTP ranges if the backend provided a filesize
153
-        if (!$ignoreRangeHeader && $nodeSize && $range) {
154
-            // Determining the exact byte offsets
155
-            if (!is_null($range[0])) {
156
-                $start = $range[0];
157
-                $end = $range[1] ? $range[1] : $nodeSize - 1;
158
-                if ($start >= $nodeSize) {
159
-                    throw new Exception\RequestedRangeNotSatisfiable('The start offset ('.$range[0].') exceeded the size of the entity ('.$nodeSize.')');
160
-                }
161
-                if ($end < $start) {
162
-                    throw new Exception\RequestedRangeNotSatisfiable('The end offset ('.$range[1].') is lower than the start offset ('.$range[0].')');
163
-                }
164
-                if ($end >= $nodeSize) {
165
-                    $end = $nodeSize - 1;
166
-                }
167
-            } else {
168
-                $start = $nodeSize - $range[1];
169
-                $end = $nodeSize - 1;
170
-
171
-                if ($start < 0) {
172
-                    $start = 0;
173
-                }
174
-            }
175
-
176
-            // Streams may advertise themselves as seekable, but still not
177
-            // actually allow fseek.  We'll manually go forward in the stream
178
-            // if fseek failed.
179
-            if (!stream_get_meta_data($body)['seekable'] || -1 === fseek($body, $start, SEEK_SET)) {
180
-                $consumeBlock = 8192;
181
-                for ($consumed = 0; $start - $consumed > 0;) {
182
-                    if (feof($body)) {
183
-                        throw new Exception\RequestedRangeNotSatisfiable('The start offset ('.$start.') exceeded the size of the entity ('.$consumed.')');
184
-                    }
185
-                    $consumed += strlen(fread($body, min($start - $consumed, $consumeBlock)));
186
-                }
187
-            }
188
-
189
-            $response->setHeader('Content-Length', $end - $start + 1);
190
-            $response->setHeader('Content-Range', 'bytes '.$start.'-'.$end.'/'.$nodeSize);
191
-            $response->setStatus(206);
192
-            $response->setBody($body);
193
-        } else {
194
-            if ($nodeSize) {
195
-                $response->setHeader('Content-Length', $nodeSize);
196
-            }
197
-            $response->setStatus(200);
198
-            $response->setBody($body);
199
-        }
200
-        // Sending back false will interrupt the event chain and tell the server
201
-        // we've handled this method.
202
-        return false;
203
-    }
204
-
205
-    /**
206
-     * HTTP OPTIONS.
207
-     *
208
-     * @return bool
209
-     */
210
-    public function httpOptions(RequestInterface $request, ResponseInterface $response)
211
-    {
212
-        $methods = $this->server->getAllowedMethods($request->getPath());
213
-
214
-        $response->setHeader('Allow', strtoupper(implode(', ', $methods)));
215
-        $features = ['1', '3', 'extended-mkcol'];
216
-
217
-        foreach ($this->server->getPlugins() as $plugin) {
218
-            $features = array_merge($features, $plugin->getFeatures());
219
-        }
220
-
221
-        $response->setHeader('DAV', implode(', ', $features));
222
-        $response->setHeader('MS-Author-Via', 'DAV');
223
-        $response->setHeader('Accept-Ranges', 'bytes');
224
-        $response->setHeader('Content-Length', '0');
225
-        $response->setStatus(200);
226
-
227
-        // Sending back false will interrupt the event chain and tell the server
228
-        // we've handled this method.
229
-        return false;
230
-    }
231
-
232
-    /**
233
-     * HTTP HEAD.
234
-     *
235
-     * This method is normally used to take a peak at a url, and only get the
236
-     * HTTP response headers, without the body. This is used by clients to
237
-     * determine if a remote file was changed, so they can use a local cached
238
-     * version, instead of downloading it again
239
-     *
240
-     * @return bool
241
-     */
242
-    public function httpHead(RequestInterface $request, ResponseInterface $response)
243
-    {
244
-        // This is implemented by changing the HEAD request to a GET request,
245
-        // and telling the request handler that is doesn't need to create the body.
246
-        $subRequest = clone $request;
247
-        $subRequest->setMethod('GET');
248
-        $subRequest->setHeader('X-Sabre-Original-Method', 'HEAD');
249
-
250
-        try {
251
-            $this->server->invokeMethod($subRequest, $response, false);
252
-        } catch (Exception\NotImplemented $e) {
253
-            // Some clients may do HEAD requests on collections, however, GET
254
-            // requests and HEAD requests _may_ not be defined on a collection,
255
-            // which would trigger a 501.
256
-            // This breaks some clients though, so we're transforming these
257
-            // 501s into 200s.
258
-            $response->setStatus(200);
259
-            $response->setBody('');
260
-            $response->setHeader('Content-Type', 'text/plain');
261
-            $response->setHeader('X-Sabre-Real-Status', $e->getHTTPCode());
262
-        }
263
-
264
-        // Sending back false will interrupt the event chain and tell the server
265
-        // we've handled this method.
266
-        return false;
267
-    }
268
-
269
-    /**
270
-     * HTTP Delete.
271
-     *
272
-     * The HTTP delete method, deletes a given uri
273
-     */
274
-    public function httpDelete(RequestInterface $request, ResponseInterface $response)
275
-    {
276
-        $path = $request->getPath();
277
-
278
-        if (!$this->server->emit('beforeUnbind', [$path])) {
279
-            return false;
280
-        }
281
-        $this->server->tree->delete($path);
282
-        $this->server->emit('afterUnbind', [$path]);
283
-
284
-        $response->setStatus(204);
285
-        $response->setHeader('Content-Length', '0');
286
-
287
-        // Sending back false will interrupt the event chain and tell the server
288
-        // we've handled this method.
289
-        return false;
290
-    }
291
-
292
-    /**
293
-     * WebDAV PROPFIND.
294
-     *
295
-     * This WebDAV method requests information about an uri resource, or a list of resources
296
-     * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value
297
-     * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory)
298
-     *
299
-     * The request body contains an XML data structure that has a list of properties the client understands
300
-     * The response body is also an xml document, containing information about every uri resource and the requested properties
301
-     *
302
-     * It has to return a HTTP 207 Multi-status status code
303
-     */
304
-    public function httpPropFind(RequestInterface $request, ResponseInterface $response)
305
-    {
306
-        $path = $request->getPath();
307
-
308
-        $requestBody = $request->getBodyAsString();
309
-        if (strlen($requestBody)) {
310
-            try {
311
-                $propFindXml = $this->server->xml->expect('{DAV:}propfind', $requestBody);
312
-            } catch (ParseException $e) {
313
-                throw new BadRequest($e->getMessage(), 0, $e);
314
-            }
315
-        } else {
316
-            $propFindXml = new Xml\Request\PropFind();
317
-            $propFindXml->allProp = true;
318
-            $propFindXml->properties = [];
319
-        }
320
-
321
-        $depth = $this->server->getHTTPDepth(1);
322
-        // The only two options for the depth of a propfind is 0 or 1 - as long as depth infinity is not enabled
323
-        if (!$this->server->enablePropfindDepthInfinity && 0 != $depth) {
324
-            $depth = 1;
325
-        }
326
-
327
-        $newProperties = $this->server->getPropertiesIteratorForPath($path, $propFindXml->properties, $depth);
328
-
329
-        // This is a multi-status response
330
-        $response->setStatus(207);
331
-        $response->setHeader('Content-Type', 'application/xml; charset=utf-8');
332
-        $response->setHeader('Vary', 'Brief,Prefer');
333
-
334
-        // Normally this header is only needed for OPTIONS responses, however..
335
-        // iCal seems to also depend on these being set for PROPFIND. Since
336
-        // this is not harmful, we'll add it.
337
-        $features = ['1', '3', 'extended-mkcol'];
338
-        foreach ($this->server->getPlugins() as $plugin) {
339
-            $features = array_merge($features, $plugin->getFeatures());
340
-        }
341
-        $response->setHeader('DAV', implode(', ', $features));
342
-
343
-        $prefer = $this->server->getHTTPPrefer();
344
-        $minimal = 'minimal' === $prefer['return'];
345
-
346
-        $data = $this->server->generateMultiStatus($newProperties, $minimal);
347
-        $response->setBody($data);
348
-
349
-        // Sending back false will interrupt the event chain and tell the server
350
-        // we've handled this method.
351
-        return false;
352
-    }
353
-
354
-    /**
355
-     * WebDAV PROPPATCH.
356
-     *
357
-     * This method is called to update properties on a Node. The request is an XML body with all the mutations.
358
-     * In this XML body it is specified which properties should be set/updated and/or deleted
359
-     *
360
-     * @return bool
361
-     */
362
-    public function httpPropPatch(RequestInterface $request, ResponseInterface $response)
363
-    {
364
-        $path = $request->getPath();
365
-
366
-        try {
367
-            $propPatch = $this->server->xml->expect('{DAV:}propertyupdate', $request->getBody());
368
-        } catch (ParseException $e) {
369
-            throw new BadRequest($e->getMessage(), 0, $e);
370
-        }
371
-        $newProperties = $propPatch->properties;
372
-
373
-        $result = $this->server->updateProperties($path, $newProperties);
374
-
375
-        $prefer = $this->server->getHTTPPrefer();
376
-        $response->setHeader('Vary', 'Brief,Prefer');
377
-
378
-        if ('minimal' === $prefer['return']) {
379
-            // If return-minimal is specified, we only have to check if the
380
-            // request was successful, and don't need to return the
381
-            // multi-status.
382
-            $ok = true;
383
-            foreach ($result as $prop => $code) {
384
-                if ((int) $code > 299) {
385
-                    $ok = false;
386
-                }
387
-            }
388
-
389
-            if ($ok) {
390
-                $response->setStatus(204);
391
-
392
-                return false;
393
-            }
394
-        }
395
-
396
-        $response->setStatus(207);
397
-        $response->setHeader('Content-Type', 'application/xml; charset=utf-8');
398
-
399
-        // Reorganizing the result for generateMultiStatus
400
-        $multiStatus = [];
401
-        foreach ($result as $propertyName => $code) {
402
-            if (isset($multiStatus[$code])) {
403
-                $multiStatus[$code][$propertyName] = null;
404
-            } else {
405
-                $multiStatus[$code] = [$propertyName => null];
406
-            }
407
-        }
408
-        $multiStatus['href'] = $path;
409
-
410
-        $response->setBody(
411
-            $this->server->generateMultiStatus([$multiStatus])
412
-        );
413
-
414
-        // Sending back false will interrupt the event chain and tell the server
415
-        // we've handled this method.
416
-        return false;
417
-    }
418
-
419
-    /**
420
-     * HTTP PUT method.
421
-     *
422
-     * This HTTP method updates a file, or creates a new one.
423
-     *
424
-     * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content
425
-     *
426
-     * @return bool
427
-     */
428
-    public function httpPut(RequestInterface $request, ResponseInterface $response)
429
-    {
430
-        $body = $request->getBodyAsStream();
431
-        $path = $request->getPath();
432
-
433
-        // Intercepting Content-Range
434
-        if ($request->getHeader('Content-Range')) {
435
-            /*
105
+		if (!isset($httpHeaders['Content-Type'])) {
106
+			$httpHeaders['Content-Type'] = 'application/octet-stream';
107
+		}
108
+
109
+		if (isset($httpHeaders['Content-Length'])) {
110
+			$nodeSize = $httpHeaders['Content-Length'];
111
+
112
+			// Need to unset Content-Length, because we'll handle that during figuring out the range
113
+			unset($httpHeaders['Content-Length']);
114
+		} else {
115
+			$nodeSize = null;
116
+		}
117
+
118
+		$response->addHeaders($httpHeaders);
119
+
120
+		$range = $this->server->getHTTPRange();
121
+		$ifRange = $request->getHeader('If-Range');
122
+		$ignoreRangeHeader = false;
123
+
124
+		// If ifRange is set, and range is specified, we first need to check
125
+		// the precondition.
126
+		if ($nodeSize && $range && $ifRange) {
127
+			// if IfRange is parsable as a date we'll treat it as a DateTime
128
+			// otherwise, we must treat it as an etag.
129
+			try {
130
+				$ifRangeDate = new \DateTime($ifRange);
131
+
132
+				// It's a date. We must check if the entity is modified since
133
+				// the specified date.
134
+				if (!isset($httpHeaders['Last-Modified'])) {
135
+					$ignoreRangeHeader = true;
136
+				} else {
137
+					$modified = new \DateTime($httpHeaders['Last-Modified']);
138
+					if ($modified > $ifRangeDate) {
139
+						$ignoreRangeHeader = true;
140
+					}
141
+				}
142
+			} catch (\Exception $e) {
143
+				// It's an entity. We can do a simple comparison.
144
+				if (!isset($httpHeaders['ETag'])) {
145
+					$ignoreRangeHeader = true;
146
+				} elseif ($httpHeaders['ETag'] !== $ifRange) {
147
+					$ignoreRangeHeader = true;
148
+				}
149
+			}
150
+		}
151
+
152
+		// We're only going to support HTTP ranges if the backend provided a filesize
153
+		if (!$ignoreRangeHeader && $nodeSize && $range) {
154
+			// Determining the exact byte offsets
155
+			if (!is_null($range[0])) {
156
+				$start = $range[0];
157
+				$end = $range[1] ? $range[1] : $nodeSize - 1;
158
+				if ($start >= $nodeSize) {
159
+					throw new Exception\RequestedRangeNotSatisfiable('The start offset ('.$range[0].') exceeded the size of the entity ('.$nodeSize.')');
160
+				}
161
+				if ($end < $start) {
162
+					throw new Exception\RequestedRangeNotSatisfiable('The end offset ('.$range[1].') is lower than the start offset ('.$range[0].')');
163
+				}
164
+				if ($end >= $nodeSize) {
165
+					$end = $nodeSize - 1;
166
+				}
167
+			} else {
168
+				$start = $nodeSize - $range[1];
169
+				$end = $nodeSize - 1;
170
+
171
+				if ($start < 0) {
172
+					$start = 0;
173
+				}
174
+			}
175
+
176
+			// Streams may advertise themselves as seekable, but still not
177
+			// actually allow fseek.  We'll manually go forward in the stream
178
+			// if fseek failed.
179
+			if (!stream_get_meta_data($body)['seekable'] || -1 === fseek($body, $start, SEEK_SET)) {
180
+				$consumeBlock = 8192;
181
+				for ($consumed = 0; $start - $consumed > 0;) {
182
+					if (feof($body)) {
183
+						throw new Exception\RequestedRangeNotSatisfiable('The start offset ('.$start.') exceeded the size of the entity ('.$consumed.')');
184
+					}
185
+					$consumed += strlen(fread($body, min($start - $consumed, $consumeBlock)));
186
+				}
187
+			}
188
+
189
+			$response->setHeader('Content-Length', $end - $start + 1);
190
+			$response->setHeader('Content-Range', 'bytes '.$start.'-'.$end.'/'.$nodeSize);
191
+			$response->setStatus(206);
192
+			$response->setBody($body);
193
+		} else {
194
+			if ($nodeSize) {
195
+				$response->setHeader('Content-Length', $nodeSize);
196
+			}
197
+			$response->setStatus(200);
198
+			$response->setBody($body);
199
+		}
200
+		// Sending back false will interrupt the event chain and tell the server
201
+		// we've handled this method.
202
+		return false;
203
+	}
204
+
205
+	/**
206
+	 * HTTP OPTIONS.
207
+	 *
208
+	 * @return bool
209
+	 */
210
+	public function httpOptions(RequestInterface $request, ResponseInterface $response)
211
+	{
212
+		$methods = $this->server->getAllowedMethods($request->getPath());
213
+
214
+		$response->setHeader('Allow', strtoupper(implode(', ', $methods)));
215
+		$features = ['1', '3', 'extended-mkcol'];
216
+
217
+		foreach ($this->server->getPlugins() as $plugin) {
218
+			$features = array_merge($features, $plugin->getFeatures());
219
+		}
220
+
221
+		$response->setHeader('DAV', implode(', ', $features));
222
+		$response->setHeader('MS-Author-Via', 'DAV');
223
+		$response->setHeader('Accept-Ranges', 'bytes');
224
+		$response->setHeader('Content-Length', '0');
225
+		$response->setStatus(200);
226
+
227
+		// Sending back false will interrupt the event chain and tell the server
228
+		// we've handled this method.
229
+		return false;
230
+	}
231
+
232
+	/**
233
+	 * HTTP HEAD.
234
+	 *
235
+	 * This method is normally used to take a peak at a url, and only get the
236
+	 * HTTP response headers, without the body. This is used by clients to
237
+	 * determine if a remote file was changed, so they can use a local cached
238
+	 * version, instead of downloading it again
239
+	 *
240
+	 * @return bool
241
+	 */
242
+	public function httpHead(RequestInterface $request, ResponseInterface $response)
243
+	{
244
+		// This is implemented by changing the HEAD request to a GET request,
245
+		// and telling the request handler that is doesn't need to create the body.
246
+		$subRequest = clone $request;
247
+		$subRequest->setMethod('GET');
248
+		$subRequest->setHeader('X-Sabre-Original-Method', 'HEAD');
249
+
250
+		try {
251
+			$this->server->invokeMethod($subRequest, $response, false);
252
+		} catch (Exception\NotImplemented $e) {
253
+			// Some clients may do HEAD requests on collections, however, GET
254
+			// requests and HEAD requests _may_ not be defined on a collection,
255
+			// which would trigger a 501.
256
+			// This breaks some clients though, so we're transforming these
257
+			// 501s into 200s.
258
+			$response->setStatus(200);
259
+			$response->setBody('');
260
+			$response->setHeader('Content-Type', 'text/plain');
261
+			$response->setHeader('X-Sabre-Real-Status', $e->getHTTPCode());
262
+		}
263
+
264
+		// Sending back false will interrupt the event chain and tell the server
265
+		// we've handled this method.
266
+		return false;
267
+	}
268
+
269
+	/**
270
+	 * HTTP Delete.
271
+	 *
272
+	 * The HTTP delete method, deletes a given uri
273
+	 */
274
+	public function httpDelete(RequestInterface $request, ResponseInterface $response)
275
+	{
276
+		$path = $request->getPath();
277
+
278
+		if (!$this->server->emit('beforeUnbind', [$path])) {
279
+			return false;
280
+		}
281
+		$this->server->tree->delete($path);
282
+		$this->server->emit('afterUnbind', [$path]);
283
+
284
+		$response->setStatus(204);
285
+		$response->setHeader('Content-Length', '0');
286
+
287
+		// Sending back false will interrupt the event chain and tell the server
288
+		// we've handled this method.
289
+		return false;
290
+	}
291
+
292
+	/**
293
+	 * WebDAV PROPFIND.
294
+	 *
295
+	 * This WebDAV method requests information about an uri resource, or a list of resources
296
+	 * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value
297
+	 * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory)
298
+	 *
299
+	 * The request body contains an XML data structure that has a list of properties the client understands
300
+	 * The response body is also an xml document, containing information about every uri resource and the requested properties
301
+	 *
302
+	 * It has to return a HTTP 207 Multi-status status code
303
+	 */
304
+	public function httpPropFind(RequestInterface $request, ResponseInterface $response)
305
+	{
306
+		$path = $request->getPath();
307
+
308
+		$requestBody = $request->getBodyAsString();
309
+		if (strlen($requestBody)) {
310
+			try {
311
+				$propFindXml = $this->server->xml->expect('{DAV:}propfind', $requestBody);
312
+			} catch (ParseException $e) {
313
+				throw new BadRequest($e->getMessage(), 0, $e);
314
+			}
315
+		} else {
316
+			$propFindXml = new Xml\Request\PropFind();
317
+			$propFindXml->allProp = true;
318
+			$propFindXml->properties = [];
319
+		}
320
+
321
+		$depth = $this->server->getHTTPDepth(1);
322
+		// The only two options for the depth of a propfind is 0 or 1 - as long as depth infinity is not enabled
323
+		if (!$this->server->enablePropfindDepthInfinity && 0 != $depth) {
324
+			$depth = 1;
325
+		}
326
+
327
+		$newProperties = $this->server->getPropertiesIteratorForPath($path, $propFindXml->properties, $depth);
328
+
329
+		// This is a multi-status response
330
+		$response->setStatus(207);
331
+		$response->setHeader('Content-Type', 'application/xml; charset=utf-8');
332
+		$response->setHeader('Vary', 'Brief,Prefer');
333
+
334
+		// Normally this header is only needed for OPTIONS responses, however..
335
+		// iCal seems to also depend on these being set for PROPFIND. Since
336
+		// this is not harmful, we'll add it.
337
+		$features = ['1', '3', 'extended-mkcol'];
338
+		foreach ($this->server->getPlugins() as $plugin) {
339
+			$features = array_merge($features, $plugin->getFeatures());
340
+		}
341
+		$response->setHeader('DAV', implode(', ', $features));
342
+
343
+		$prefer = $this->server->getHTTPPrefer();
344
+		$minimal = 'minimal' === $prefer['return'];
345
+
346
+		$data = $this->server->generateMultiStatus($newProperties, $minimal);
347
+		$response->setBody($data);
348
+
349
+		// Sending back false will interrupt the event chain and tell the server
350
+		// we've handled this method.
351
+		return false;
352
+	}
353
+
354
+	/**
355
+	 * WebDAV PROPPATCH.
356
+	 *
357
+	 * This method is called to update properties on a Node. The request is an XML body with all the mutations.
358
+	 * In this XML body it is specified which properties should be set/updated and/or deleted
359
+	 *
360
+	 * @return bool
361
+	 */
362
+	public function httpPropPatch(RequestInterface $request, ResponseInterface $response)
363
+	{
364
+		$path = $request->getPath();
365
+
366
+		try {
367
+			$propPatch = $this->server->xml->expect('{DAV:}propertyupdate', $request->getBody());
368
+		} catch (ParseException $e) {
369
+			throw new BadRequest($e->getMessage(), 0, $e);
370
+		}
371
+		$newProperties = $propPatch->properties;
372
+
373
+		$result = $this->server->updateProperties($path, $newProperties);
374
+
375
+		$prefer = $this->server->getHTTPPrefer();
376
+		$response->setHeader('Vary', 'Brief,Prefer');
377
+
378
+		if ('minimal' === $prefer['return']) {
379
+			// If return-minimal is specified, we only have to check if the
380
+			// request was successful, and don't need to return the
381
+			// multi-status.
382
+			$ok = true;
383
+			foreach ($result as $prop => $code) {
384
+				if ((int) $code > 299) {
385
+					$ok = false;
386
+				}
387
+			}
388
+
389
+			if ($ok) {
390
+				$response->setStatus(204);
391
+
392
+				return false;
393
+			}
394
+		}
395
+
396
+		$response->setStatus(207);
397
+		$response->setHeader('Content-Type', 'application/xml; charset=utf-8');
398
+
399
+		// Reorganizing the result for generateMultiStatus
400
+		$multiStatus = [];
401
+		foreach ($result as $propertyName => $code) {
402
+			if (isset($multiStatus[$code])) {
403
+				$multiStatus[$code][$propertyName] = null;
404
+			} else {
405
+				$multiStatus[$code] = [$propertyName => null];
406
+			}
407
+		}
408
+		$multiStatus['href'] = $path;
409
+
410
+		$response->setBody(
411
+			$this->server->generateMultiStatus([$multiStatus])
412
+		);
413
+
414
+		// Sending back false will interrupt the event chain and tell the server
415
+		// we've handled this method.
416
+		return false;
417
+	}
418
+
419
+	/**
420
+	 * HTTP PUT method.
421
+	 *
422
+	 * This HTTP method updates a file, or creates a new one.
423
+	 *
424
+	 * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content
425
+	 *
426
+	 * @return bool
427
+	 */
428
+	public function httpPut(RequestInterface $request, ResponseInterface $response)
429
+	{
430
+		$body = $request->getBodyAsStream();
431
+		$path = $request->getPath();
432
+
433
+		// Intercepting Content-Range
434
+		if ($request->getHeader('Content-Range')) {
435
+			/*
436 436
                An origin server that allows PUT on a given target resource MUST send
437 437
                a 400 (Bad Request) response to a PUT request that contains a
438 438
                Content-Range header field.
439 439
 
440 440
                Reference: http://tools.ietf.org/html/rfc7231#section-4.3.4
441 441
             */
442
-            throw new Exception\BadRequest('Content-Range on PUT requests are forbidden.');
443
-        }
442
+			throw new Exception\BadRequest('Content-Range on PUT requests are forbidden.');
443
+		}
444 444
 
445
-        // Intercepting the Finder problem
446
-        if (($expected = $request->getHeader('X-Expected-Entity-Length')) && $expected > 0) {
447
-            /*
445
+		// Intercepting the Finder problem
446
+		if (($expected = $request->getHeader('X-Expected-Entity-Length')) && $expected > 0) {
447
+			/*
448 448
             Many webservers will not cooperate well with Finder PUT requests,
449 449
             because it uses 'Chunked' transfer encoding for the request body.
450 450
 
@@ -465,443 +465,443 @@  discard block
 block discarded – undo
465 465
             protect the end-user.
466 466
             */
467 467
 
468
-            // Only reading first byte
469
-            $firstByte = fread($body, 1);
470
-            if (1 !== strlen($firstByte)) {
471
-                throw new Exception\Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.');
472
-            }
473
-
474
-            // The body needs to stay intact, so we copy everything to a
475
-            // temporary stream.
476
-
477
-            $newBody = fopen('php://temp', 'r+');
478
-            fwrite($newBody, $firstByte);
479
-            stream_copy_to_stream($body, $newBody);
480
-            rewind($newBody);
481
-
482
-            $body = $newBody;
483
-        }
484
-
485
-        if ($this->server->tree->nodeExists($path)) {
486
-            $node = $this->server->tree->getNodeForPath($path);
487
-
488
-            // If the node is a collection, we'll deny it
489
-            if (!($node instanceof IFile)) {
490
-                throw new Exception\Conflict('PUT is not allowed on non-files.');
491
-            }
492
-            if (!$this->server->updateFile($path, $body, $etag)) {
493
-                return false;
494
-            }
495
-
496
-            $response->setHeader('Content-Length', '0');
497
-            if ($etag) {
498
-                $response->setHeader('ETag', $etag);
499
-            }
500
-            $response->setStatus(204);
501
-        } else {
502
-            $etag = null;
503
-            // If we got here, the resource didn't exist yet.
504
-            if (!$this->server->createFile($path, $body, $etag)) {
505
-                // For one reason or another the file was not created.
506
-                return false;
507
-            }
508
-
509
-            $response->setHeader('Content-Length', '0');
510
-            if ($etag) {
511
-                $response->setHeader('ETag', $etag);
512
-            }
513
-            $response->setStatus(201);
514
-        }
515
-
516
-        // Sending back false will interrupt the event chain and tell the server
517
-        // we've handled this method.
518
-        return false;
519
-    }
520
-
521
-    /**
522
-     * WebDAV MKCOL.
523
-     *
524
-     * The MKCOL method is used to create a new collection (directory) on the server
525
-     *
526
-     * @return bool
527
-     */
528
-    public function httpMkcol(RequestInterface $request, ResponseInterface $response)
529
-    {
530
-        $requestBody = $request->getBodyAsString();
531
-        $path = $request->getPath();
532
-
533
-        if ($requestBody) {
534
-            $contentType = $request->getHeader('Content-Type');
535
-            if (null === $contentType || (0 !== strpos($contentType, 'application/xml') && 0 !== strpos($contentType, 'text/xml'))) {
536
-                // We must throw 415 for unsupported mkcol bodies
537
-                throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type');
538
-            }
539
-
540
-            try {
541
-                $mkcol = $this->server->xml->expect('{DAV:}mkcol', $requestBody);
542
-            } catch (\Sabre\Xml\ParseException $e) {
543
-                throw new Exception\BadRequest($e->getMessage(), 0, $e);
544
-            }
545
-
546
-            $properties = $mkcol->getProperties();
547
-
548
-            if (!isset($properties['{DAV:}resourcetype'])) {
549
-                throw new Exception\BadRequest('The mkcol request must include a {DAV:}resourcetype property');
550
-            }
551
-            $resourceType = $properties['{DAV:}resourcetype']->getValue();
552
-            unset($properties['{DAV:}resourcetype']);
553
-        } else {
554
-            $properties = [];
555
-            $resourceType = ['{DAV:}collection'];
556
-        }
557
-
558
-        $mkcol = new MkCol($resourceType, $properties);
559
-
560
-        $result = $this->server->createCollection($path, $mkcol);
561
-
562
-        if (is_array($result)) {
563
-            $response->setStatus(207);
564
-            $response->setHeader('Content-Type', 'application/xml; charset=utf-8');
565
-
566
-            $response->setBody(
567
-                $this->server->generateMultiStatus([$result])
568
-            );
569
-        } else {
570
-            $response->setHeader('Content-Length', '0');
571
-            $response->setStatus(201);
572
-        }
573
-
574
-        // Sending back false will interrupt the event chain and tell the server
575
-        // we've handled this method.
576
-        return false;
577
-    }
578
-
579
-    /**
580
-     * WebDAV HTTP MOVE method.
581
-     *
582
-     * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo
583
-     *
584
-     * @return bool
585
-     */
586
-    public function httpMove(RequestInterface $request, ResponseInterface $response)
587
-    {
588
-        $path = $request->getPath();
589
-
590
-        $moveInfo = $this->server->getCopyAndMoveInfo($request);
591
-
592
-        if ($moveInfo['destinationExists']) {
593
-            if (!$this->server->emit('beforeUnbind', [$moveInfo['destination']])) {
594
-                return false;
595
-            }
596
-        }
597
-        if (!$this->server->emit('beforeUnbind', [$path])) {
598
-            return false;
599
-        }
600
-        if (!$this->server->emit('beforeBind', [$moveInfo['destination']])) {
601
-            return false;
602
-        }
603
-        if (!$this->server->emit('beforeMove', [$path, $moveInfo['destination']])) {
604
-            return false;
605
-        }
606
-
607
-        if ($moveInfo['destinationExists']) {
608
-            $this->server->tree->delete($moveInfo['destination']);
609
-            $this->server->emit('afterUnbind', [$moveInfo['destination']]);
610
-        }
611
-
612
-        $this->server->tree->move($path, $moveInfo['destination']);
613
-
614
-        // Its important afterMove is called before afterUnbind, because it
615
-        // allows systems to transfer data from one path to another.
616
-        // PropertyStorage uses this. If afterUnbind was first, it would clean
617
-        // up all the properties before it has a chance.
618
-        $this->server->emit('afterMove', [$path, $moveInfo['destination']]);
619
-        $this->server->emit('afterUnbind', [$path]);
620
-        $this->server->emit('afterBind', [$moveInfo['destination']]);
621
-
622
-        // If a resource was overwritten we should send a 204, otherwise a 201
623
-        $response->setHeader('Content-Length', '0');
624
-        $response->setStatus($moveInfo['destinationExists'] ? 204 : 201);
625
-
626
-        // Sending back false will interrupt the event chain and tell the server
627
-        // we've handled this method.
628
-        return false;
629
-    }
630
-
631
-    /**
632
-     * WebDAV HTTP COPY method.
633
-     *
634
-     * This method copies one uri to a different uri, and works much like the MOVE request
635
-     * A lot of the actual request processing is done in getCopyMoveInfo
636
-     *
637
-     * @return bool
638
-     */
639
-    public function httpCopy(RequestInterface $request, ResponseInterface $response)
640
-    {
641
-        $path = $request->getPath();
642
-
643
-        $copyInfo = $this->server->getCopyAndMoveInfo($request);
644
-
645
-        if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) {
646
-            return false;
647
-        }
648
-        if (!$this->server->emit('beforeCopy', [$path, $copyInfo['destination']])) {
649
-            return false;
650
-        }
651
-
652
-        if ($copyInfo['destinationExists']) {
653
-            if (!$this->server->emit('beforeUnbind', [$copyInfo['destination']])) {
654
-                return false;
655
-            }
656
-            $this->server->tree->delete($copyInfo['destination']);
657
-        }
658
-
659
-        $this->server->tree->copy($path, $copyInfo['destination']);
660
-        $this->server->emit('afterCopy', [$path, $copyInfo['destination']]);
661
-        $this->server->emit('afterBind', [$copyInfo['destination']]);
662
-
663
-        // If a resource was overwritten we should send a 204, otherwise a 201
664
-        $response->setHeader('Content-Length', '0');
665
-        $response->setStatus($copyInfo['destinationExists'] ? 204 : 201);
666
-
667
-        // Sending back false will interrupt the event chain and tell the server
668
-        // we've handled this method.
669
-        return false;
670
-    }
671
-
672
-    /**
673
-     * HTTP REPORT method implementation.
674
-     *
675
-     * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253)
676
-     * It's used in a lot of extensions, so it made sense to implement it into the core.
677
-     *
678
-     * @return bool
679
-     */
680
-    public function httpReport(RequestInterface $request, ResponseInterface $response)
681
-    {
682
-        $path = $request->getPath();
683
-
684
-        $result = $this->server->xml->parse(
685
-            $request->getBody(),
686
-            $request->getUrl(),
687
-            $rootElementName
688
-        );
689
-
690
-        if ($this->server->emit('report', [$rootElementName, $result, $path])) {
691
-            // If emit returned true, it means the report was not supported
692
-            throw new Exception\ReportNotSupported();
693
-        }
694
-
695
-        // Sending back false will interrupt the event chain and tell the server
696
-        // we've handled this method.
697
-        return false;
698
-    }
699
-
700
-    /**
701
-     * This method is called during property updates.
702
-     *
703
-     * Here we check if a user attempted to update a protected property and
704
-     * ensure that the process fails if this is the case.
705
-     *
706
-     * @param string $path
707
-     */
708
-    public function propPatchProtectedPropertyCheck($path, PropPatch $propPatch)
709
-    {
710
-        // Comparing the mutation list to the list of protected properties.
711
-        $mutations = $propPatch->getMutations();
712
-
713
-        $protected = array_intersect(
714
-            $this->server->protectedProperties,
715
-            array_keys($mutations)
716
-        );
717
-
718
-        if ($protected) {
719
-            $propPatch->setResultCode($protected, 403);
720
-        }
721
-    }
722
-
723
-    /**
724
-     * This method is called during property updates.
725
-     *
726
-     * Here we check if a node implements IProperties and let the node handle
727
-     * updating of (some) properties.
728
-     *
729
-     * @param string $path
730
-     */
731
-    public function propPatchNodeUpdate($path, PropPatch $propPatch)
732
-    {
733
-        // This should trigger a 404 if the node doesn't exist.
734
-        $node = $this->server->tree->getNodeForPath($path);
735
-
736
-        if ($node instanceof IProperties) {
737
-            $node->propPatch($propPatch);
738
-        }
739
-    }
740
-
741
-    /**
742
-     * This method is called when properties are retrieved.
743
-     *
744
-     * Here we add all the default properties.
745
-     */
746
-    public function propFind(PropFind $propFind, INode $node)
747
-    {
748
-        $propFind->handle('{DAV:}getlastmodified', function () use ($node) {
749
-            $lm = $node->getLastModified();
750
-            if ($lm) {
751
-                return new Xml\Property\GetLastModified($lm);
752
-            }
753
-        });
754
-
755
-        if ($node instanceof IFile) {
756
-            $propFind->handle('{DAV:}getcontentlength', [$node, 'getSize']);
757
-            $propFind->handle('{DAV:}getetag', [$node, 'getETag']);
758
-            $propFind->handle('{DAV:}getcontenttype', [$node, 'getContentType']);
759
-        }
760
-
761
-        if ($node instanceof IQuota) {
762
-            $quotaInfo = null;
763
-            $propFind->handle('{DAV:}quota-used-bytes', function () use (&$quotaInfo, $node) {
764
-                $quotaInfo = $node->getQuotaInfo();
765
-
766
-                return $quotaInfo[0];
767
-            });
768
-            $propFind->handle('{DAV:}quota-available-bytes', function () use (&$quotaInfo, $node) {
769
-                if (!$quotaInfo) {
770
-                    $quotaInfo = $node->getQuotaInfo();
771
-                }
772
-
773
-                return $quotaInfo[1];
774
-            });
775
-        }
776
-
777
-        $propFind->handle('{DAV:}supported-report-set', function () use ($propFind) {
778
-            $reports = [];
779
-            foreach ($this->server->getPlugins() as $plugin) {
780
-                $reports = array_merge($reports, $plugin->getSupportedReportSet($propFind->getPath()));
781
-            }
782
-
783
-            return new Xml\Property\SupportedReportSet($reports);
784
-        });
785
-        $propFind->handle('{DAV:}resourcetype', function () use ($node) {
786
-            return new Xml\Property\ResourceType($this->server->getResourceTypeForNode($node));
787
-        });
788
-        $propFind->handle('{DAV:}supported-method-set', function () use ($propFind) {
789
-            return new Xml\Property\SupportedMethodSet(
790
-                $this->server->getAllowedMethods($propFind->getPath())
791
-            );
792
-        });
793
-    }
794
-
795
-    /**
796
-     * Fetches properties for a node.
797
-     *
798
-     * This event is called a bit later, so plugins have a chance first to
799
-     * populate the result.
800
-     */
801
-    public function propFindNode(PropFind $propFind, INode $node)
802
-    {
803
-        if ($node instanceof IProperties && $propertyNames = $propFind->get404Properties()) {
804
-            $nodeProperties = $node->getProperties($propertyNames);
805
-            foreach ($nodeProperties as $propertyName => $propertyValue) {
806
-                $propFind->set($propertyName, $propertyValue, 200);
807
-            }
808
-        }
809
-    }
810
-
811
-    /**
812
-     * This method is called when properties are retrieved.
813
-     *
814
-     * This specific handler is called very late in the process, because we
815
-     * want other systems to first have a chance to handle the properties.
816
-     */
817
-    public function propFindLate(PropFind $propFind, INode $node)
818
-    {
819
-        $propFind->handle('{http://calendarserver.org/ns/}getctag', function () use ($propFind) {
820
-            // If we already have a sync-token from the current propFind
821
-            // request, we can re-use that.
822
-            $val = $propFind->get('{http://sabredav.org/ns}sync-token');
823
-            if ($val) {
824
-                return $val;
825
-            }
826
-
827
-            $val = $propFind->get('{DAV:}sync-token');
828
-            if ($val && is_scalar($val)) {
829
-                return $val;
830
-            }
831
-            if ($val && $val instanceof Xml\Property\Href) {
832
-                return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX));
833
-            }
834
-
835
-            // If we got here, the earlier two properties may simply not have
836
-            // been part of the earlier request. We're going to fetch them.
837
-            $result = $this->server->getProperties($propFind->getPath(), [
838
-                '{http://sabredav.org/ns}sync-token',
839
-                '{DAV:}sync-token',
840
-            ]);
841
-
842
-            if (isset($result['{http://sabredav.org/ns}sync-token'])) {
843
-                return $result['{http://sabredav.org/ns}sync-token'];
844
-            }
845
-            if (isset($result['{DAV:}sync-token'])) {
846
-                $val = $result['{DAV:}sync-token'];
847
-                if (is_scalar($val)) {
848
-                    return $val;
849
-                } elseif ($val instanceof Xml\Property\Href) {
850
-                    return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX));
851
-                }
852
-            }
853
-        });
854
-    }
855
-
856
-    /**
857
-     * Listens for exception events, and automatically logs them.
858
-     *
859
-     * @param Exception $e
860
-     */
861
-    public function exception($e)
862
-    {
863
-        $logLevel = \Psr\Log\LogLevel::CRITICAL;
864
-        if ($e instanceof \Sabre\DAV\Exception) {
865
-            // If it's a standard sabre/dav exception, it means we have a http
866
-            // status code available.
867
-            $code = $e->getHTTPCode();
868
-
869
-            if ($code >= 400 && $code < 500) {
870
-                // user error
871
-                $logLevel = \Psr\Log\LogLevel::INFO;
872
-            } else {
873
-                // Server-side error. We mark it's as an error, but it's not
874
-                // critical.
875
-                $logLevel = \Psr\Log\LogLevel::ERROR;
876
-            }
877
-        }
878
-
879
-        $this->server->getLogger()->log(
880
-            $logLevel,
881
-            'Uncaught exception',
882
-            [
883
-                'exception' => $e,
884
-            ]
885
-        );
886
-    }
887
-
888
-    /**
889
-     * Returns a bunch of meta-data about the plugin.
890
-     *
891
-     * Providing this information is optional, and is mainly displayed by the
892
-     * Browser plugin.
893
-     *
894
-     * The description key in the returned array may contain html and will not
895
-     * be sanitized.
896
-     *
897
-     * @return array
898
-     */
899
-    public function getPluginInfo()
900
-    {
901
-        return [
902
-            'name' => $this->getPluginName(),
903
-            'description' => 'The Core plugin provides a lot of the basic functionality required by WebDAV, such as a default implementation for all HTTP and WebDAV methods.',
904
-            'link' => null,
905
-        ];
906
-    }
468
+			// Only reading first byte
469
+			$firstByte = fread($body, 1);
470
+			if (1 !== strlen($firstByte)) {
471
+				throw new Exception\Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.');
472
+			}
473
+
474
+			// The body needs to stay intact, so we copy everything to a
475
+			// temporary stream.
476
+
477
+			$newBody = fopen('php://temp', 'r+');
478
+			fwrite($newBody, $firstByte);
479
+			stream_copy_to_stream($body, $newBody);
480
+			rewind($newBody);
481
+
482
+			$body = $newBody;
483
+		}
484
+
485
+		if ($this->server->tree->nodeExists($path)) {
486
+			$node = $this->server->tree->getNodeForPath($path);
487
+
488
+			// If the node is a collection, we'll deny it
489
+			if (!($node instanceof IFile)) {
490
+				throw new Exception\Conflict('PUT is not allowed on non-files.');
491
+			}
492
+			if (!$this->server->updateFile($path, $body, $etag)) {
493
+				return false;
494
+			}
495
+
496
+			$response->setHeader('Content-Length', '0');
497
+			if ($etag) {
498
+				$response->setHeader('ETag', $etag);
499
+			}
500
+			$response->setStatus(204);
501
+		} else {
502
+			$etag = null;
503
+			// If we got here, the resource didn't exist yet.
504
+			if (!$this->server->createFile($path, $body, $etag)) {
505
+				// For one reason or another the file was not created.
506
+				return false;
507
+			}
508
+
509
+			$response->setHeader('Content-Length', '0');
510
+			if ($etag) {
511
+				$response->setHeader('ETag', $etag);
512
+			}
513
+			$response->setStatus(201);
514
+		}
515
+
516
+		// Sending back false will interrupt the event chain and tell the server
517
+		// we've handled this method.
518
+		return false;
519
+	}
520
+
521
+	/**
522
+	 * WebDAV MKCOL.
523
+	 *
524
+	 * The MKCOL method is used to create a new collection (directory) on the server
525
+	 *
526
+	 * @return bool
527
+	 */
528
+	public function httpMkcol(RequestInterface $request, ResponseInterface $response)
529
+	{
530
+		$requestBody = $request->getBodyAsString();
531
+		$path = $request->getPath();
532
+
533
+		if ($requestBody) {
534
+			$contentType = $request->getHeader('Content-Type');
535
+			if (null === $contentType || (0 !== strpos($contentType, 'application/xml') && 0 !== strpos($contentType, 'text/xml'))) {
536
+				// We must throw 415 for unsupported mkcol bodies
537
+				throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type');
538
+			}
539
+
540
+			try {
541
+				$mkcol = $this->server->xml->expect('{DAV:}mkcol', $requestBody);
542
+			} catch (\Sabre\Xml\ParseException $e) {
543
+				throw new Exception\BadRequest($e->getMessage(), 0, $e);
544
+			}
545
+
546
+			$properties = $mkcol->getProperties();
547
+
548
+			if (!isset($properties['{DAV:}resourcetype'])) {
549
+				throw new Exception\BadRequest('The mkcol request must include a {DAV:}resourcetype property');
550
+			}
551
+			$resourceType = $properties['{DAV:}resourcetype']->getValue();
552
+			unset($properties['{DAV:}resourcetype']);
553
+		} else {
554
+			$properties = [];
555
+			$resourceType = ['{DAV:}collection'];
556
+		}
557
+
558
+		$mkcol = new MkCol($resourceType, $properties);
559
+
560
+		$result = $this->server->createCollection($path, $mkcol);
561
+
562
+		if (is_array($result)) {
563
+			$response->setStatus(207);
564
+			$response->setHeader('Content-Type', 'application/xml; charset=utf-8');
565
+
566
+			$response->setBody(
567
+				$this->server->generateMultiStatus([$result])
568
+			);
569
+		} else {
570
+			$response->setHeader('Content-Length', '0');
571
+			$response->setStatus(201);
572
+		}
573
+
574
+		// Sending back false will interrupt the event chain and tell the server
575
+		// we've handled this method.
576
+		return false;
577
+	}
578
+
579
+	/**
580
+	 * WebDAV HTTP MOVE method.
581
+	 *
582
+	 * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo
583
+	 *
584
+	 * @return bool
585
+	 */
586
+	public function httpMove(RequestInterface $request, ResponseInterface $response)
587
+	{
588
+		$path = $request->getPath();
589
+
590
+		$moveInfo = $this->server->getCopyAndMoveInfo($request);
591
+
592
+		if ($moveInfo['destinationExists']) {
593
+			if (!$this->server->emit('beforeUnbind', [$moveInfo['destination']])) {
594
+				return false;
595
+			}
596
+		}
597
+		if (!$this->server->emit('beforeUnbind', [$path])) {
598
+			return false;
599
+		}
600
+		if (!$this->server->emit('beforeBind', [$moveInfo['destination']])) {
601
+			return false;
602
+		}
603
+		if (!$this->server->emit('beforeMove', [$path, $moveInfo['destination']])) {
604
+			return false;
605
+		}
606
+
607
+		if ($moveInfo['destinationExists']) {
608
+			$this->server->tree->delete($moveInfo['destination']);
609
+			$this->server->emit('afterUnbind', [$moveInfo['destination']]);
610
+		}
611
+
612
+		$this->server->tree->move($path, $moveInfo['destination']);
613
+
614
+		// Its important afterMove is called before afterUnbind, because it
615
+		// allows systems to transfer data from one path to another.
616
+		// PropertyStorage uses this. If afterUnbind was first, it would clean
617
+		// up all the properties before it has a chance.
618
+		$this->server->emit('afterMove', [$path, $moveInfo['destination']]);
619
+		$this->server->emit('afterUnbind', [$path]);
620
+		$this->server->emit('afterBind', [$moveInfo['destination']]);
621
+
622
+		// If a resource was overwritten we should send a 204, otherwise a 201
623
+		$response->setHeader('Content-Length', '0');
624
+		$response->setStatus($moveInfo['destinationExists'] ? 204 : 201);
625
+
626
+		// Sending back false will interrupt the event chain and tell the server
627
+		// we've handled this method.
628
+		return false;
629
+	}
630
+
631
+	/**
632
+	 * WebDAV HTTP COPY method.
633
+	 *
634
+	 * This method copies one uri to a different uri, and works much like the MOVE request
635
+	 * A lot of the actual request processing is done in getCopyMoveInfo
636
+	 *
637
+	 * @return bool
638
+	 */
639
+	public function httpCopy(RequestInterface $request, ResponseInterface $response)
640
+	{
641
+		$path = $request->getPath();
642
+
643
+		$copyInfo = $this->server->getCopyAndMoveInfo($request);
644
+
645
+		if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) {
646
+			return false;
647
+		}
648
+		if (!$this->server->emit('beforeCopy', [$path, $copyInfo['destination']])) {
649
+			return false;
650
+		}
651
+
652
+		if ($copyInfo['destinationExists']) {
653
+			if (!$this->server->emit('beforeUnbind', [$copyInfo['destination']])) {
654
+				return false;
655
+			}
656
+			$this->server->tree->delete($copyInfo['destination']);
657
+		}
658
+
659
+		$this->server->tree->copy($path, $copyInfo['destination']);
660
+		$this->server->emit('afterCopy', [$path, $copyInfo['destination']]);
661
+		$this->server->emit('afterBind', [$copyInfo['destination']]);
662
+
663
+		// If a resource was overwritten we should send a 204, otherwise a 201
664
+		$response->setHeader('Content-Length', '0');
665
+		$response->setStatus($copyInfo['destinationExists'] ? 204 : 201);
666
+
667
+		// Sending back false will interrupt the event chain and tell the server
668
+		// we've handled this method.
669
+		return false;
670
+	}
671
+
672
+	/**
673
+	 * HTTP REPORT method implementation.
674
+	 *
675
+	 * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253)
676
+	 * It's used in a lot of extensions, so it made sense to implement it into the core.
677
+	 *
678
+	 * @return bool
679
+	 */
680
+	public function httpReport(RequestInterface $request, ResponseInterface $response)
681
+	{
682
+		$path = $request->getPath();
683
+
684
+		$result = $this->server->xml->parse(
685
+			$request->getBody(),
686
+			$request->getUrl(),
687
+			$rootElementName
688
+		);
689
+
690
+		if ($this->server->emit('report', [$rootElementName, $result, $path])) {
691
+			// If emit returned true, it means the report was not supported
692
+			throw new Exception\ReportNotSupported();
693
+		}
694
+
695
+		// Sending back false will interrupt the event chain and tell the server
696
+		// we've handled this method.
697
+		return false;
698
+	}
699
+
700
+	/**
701
+	 * This method is called during property updates.
702
+	 *
703
+	 * Here we check if a user attempted to update a protected property and
704
+	 * ensure that the process fails if this is the case.
705
+	 *
706
+	 * @param string $path
707
+	 */
708
+	public function propPatchProtectedPropertyCheck($path, PropPatch $propPatch)
709
+	{
710
+		// Comparing the mutation list to the list of protected properties.
711
+		$mutations = $propPatch->getMutations();
712
+
713
+		$protected = array_intersect(
714
+			$this->server->protectedProperties,
715
+			array_keys($mutations)
716
+		);
717
+
718
+		if ($protected) {
719
+			$propPatch->setResultCode($protected, 403);
720
+		}
721
+	}
722
+
723
+	/**
724
+	 * This method is called during property updates.
725
+	 *
726
+	 * Here we check if a node implements IProperties and let the node handle
727
+	 * updating of (some) properties.
728
+	 *
729
+	 * @param string $path
730
+	 */
731
+	public function propPatchNodeUpdate($path, PropPatch $propPatch)
732
+	{
733
+		// This should trigger a 404 if the node doesn't exist.
734
+		$node = $this->server->tree->getNodeForPath($path);
735
+
736
+		if ($node instanceof IProperties) {
737
+			$node->propPatch($propPatch);
738
+		}
739
+	}
740
+
741
+	/**
742
+	 * This method is called when properties are retrieved.
743
+	 *
744
+	 * Here we add all the default properties.
745
+	 */
746
+	public function propFind(PropFind $propFind, INode $node)
747
+	{
748
+		$propFind->handle('{DAV:}getlastmodified', function () use ($node) {
749
+			$lm = $node->getLastModified();
750
+			if ($lm) {
751
+				return new Xml\Property\GetLastModified($lm);
752
+			}
753
+		});
754
+
755
+		if ($node instanceof IFile) {
756
+			$propFind->handle('{DAV:}getcontentlength', [$node, 'getSize']);
757
+			$propFind->handle('{DAV:}getetag', [$node, 'getETag']);
758
+			$propFind->handle('{DAV:}getcontenttype', [$node, 'getContentType']);
759
+		}
760
+
761
+		if ($node instanceof IQuota) {
762
+			$quotaInfo = null;
763
+			$propFind->handle('{DAV:}quota-used-bytes', function () use (&$quotaInfo, $node) {
764
+				$quotaInfo = $node->getQuotaInfo();
765
+
766
+				return $quotaInfo[0];
767
+			});
768
+			$propFind->handle('{DAV:}quota-available-bytes', function () use (&$quotaInfo, $node) {
769
+				if (!$quotaInfo) {
770
+					$quotaInfo = $node->getQuotaInfo();
771
+				}
772
+
773
+				return $quotaInfo[1];
774
+			});
775
+		}
776
+
777
+		$propFind->handle('{DAV:}supported-report-set', function () use ($propFind) {
778
+			$reports = [];
779
+			foreach ($this->server->getPlugins() as $plugin) {
780
+				$reports = array_merge($reports, $plugin->getSupportedReportSet($propFind->getPath()));
781
+			}
782
+
783
+			return new Xml\Property\SupportedReportSet($reports);
784
+		});
785
+		$propFind->handle('{DAV:}resourcetype', function () use ($node) {
786
+			return new Xml\Property\ResourceType($this->server->getResourceTypeForNode($node));
787
+		});
788
+		$propFind->handle('{DAV:}supported-method-set', function () use ($propFind) {
789
+			return new Xml\Property\SupportedMethodSet(
790
+				$this->server->getAllowedMethods($propFind->getPath())
791
+			);
792
+		});
793
+	}
794
+
795
+	/**
796
+	 * Fetches properties for a node.
797
+	 *
798
+	 * This event is called a bit later, so plugins have a chance first to
799
+	 * populate the result.
800
+	 */
801
+	public function propFindNode(PropFind $propFind, INode $node)
802
+	{
803
+		if ($node instanceof IProperties && $propertyNames = $propFind->get404Properties()) {
804
+			$nodeProperties = $node->getProperties($propertyNames);
805
+			foreach ($nodeProperties as $propertyName => $propertyValue) {
806
+				$propFind->set($propertyName, $propertyValue, 200);
807
+			}
808
+		}
809
+	}
810
+
811
+	/**
812
+	 * This method is called when properties are retrieved.
813
+	 *
814
+	 * This specific handler is called very late in the process, because we
815
+	 * want other systems to first have a chance to handle the properties.
816
+	 */
817
+	public function propFindLate(PropFind $propFind, INode $node)
818
+	{
819
+		$propFind->handle('{http://calendarserver.org/ns/}getctag', function () use ($propFind) {
820
+			// If we already have a sync-token from the current propFind
821
+			// request, we can re-use that.
822
+			$val = $propFind->get('{http://sabredav.org/ns}sync-token');
823
+			if ($val) {
824
+				return $val;
825
+			}
826
+
827
+			$val = $propFind->get('{DAV:}sync-token');
828
+			if ($val && is_scalar($val)) {
829
+				return $val;
830
+			}
831
+			if ($val && $val instanceof Xml\Property\Href) {
832
+				return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX));
833
+			}
834
+
835
+			// If we got here, the earlier two properties may simply not have
836
+			// been part of the earlier request. We're going to fetch them.
837
+			$result = $this->server->getProperties($propFind->getPath(), [
838
+				'{http://sabredav.org/ns}sync-token',
839
+				'{DAV:}sync-token',
840
+			]);
841
+
842
+			if (isset($result['{http://sabredav.org/ns}sync-token'])) {
843
+				return $result['{http://sabredav.org/ns}sync-token'];
844
+			}
845
+			if (isset($result['{DAV:}sync-token'])) {
846
+				$val = $result['{DAV:}sync-token'];
847
+				if (is_scalar($val)) {
848
+					return $val;
849
+				} elseif ($val instanceof Xml\Property\Href) {
850
+					return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX));
851
+				}
852
+			}
853
+		});
854
+	}
855
+
856
+	/**
857
+	 * Listens for exception events, and automatically logs them.
858
+	 *
859
+	 * @param Exception $e
860
+	 */
861
+	public function exception($e)
862
+	{
863
+		$logLevel = \Psr\Log\LogLevel::CRITICAL;
864
+		if ($e instanceof \Sabre\DAV\Exception) {
865
+			// If it's a standard sabre/dav exception, it means we have a http
866
+			// status code available.
867
+			$code = $e->getHTTPCode();
868
+
869
+			if ($code >= 400 && $code < 500) {
870
+				// user error
871
+				$logLevel = \Psr\Log\LogLevel::INFO;
872
+			} else {
873
+				// Server-side error. We mark it's as an error, but it's not
874
+				// critical.
875
+				$logLevel = \Psr\Log\LogLevel::ERROR;
876
+			}
877
+		}
878
+
879
+		$this->server->getLogger()->log(
880
+			$logLevel,
881
+			'Uncaught exception',
882
+			[
883
+				'exception' => $e,
884
+			]
885
+		);
886
+	}
887
+
888
+	/**
889
+	 * Returns a bunch of meta-data about the plugin.
890
+	 *
891
+	 * Providing this information is optional, and is mainly displayed by the
892
+	 * Browser plugin.
893
+	 *
894
+	 * The description key in the returned array may contain html and will not
895
+	 * be sanitized.
896
+	 *
897
+	 * @return array
898
+	 */
899
+	public function getPluginInfo()
900
+	{
901
+		return [
902
+			'name' => $this->getPluginName(),
903
+			'description' => 'The Core plugin provides a lot of the basic functionality required by WebDAV, such as a default implementation for all HTTP and WebDAV methods.',
904
+			'link' => null,
905
+		];
906
+	}
907 907
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -745,7 +745,7 @@  discard block
 block discarded – undo
745 745
      */
746 746
     public function propFind(PropFind $propFind, INode $node)
747 747
     {
748
-        $propFind->handle('{DAV:}getlastmodified', function () use ($node) {
748
+        $propFind->handle('{DAV:}getlastmodified', function() use ($node) {
749 749
             $lm = $node->getLastModified();
750 750
             if ($lm) {
751 751
                 return new Xml\Property\GetLastModified($lm);
@@ -760,12 +760,12 @@  discard block
 block discarded – undo
760 760
 
761 761
         if ($node instanceof IQuota) {
762 762
             $quotaInfo = null;
763
-            $propFind->handle('{DAV:}quota-used-bytes', function () use (&$quotaInfo, $node) {
763
+            $propFind->handle('{DAV:}quota-used-bytes', function() use (&$quotaInfo, $node) {
764 764
                 $quotaInfo = $node->getQuotaInfo();
765 765
 
766 766
                 return $quotaInfo[0];
767 767
             });
768
-            $propFind->handle('{DAV:}quota-available-bytes', function () use (&$quotaInfo, $node) {
768
+            $propFind->handle('{DAV:}quota-available-bytes', function() use (&$quotaInfo, $node) {
769 769
                 if (!$quotaInfo) {
770 770
                     $quotaInfo = $node->getQuotaInfo();
771 771
                 }
@@ -774,7 +774,7 @@  discard block
 block discarded – undo
774 774
             });
775 775
         }
776 776
 
777
-        $propFind->handle('{DAV:}supported-report-set', function () use ($propFind) {
777
+        $propFind->handle('{DAV:}supported-report-set', function() use ($propFind) {
778 778
             $reports = [];
779 779
             foreach ($this->server->getPlugins() as $plugin) {
780 780
                 $reports = array_merge($reports, $plugin->getSupportedReportSet($propFind->getPath()));
@@ -782,10 +782,10 @@  discard block
 block discarded – undo
782 782
 
783 783
             return new Xml\Property\SupportedReportSet($reports);
784 784
         });
785
-        $propFind->handle('{DAV:}resourcetype', function () use ($node) {
785
+        $propFind->handle('{DAV:}resourcetype', function() use ($node) {
786 786
             return new Xml\Property\ResourceType($this->server->getResourceTypeForNode($node));
787 787
         });
788
-        $propFind->handle('{DAV:}supported-method-set', function () use ($propFind) {
788
+        $propFind->handle('{DAV:}supported-method-set', function() use ($propFind) {
789 789
             return new Xml\Property\SupportedMethodSet(
790 790
                 $this->server->getAllowedMethods($propFind->getPath())
791 791
             );
@@ -816,7 +816,7 @@  discard block
 block discarded – undo
816 816
      */
817 817
     public function propFindLate(PropFind $propFind, INode $node)
818 818
     {
819
-        $propFind->handle('{http://calendarserver.org/ns/}getctag', function () use ($propFind) {
819
+        $propFind->handle('{http://calendarserver.org/ns/}getctag', function() use ($propFind) {
820 820
             // If we already have a sync-token from the current propFind
821 821
             // request, we can re-use that.
822 822
             $val = $propFind->get('{http://sabredav.org/ns}sync-token');
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Plugin.php 1 patch
Indentation   +136 added lines, -136 removed lines patch added patch discarded remove patch
@@ -27,150 +27,150 @@
 block discarded – undo
27 27
  */
28 28
 class Plugin extends ServerPlugin
29 29
 {
30
-    /**
31
-     * If you only want this plugin to store properties for a limited set of
32
-     * paths, you can use a pathFilter to do this.
33
-     *
34
-     * The pathFilter should be a callable. The callable retrieves a path as
35
-     * its argument, and should return true or false whether it allows
36
-     * properties to be stored.
37
-     *
38
-     * @var callable
39
-     */
40
-    public $pathFilter;
30
+	/**
31
+	 * If you only want this plugin to store properties for a limited set of
32
+	 * paths, you can use a pathFilter to do this.
33
+	 *
34
+	 * The pathFilter should be a callable. The callable retrieves a path as
35
+	 * its argument, and should return true or false whether it allows
36
+	 * properties to be stored.
37
+	 *
38
+	 * @var callable
39
+	 */
40
+	public $pathFilter;
41 41
 
42
-    /**
43
-     * @var Backend\BackendInterface
44
-     */
45
-    public $backend;
42
+	/**
43
+	 * @var Backend\BackendInterface
44
+	 */
45
+	public $backend;
46 46
 
47
-    /**
48
-     * Creates the plugin.
49
-     */
50
-    public function __construct(Backend\BackendInterface $backend)
51
-    {
52
-        $this->backend = $backend;
53
-    }
47
+	/**
48
+	 * Creates the plugin.
49
+	 */
50
+	public function __construct(Backend\BackendInterface $backend)
51
+	{
52
+		$this->backend = $backend;
53
+	}
54 54
 
55
-    /**
56
-     * This initializes the plugin.
57
-     *
58
-     * This function is called by Sabre\DAV\Server, after
59
-     * addPlugin is called.
60
-     *
61
-     * This method should set up the required event subscriptions.
62
-     */
63
-    public function initialize(Server $server)
64
-    {
65
-        $server->on('propFind', [$this, 'propFind'], 130);
66
-        $server->on('propPatch', [$this, 'propPatch'], 300);
67
-        $server->on('afterMove', [$this, 'afterMove']);
68
-        $server->on('afterUnbind', [$this, 'afterUnbind']);
69
-    }
55
+	/**
56
+	 * This initializes the plugin.
57
+	 *
58
+	 * This function is called by Sabre\DAV\Server, after
59
+	 * addPlugin is called.
60
+	 *
61
+	 * This method should set up the required event subscriptions.
62
+	 */
63
+	public function initialize(Server $server)
64
+	{
65
+		$server->on('propFind', [$this, 'propFind'], 130);
66
+		$server->on('propPatch', [$this, 'propPatch'], 300);
67
+		$server->on('afterMove', [$this, 'afterMove']);
68
+		$server->on('afterUnbind', [$this, 'afterUnbind']);
69
+	}
70 70
 
71
-    /**
72
-     * Called during PROPFIND operations.
73
-     *
74
-     * If there's any requested properties that don't have a value yet, this
75
-     * plugin will look in the property storage backend to find them.
76
-     */
77
-    public function propFind(PropFind $propFind, INode $node)
78
-    {
79
-        $path = $propFind->getPath();
80
-        $pathFilter = $this->pathFilter;
81
-        if ($pathFilter && !$pathFilter($path)) {
82
-            return;
83
-        }
84
-        $this->backend->propFind($propFind->getPath(), $propFind);
85
-    }
71
+	/**
72
+	 * Called during PROPFIND operations.
73
+	 *
74
+	 * If there's any requested properties that don't have a value yet, this
75
+	 * plugin will look in the property storage backend to find them.
76
+	 */
77
+	public function propFind(PropFind $propFind, INode $node)
78
+	{
79
+		$path = $propFind->getPath();
80
+		$pathFilter = $this->pathFilter;
81
+		if ($pathFilter && !$pathFilter($path)) {
82
+			return;
83
+		}
84
+		$this->backend->propFind($propFind->getPath(), $propFind);
85
+	}
86 86
 
87
-    /**
88
-     * Called during PROPPATCH operations.
89
-     *
90
-     * If there's any updated properties that haven't been stored, the
91
-     * propertystorage backend can handle it.
92
-     *
93
-     * @param string $path
94
-     */
95
-    public function propPatch($path, PropPatch $propPatch)
96
-    {
97
-        $pathFilter = $this->pathFilter;
98
-        if ($pathFilter && !$pathFilter($path)) {
99
-            return;
100
-        }
101
-        $this->backend->propPatch($path, $propPatch);
102
-    }
87
+	/**
88
+	 * Called during PROPPATCH operations.
89
+	 *
90
+	 * If there's any updated properties that haven't been stored, the
91
+	 * propertystorage backend can handle it.
92
+	 *
93
+	 * @param string $path
94
+	 */
95
+	public function propPatch($path, PropPatch $propPatch)
96
+	{
97
+		$pathFilter = $this->pathFilter;
98
+		if ($pathFilter && !$pathFilter($path)) {
99
+			return;
100
+		}
101
+		$this->backend->propPatch($path, $propPatch);
102
+	}
103 103
 
104
-    /**
105
-     * Called after a node is deleted.
106
-     *
107
-     * This allows the backend to clean up any properties still in the
108
-     * database.
109
-     *
110
-     * @param string $path
111
-     */
112
-    public function afterUnbind($path)
113
-    {
114
-        $pathFilter = $this->pathFilter;
115
-        if ($pathFilter && !$pathFilter($path)) {
116
-            return;
117
-        }
118
-        $this->backend->delete($path);
119
-    }
104
+	/**
105
+	 * Called after a node is deleted.
106
+	 *
107
+	 * This allows the backend to clean up any properties still in the
108
+	 * database.
109
+	 *
110
+	 * @param string $path
111
+	 */
112
+	public function afterUnbind($path)
113
+	{
114
+		$pathFilter = $this->pathFilter;
115
+		if ($pathFilter && !$pathFilter($path)) {
116
+			return;
117
+		}
118
+		$this->backend->delete($path);
119
+	}
120 120
 
121
-    /**
122
-     * Called after a node is moved.
123
-     *
124
-     * This allows the backend to move all the associated properties.
125
-     *
126
-     * @param string $source
127
-     * @param string $destination
128
-     */
129
-    public function afterMove($source, $destination)
130
-    {
131
-        $pathFilter = $this->pathFilter;
132
-        if ($pathFilter && !$pathFilter($source)) {
133
-            return;
134
-        }
135
-        // If the destination is filtered, afterUnbind will handle cleaning up
136
-        // the properties.
137
-        if ($pathFilter && !$pathFilter($destination)) {
138
-            return;
139
-        }
121
+	/**
122
+	 * Called after a node is moved.
123
+	 *
124
+	 * This allows the backend to move all the associated properties.
125
+	 *
126
+	 * @param string $source
127
+	 * @param string $destination
128
+	 */
129
+	public function afterMove($source, $destination)
130
+	{
131
+		$pathFilter = $this->pathFilter;
132
+		if ($pathFilter && !$pathFilter($source)) {
133
+			return;
134
+		}
135
+		// If the destination is filtered, afterUnbind will handle cleaning up
136
+		// the properties.
137
+		if ($pathFilter && !$pathFilter($destination)) {
138
+			return;
139
+		}
140 140
 
141
-        $this->backend->move($source, $destination);
142
-    }
141
+		$this->backend->move($source, $destination);
142
+	}
143 143
 
144
-    /**
145
-     * Returns a plugin name.
146
-     *
147
-     * Using this name other plugins will be able to access other plugins
148
-     * using \Sabre\DAV\Server::getPlugin
149
-     *
150
-     * @return string
151
-     */
152
-    public function getPluginName()
153
-    {
154
-        return 'property-storage';
155
-    }
144
+	/**
145
+	 * Returns a plugin name.
146
+	 *
147
+	 * Using this name other plugins will be able to access other plugins
148
+	 * using \Sabre\DAV\Server::getPlugin
149
+	 *
150
+	 * @return string
151
+	 */
152
+	public function getPluginName()
153
+	{
154
+		return 'property-storage';
155
+	}
156 156
 
157
-    /**
158
-     * Returns a bunch of meta-data about the plugin.
159
-     *
160
-     * Providing this information is optional, and is mainly displayed by the
161
-     * Browser plugin.
162
-     *
163
-     * The description key in the returned array may contain html and will not
164
-     * be sanitized.
165
-     *
166
-     * @return array
167
-     */
168
-    public function getPluginInfo()
169
-    {
170
-        return [
171
-            'name' => $this->getPluginName(),
172
-            'description' => 'This plugin allows any arbitrary WebDAV property to be set on any resource.',
173
-            'link' => 'http://sabre.io/dav/property-storage/',
174
-        ];
175
-    }
157
+	/**
158
+	 * Returns a bunch of meta-data about the plugin.
159
+	 *
160
+	 * Providing this information is optional, and is mainly displayed by the
161
+	 * Browser plugin.
162
+	 *
163
+	 * The description key in the returned array may contain html and will not
164
+	 * be sanitized.
165
+	 *
166
+	 * @return array
167
+	 */
168
+	public function getPluginInfo()
169
+	{
170
+		return [
171
+			'name' => $this->getPluginName(),
172
+			'description' => 'This plugin allows any arbitrary WebDAV property to be set on any resource.',
173
+			'link' => 'http://sabre.io/dav/property-storage/',
174
+		];
175
+	}
176 176
 }
Please login to merge, or discard this patch.
sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php 1 patch
Indentation   +50 added lines, -50 removed lines patch added patch discarded remove patch
@@ -19,57 +19,57 @@
 block discarded – undo
19 19
  */
20 20
 interface BackendInterface
21 21
 {
22
-    /**
23
-     * Fetches properties for a path.
24
-     *
25
-     * This method received a PropFind object, which contains all the
26
-     * information about the properties that need to be fetched.
27
-     *
28
-     * Usually you would just want to call 'get404Properties' on this object,
29
-     * as this will give you the _exact_ list of properties that need to be
30
-     * fetched, and haven't yet.
31
-     *
32
-     * However, you can also support the 'allprops' property here. In that
33
-     * case, you should check for $propFind->isAllProps().
34
-     *
35
-     * @param string $path
36
-     */
37
-    public function propFind($path, PropFind $propFind);
22
+	/**
23
+	 * Fetches properties for a path.
24
+	 *
25
+	 * This method received a PropFind object, which contains all the
26
+	 * information about the properties that need to be fetched.
27
+	 *
28
+	 * Usually you would just want to call 'get404Properties' on this object,
29
+	 * as this will give you the _exact_ list of properties that need to be
30
+	 * fetched, and haven't yet.
31
+	 *
32
+	 * However, you can also support the 'allprops' property here. In that
33
+	 * case, you should check for $propFind->isAllProps().
34
+	 *
35
+	 * @param string $path
36
+	 */
37
+	public function propFind($path, PropFind $propFind);
38 38
 
39
-    /**
40
-     * Updates properties for a path.
41
-     *
42
-     * This method received a PropPatch object, which contains all the
43
-     * information about the update.
44
-     *
45
-     * Usually you would want to call 'handleRemaining' on this object, to get;
46
-     * a list of all properties that need to be stored.
47
-     *
48
-     * @param string $path
49
-     */
50
-    public function propPatch($path, PropPatch $propPatch);
39
+	/**
40
+	 * Updates properties for a path.
41
+	 *
42
+	 * This method received a PropPatch object, which contains all the
43
+	 * information about the update.
44
+	 *
45
+	 * Usually you would want to call 'handleRemaining' on this object, to get;
46
+	 * a list of all properties that need to be stored.
47
+	 *
48
+	 * @param string $path
49
+	 */
50
+	public function propPatch($path, PropPatch $propPatch);
51 51
 
52
-    /**
53
-     * This method is called after a node is deleted.
54
-     *
55
-     * This allows a backend to clean up all associated properties.
56
-     *
57
-     * The delete method will get called once for the deletion of an entire
58
-     * tree.
59
-     *
60
-     * @param string $path
61
-     */
62
-    public function delete($path);
52
+	/**
53
+	 * This method is called after a node is deleted.
54
+	 *
55
+	 * This allows a backend to clean up all associated properties.
56
+	 *
57
+	 * The delete method will get called once for the deletion of an entire
58
+	 * tree.
59
+	 *
60
+	 * @param string $path
61
+	 */
62
+	public function delete($path);
63 63
 
64
-    /**
65
-     * This method is called after a successful MOVE.
66
-     *
67
-     * This should be used to migrate all properties from one path to another.
68
-     * Note that entire collections may be moved, so ensure that all properties
69
-     * for children are also moved along.
70
-     *
71
-     * @param string $source
72
-     * @param string $destination
73
-     */
74
-    public function move($source, $destination);
64
+	/**
65
+	 * This method is called after a successful MOVE.
66
+	 *
67
+	 * This should be used to migrate all properties from one path to another.
68
+	 * Note that entire collections may be moved, so ensure that all properties
69
+	 * for children are also moved along.
70
+	 *
71
+	 * @param string $source
72
+	 * @param string $destination
73
+	 */
74
+	public function move($source, $destination);
75 75
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php 3 patches
Indentation   +191 added lines, -191 removed lines patch added patch discarded remove patch
@@ -22,203 +22,203 @@
 block discarded – undo
22 22
  */
23 23
 class PDO implements BackendInterface
24 24
 {
25
-    /**
26
-     * Value is stored as string.
27
-     */
28
-    const VT_STRING = 1;
29
-
30
-    /**
31
-     * Value is stored as XML fragment.
32
-     */
33
-    const VT_XML = 2;
34
-
35
-    /**
36
-     * Value is stored as a property object.
37
-     */
38
-    const VT_OBJECT = 3;
39
-
40
-    /**
41
-     * PDO.
42
-     *
43
-     * @var \PDO
44
-     */
45
-    protected $pdo;
46
-
47
-    /**
48
-     * PDO table name we'll be using.
49
-     *
50
-     * @var string
51
-     */
52
-    public $tableName = 'propertystorage';
53
-
54
-    /**
55
-     * Creates the PDO property storage engine.
56
-     */
57
-    public function __construct(\PDO $pdo)
58
-    {
59
-        $this->pdo = $pdo;
60
-    }
61
-
62
-    /**
63
-     * Fetches properties for a path.
64
-     *
65
-     * This method received a PropFind object, which contains all the
66
-     * information about the properties that need to be fetched.
67
-     *
68
-     * Usually you would just want to call 'get404Properties' on this object,
69
-     * as this will give you the _exact_ list of properties that need to be
70
-     * fetched, and haven't yet.
71
-     *
72
-     * However, you can also support the 'allprops' property here. In that
73
-     * case, you should check for $propFind->isAllProps().
74
-     *
75
-     * @param string $path
76
-     */
77
-    public function propFind($path, PropFind $propFind)
78
-    {
79
-        if (!$propFind->isAllProps() && 0 === count($propFind->get404Properties())) {
80
-            return;
81
-        }
82
-
83
-        $query = 'SELECT name, value, valuetype FROM '.$this->tableName.' WHERE path = ?';
84
-        $stmt = $this->pdo->prepare($query);
85
-        $stmt->execute([$path]);
86
-
87
-        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
88
-            if ('resource' === gettype($row['value'])) {
89
-                $row['value'] = stream_get_contents($row['value']);
90
-            }
91
-            switch ($row['valuetype']) {
92
-                case null:
93
-                case self::VT_STRING:
94
-                    $propFind->set($row['name'], $row['value']);
95
-                    break;
96
-                case self::VT_XML:
97
-                    $propFind->set($row['name'], new Complex($row['value']));
98
-                    break;
99
-                case self::VT_OBJECT:
100
-                    $propFind->set($row['name'], unserialize($row['value']));
101
-                    break;
102
-            }
103
-        }
104
-    }
105
-
106
-    /**
107
-     * Updates properties for a path.
108
-     *
109
-     * This method received a PropPatch object, which contains all the
110
-     * information about the update.
111
-     *
112
-     * Usually you would want to call 'handleRemaining' on this object, to get;
113
-     * a list of all properties that need to be stored.
114
-     *
115
-     * @param string $path
116
-     */
117
-    public function propPatch($path, PropPatch $propPatch)
118
-    {
119
-        $propPatch->handleRemaining(function ($properties) use ($path) {
120
-            if ('pgsql' === $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
121
-                $updateSql = <<<SQL
25
+	/**
26
+	 * Value is stored as string.
27
+	 */
28
+	const VT_STRING = 1;
29
+
30
+	/**
31
+	 * Value is stored as XML fragment.
32
+	 */
33
+	const VT_XML = 2;
34
+
35
+	/**
36
+	 * Value is stored as a property object.
37
+	 */
38
+	const VT_OBJECT = 3;
39
+
40
+	/**
41
+	 * PDO.
42
+	 *
43
+	 * @var \PDO
44
+	 */
45
+	protected $pdo;
46
+
47
+	/**
48
+	 * PDO table name we'll be using.
49
+	 *
50
+	 * @var string
51
+	 */
52
+	public $tableName = 'propertystorage';
53
+
54
+	/**
55
+	 * Creates the PDO property storage engine.
56
+	 */
57
+	public function __construct(\PDO $pdo)
58
+	{
59
+		$this->pdo = $pdo;
60
+	}
61
+
62
+	/**
63
+	 * Fetches properties for a path.
64
+	 *
65
+	 * This method received a PropFind object, which contains all the
66
+	 * information about the properties that need to be fetched.
67
+	 *
68
+	 * Usually you would just want to call 'get404Properties' on this object,
69
+	 * as this will give you the _exact_ list of properties that need to be
70
+	 * fetched, and haven't yet.
71
+	 *
72
+	 * However, you can also support the 'allprops' property here. In that
73
+	 * case, you should check for $propFind->isAllProps().
74
+	 *
75
+	 * @param string $path
76
+	 */
77
+	public function propFind($path, PropFind $propFind)
78
+	{
79
+		if (!$propFind->isAllProps() && 0 === count($propFind->get404Properties())) {
80
+			return;
81
+		}
82
+
83
+		$query = 'SELECT name, value, valuetype FROM '.$this->tableName.' WHERE path = ?';
84
+		$stmt = $this->pdo->prepare($query);
85
+		$stmt->execute([$path]);
86
+
87
+		while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
88
+			if ('resource' === gettype($row['value'])) {
89
+				$row['value'] = stream_get_contents($row['value']);
90
+			}
91
+			switch ($row['valuetype']) {
92
+				case null:
93
+				case self::VT_STRING:
94
+					$propFind->set($row['name'], $row['value']);
95
+					break;
96
+				case self::VT_XML:
97
+					$propFind->set($row['name'], new Complex($row['value']));
98
+					break;
99
+				case self::VT_OBJECT:
100
+					$propFind->set($row['name'], unserialize($row['value']));
101
+					break;
102
+			}
103
+		}
104
+	}
105
+
106
+	/**
107
+	 * Updates properties for a path.
108
+	 *
109
+	 * This method received a PropPatch object, which contains all the
110
+	 * information about the update.
111
+	 *
112
+	 * Usually you would want to call 'handleRemaining' on this object, to get;
113
+	 * a list of all properties that need to be stored.
114
+	 *
115
+	 * @param string $path
116
+	 */
117
+	public function propPatch($path, PropPatch $propPatch)
118
+	{
119
+		$propPatch->handleRemaining(function ($properties) use ($path) {
120
+			if ('pgsql' === $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
121
+				$updateSql = <<<SQL
122 122
 INSERT INTO {$this->tableName} (path, name, valuetype, value)
123 123
 VALUES (:path, :name, :valuetype, :value)
124 124
 ON CONFLICT (path, name)
125 125
 DO UPDATE SET valuetype = :valuetype, value = :value
126 126
 SQL;
127
-            } else {
128
-                $updateSql = <<<SQL
127
+			} else {
128
+				$updateSql = <<<SQL
129 129
 REPLACE INTO {$this->tableName} (path, name, valuetype, value)
130 130
 VALUES (:path, :name, :valuetype, :value)
131 131
 SQL;
132
-            }
133
-
134
-            $updateStmt = $this->pdo->prepare($updateSql);
135
-            $deleteStmt = $this->pdo->prepare('DELETE FROM '.$this->tableName.' WHERE path = ? AND name = ?');
136
-
137
-            foreach ($properties as $name => $value) {
138
-                if (!is_null($value)) {
139
-                    if (is_scalar($value)) {
140
-                        $valueType = self::VT_STRING;
141
-                    } elseif ($value instanceof Complex) {
142
-                        $valueType = self::VT_XML;
143
-                        $value = $value->getXml();
144
-                    } else {
145
-                        $valueType = self::VT_OBJECT;
146
-                        $value = serialize($value);
147
-                    }
148
-
149
-                    $updateStmt->bindParam('path', $path, \PDO::PARAM_STR);
150
-                    $updateStmt->bindParam('name', $name, \PDO::PARAM_STR);
151
-                    $updateStmt->bindParam('valuetype', $valueType, \PDO::PARAM_INT);
152
-                    $updateStmt->bindParam('value', $value, \PDO::PARAM_LOB);
153
-
154
-                    $updateStmt->execute();
155
-                } else {
156
-                    $deleteStmt->execute([$path, $name]);
157
-                }
158
-            }
159
-
160
-            return true;
161
-        });
162
-    }
163
-
164
-    /**
165
-     * This method is called after a node is deleted.
166
-     *
167
-     * This allows a backend to clean up all associated properties.
168
-     *
169
-     * The delete method will get called once for the deletion of an entire
170
-     * tree.
171
-     *
172
-     * @param string $path
173
-     */
174
-    public function delete($path)
175
-    {
176
-        $stmt = $this->pdo->prepare('DELETE FROM '.$this->tableName."  WHERE path = ? OR path LIKE ? ESCAPE '='");
177
-        $childPath = strtr(
178
-            $path,
179
-            [
180
-                '=' => '==',
181
-                '%' => '=%',
182
-                '_' => '=_',
183
-            ]
184
-        ).'/%';
185
-
186
-        $stmt->execute([$path, $childPath]);
187
-    }
188
-
189
-    /**
190
-     * This method is called after a successful MOVE.
191
-     *
192
-     * This should be used to migrate all properties from one path to another.
193
-     * Note that entire collections may be moved, so ensure that all properties
194
-     * for children are also moved along.
195
-     *
196
-     * @param string $source
197
-     * @param string $destination
198
-     */
199
-    public function move($source, $destination)
200
-    {
201
-        // I don't know a way to write this all in a single sql query that's
202
-        // also compatible across db engines, so we're letting PHP do all the
203
-        // updates. Much slower, but it should still be pretty fast in most
204
-        // cases.
205
-        $select = $this->pdo->prepare('SELECT id, path FROM '.$this->tableName.'  WHERE path = ? OR path LIKE ?');
206
-        $select->execute([$source, $source.'/%']);
207
-
208
-        $update = $this->pdo->prepare('UPDATE '.$this->tableName.' SET path = ? WHERE id = ?');
209
-        while ($row = $select->fetch(\PDO::FETCH_ASSOC)) {
210
-            // Sanity check. SQL may select too many records, such as records
211
-            // with different cases.
212
-            if ($row['path'] !== $source && 0 !== strpos($row['path'], $source.'/')) {
213
-                continue;
214
-            }
215
-
216
-            $trailingPart = substr($row['path'], strlen($source) + 1);
217
-            $newPath = $destination;
218
-            if ($trailingPart) {
219
-                $newPath .= '/'.$trailingPart;
220
-            }
221
-            $update->execute([$newPath, $row['id']]);
222
-        }
223
-    }
132
+			}
133
+
134
+			$updateStmt = $this->pdo->prepare($updateSql);
135
+			$deleteStmt = $this->pdo->prepare('DELETE FROM '.$this->tableName.' WHERE path = ? AND name = ?');
136
+
137
+			foreach ($properties as $name => $value) {
138
+				if (!is_null($value)) {
139
+					if (is_scalar($value)) {
140
+						$valueType = self::VT_STRING;
141
+					} elseif ($value instanceof Complex) {
142
+						$valueType = self::VT_XML;
143
+						$value = $value->getXml();
144
+					} else {
145
+						$valueType = self::VT_OBJECT;
146
+						$value = serialize($value);
147
+					}
148
+
149
+					$updateStmt->bindParam('path', $path, \PDO::PARAM_STR);
150
+					$updateStmt->bindParam('name', $name, \PDO::PARAM_STR);
151
+					$updateStmt->bindParam('valuetype', $valueType, \PDO::PARAM_INT);
152
+					$updateStmt->bindParam('value', $value, \PDO::PARAM_LOB);
153
+
154
+					$updateStmt->execute();
155
+				} else {
156
+					$deleteStmt->execute([$path, $name]);
157
+				}
158
+			}
159
+
160
+			return true;
161
+		});
162
+	}
163
+
164
+	/**
165
+	 * This method is called after a node is deleted.
166
+	 *
167
+	 * This allows a backend to clean up all associated properties.
168
+	 *
169
+	 * The delete method will get called once for the deletion of an entire
170
+	 * tree.
171
+	 *
172
+	 * @param string $path
173
+	 */
174
+	public function delete($path)
175
+	{
176
+		$stmt = $this->pdo->prepare('DELETE FROM '.$this->tableName."  WHERE path = ? OR path LIKE ? ESCAPE '='");
177
+		$childPath = strtr(
178
+			$path,
179
+			[
180
+				'=' => '==',
181
+				'%' => '=%',
182
+				'_' => '=_',
183
+			]
184
+		).'/%';
185
+
186
+		$stmt->execute([$path, $childPath]);
187
+	}
188
+
189
+	/**
190
+	 * This method is called after a successful MOVE.
191
+	 *
192
+	 * This should be used to migrate all properties from one path to another.
193
+	 * Note that entire collections may be moved, so ensure that all properties
194
+	 * for children are also moved along.
195
+	 *
196
+	 * @param string $source
197
+	 * @param string $destination
198
+	 */
199
+	public function move($source, $destination)
200
+	{
201
+		// I don't know a way to write this all in a single sql query that's
202
+		// also compatible across db engines, so we're letting PHP do all the
203
+		// updates. Much slower, but it should still be pretty fast in most
204
+		// cases.
205
+		$select = $this->pdo->prepare('SELECT id, path FROM '.$this->tableName.'  WHERE path = ? OR path LIKE ?');
206
+		$select->execute([$source, $source.'/%']);
207
+
208
+		$update = $this->pdo->prepare('UPDATE '.$this->tableName.' SET path = ? WHERE id = ?');
209
+		while ($row = $select->fetch(\PDO::FETCH_ASSOC)) {
210
+			// Sanity check. SQL may select too many records, such as records
211
+			// with different cases.
212
+			if ($row['path'] !== $source && 0 !== strpos($row['path'], $source.'/')) {
213
+				continue;
214
+			}
215
+
216
+			$trailingPart = substr($row['path'], strlen($source) + 1);
217
+			$newPath = $destination;
218
+			if ($trailingPart) {
219
+				$newPath .= '/'.$trailingPart;
220
+			}
221
+			$update->execute([$newPath, $row['id']]);
222
+		}
223
+	}
224 224
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -116,7 +116,7 @@
 block discarded – undo
116 116
      */
117 117
     public function propPatch($path, PropPatch $propPatch)
118 118
     {
119
-        $propPatch->handleRemaining(function ($properties) use ($path) {
119
+        $propPatch->handleRemaining(function($properties) use ($path) {
120 120
             if ('pgsql' === $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
121 121
                 $updateSql = <<<SQL
122 122
 INSERT INTO {$this->tableName} (path, name, valuetype, value)
Please login to merge, or discard this patch.
Upper-Lower-Casing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -123,12 +123,12 @@
 block discarded – undo
123 123
 VALUES (:path, :name, :valuetype, :value)
124 124
 ON CONFLICT (path, name)
125 125
 DO UPDATE SET valuetype = :valuetype, value = :value
126
-SQL;
126
+sql;
127 127
             } else {
128 128
                 $updateSql = <<<SQL
129 129
 REPLACE INTO {$this->tableName} (path, name, valuetype, value)
130 130
 VALUES (:path, :name, :valuetype, :value)
131
-SQL;
131
+sql;
132 132
             }
133 133
 
134 134
             $updateStmt = $this->pdo->prepare($updateSql);
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/DAV/ServerPlugin.php 1 patch
Indentation   +82 added lines, -82 removed lines patch added patch discarded remove patch
@@ -15,91 +15,91 @@
 block discarded – undo
15 15
  */
16 16
 abstract class ServerPlugin
17 17
 {
18
-    /**
19
-     * This initializes the plugin.
20
-     *
21
-     * This function is called by Sabre\DAV\Server, after
22
-     * addPlugin is called.
23
-     *
24
-     * This method should set up the required event subscriptions.
25
-     */
26
-    abstract public function initialize(Server $server);
18
+	/**
19
+	 * This initializes the plugin.
20
+	 *
21
+	 * This function is called by Sabre\DAV\Server, after
22
+	 * addPlugin is called.
23
+	 *
24
+	 * This method should set up the required event subscriptions.
25
+	 */
26
+	abstract public function initialize(Server $server);
27 27
 
28
-    /**
29
-     * This method should return a list of server-features.
30
-     *
31
-     * This is for example 'versioning' and is added to the DAV: header
32
-     * in an OPTIONS response.
33
-     *
34
-     * @return array
35
-     */
36
-    public function getFeatures()
37
-    {
38
-        return [];
39
-    }
28
+	/**
29
+	 * This method should return a list of server-features.
30
+	 *
31
+	 * This is for example 'versioning' and is added to the DAV: header
32
+	 * in an OPTIONS response.
33
+	 *
34
+	 * @return array
35
+	 */
36
+	public function getFeatures()
37
+	{
38
+		return [];
39
+	}
40 40
 
41
-    /**
42
-     * Use this method to tell the server this plugin defines additional
43
-     * HTTP methods.
44
-     *
45
-     * This method is passed a uri. It should only return HTTP methods that are
46
-     * available for the specified uri.
47
-     *
48
-     * @param string $path
49
-     *
50
-     * @return array
51
-     */
52
-    public function getHTTPMethods($path)
53
-    {
54
-        return [];
55
-    }
41
+	/**
42
+	 * Use this method to tell the server this plugin defines additional
43
+	 * HTTP methods.
44
+	 *
45
+	 * This method is passed a uri. It should only return HTTP methods that are
46
+	 * available for the specified uri.
47
+	 *
48
+	 * @param string $path
49
+	 *
50
+	 * @return array
51
+	 */
52
+	public function getHTTPMethods($path)
53
+	{
54
+		return [];
55
+	}
56 56
 
57
-    /**
58
-     * Returns a plugin name.
59
-     *
60
-     * Using this name other plugins will be able to access other plugins
61
-     * using \Sabre\DAV\Server::getPlugin
62
-     *
63
-     * @return string
64
-     */
65
-    public function getPluginName()
66
-    {
67
-        return get_class($this);
68
-    }
57
+	/**
58
+	 * Returns a plugin name.
59
+	 *
60
+	 * Using this name other plugins will be able to access other plugins
61
+	 * using \Sabre\DAV\Server::getPlugin
62
+	 *
63
+	 * @return string
64
+	 */
65
+	public function getPluginName()
66
+	{
67
+		return get_class($this);
68
+	}
69 69
 
70
-    /**
71
-     * Returns a list of reports this plugin supports.
72
-     *
73
-     * This will be used in the {DAV:}supported-report-set property.
74
-     * Note that you still need to subscribe to the 'report' event to actually
75
-     * implement them
76
-     *
77
-     * @param string $uri
78
-     *
79
-     * @return array
80
-     */
81
-    public function getSupportedReportSet($uri)
82
-    {
83
-        return [];
84
-    }
70
+	/**
71
+	 * Returns a list of reports this plugin supports.
72
+	 *
73
+	 * This will be used in the {DAV:}supported-report-set property.
74
+	 * Note that you still need to subscribe to the 'report' event to actually
75
+	 * implement them
76
+	 *
77
+	 * @param string $uri
78
+	 *
79
+	 * @return array
80
+	 */
81
+	public function getSupportedReportSet($uri)
82
+	{
83
+		return [];
84
+	}
85 85
 
86
-    /**
87
-     * Returns a bunch of meta-data about the plugin.
88
-     *
89
-     * Providing this information is optional, and is mainly displayed by the
90
-     * Browser plugin.
91
-     *
92
-     * The description key in the returned array may contain html and will not
93
-     * be sanitized.
94
-     *
95
-     * @return array
96
-     */
97
-    public function getPluginInfo()
98
-    {
99
-        return [
100
-            'name' => $this->getPluginName(),
101
-            'description' => null,
102
-            'link' => null,
103
-        ];
104
-    }
86
+	/**
87
+	 * Returns a bunch of meta-data about the plugin.
88
+	 *
89
+	 * Providing this information is optional, and is mainly displayed by the
90
+	 * Browser plugin.
91
+	 *
92
+	 * The description key in the returned array may contain html and will not
93
+	 * be sanitized.
94
+	 *
95
+	 * @return array
96
+	 */
97
+	public function getPluginInfo()
98
+	{
99
+		return [
100
+			'name' => $this->getPluginName(),
101
+			'description' => null,
102
+			'link' => null,
103
+		];
104
+	}
105 105
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/DAV/IExtendedCollection.php 1 patch
Indentation   +24 added lines, -24 removed lines patch added patch discarded remove patch
@@ -16,28 +16,28 @@
 block discarded – undo
16 16
  */
17 17
 interface IExtendedCollection extends ICollection
18 18
 {
19
-    /**
20
-     * Creates a new collection.
21
-     *
22
-     * This method will receive a MkCol object with all the information about
23
-     * the new collection that's being created.
24
-     *
25
-     * The MkCol object contains information about the resourceType of the new
26
-     * collection. If you don't support the specified resourceType, you should
27
-     * throw Exception\InvalidResourceType.
28
-     *
29
-     * The object also contains a list of WebDAV properties for the new
30
-     * collection.
31
-     *
32
-     * You should call the handle() method on this object to specify exactly
33
-     * which properties you are storing. This allows the system to figure out
34
-     * exactly which properties you didn't store, which in turn allows other
35
-     * plugins (such as the propertystorage plugin) to handle storing the
36
-     * property for you.
37
-     *
38
-     * @param string $name
39
-     *
40
-     * @throws Exception\InvalidResourceType
41
-     */
42
-    public function createExtendedCollection($name, MkCol $mkCol);
19
+	/**
20
+	 * Creates a new collection.
21
+	 *
22
+	 * This method will receive a MkCol object with all the information about
23
+	 * the new collection that's being created.
24
+	 *
25
+	 * The MkCol object contains information about the resourceType of the new
26
+	 * collection. If you don't support the specified resourceType, you should
27
+	 * throw Exception\InvalidResourceType.
28
+	 *
29
+	 * The object also contains a list of WebDAV properties for the new
30
+	 * collection.
31
+	 *
32
+	 * You should call the handle() method on this object to specify exactly
33
+	 * which properties you are storing. This allows the system to figure out
34
+	 * exactly which properties you didn't store, which in turn allows other
35
+	 * plugins (such as the propertystorage plugin) to handle storing the
36
+	 * property for you.
37
+	 *
38
+	 * @param string $name
39
+	 *
40
+	 * @throws Exception\InvalidResourceType
41
+	 */
42
+	public function createExtendedCollection($name, MkCol $mkCol);
43 43
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/DAV/SimpleFile.php 1 patch
Indentation   +90 added lines, -90 removed lines patch added patch discarded remove patch
@@ -17,102 +17,102 @@
 block discarded – undo
17 17
  */
18 18
 class SimpleFile extends File
19 19
 {
20
-    /**
21
-     * File contents.
22
-     *
23
-     * @var string
24
-     */
25
-    protected $contents = [];
20
+	/**
21
+	 * File contents.
22
+	 *
23
+	 * @var string
24
+	 */
25
+	protected $contents = [];
26 26
 
27
-    /**
28
-     * Name of this resource.
29
-     *
30
-     * @var string
31
-     */
32
-    protected $name;
27
+	/**
28
+	 * Name of this resource.
29
+	 *
30
+	 * @var string
31
+	 */
32
+	protected $name;
33 33
 
34
-    /**
35
-     * A mimetype, such as 'text/plain' or 'text/html'.
36
-     *
37
-     * @var string
38
-     */
39
-    protected $mimeType;
34
+	/**
35
+	 * A mimetype, such as 'text/plain' or 'text/html'.
36
+	 *
37
+	 * @var string
38
+	 */
39
+	protected $mimeType;
40 40
 
41
-    /**
42
-     * Creates this node.
43
-     *
44
-     * The name of the node must be passed, as well as the contents of the
45
-     * file.
46
-     *
47
-     * @param string      $name
48
-     * @param string      $contents
49
-     * @param string|null $mimeType
50
-     */
51
-    public function __construct($name, $contents, $mimeType = null)
52
-    {
53
-        $this->name = $name;
54
-        $this->contents = $contents;
55
-        $this->mimeType = $mimeType;
56
-    }
41
+	/**
42
+	 * Creates this node.
43
+	 *
44
+	 * The name of the node must be passed, as well as the contents of the
45
+	 * file.
46
+	 *
47
+	 * @param string      $name
48
+	 * @param string      $contents
49
+	 * @param string|null $mimeType
50
+	 */
51
+	public function __construct($name, $contents, $mimeType = null)
52
+	{
53
+		$this->name = $name;
54
+		$this->contents = $contents;
55
+		$this->mimeType = $mimeType;
56
+	}
57 57
 
58
-    /**
59
-     * Returns the node name for this file.
60
-     *
61
-     * This name is used to construct the url.
62
-     *
63
-     * @return string
64
-     */
65
-    public function getName()
66
-    {
67
-        return $this->name;
68
-    }
58
+	/**
59
+	 * Returns the node name for this file.
60
+	 *
61
+	 * This name is used to construct the url.
62
+	 *
63
+	 * @return string
64
+	 */
65
+	public function getName()
66
+	{
67
+		return $this->name;
68
+	}
69 69
 
70
-    /**
71
-     * Returns the data.
72
-     *
73
-     * This method may either return a string or a readable stream resource
74
-     *
75
-     * @return mixed
76
-     */
77
-    public function get()
78
-    {
79
-        return $this->contents;
80
-    }
70
+	/**
71
+	 * Returns the data.
72
+	 *
73
+	 * This method may either return a string or a readable stream resource
74
+	 *
75
+	 * @return mixed
76
+	 */
77
+	public function get()
78
+	{
79
+		return $this->contents;
80
+	}
81 81
 
82
-    /**
83
-     * Returns the size of the file, in bytes.
84
-     *
85
-     * @return int
86
-     */
87
-    public function getSize()
88
-    {
89
-        return strlen($this->contents);
90
-    }
82
+	/**
83
+	 * Returns the size of the file, in bytes.
84
+	 *
85
+	 * @return int
86
+	 */
87
+	public function getSize()
88
+	{
89
+		return strlen($this->contents);
90
+	}
91 91
 
92
-    /**
93
-     * Returns the ETag for a file.
94
-     *
95
-     * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
96
-     * The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
97
-     *
98
-     * Return null if the ETag can not effectively be determined
99
-     *
100
-     * @return string
101
-     */
102
-    public function getETag()
103
-    {
104
-        return '"'.sha1($this->contents).'"';
105
-    }
92
+	/**
93
+	 * Returns the ETag for a file.
94
+	 *
95
+	 * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
96
+	 * The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
97
+	 *
98
+	 * Return null if the ETag can not effectively be determined
99
+	 *
100
+	 * @return string
101
+	 */
102
+	public function getETag()
103
+	{
104
+		return '"'.sha1($this->contents).'"';
105
+	}
106 106
 
107
-    /**
108
-     * Returns the mime-type for a file.
109
-     *
110
-     * If null is returned, we'll assume application/octet-stream
111
-     *
112
-     * @return string
113
-     */
114
-    public function getContentType()
115
-    {
116
-        return $this->mimeType;
117
-    }
107
+	/**
108
+	 * Returns the mime-type for a file.
109
+	 *
110
+	 * If null is returned, we'll assume application/octet-stream
111
+	 *
112
+	 * @return string
113
+	 */
114
+	public function getContentType()
115
+	{
116
+		return $this->mimeType;
117
+	}
118 118
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/DAV/IQuota.php 1 patch
Indentation   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -17,11 +17,11 @@
 block discarded – undo
17 17
  */
18 18
 interface IQuota extends ICollection
19 19
 {
20
-    /**
21
-     * Returns the quota information.
22
-     *
23
-     * This method MUST return an array with 2 values, the first being the total used space,
24
-     * the second the available space (in bytes)
25
-     */
26
-    public function getQuotaInfo();
20
+	/**
21
+	 * Returns the quota information.
22
+	 *
23
+	 * This method MUST return an array with 2 values, the first being the total used space,
24
+	 * the second the available space (in bytes)
25
+	 */
26
+	public function getQuotaInfo();
27 27
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/DAV/FS/Directory.php 1 patch
Indentation   +119 added lines, -119 removed lines patch added patch discarded remove patch
@@ -15,133 +15,133 @@
 block discarded – undo
15 15
  */
16 16
 class Directory extends Node implements DAV\ICollection, DAV\IQuota
17 17
 {
18
-    /**
19
-     * Creates a new file in the directory.
20
-     *
21
-     * Data will either be supplied as a stream resource, or in certain cases
22
-     * as a string. Keep in mind that you may have to support either.
23
-     *
24
-     * After successful creation of the file, you may choose to return the ETag
25
-     * of the new file here.
26
-     *
27
-     * The returned ETag must be surrounded by double-quotes (The quotes should
28
-     * be part of the actual string).
29
-     *
30
-     * If you cannot accurately determine the ETag, you should not return it.
31
-     * If you don't store the file exactly as-is (you're transforming it
32
-     * somehow) you should also not return an ETag.
33
-     *
34
-     * This means that if a subsequent GET to this new file does not exactly
35
-     * return the same contents of what was submitted here, you are strongly
36
-     * recommended to omit the ETag.
37
-     *
38
-     * @param string          $name Name of the file
39
-     * @param resource|string $data Initial payload
40
-     *
41
-     * @return string|null
42
-     */
43
-    public function createFile($name, $data = null)
44
-    {
45
-        $newPath = $this->path.'/'.$name;
46
-        file_put_contents($newPath, $data);
47
-        clearstatcache(true, $newPath);
48
-    }
18
+	/**
19
+	 * Creates a new file in the directory.
20
+	 *
21
+	 * Data will either be supplied as a stream resource, or in certain cases
22
+	 * as a string. Keep in mind that you may have to support either.
23
+	 *
24
+	 * After successful creation of the file, you may choose to return the ETag
25
+	 * of the new file here.
26
+	 *
27
+	 * The returned ETag must be surrounded by double-quotes (The quotes should
28
+	 * be part of the actual string).
29
+	 *
30
+	 * If you cannot accurately determine the ETag, you should not return it.
31
+	 * If you don't store the file exactly as-is (you're transforming it
32
+	 * somehow) you should also not return an ETag.
33
+	 *
34
+	 * This means that if a subsequent GET to this new file does not exactly
35
+	 * return the same contents of what was submitted here, you are strongly
36
+	 * recommended to omit the ETag.
37
+	 *
38
+	 * @param string          $name Name of the file
39
+	 * @param resource|string $data Initial payload
40
+	 *
41
+	 * @return string|null
42
+	 */
43
+	public function createFile($name, $data = null)
44
+	{
45
+		$newPath = $this->path.'/'.$name;
46
+		file_put_contents($newPath, $data);
47
+		clearstatcache(true, $newPath);
48
+	}
49 49
 
50
-    /**
51
-     * Creates a new subdirectory.
52
-     *
53
-     * @param string $name
54
-     */
55
-    public function createDirectory($name)
56
-    {
57
-        $newPath = $this->path.'/'.$name;
58
-        mkdir($newPath);
59
-        clearstatcache(true, $newPath);
60
-    }
50
+	/**
51
+	 * Creates a new subdirectory.
52
+	 *
53
+	 * @param string $name
54
+	 */
55
+	public function createDirectory($name)
56
+	{
57
+		$newPath = $this->path.'/'.$name;
58
+		mkdir($newPath);
59
+		clearstatcache(true, $newPath);
60
+	}
61 61
 
62
-    /**
63
-     * Returns a specific child node, referenced by its name.
64
-     *
65
-     * This method must throw DAV\Exception\NotFound if the node does not
66
-     * exist.
67
-     *
68
-     * @param string $name
69
-     *
70
-     * @throws DAV\Exception\NotFound
71
-     *
72
-     * @return DAV\INode
73
-     */
74
-    public function getChild($name)
75
-    {
76
-        $path = $this->path.'/'.$name;
62
+	/**
63
+	 * Returns a specific child node, referenced by its name.
64
+	 *
65
+	 * This method must throw DAV\Exception\NotFound if the node does not
66
+	 * exist.
67
+	 *
68
+	 * @param string $name
69
+	 *
70
+	 * @throws DAV\Exception\NotFound
71
+	 *
72
+	 * @return DAV\INode
73
+	 */
74
+	public function getChild($name)
75
+	{
76
+		$path = $this->path.'/'.$name;
77 77
 
78
-        if (!file_exists($path)) {
79
-            throw new DAV\Exception\NotFound('File with name '.$path.' could not be located');
80
-        }
81
-        if (is_dir($path)) {
82
-            return new self($path);
83
-        } else {
84
-            return new File($path);
85
-        }
86
-    }
78
+		if (!file_exists($path)) {
79
+			throw new DAV\Exception\NotFound('File with name '.$path.' could not be located');
80
+		}
81
+		if (is_dir($path)) {
82
+			return new self($path);
83
+		} else {
84
+			return new File($path);
85
+		}
86
+	}
87 87
 
88
-    /**
89
-     * Returns an array with all the child nodes.
90
-     *
91
-     * @return DAV\INode[]
92
-     */
93
-    public function getChildren()
94
-    {
95
-        $nodes = [];
96
-        $iterator = new \FilesystemIterator(
97
-            $this->path,
98
-            \FilesystemIterator::CURRENT_AS_SELF
99
-          | \FilesystemIterator::SKIP_DOTS
100
-        );
101
-        foreach ($iterator as $entry) {
102
-            $nodes[] = $this->getChild($entry->getFilename());
103
-        }
88
+	/**
89
+	 * Returns an array with all the child nodes.
90
+	 *
91
+	 * @return DAV\INode[]
92
+	 */
93
+	public function getChildren()
94
+	{
95
+		$nodes = [];
96
+		$iterator = new \FilesystemIterator(
97
+			$this->path,
98
+			\FilesystemIterator::CURRENT_AS_SELF
99
+		  | \FilesystemIterator::SKIP_DOTS
100
+		);
101
+		foreach ($iterator as $entry) {
102
+			$nodes[] = $this->getChild($entry->getFilename());
103
+		}
104 104
 
105
-        return $nodes;
106
-    }
105
+		return $nodes;
106
+	}
107 107
 
108
-    /**
109
-     * Checks if a child exists.
110
-     *
111
-     * @param string $name
112
-     *
113
-     * @return bool
114
-     */
115
-    public function childExists($name)
116
-    {
117
-        $path = $this->path.'/'.$name;
108
+	/**
109
+	 * Checks if a child exists.
110
+	 *
111
+	 * @param string $name
112
+	 *
113
+	 * @return bool
114
+	 */
115
+	public function childExists($name)
116
+	{
117
+		$path = $this->path.'/'.$name;
118 118
 
119
-        return file_exists($path);
120
-    }
119
+		return file_exists($path);
120
+	}
121 121
 
122
-    /**
123
-     * Deletes all files in this directory, and then itself.
124
-     */
125
-    public function delete()
126
-    {
127
-        foreach ($this->getChildren() as $child) {
128
-            $child->delete();
129
-        }
130
-        rmdir($this->path);
131
-    }
122
+	/**
123
+	 * Deletes all files in this directory, and then itself.
124
+	 */
125
+	public function delete()
126
+	{
127
+		foreach ($this->getChildren() as $child) {
128
+			$child->delete();
129
+		}
130
+		rmdir($this->path);
131
+	}
132 132
 
133
-    /**
134
-     * Returns available diskspace information.
135
-     *
136
-     * @return array
137
-     */
138
-    public function getQuotaInfo()
139
-    {
140
-        $absolute = realpath($this->path);
133
+	/**
134
+	 * Returns available diskspace information.
135
+	 *
136
+	 * @return array
137
+	 */
138
+	public function getQuotaInfo()
139
+	{
140
+		$absolute = realpath($this->path);
141 141
 
142
-        return [
143
-            disk_total_space($absolute) - disk_free_space($absolute),
144
-            disk_free_space($absolute),
145
-        ];
146
-    }
142
+		return [
143
+			disk_total_space($absolute) - disk_free_space($absolute),
144
+			disk_free_space($absolute),
145
+		];
146
+	}
147 147
 }
Please login to merge, or discard this patch.