Completed
Push — master ( f38d36...9039b7 )
by Lukas
21:28 queued 09:01
created
apps/files_external/lib/Lib/Storage/OwnCloud.php 2 patches
Indentation   +36 added lines, -36 removed lines patch added patch discarded remove patch
@@ -33,45 +33,45 @@
 block discarded – undo
33 33
  *
34 34
  */
35 35
 class OwnCloud extends \OC\Files\Storage\DAV{
36
-	const OC_URL_SUFFIX = 'remote.php/webdav';
36
+    const OC_URL_SUFFIX = 'remote.php/webdav';
37 37
 
38
-	public function __construct($params) {
39
-		// extract context path from host if specified
40
-		// (owncloud install path on host)
41
-		$host = $params['host'];
42
-		// strip protocol
43
-		if (substr($host, 0, 8) == "https://") {
44
-			$host = substr($host, 8);
45
-			$params['secure'] = true;
46
-		} else if (substr($host, 0, 7) == "http://") {
47
-			$host = substr($host, 7);
48
-			$params['secure'] = false;
49
-		}
50
-		$contextPath = '';
51
-		$hostSlashPos = strpos($host, '/');
52
-		if ($hostSlashPos !== false){
53
-			$contextPath = substr($host, $hostSlashPos);
54
-			$host = substr($host, 0, $hostSlashPos);
55
-		}
38
+    public function __construct($params) {
39
+        // extract context path from host if specified
40
+        // (owncloud install path on host)
41
+        $host = $params['host'];
42
+        // strip protocol
43
+        if (substr($host, 0, 8) == "https://") {
44
+            $host = substr($host, 8);
45
+            $params['secure'] = true;
46
+        } else if (substr($host, 0, 7) == "http://") {
47
+            $host = substr($host, 7);
48
+            $params['secure'] = false;
49
+        }
50
+        $contextPath = '';
51
+        $hostSlashPos = strpos($host, '/');
52
+        if ($hostSlashPos !== false){
53
+            $contextPath = substr($host, $hostSlashPos);
54
+            $host = substr($host, 0, $hostSlashPos);
55
+        }
56 56
 
57
-		if (substr($contextPath, -1) !== '/'){
58
-			$contextPath .= '/';
59
-		}
57
+        if (substr($contextPath, -1) !== '/'){
58
+            $contextPath .= '/';
59
+        }
60 60
 
61
-		if (isset($params['root'])){
62
-			$root = $params['root'];
63
-			if (substr($root, 0, 1) !== '/'){
64
-				$root = '/' . $root;
65
-			}
66
-		}
67
-		else{
68
-			$root = '/';
69
-		}
61
+        if (isset($params['root'])){
62
+            $root = $params['root'];
63
+            if (substr($root, 0, 1) !== '/'){
64
+                $root = '/' . $root;
65
+            }
66
+        }
67
+        else{
68
+            $root = '/';
69
+        }
70 70
 
71
-		$params['host'] = $host;
72
-		$params['root'] = $contextPath . self::OC_URL_SUFFIX . $root;
73
-		$params['authType'] = Client::AUTH_BASIC;
71
+        $params['host'] = $host;
72
+        $params['root'] = $contextPath . self::OC_URL_SUFFIX . $root;
73
+        $params['authType'] = Client::AUTH_BASIC;
74 74
 
75
-		parent::__construct($params);
76
-	}
75
+        parent::__construct($params);
76
+    }
77 77
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -32,7 +32,7 @@  discard block
 block discarded – undo
32 32
  * http://%host/%context/remote.php/webdav/%root
33 33
  *
34 34
  */
35
-class OwnCloud extends \OC\Files\Storage\DAV{
35
+class OwnCloud extends \OC\Files\Storage\DAV {
36 36
 	const OC_URL_SUFFIX = 'remote.php/webdav';
37 37
 
38 38
 	public function __construct($params) {
@@ -49,27 +49,27 @@  discard block
 block discarded – undo
49 49
 		}
50 50
 		$contextPath = '';
51 51
 		$hostSlashPos = strpos($host, '/');
52
-		if ($hostSlashPos !== false){
52
+		if ($hostSlashPos !== false) {
53 53
 			$contextPath = substr($host, $hostSlashPos);
54 54
 			$host = substr($host, 0, $hostSlashPos);
55 55
 		}
56 56
 
57
-		if (substr($contextPath, -1) !== '/'){
57
+		if (substr($contextPath, -1) !== '/') {
58 58
 			$contextPath .= '/';
59 59
 		}
60 60
 
61
-		if (isset($params['root'])){
61
+		if (isset($params['root'])) {
62 62
 			$root = $params['root'];
63
-			if (substr($root, 0, 1) !== '/'){
64
-				$root = '/' . $root;
63
+			if (substr($root, 0, 1) !== '/') {
64
+				$root = '/'.$root;
65 65
 			}
66 66
 		}
67
-		else{
67
+		else {
68 68
 			$root = '/';
69 69
 		}
70 70
 
71 71
 		$params['host'] = $host;
72
-		$params['root'] = $contextPath . self::OC_URL_SUFFIX . $root;
72
+		$params['root'] = $contextPath.self::OC_URL_SUFFIX.$root;
73 73
 		$params['authType'] = Client::AUTH_BASIC;
74 74
 
75 75
 		parent::__construct($params);
Please login to merge, or discard this patch.
lib/private/Files/Storage/DAV.php 2 patches
Indentation   +793 added lines, -793 removed lines patch added patch discarded remove patch
@@ -59,798 +59,798 @@
 block discarded – undo
59 59
  * @package OC\Files\Storage
60 60
  */
61 61
 class DAV extends Common {
62
-	/** @var string */
63
-	protected $password;
64
-	/** @var string */
65
-	protected $user;
66
-	/** @var string */
67
-	protected $authType;
68
-	/** @var string */
69
-	protected $host;
70
-	/** @var bool */
71
-	protected $secure;
72
-	/** @var string */
73
-	protected $root;
74
-	/** @var string */
75
-	protected $certPath;
76
-	/** @var bool */
77
-	protected $ready;
78
-	/** @var Client */
79
-	protected $client;
80
-	/** @var ArrayCache */
81
-	protected $statCache;
82
-	/** @var \OCP\Http\Client\IClientService */
83
-	protected $httpClientService;
84
-
85
-	/**
86
-	 * @param array $params
87
-	 * @throws \Exception
88
-	 */
89
-	public function __construct($params) {
90
-		$this->statCache = new ArrayCache();
91
-		$this->httpClientService = \OC::$server->getHTTPClientService();
92
-		if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
93
-			$host = $params['host'];
94
-			//remove leading http[s], will be generated in createBaseUri()
95
-			if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
96
-			else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
97
-			$this->host = $host;
98
-			$this->user = $params['user'];
99
-			$this->password = $params['password'];
100
-			if (isset($params['authType'])) {
101
-				$this->authType = $params['authType'];
102
-			}
103
-			if (isset($params['secure'])) {
104
-				if (is_string($params['secure'])) {
105
-					$this->secure = ($params['secure'] === 'true');
106
-				} else {
107
-					$this->secure = (bool)$params['secure'];
108
-				}
109
-			} else {
110
-				$this->secure = false;
111
-			}
112
-			if ($this->secure === true) {
113
-				// inject mock for testing
114
-				$certManager = \OC::$server->getCertificateManager();
115
-				if (is_null($certManager)) { //no user
116
-					$certManager = \OC::$server->getCertificateManager(null);
117
-				}
118
-				$certPath = $certManager->getAbsoluteBundlePath();
119
-				if (file_exists($certPath)) {
120
-					$this->certPath = $certPath;
121
-				}
122
-			}
123
-			$this->root = isset($params['root']) ? $params['root'] : '/';
124
-			if (!$this->root || $this->root[0] != '/') {
125
-				$this->root = '/' . $this->root;
126
-			}
127
-			if (substr($this->root, -1, 1) != '/') {
128
-				$this->root .= '/';
129
-			}
130
-		} else {
131
-			throw new \Exception('Invalid webdav storage configuration');
132
-		}
133
-	}
134
-
135
-	protected function init() {
136
-		if ($this->ready) {
137
-			return;
138
-		}
139
-		$this->ready = true;
140
-
141
-		$settings = [
142
-			'baseUri' => $this->createBaseUri(),
143
-			'userName' => $this->user,
144
-			'password' => $this->password,
145
-		];
146
-		if (isset($this->authType)) {
147
-			$settings['authType'] = $this->authType;
148
-		}
149
-
150
-		$proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
151
-		if($proxy !== '') {
152
-			$settings['proxy'] = $proxy;
153
-		}
154
-
155
-		$this->client = new Client($settings);
156
-		$this->client->setThrowExceptions(true);
157
-		if ($this->secure === true && $this->certPath) {
158
-			$this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
159
-		}
160
-	}
161
-
162
-	/**
163
-	 * Clear the stat cache
164
-	 */
165
-	public function clearStatCache() {
166
-		$this->statCache->clear();
167
-	}
168
-
169
-	/** {@inheritdoc} */
170
-	public function getId() {
171
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
172
-	}
173
-
174
-	/** {@inheritdoc} */
175
-	public function createBaseUri() {
176
-		$baseUri = 'http';
177
-		if ($this->secure) {
178
-			$baseUri .= 's';
179
-		}
180
-		$baseUri .= '://' . $this->host . $this->root;
181
-		return $baseUri;
182
-	}
183
-
184
-	/** {@inheritdoc} */
185
-	public function mkdir($path) {
186
-		$this->init();
187
-		$path = $this->cleanPath($path);
188
-		$result = $this->simpleResponse('MKCOL', $path, null, 201);
189
-		if ($result) {
190
-			$this->statCache->set($path, true);
191
-		}
192
-		return $result;
193
-	}
194
-
195
-	/** {@inheritdoc} */
196
-	public function rmdir($path) {
197
-		$this->init();
198
-		$path = $this->cleanPath($path);
199
-		// FIXME: some WebDAV impl return 403 when trying to DELETE
200
-		// a non-empty folder
201
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
202
-		$this->statCache->clear($path . '/');
203
-		$this->statCache->remove($path);
204
-		return $result;
205
-	}
206
-
207
-	/** {@inheritdoc} */
208
-	public function opendir($path) {
209
-		$this->init();
210
-		$path = $this->cleanPath($path);
211
-		try {
212
-			$response = $this->client->propFind(
213
-				$this->encodePath($path),
214
-				['{DAV:}href'],
215
-				1
216
-			);
217
-			if ($response === false) {
218
-				return false;
219
-			}
220
-			$content = [];
221
-			$files = array_keys($response);
222
-			array_shift($files); //the first entry is the current directory
223
-
224
-			if (!$this->statCache->hasKey($path)) {
225
-				$this->statCache->set($path, true);
226
-			}
227
-			foreach ($files as $file) {
228
-				$file = urldecode($file);
229
-				// do not store the real entry, we might not have all properties
230
-				if (!$this->statCache->hasKey($path)) {
231
-					$this->statCache->set($file, true);
232
-				}
233
-				$file = basename($file);
234
-				$content[] = $file;
235
-			}
236
-			return IteratorDirectory::wrap($content);
237
-		} catch (\Exception $e) {
238
-			$this->convertException($e, $path);
239
-		}
240
-		return false;
241
-	}
242
-
243
-	/**
244
-	 * Propfind call with cache handling.
245
-	 *
246
-	 * First checks if information is cached.
247
-	 * If not, request it from the server then store to cache.
248
-	 *
249
-	 * @param string $path path to propfind
250
-	 *
251
-	 * @return array|boolean propfind response or false if the entry was not found
252
-	 *
253
-	 * @throws ClientHttpException
254
-	 */
255
-	protected function propfind($path) {
256
-		$path = $this->cleanPath($path);
257
-		$cachedResponse = $this->statCache->get($path);
258
-		// we either don't know it, or we know it exists but need more details
259
-		if (is_null($cachedResponse) || $cachedResponse === true) {
260
-			$this->init();
261
-			try {
262
-				$response = $this->client->propFind(
263
-					$this->encodePath($path),
264
-					array(
265
-						'{DAV:}getlastmodified',
266
-						'{DAV:}getcontentlength',
267
-						'{DAV:}getcontenttype',
268
-						'{http://owncloud.org/ns}permissions',
269
-						'{http://open-collaboration-services.org/ns}share-permissions',
270
-						'{DAV:}resourcetype',
271
-						'{DAV:}getetag',
272
-					)
273
-				);
274
-				$this->statCache->set($path, $response);
275
-			} catch (ClientHttpException $e) {
276
-				if ($e->getHttpStatus() === 404) {
277
-					$this->statCache->clear($path . '/');
278
-					$this->statCache->set($path, false);
279
-					return false;
280
-				}
281
-				$this->convertException($e, $path);
282
-			} catch (\Exception $e) {
283
-				$this->convertException($e, $path);
284
-			}
285
-		} else {
286
-			$response = $cachedResponse;
287
-		}
288
-		return $response;
289
-	}
290
-
291
-	/** {@inheritdoc} */
292
-	public function filetype($path) {
293
-		try {
294
-			$response = $this->propfind($path);
295
-			if ($response === false) {
296
-				return false;
297
-			}
298
-			$responseType = [];
299
-			if (isset($response["{DAV:}resourcetype"])) {
300
-				/** @var ResourceType[] $response */
301
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
302
-			}
303
-			return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
304
-		} catch (\Exception $e) {
305
-			$this->convertException($e, $path);
306
-		}
307
-		return false;
308
-	}
309
-
310
-	/** {@inheritdoc} */
311
-	public function file_exists($path) {
312
-		try {
313
-			$path = $this->cleanPath($path);
314
-			$cachedState = $this->statCache->get($path);
315
-			if ($cachedState === false) {
316
-				// we know the file doesn't exist
317
-				return false;
318
-			} else if (!is_null($cachedState)) {
319
-				return true;
320
-			}
321
-			// need to get from server
322
-			return ($this->propfind($path) !== false);
323
-		} catch (\Exception $e) {
324
-			$this->convertException($e, $path);
325
-		}
326
-		return false;
327
-	}
328
-
329
-	/** {@inheritdoc} */
330
-	public function unlink($path) {
331
-		$this->init();
332
-		$path = $this->cleanPath($path);
333
-		$result = $this->simpleResponse('DELETE', $path, null, 204);
334
-		$this->statCache->clear($path . '/');
335
-		$this->statCache->remove($path);
336
-		return $result;
337
-	}
338
-
339
-	/** {@inheritdoc} */
340
-	public function fopen($path, $mode) {
341
-		$this->init();
342
-		$path = $this->cleanPath($path);
343
-		switch ($mode) {
344
-			case 'r':
345
-			case 'rb':
346
-				try {
347
-					$response = $this->httpClientService
348
-							->newClient()
349
-							->get($this->createBaseUri() . $this->encodePath($path), [
350
-									'auth' => [$this->user, $this->password],
351
-									'stream' => true
352
-							]);
353
-				} catch (RequestException $e) {
354
-					if ($e->getResponse() instanceof ResponseInterface
355
-						&& $e->getResponse()->getStatusCode() === 404) {
356
-						return false;
357
-					} else {
358
-						throw $e;
359
-					}
360
-				}
361
-
362
-				if ($response->getStatusCode() !== Http::STATUS_OK) {
363
-					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
364
-						throw new \OCP\Lock\LockedException($path);
365
-					} else {
366
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
367
-					}
368
-				}
369
-
370
-				return $response->getBody();
371
-			case 'w':
372
-			case 'wb':
373
-			case 'a':
374
-			case 'ab':
375
-			case 'r+':
376
-			case 'w+':
377
-			case 'wb+':
378
-			case 'a+':
379
-			case 'x':
380
-			case 'x+':
381
-			case 'c':
382
-			case 'c+':
383
-				//emulate these
384
-				$tempManager = \OC::$server->getTempManager();
385
-				if (strrpos($path, '.') !== false) {
386
-					$ext = substr($path, strrpos($path, '.'));
387
-				} else {
388
-					$ext = '';
389
-				}
390
-				if ($this->file_exists($path)) {
391
-					if (!$this->isUpdatable($path)) {
392
-						return false;
393
-					}
394
-					if ($mode === 'w' or $mode === 'w+') {
395
-						$tmpFile = $tempManager->getTemporaryFile($ext);
396
-					} else {
397
-						$tmpFile = $this->getCachedFile($path);
398
-					}
399
-				} else {
400
-					if (!$this->isCreatable(dirname($path))) {
401
-						return false;
402
-					}
403
-					$tmpFile = $tempManager->getTemporaryFile($ext);
404
-				}
405
-				$handle = fopen($tmpFile, $mode);
406
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
407
-					$this->writeBack($tmpFile, $path);
408
-				});
409
-		}
410
-	}
411
-
412
-	/**
413
-	 * @param string $tmpFile
414
-	 */
415
-	public function writeBack($tmpFile, $path) {
416
-		$this->uploadFile($tmpFile, $path);
417
-		unlink($tmpFile);
418
-	}
419
-
420
-	/** {@inheritdoc} */
421
-	public function free_space($path) {
422
-		$this->init();
423
-		$path = $this->cleanPath($path);
424
-		try {
425
-			// TODO: cacheable ?
426
-			$response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
427
-			if ($response === false) {
428
-				return FileInfo::SPACE_UNKNOWN;
429
-			}
430
-			if (isset($response['{DAV:}quota-available-bytes'])) {
431
-				return (int)$response['{DAV:}quota-available-bytes'];
432
-			} else {
433
-				return FileInfo::SPACE_UNKNOWN;
434
-			}
435
-		} catch (\Exception $e) {
436
-			return FileInfo::SPACE_UNKNOWN;
437
-		}
438
-	}
439
-
440
-	/** {@inheritdoc} */
441
-	public function touch($path, $mtime = null) {
442
-		$this->init();
443
-		if (is_null($mtime)) {
444
-			$mtime = time();
445
-		}
446
-		$path = $this->cleanPath($path);
447
-
448
-		// if file exists, update the mtime, else create a new empty file
449
-		if ($this->file_exists($path)) {
450
-			try {
451
-				$this->statCache->remove($path);
452
-				$this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
453
-				// non-owncloud clients might not have accepted the property, need to recheck it
454
-				$response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
455
-				if ($response === false) {
456
-					return false;
457
-				}
458
-				if (isset($response['{DAV:}getlastmodified'])) {
459
-					$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
460
-					if ($remoteMtime !== $mtime) {
461
-						// server has not accepted the mtime
462
-						return false;
463
-					}
464
-				}
465
-			} catch (ClientHttpException $e) {
466
-				if ($e->getHttpStatus() === 501) {
467
-					return false;
468
-				}
469
-				$this->convertException($e, $path);
470
-				return false;
471
-			} catch (\Exception $e) {
472
-				$this->convertException($e, $path);
473
-				return false;
474
-			}
475
-		} else {
476
-			$this->file_put_contents($path, '');
477
-		}
478
-		return true;
479
-	}
480
-
481
-	/**
482
-	 * @param string $path
483
-	 * @param string $data
484
-	 * @return int
485
-	 */
486
-	public function file_put_contents($path, $data) {
487
-		$path = $this->cleanPath($path);
488
-		$result = parent::file_put_contents($path, $data);
489
-		$this->statCache->remove($path);
490
-		return $result;
491
-	}
492
-
493
-	/**
494
-	 * @param string $path
495
-	 * @param string $target
496
-	 */
497
-	protected function uploadFile($path, $target) {
498
-		$this->init();
499
-
500
-		// invalidate
501
-		$target = $this->cleanPath($target);
502
-		$this->statCache->remove($target);
503
-		$source = fopen($path, 'r');
504
-
505
-		$this->httpClientService
506
-			->newClient()
507
-			->put($this->createBaseUri() . $this->encodePath($target), [
508
-				'body' => $source,
509
-				'auth' => [$this->user, $this->password]
510
-			]);
511
-
512
-		$this->removeCachedFile($target);
513
-	}
514
-
515
-	/** {@inheritdoc} */
516
-	public function rename($path1, $path2) {
517
-		$this->init();
518
-		$path1 = $this->cleanPath($path1);
519
-		$path2 = $this->cleanPath($path2);
520
-		try {
521
-			// overwrite directory ?
522
-			if ($this->is_dir($path2)) {
523
-				// needs trailing slash in destination
524
-				$path2 = rtrim($path2, '/') . '/';
525
-			}
526
-			$this->client->request(
527
-				'MOVE',
528
-				$this->encodePath($path1),
529
-				null,
530
-				[
531
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
532
-				]
533
-			);
534
-			$this->statCache->clear($path1 . '/');
535
-			$this->statCache->clear($path2 . '/');
536
-			$this->statCache->set($path1, false);
537
-			$this->statCache->set($path2, true);
538
-			$this->removeCachedFile($path1);
539
-			$this->removeCachedFile($path2);
540
-			return true;
541
-		} catch (\Exception $e) {
542
-			$this->convertException($e);
543
-		}
544
-		return false;
545
-	}
546
-
547
-	/** {@inheritdoc} */
548
-	public function copy($path1, $path2) {
549
-		$this->init();
550
-		$path1 = $this->cleanPath($path1);
551
-		$path2 = $this->cleanPath($path2);
552
-		try {
553
-			// overwrite directory ?
554
-			if ($this->is_dir($path2)) {
555
-				// needs trailing slash in destination
556
-				$path2 = rtrim($path2, '/') . '/';
557
-			}
558
-			$this->client->request(
559
-				'COPY',
560
-				$this->encodePath($path1),
561
-				null,
562
-				[
563
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
564
-				]
565
-			);
566
-			$this->statCache->clear($path2 . '/');
567
-			$this->statCache->set($path2, true);
568
-			$this->removeCachedFile($path2);
569
-			return true;
570
-		} catch (\Exception $e) {
571
-			$this->convertException($e);
572
-		}
573
-		return false;
574
-	}
575
-
576
-	/** {@inheritdoc} */
577
-	public function stat($path) {
578
-		try {
579
-			$response = $this->propfind($path);
580
-			if (!$response) {
581
-				return false;
582
-			}
583
-			return [
584
-				'mtime' => strtotime($response['{DAV:}getlastmodified']),
585
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
586
-			];
587
-		} catch (\Exception $e) {
588
-			$this->convertException($e, $path);
589
-		}
590
-		return array();
591
-	}
592
-
593
-	/** {@inheritdoc} */
594
-	public function getMimeType($path) {
595
-		try {
596
-			$response = $this->propfind($path);
597
-			if ($response === false) {
598
-				return false;
599
-			}
600
-			$responseType = [];
601
-			if (isset($response["{DAV:}resourcetype"])) {
602
-				/** @var ResourceType[] $response */
603
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
604
-			}
605
-			$type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
606
-			if ($type == 'dir') {
607
-				return 'httpd/unix-directory';
608
-			} elseif (isset($response['{DAV:}getcontenttype'])) {
609
-				return $response['{DAV:}getcontenttype'];
610
-			} else {
611
-				return false;
612
-			}
613
-		} catch (\Exception $e) {
614
-			$this->convertException($e, $path);
615
-		}
616
-		return false;
617
-	}
618
-
619
-	/**
620
-	 * @param string $path
621
-	 * @return string
622
-	 */
623
-	public function cleanPath($path) {
624
-		if ($path === '') {
625
-			return $path;
626
-		}
627
-		$path = Filesystem::normalizePath($path);
628
-		// remove leading slash
629
-		return substr($path, 1);
630
-	}
631
-
632
-	/**
633
-	 * URL encodes the given path but keeps the slashes
634
-	 *
635
-	 * @param string $path to encode
636
-	 * @return string encoded path
637
-	 */
638
-	protected function encodePath($path) {
639
-		// slashes need to stay
640
-		return str_replace('%2F', '/', rawurlencode($path));
641
-	}
642
-
643
-	/**
644
-	 * @param string $method
645
-	 * @param string $path
646
-	 * @param string|resource|null $body
647
-	 * @param int $expected
648
-	 * @return bool
649
-	 * @throws StorageInvalidException
650
-	 * @throws StorageNotAvailableException
651
-	 */
652
-	protected function simpleResponse($method, $path, $body, $expected) {
653
-		$path = $this->cleanPath($path);
654
-		try {
655
-			$response = $this->client->request($method, $this->encodePath($path), $body);
656
-			return $response['statusCode'] == $expected;
657
-		} catch (ClientHttpException $e) {
658
-			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
659
-				$this->statCache->clear($path . '/');
660
-				$this->statCache->set($path, false);
661
-				return false;
662
-			}
663
-
664
-			$this->convertException($e, $path);
665
-		} catch (\Exception $e) {
666
-			$this->convertException($e, $path);
667
-		}
668
-		return false;
669
-	}
670
-
671
-	/**
672
-	 * check if curl is installed
673
-	 */
674
-	public static function checkDependencies() {
675
-		return true;
676
-	}
677
-
678
-	/** {@inheritdoc} */
679
-	public function isUpdatable($path) {
680
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
681
-	}
682
-
683
-	/** {@inheritdoc} */
684
-	public function isCreatable($path) {
685
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
686
-	}
687
-
688
-	/** {@inheritdoc} */
689
-	public function isSharable($path) {
690
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
691
-	}
692
-
693
-	/** {@inheritdoc} */
694
-	public function isDeletable($path) {
695
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
696
-	}
697
-
698
-	/** {@inheritdoc} */
699
-	public function getPermissions($path) {
700
-		$this->init();
701
-		$path = $this->cleanPath($path);
702
-		$response = $this->propfind($path);
703
-		if ($response === false) {
704
-			return 0;
705
-		}
706
-		if (isset($response['{http://owncloud.org/ns}permissions'])) {
707
-			return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
708
-		} else if ($this->is_dir($path)) {
709
-			return Constants::PERMISSION_ALL;
710
-		} else if ($this->file_exists($path)) {
711
-			return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
712
-		} else {
713
-			return 0;
714
-		}
715
-	}
716
-
717
-	/** {@inheritdoc} */
718
-	public function getETag($path) {
719
-		$this->init();
720
-		$path = $this->cleanPath($path);
721
-		$response = $this->propfind($path);
722
-		if ($response === false) {
723
-			return null;
724
-		}
725
-		if (isset($response['{DAV:}getetag'])) {
726
-			return trim($response['{DAV:}getetag'], '"');
727
-		}
728
-		return parent::getEtag($path);
729
-	}
730
-
731
-	/**
732
-	 * @param string $permissionsString
733
-	 * @return int
734
-	 */
735
-	protected function parsePermissions($permissionsString) {
736
-		$permissions = Constants::PERMISSION_READ;
737
-		if (strpos($permissionsString, 'R') !== false) {
738
-			$permissions |= Constants::PERMISSION_SHARE;
739
-		}
740
-		if (strpos($permissionsString, 'D') !== false) {
741
-			$permissions |= Constants::PERMISSION_DELETE;
742
-		}
743
-		if (strpos($permissionsString, 'W') !== false) {
744
-			$permissions |= Constants::PERMISSION_UPDATE;
745
-		}
746
-		if (strpos($permissionsString, 'CK') !== false) {
747
-			$permissions |= Constants::PERMISSION_CREATE;
748
-			$permissions |= Constants::PERMISSION_UPDATE;
749
-		}
750
-		return $permissions;
751
-	}
752
-
753
-	/**
754
-	 * check if a file or folder has been updated since $time
755
-	 *
756
-	 * @param string $path
757
-	 * @param int $time
758
-	 * @throws \OCP\Files\StorageNotAvailableException
759
-	 * @return bool
760
-	 */
761
-	public function hasUpdated($path, $time) {
762
-		$this->init();
763
-		$path = $this->cleanPath($path);
764
-		try {
765
-			// force refresh for $path
766
-			$this->statCache->remove($path);
767
-			$response = $this->propfind($path);
768
-			if ($response === false) {
769
-				if ($path === '') {
770
-					// if root is gone it means the storage is not available
771
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
772
-				}
773
-				return false;
774
-			}
775
-			if (isset($response['{DAV:}getetag'])) {
776
-				$cachedData = $this->getCache()->get($path);
777
-				$etag = null;
778
-				if (isset($response['{DAV:}getetag'])) {
779
-					$etag = trim($response['{DAV:}getetag'], '"');
780
-				}
781
-				if (!empty($etag) && $cachedData['etag'] !== $etag) {
782
-					return true;
783
-				} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
784
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
785
-					return $sharePermissions !== $cachedData['permissions'];
786
-				} else if (isset($response['{http://owncloud.org/ns}permissions'])) {
787
-					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
788
-					return $permissions !== $cachedData['permissions'];
789
-				} else {
790
-					return false;
791
-				}
792
-			} else {
793
-				$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
794
-				return $remoteMtime > $time;
795
-			}
796
-		} catch (ClientHttpException $e) {
797
-			if ($e->getHttpStatus() === 405) {
798
-				if ($path === '') {
799
-					// if root is gone it means the storage is not available
800
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
801
-				}
802
-				return false;
803
-			}
804
-			$this->convertException($e, $path);
805
-			return false;
806
-		} catch (\Exception $e) {
807
-			$this->convertException($e, $path);
808
-			return false;
809
-		}
810
-	}
811
-
812
-	/**
813
-	 * Interpret the given exception and decide whether it is due to an
814
-	 * unavailable storage, invalid storage or other.
815
-	 * This will either throw StorageInvalidException, StorageNotAvailableException
816
-	 * or do nothing.
817
-	 *
818
-	 * @param Exception $e sabre exception
819
-	 * @param string $path optional path from the operation
820
-	 *
821
-	 * @throws StorageInvalidException if the storage is invalid, for example
822
-	 * when the authentication expired or is invalid
823
-	 * @throws StorageNotAvailableException if the storage is not available,
824
-	 * which might be temporary
825
-	 */
826
-	protected function convertException(Exception $e, $path = '') {
827
-		\OC::$server->getLogger()->logException($e);
828
-		Util::writeLog('files_external', $e->getMessage(), Util::ERROR);
829
-		if ($e instanceof ClientHttpException) {
830
-			if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
831
-				throw new \OCP\Lock\LockedException($path);
832
-			}
833
-			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
834
-				// either password was changed or was invalid all along
835
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
836
-			} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
837
-				// ignore exception for MethodNotAllowed, false will be returned
838
-				return;
839
-			}
840
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
841
-		} else if ($e instanceof ClientException) {
842
-			// connection timeout or refused, server could be temporarily down
843
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
844
-		} else if ($e instanceof \InvalidArgumentException) {
845
-			// parse error because the server returned HTML instead of XML,
846
-			// possibly temporarily down
847
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
848
-		} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
849
-			// rethrow
850
-			throw $e;
851
-		}
852
-
853
-		// TODO: only log for now, but in the future need to wrap/rethrow exception
854
-	}
62
+    /** @var string */
63
+    protected $password;
64
+    /** @var string */
65
+    protected $user;
66
+    /** @var string */
67
+    protected $authType;
68
+    /** @var string */
69
+    protected $host;
70
+    /** @var bool */
71
+    protected $secure;
72
+    /** @var string */
73
+    protected $root;
74
+    /** @var string */
75
+    protected $certPath;
76
+    /** @var bool */
77
+    protected $ready;
78
+    /** @var Client */
79
+    protected $client;
80
+    /** @var ArrayCache */
81
+    protected $statCache;
82
+    /** @var \OCP\Http\Client\IClientService */
83
+    protected $httpClientService;
84
+
85
+    /**
86
+     * @param array $params
87
+     * @throws \Exception
88
+     */
89
+    public function __construct($params) {
90
+        $this->statCache = new ArrayCache();
91
+        $this->httpClientService = \OC::$server->getHTTPClientService();
92
+        if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
93
+            $host = $params['host'];
94
+            //remove leading http[s], will be generated in createBaseUri()
95
+            if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
96
+            else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
97
+            $this->host = $host;
98
+            $this->user = $params['user'];
99
+            $this->password = $params['password'];
100
+            if (isset($params['authType'])) {
101
+                $this->authType = $params['authType'];
102
+            }
103
+            if (isset($params['secure'])) {
104
+                if (is_string($params['secure'])) {
105
+                    $this->secure = ($params['secure'] === 'true');
106
+                } else {
107
+                    $this->secure = (bool)$params['secure'];
108
+                }
109
+            } else {
110
+                $this->secure = false;
111
+            }
112
+            if ($this->secure === true) {
113
+                // inject mock for testing
114
+                $certManager = \OC::$server->getCertificateManager();
115
+                if (is_null($certManager)) { //no user
116
+                    $certManager = \OC::$server->getCertificateManager(null);
117
+                }
118
+                $certPath = $certManager->getAbsoluteBundlePath();
119
+                if (file_exists($certPath)) {
120
+                    $this->certPath = $certPath;
121
+                }
122
+            }
123
+            $this->root = isset($params['root']) ? $params['root'] : '/';
124
+            if (!$this->root || $this->root[0] != '/') {
125
+                $this->root = '/' . $this->root;
126
+            }
127
+            if (substr($this->root, -1, 1) != '/') {
128
+                $this->root .= '/';
129
+            }
130
+        } else {
131
+            throw new \Exception('Invalid webdav storage configuration');
132
+        }
133
+    }
134
+
135
+    protected function init() {
136
+        if ($this->ready) {
137
+            return;
138
+        }
139
+        $this->ready = true;
140
+
141
+        $settings = [
142
+            'baseUri' => $this->createBaseUri(),
143
+            'userName' => $this->user,
144
+            'password' => $this->password,
145
+        ];
146
+        if (isset($this->authType)) {
147
+            $settings['authType'] = $this->authType;
148
+        }
149
+
150
+        $proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
151
+        if($proxy !== '') {
152
+            $settings['proxy'] = $proxy;
153
+        }
154
+
155
+        $this->client = new Client($settings);
156
+        $this->client->setThrowExceptions(true);
157
+        if ($this->secure === true && $this->certPath) {
158
+            $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
159
+        }
160
+    }
161
+
162
+    /**
163
+     * Clear the stat cache
164
+     */
165
+    public function clearStatCache() {
166
+        $this->statCache->clear();
167
+    }
168
+
169
+    /** {@inheritdoc} */
170
+    public function getId() {
171
+        return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
172
+    }
173
+
174
+    /** {@inheritdoc} */
175
+    public function createBaseUri() {
176
+        $baseUri = 'http';
177
+        if ($this->secure) {
178
+            $baseUri .= 's';
179
+        }
180
+        $baseUri .= '://' . $this->host . $this->root;
181
+        return $baseUri;
182
+    }
183
+
184
+    /** {@inheritdoc} */
185
+    public function mkdir($path) {
186
+        $this->init();
187
+        $path = $this->cleanPath($path);
188
+        $result = $this->simpleResponse('MKCOL', $path, null, 201);
189
+        if ($result) {
190
+            $this->statCache->set($path, true);
191
+        }
192
+        return $result;
193
+    }
194
+
195
+    /** {@inheritdoc} */
196
+    public function rmdir($path) {
197
+        $this->init();
198
+        $path = $this->cleanPath($path);
199
+        // FIXME: some WebDAV impl return 403 when trying to DELETE
200
+        // a non-empty folder
201
+        $result = $this->simpleResponse('DELETE', $path . '/', null, 204);
202
+        $this->statCache->clear($path . '/');
203
+        $this->statCache->remove($path);
204
+        return $result;
205
+    }
206
+
207
+    /** {@inheritdoc} */
208
+    public function opendir($path) {
209
+        $this->init();
210
+        $path = $this->cleanPath($path);
211
+        try {
212
+            $response = $this->client->propFind(
213
+                $this->encodePath($path),
214
+                ['{DAV:}href'],
215
+                1
216
+            );
217
+            if ($response === false) {
218
+                return false;
219
+            }
220
+            $content = [];
221
+            $files = array_keys($response);
222
+            array_shift($files); //the first entry is the current directory
223
+
224
+            if (!$this->statCache->hasKey($path)) {
225
+                $this->statCache->set($path, true);
226
+            }
227
+            foreach ($files as $file) {
228
+                $file = urldecode($file);
229
+                // do not store the real entry, we might not have all properties
230
+                if (!$this->statCache->hasKey($path)) {
231
+                    $this->statCache->set($file, true);
232
+                }
233
+                $file = basename($file);
234
+                $content[] = $file;
235
+            }
236
+            return IteratorDirectory::wrap($content);
237
+        } catch (\Exception $e) {
238
+            $this->convertException($e, $path);
239
+        }
240
+        return false;
241
+    }
242
+
243
+    /**
244
+     * Propfind call with cache handling.
245
+     *
246
+     * First checks if information is cached.
247
+     * If not, request it from the server then store to cache.
248
+     *
249
+     * @param string $path path to propfind
250
+     *
251
+     * @return array|boolean propfind response or false if the entry was not found
252
+     *
253
+     * @throws ClientHttpException
254
+     */
255
+    protected function propfind($path) {
256
+        $path = $this->cleanPath($path);
257
+        $cachedResponse = $this->statCache->get($path);
258
+        // we either don't know it, or we know it exists but need more details
259
+        if (is_null($cachedResponse) || $cachedResponse === true) {
260
+            $this->init();
261
+            try {
262
+                $response = $this->client->propFind(
263
+                    $this->encodePath($path),
264
+                    array(
265
+                        '{DAV:}getlastmodified',
266
+                        '{DAV:}getcontentlength',
267
+                        '{DAV:}getcontenttype',
268
+                        '{http://owncloud.org/ns}permissions',
269
+                        '{http://open-collaboration-services.org/ns}share-permissions',
270
+                        '{DAV:}resourcetype',
271
+                        '{DAV:}getetag',
272
+                    )
273
+                );
274
+                $this->statCache->set($path, $response);
275
+            } catch (ClientHttpException $e) {
276
+                if ($e->getHttpStatus() === 404) {
277
+                    $this->statCache->clear($path . '/');
278
+                    $this->statCache->set($path, false);
279
+                    return false;
280
+                }
281
+                $this->convertException($e, $path);
282
+            } catch (\Exception $e) {
283
+                $this->convertException($e, $path);
284
+            }
285
+        } else {
286
+            $response = $cachedResponse;
287
+        }
288
+        return $response;
289
+    }
290
+
291
+    /** {@inheritdoc} */
292
+    public function filetype($path) {
293
+        try {
294
+            $response = $this->propfind($path);
295
+            if ($response === false) {
296
+                return false;
297
+            }
298
+            $responseType = [];
299
+            if (isset($response["{DAV:}resourcetype"])) {
300
+                /** @var ResourceType[] $response */
301
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
302
+            }
303
+            return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
304
+        } catch (\Exception $e) {
305
+            $this->convertException($e, $path);
306
+        }
307
+        return false;
308
+    }
309
+
310
+    /** {@inheritdoc} */
311
+    public function file_exists($path) {
312
+        try {
313
+            $path = $this->cleanPath($path);
314
+            $cachedState = $this->statCache->get($path);
315
+            if ($cachedState === false) {
316
+                // we know the file doesn't exist
317
+                return false;
318
+            } else if (!is_null($cachedState)) {
319
+                return true;
320
+            }
321
+            // need to get from server
322
+            return ($this->propfind($path) !== false);
323
+        } catch (\Exception $e) {
324
+            $this->convertException($e, $path);
325
+        }
326
+        return false;
327
+    }
328
+
329
+    /** {@inheritdoc} */
330
+    public function unlink($path) {
331
+        $this->init();
332
+        $path = $this->cleanPath($path);
333
+        $result = $this->simpleResponse('DELETE', $path, null, 204);
334
+        $this->statCache->clear($path . '/');
335
+        $this->statCache->remove($path);
336
+        return $result;
337
+    }
338
+
339
+    /** {@inheritdoc} */
340
+    public function fopen($path, $mode) {
341
+        $this->init();
342
+        $path = $this->cleanPath($path);
343
+        switch ($mode) {
344
+            case 'r':
345
+            case 'rb':
346
+                try {
347
+                    $response = $this->httpClientService
348
+                            ->newClient()
349
+                            ->get($this->createBaseUri() . $this->encodePath($path), [
350
+                                    'auth' => [$this->user, $this->password],
351
+                                    'stream' => true
352
+                            ]);
353
+                } catch (RequestException $e) {
354
+                    if ($e->getResponse() instanceof ResponseInterface
355
+                        && $e->getResponse()->getStatusCode() === 404) {
356
+                        return false;
357
+                    } else {
358
+                        throw $e;
359
+                    }
360
+                }
361
+
362
+                if ($response->getStatusCode() !== Http::STATUS_OK) {
363
+                    if ($response->getStatusCode() === Http::STATUS_LOCKED) {
364
+                        throw new \OCP\Lock\LockedException($path);
365
+                    } else {
366
+                        Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
367
+                    }
368
+                }
369
+
370
+                return $response->getBody();
371
+            case 'w':
372
+            case 'wb':
373
+            case 'a':
374
+            case 'ab':
375
+            case 'r+':
376
+            case 'w+':
377
+            case 'wb+':
378
+            case 'a+':
379
+            case 'x':
380
+            case 'x+':
381
+            case 'c':
382
+            case 'c+':
383
+                //emulate these
384
+                $tempManager = \OC::$server->getTempManager();
385
+                if (strrpos($path, '.') !== false) {
386
+                    $ext = substr($path, strrpos($path, '.'));
387
+                } else {
388
+                    $ext = '';
389
+                }
390
+                if ($this->file_exists($path)) {
391
+                    if (!$this->isUpdatable($path)) {
392
+                        return false;
393
+                    }
394
+                    if ($mode === 'w' or $mode === 'w+') {
395
+                        $tmpFile = $tempManager->getTemporaryFile($ext);
396
+                    } else {
397
+                        $tmpFile = $this->getCachedFile($path);
398
+                    }
399
+                } else {
400
+                    if (!$this->isCreatable(dirname($path))) {
401
+                        return false;
402
+                    }
403
+                    $tmpFile = $tempManager->getTemporaryFile($ext);
404
+                }
405
+                $handle = fopen($tmpFile, $mode);
406
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
407
+                    $this->writeBack($tmpFile, $path);
408
+                });
409
+        }
410
+    }
411
+
412
+    /**
413
+     * @param string $tmpFile
414
+     */
415
+    public function writeBack($tmpFile, $path) {
416
+        $this->uploadFile($tmpFile, $path);
417
+        unlink($tmpFile);
418
+    }
419
+
420
+    /** {@inheritdoc} */
421
+    public function free_space($path) {
422
+        $this->init();
423
+        $path = $this->cleanPath($path);
424
+        try {
425
+            // TODO: cacheable ?
426
+            $response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
427
+            if ($response === false) {
428
+                return FileInfo::SPACE_UNKNOWN;
429
+            }
430
+            if (isset($response['{DAV:}quota-available-bytes'])) {
431
+                return (int)$response['{DAV:}quota-available-bytes'];
432
+            } else {
433
+                return FileInfo::SPACE_UNKNOWN;
434
+            }
435
+        } catch (\Exception $e) {
436
+            return FileInfo::SPACE_UNKNOWN;
437
+        }
438
+    }
439
+
440
+    /** {@inheritdoc} */
441
+    public function touch($path, $mtime = null) {
442
+        $this->init();
443
+        if (is_null($mtime)) {
444
+            $mtime = time();
445
+        }
446
+        $path = $this->cleanPath($path);
447
+
448
+        // if file exists, update the mtime, else create a new empty file
449
+        if ($this->file_exists($path)) {
450
+            try {
451
+                $this->statCache->remove($path);
452
+                $this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
453
+                // non-owncloud clients might not have accepted the property, need to recheck it
454
+                $response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
455
+                if ($response === false) {
456
+                    return false;
457
+                }
458
+                if (isset($response['{DAV:}getlastmodified'])) {
459
+                    $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
460
+                    if ($remoteMtime !== $mtime) {
461
+                        // server has not accepted the mtime
462
+                        return false;
463
+                    }
464
+                }
465
+            } catch (ClientHttpException $e) {
466
+                if ($e->getHttpStatus() === 501) {
467
+                    return false;
468
+                }
469
+                $this->convertException($e, $path);
470
+                return false;
471
+            } catch (\Exception $e) {
472
+                $this->convertException($e, $path);
473
+                return false;
474
+            }
475
+        } else {
476
+            $this->file_put_contents($path, '');
477
+        }
478
+        return true;
479
+    }
480
+
481
+    /**
482
+     * @param string $path
483
+     * @param string $data
484
+     * @return int
485
+     */
486
+    public function file_put_contents($path, $data) {
487
+        $path = $this->cleanPath($path);
488
+        $result = parent::file_put_contents($path, $data);
489
+        $this->statCache->remove($path);
490
+        return $result;
491
+    }
492
+
493
+    /**
494
+     * @param string $path
495
+     * @param string $target
496
+     */
497
+    protected function uploadFile($path, $target) {
498
+        $this->init();
499
+
500
+        // invalidate
501
+        $target = $this->cleanPath($target);
502
+        $this->statCache->remove($target);
503
+        $source = fopen($path, 'r');
504
+
505
+        $this->httpClientService
506
+            ->newClient()
507
+            ->put($this->createBaseUri() . $this->encodePath($target), [
508
+                'body' => $source,
509
+                'auth' => [$this->user, $this->password]
510
+            ]);
511
+
512
+        $this->removeCachedFile($target);
513
+    }
514
+
515
+    /** {@inheritdoc} */
516
+    public function rename($path1, $path2) {
517
+        $this->init();
518
+        $path1 = $this->cleanPath($path1);
519
+        $path2 = $this->cleanPath($path2);
520
+        try {
521
+            // overwrite directory ?
522
+            if ($this->is_dir($path2)) {
523
+                // needs trailing slash in destination
524
+                $path2 = rtrim($path2, '/') . '/';
525
+            }
526
+            $this->client->request(
527
+                'MOVE',
528
+                $this->encodePath($path1),
529
+                null,
530
+                [
531
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
532
+                ]
533
+            );
534
+            $this->statCache->clear($path1 . '/');
535
+            $this->statCache->clear($path2 . '/');
536
+            $this->statCache->set($path1, false);
537
+            $this->statCache->set($path2, true);
538
+            $this->removeCachedFile($path1);
539
+            $this->removeCachedFile($path2);
540
+            return true;
541
+        } catch (\Exception $e) {
542
+            $this->convertException($e);
543
+        }
544
+        return false;
545
+    }
546
+
547
+    /** {@inheritdoc} */
548
+    public function copy($path1, $path2) {
549
+        $this->init();
550
+        $path1 = $this->cleanPath($path1);
551
+        $path2 = $this->cleanPath($path2);
552
+        try {
553
+            // overwrite directory ?
554
+            if ($this->is_dir($path2)) {
555
+                // needs trailing slash in destination
556
+                $path2 = rtrim($path2, '/') . '/';
557
+            }
558
+            $this->client->request(
559
+                'COPY',
560
+                $this->encodePath($path1),
561
+                null,
562
+                [
563
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
564
+                ]
565
+            );
566
+            $this->statCache->clear($path2 . '/');
567
+            $this->statCache->set($path2, true);
568
+            $this->removeCachedFile($path2);
569
+            return true;
570
+        } catch (\Exception $e) {
571
+            $this->convertException($e);
572
+        }
573
+        return false;
574
+    }
575
+
576
+    /** {@inheritdoc} */
577
+    public function stat($path) {
578
+        try {
579
+            $response = $this->propfind($path);
580
+            if (!$response) {
581
+                return false;
582
+            }
583
+            return [
584
+                'mtime' => strtotime($response['{DAV:}getlastmodified']),
585
+                'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
586
+            ];
587
+        } catch (\Exception $e) {
588
+            $this->convertException($e, $path);
589
+        }
590
+        return array();
591
+    }
592
+
593
+    /** {@inheritdoc} */
594
+    public function getMimeType($path) {
595
+        try {
596
+            $response = $this->propfind($path);
597
+            if ($response === false) {
598
+                return false;
599
+            }
600
+            $responseType = [];
601
+            if (isset($response["{DAV:}resourcetype"])) {
602
+                /** @var ResourceType[] $response */
603
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
604
+            }
605
+            $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
606
+            if ($type == 'dir') {
607
+                return 'httpd/unix-directory';
608
+            } elseif (isset($response['{DAV:}getcontenttype'])) {
609
+                return $response['{DAV:}getcontenttype'];
610
+            } else {
611
+                return false;
612
+            }
613
+        } catch (\Exception $e) {
614
+            $this->convertException($e, $path);
615
+        }
616
+        return false;
617
+    }
618
+
619
+    /**
620
+     * @param string $path
621
+     * @return string
622
+     */
623
+    public function cleanPath($path) {
624
+        if ($path === '') {
625
+            return $path;
626
+        }
627
+        $path = Filesystem::normalizePath($path);
628
+        // remove leading slash
629
+        return substr($path, 1);
630
+    }
631
+
632
+    /**
633
+     * URL encodes the given path but keeps the slashes
634
+     *
635
+     * @param string $path to encode
636
+     * @return string encoded path
637
+     */
638
+    protected function encodePath($path) {
639
+        // slashes need to stay
640
+        return str_replace('%2F', '/', rawurlencode($path));
641
+    }
642
+
643
+    /**
644
+     * @param string $method
645
+     * @param string $path
646
+     * @param string|resource|null $body
647
+     * @param int $expected
648
+     * @return bool
649
+     * @throws StorageInvalidException
650
+     * @throws StorageNotAvailableException
651
+     */
652
+    protected function simpleResponse($method, $path, $body, $expected) {
653
+        $path = $this->cleanPath($path);
654
+        try {
655
+            $response = $this->client->request($method, $this->encodePath($path), $body);
656
+            return $response['statusCode'] == $expected;
657
+        } catch (ClientHttpException $e) {
658
+            if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
659
+                $this->statCache->clear($path . '/');
660
+                $this->statCache->set($path, false);
661
+                return false;
662
+            }
663
+
664
+            $this->convertException($e, $path);
665
+        } catch (\Exception $e) {
666
+            $this->convertException($e, $path);
667
+        }
668
+        return false;
669
+    }
670
+
671
+    /**
672
+     * check if curl is installed
673
+     */
674
+    public static function checkDependencies() {
675
+        return true;
676
+    }
677
+
678
+    /** {@inheritdoc} */
679
+    public function isUpdatable($path) {
680
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
681
+    }
682
+
683
+    /** {@inheritdoc} */
684
+    public function isCreatable($path) {
685
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
686
+    }
687
+
688
+    /** {@inheritdoc} */
689
+    public function isSharable($path) {
690
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
691
+    }
692
+
693
+    /** {@inheritdoc} */
694
+    public function isDeletable($path) {
695
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
696
+    }
697
+
698
+    /** {@inheritdoc} */
699
+    public function getPermissions($path) {
700
+        $this->init();
701
+        $path = $this->cleanPath($path);
702
+        $response = $this->propfind($path);
703
+        if ($response === false) {
704
+            return 0;
705
+        }
706
+        if (isset($response['{http://owncloud.org/ns}permissions'])) {
707
+            return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
708
+        } else if ($this->is_dir($path)) {
709
+            return Constants::PERMISSION_ALL;
710
+        } else if ($this->file_exists($path)) {
711
+            return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
712
+        } else {
713
+            return 0;
714
+        }
715
+    }
716
+
717
+    /** {@inheritdoc} */
718
+    public function getETag($path) {
719
+        $this->init();
720
+        $path = $this->cleanPath($path);
721
+        $response = $this->propfind($path);
722
+        if ($response === false) {
723
+            return null;
724
+        }
725
+        if (isset($response['{DAV:}getetag'])) {
726
+            return trim($response['{DAV:}getetag'], '"');
727
+        }
728
+        return parent::getEtag($path);
729
+    }
730
+
731
+    /**
732
+     * @param string $permissionsString
733
+     * @return int
734
+     */
735
+    protected function parsePermissions($permissionsString) {
736
+        $permissions = Constants::PERMISSION_READ;
737
+        if (strpos($permissionsString, 'R') !== false) {
738
+            $permissions |= Constants::PERMISSION_SHARE;
739
+        }
740
+        if (strpos($permissionsString, 'D') !== false) {
741
+            $permissions |= Constants::PERMISSION_DELETE;
742
+        }
743
+        if (strpos($permissionsString, 'W') !== false) {
744
+            $permissions |= Constants::PERMISSION_UPDATE;
745
+        }
746
+        if (strpos($permissionsString, 'CK') !== false) {
747
+            $permissions |= Constants::PERMISSION_CREATE;
748
+            $permissions |= Constants::PERMISSION_UPDATE;
749
+        }
750
+        return $permissions;
751
+    }
752
+
753
+    /**
754
+     * check if a file or folder has been updated since $time
755
+     *
756
+     * @param string $path
757
+     * @param int $time
758
+     * @throws \OCP\Files\StorageNotAvailableException
759
+     * @return bool
760
+     */
761
+    public function hasUpdated($path, $time) {
762
+        $this->init();
763
+        $path = $this->cleanPath($path);
764
+        try {
765
+            // force refresh for $path
766
+            $this->statCache->remove($path);
767
+            $response = $this->propfind($path);
768
+            if ($response === false) {
769
+                if ($path === '') {
770
+                    // if root is gone it means the storage is not available
771
+                    throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
772
+                }
773
+                return false;
774
+            }
775
+            if (isset($response['{DAV:}getetag'])) {
776
+                $cachedData = $this->getCache()->get($path);
777
+                $etag = null;
778
+                if (isset($response['{DAV:}getetag'])) {
779
+                    $etag = trim($response['{DAV:}getetag'], '"');
780
+                }
781
+                if (!empty($etag) && $cachedData['etag'] !== $etag) {
782
+                    return true;
783
+                } else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
784
+                    $sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
785
+                    return $sharePermissions !== $cachedData['permissions'];
786
+                } else if (isset($response['{http://owncloud.org/ns}permissions'])) {
787
+                    $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
788
+                    return $permissions !== $cachedData['permissions'];
789
+                } else {
790
+                    return false;
791
+                }
792
+            } else {
793
+                $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
794
+                return $remoteMtime > $time;
795
+            }
796
+        } catch (ClientHttpException $e) {
797
+            if ($e->getHttpStatus() === 405) {
798
+                if ($path === '') {
799
+                    // if root is gone it means the storage is not available
800
+                    throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
801
+                }
802
+                return false;
803
+            }
804
+            $this->convertException($e, $path);
805
+            return false;
806
+        } catch (\Exception $e) {
807
+            $this->convertException($e, $path);
808
+            return false;
809
+        }
810
+    }
811
+
812
+    /**
813
+     * Interpret the given exception and decide whether it is due to an
814
+     * unavailable storage, invalid storage or other.
815
+     * This will either throw StorageInvalidException, StorageNotAvailableException
816
+     * or do nothing.
817
+     *
818
+     * @param Exception $e sabre exception
819
+     * @param string $path optional path from the operation
820
+     *
821
+     * @throws StorageInvalidException if the storage is invalid, for example
822
+     * when the authentication expired or is invalid
823
+     * @throws StorageNotAvailableException if the storage is not available,
824
+     * which might be temporary
825
+     */
826
+    protected function convertException(Exception $e, $path = '') {
827
+        \OC::$server->getLogger()->logException($e);
828
+        Util::writeLog('files_external', $e->getMessage(), Util::ERROR);
829
+        if ($e instanceof ClientHttpException) {
830
+            if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
831
+                throw new \OCP\Lock\LockedException($path);
832
+            }
833
+            if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
834
+                // either password was changed or was invalid all along
835
+                throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
836
+            } else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
837
+                // ignore exception for MethodNotAllowed, false will be returned
838
+                return;
839
+            }
840
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
841
+        } else if ($e instanceof ClientException) {
842
+            // connection timeout or refused, server could be temporarily down
843
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
844
+        } else if ($e instanceof \InvalidArgumentException) {
845
+            // parse error because the server returned HTML instead of XML,
846
+            // possibly temporarily down
847
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
848
+        } else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
849
+            // rethrow
850
+            throw $e;
851
+        }
852
+
853
+        // TODO: only log for now, but in the future need to wrap/rethrow exception
854
+    }
855 855
 }
856 856
 
Please login to merge, or discard this patch.
Spacing   +34 added lines, -34 removed lines patch added patch discarded remove patch
@@ -104,7 +104,7 @@  discard block
 block discarded – undo
104 104
 				if (is_string($params['secure'])) {
105 105
 					$this->secure = ($params['secure'] === 'true');
106 106
 				} else {
107
-					$this->secure = (bool)$params['secure'];
107
+					$this->secure = (bool) $params['secure'];
108 108
 				}
109 109
 			} else {
110 110
 				$this->secure = false;
@@ -122,7 +122,7 @@  discard block
 block discarded – undo
122 122
 			}
123 123
 			$this->root = isset($params['root']) ? $params['root'] : '/';
124 124
 			if (!$this->root || $this->root[0] != '/') {
125
-				$this->root = '/' . $this->root;
125
+				$this->root = '/'.$this->root;
126 126
 			}
127 127
 			if (substr($this->root, -1, 1) != '/') {
128 128
 				$this->root .= '/';
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
 		}
149 149
 
150 150
 		$proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
151
-		if($proxy !== '') {
151
+		if ($proxy !== '') {
152 152
 			$settings['proxy'] = $proxy;
153 153
 		}
154 154
 
@@ -168,7 +168,7 @@  discard block
 block discarded – undo
168 168
 
169 169
 	/** {@inheritdoc} */
170 170
 	public function getId() {
171
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
171
+		return 'webdav::'.$this->user.'@'.$this->host.'/'.$this->root;
172 172
 	}
173 173
 
174 174
 	/** {@inheritdoc} */
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
 		if ($this->secure) {
178 178
 			$baseUri .= 's';
179 179
 		}
180
-		$baseUri .= '://' . $this->host . $this->root;
180
+		$baseUri .= '://'.$this->host.$this->root;
181 181
 		return $baseUri;
182 182
 	}
183 183
 
@@ -198,8 +198,8 @@  discard block
 block discarded – undo
198 198
 		$path = $this->cleanPath($path);
199 199
 		// FIXME: some WebDAV impl return 403 when trying to DELETE
200 200
 		// a non-empty folder
201
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
202
-		$this->statCache->clear($path . '/');
201
+		$result = $this->simpleResponse('DELETE', $path.'/', null, 204);
202
+		$this->statCache->clear($path.'/');
203 203
 		$this->statCache->remove($path);
204 204
 		return $result;
205 205
 	}
@@ -274,7 +274,7 @@  discard block
 block discarded – undo
274 274
 				$this->statCache->set($path, $response);
275 275
 			} catch (ClientHttpException $e) {
276 276
 				if ($e->getHttpStatus() === 404) {
277
-					$this->statCache->clear($path . '/');
277
+					$this->statCache->clear($path.'/');
278 278
 					$this->statCache->set($path, false);
279 279
 					return false;
280 280
 				}
@@ -331,7 +331,7 @@  discard block
 block discarded – undo
331 331
 		$this->init();
332 332
 		$path = $this->cleanPath($path);
333 333
 		$result = $this->simpleResponse('DELETE', $path, null, 204);
334
-		$this->statCache->clear($path . '/');
334
+		$this->statCache->clear($path.'/');
335 335
 		$this->statCache->remove($path);
336 336
 		return $result;
337 337
 	}
@@ -346,7 +346,7 @@  discard block
 block discarded – undo
346 346
 				try {
347 347
 					$response = $this->httpClientService
348 348
 							->newClient()
349
-							->get($this->createBaseUri() . $this->encodePath($path), [
349
+							->get($this->createBaseUri().$this->encodePath($path), [
350 350
 									'auth' => [$this->user, $this->password],
351 351
 									'stream' => true
352 352
 							]);
@@ -363,7 +363,7 @@  discard block
 block discarded – undo
363 363
 					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
364 364
 						throw new \OCP\Lock\LockedException($path);
365 365
 					} else {
366
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
366
+						Util::writeLog("webdav client", 'Guzzle get returned status code '.$response->getStatusCode(), Util::ERROR);
367 367
 					}
368 368
 				}
369 369
 
@@ -403,7 +403,7 @@  discard block
 block discarded – undo
403 403
 					$tmpFile = $tempManager->getTemporaryFile($ext);
404 404
 				}
405 405
 				$handle = fopen($tmpFile, $mode);
406
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
406
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
407 407
 					$this->writeBack($tmpFile, $path);
408 408
 				});
409 409
 		}
@@ -428,7 +428,7 @@  discard block
 block discarded – undo
428 428
 				return FileInfo::SPACE_UNKNOWN;
429 429
 			}
430 430
 			if (isset($response['{DAV:}quota-available-bytes'])) {
431
-				return (int)$response['{DAV:}quota-available-bytes'];
431
+				return (int) $response['{DAV:}quota-available-bytes'];
432 432
 			} else {
433 433
 				return FileInfo::SPACE_UNKNOWN;
434 434
 			}
@@ -504,7 +504,7 @@  discard block
 block discarded – undo
504 504
 
505 505
 		$this->httpClientService
506 506
 			->newClient()
507
-			->put($this->createBaseUri() . $this->encodePath($target), [
507
+			->put($this->createBaseUri().$this->encodePath($target), [
508 508
 				'body' => $source,
509 509
 				'auth' => [$this->user, $this->password]
510 510
 			]);
@@ -521,18 +521,18 @@  discard block
 block discarded – undo
521 521
 			// overwrite directory ?
522 522
 			if ($this->is_dir($path2)) {
523 523
 				// needs trailing slash in destination
524
-				$path2 = rtrim($path2, '/') . '/';
524
+				$path2 = rtrim($path2, '/').'/';
525 525
 			}
526 526
 			$this->client->request(
527 527
 				'MOVE',
528 528
 				$this->encodePath($path1),
529 529
 				null,
530 530
 				[
531
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
531
+					'Destination' => $this->createBaseUri().$this->encodePath($path2),
532 532
 				]
533 533
 			);
534
-			$this->statCache->clear($path1 . '/');
535
-			$this->statCache->clear($path2 . '/');
534
+			$this->statCache->clear($path1.'/');
535
+			$this->statCache->clear($path2.'/');
536 536
 			$this->statCache->set($path1, false);
537 537
 			$this->statCache->set($path2, true);
538 538
 			$this->removeCachedFile($path1);
@@ -553,17 +553,17 @@  discard block
 block discarded – undo
553 553
 			// overwrite directory ?
554 554
 			if ($this->is_dir($path2)) {
555 555
 				// needs trailing slash in destination
556
-				$path2 = rtrim($path2, '/') . '/';
556
+				$path2 = rtrim($path2, '/').'/';
557 557
 			}
558 558
 			$this->client->request(
559 559
 				'COPY',
560 560
 				$this->encodePath($path1),
561 561
 				null,
562 562
 				[
563
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
563
+					'Destination' => $this->createBaseUri().$this->encodePath($path2),
564 564
 				]
565 565
 			);
566
-			$this->statCache->clear($path2 . '/');
566
+			$this->statCache->clear($path2.'/');
567 567
 			$this->statCache->set($path2, true);
568 568
 			$this->removeCachedFile($path2);
569 569
 			return true;
@@ -582,7 +582,7 @@  discard block
 block discarded – undo
582 582
 			}
583 583
 			return [
584 584
 				'mtime' => strtotime($response['{DAV:}getlastmodified']),
585
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
585
+				'size' => (int) isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
586 586
 			];
587 587
 		} catch (\Exception $e) {
588 588
 			$this->convertException($e, $path);
@@ -656,7 +656,7 @@  discard block
 block discarded – undo
656 656
 			return $response['statusCode'] == $expected;
657 657
 		} catch (ClientHttpException $e) {
658 658
 			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
659
-				$this->statCache->clear($path . '/');
659
+				$this->statCache->clear($path.'/');
660 660
 				$this->statCache->set($path, false);
661 661
 				return false;
662 662
 			}
@@ -677,22 +677,22 @@  discard block
 block discarded – undo
677 677
 
678 678
 	/** {@inheritdoc} */
679 679
 	public function isUpdatable($path) {
680
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
680
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
681 681
 	}
682 682
 
683 683
 	/** {@inheritdoc} */
684 684
 	public function isCreatable($path) {
685
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
685
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_CREATE);
686 686
 	}
687 687
 
688 688
 	/** {@inheritdoc} */
689 689
 	public function isSharable($path) {
690
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
690
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_SHARE);
691 691
 	}
692 692
 
693 693
 	/** {@inheritdoc} */
694 694
 	public function isDeletable($path) {
695
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
695
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_DELETE);
696 696
 	}
697 697
 
698 698
 	/** {@inheritdoc} */
@@ -768,7 +768,7 @@  discard block
 block discarded – undo
768 768
 			if ($response === false) {
769 769
 				if ($path === '') {
770 770
 					// if root is gone it means the storage is not available
771
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
771
+					throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
772 772
 				}
773 773
 				return false;
774 774
 			}
@@ -781,7 +781,7 @@  discard block
 block discarded – undo
781 781
 				if (!empty($etag) && $cachedData['etag'] !== $etag) {
782 782
 					return true;
783 783
 				} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
784
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
784
+					$sharePermissions = (int) $response['{http://open-collaboration-services.org/ns}share-permissions'];
785 785
 					return $sharePermissions !== $cachedData['permissions'];
786 786
 				} else if (isset($response['{http://owncloud.org/ns}permissions'])) {
787 787
 					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
@@ -797,7 +797,7 @@  discard block
 block discarded – undo
797 797
 			if ($e->getHttpStatus() === 405) {
798 798
 				if ($path === '') {
799 799
 					// if root is gone it means the storage is not available
800
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
800
+					throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
801 801
 				}
802 802
 				return false;
803 803
 			}
@@ -832,19 +832,19 @@  discard block
 block discarded – undo
832 832
 			}
833 833
 			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
834 834
 				// either password was changed or was invalid all along
835
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
835
+				throw new StorageInvalidException(get_class($e).': '.$e->getMessage());
836 836
 			} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
837 837
 				// ignore exception for MethodNotAllowed, false will be returned
838 838
 				return;
839 839
 			}
840
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
840
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
841 841
 		} else if ($e instanceof ClientException) {
842 842
 			// connection timeout or refused, server could be temporarily down
843
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
843
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
844 844
 		} else if ($e instanceof \InvalidArgumentException) {
845 845
 			// parse error because the server returned HTML instead of XML,
846 846
 			// possibly temporarily down
847
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
847
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
848 848
 		} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
849 849
 			// rethrow
850 850
 			throw $e;
Please login to merge, or discard this patch.