Passed
Push — master ( b306a8...3e64be )
by Roeland
09:52 queued 10s
created
lib/private/Files/Storage/DAV.php 3 patches
Braces   +5 added lines, -2 removed lines patch added patch discarded remove patch
@@ -90,8 +90,11 @@
 block discarded – undo
90 90
 		if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
91 91
 			$host = $params['host'];
92 92
 			//remove leading http[s], will be generated in createBaseUri()
93
-			if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
94
-			else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
93
+			if (substr($host, 0, 8) == "https://") {
94
+			    $host = substr($host, 8);
95
+			} else if (substr($host, 0, 7) == "http://") {
96
+			    $host = substr($host, 7);
97
+			}
95 98
 			$this->host = $host;
96 99
 			$this->user = $params['user'];
97 100
 			$this->password = $params['password'];
Please login to merge, or discard this patch.
Indentation   +809 added lines, -809 removed lines patch added patch discarded remove patch
@@ -60,813 +60,813 @@
 block discarded – undo
60 60
  * @package OC\Files\Storage
61 61
  */
62 62
 class DAV extends Common {
63
-	/** @var string */
64
-	protected $password;
65
-	/** @var string */
66
-	protected $user;
67
-	/** @var string */
68
-	protected $authType;
69
-	/** @var string */
70
-	protected $host;
71
-	/** @var bool */
72
-	protected $secure;
73
-	/** @var string */
74
-	protected $root;
75
-	/** @var string */
76
-	protected $certPath;
77
-	/** @var bool */
78
-	protected $ready;
79
-	/** @var Client */
80
-	protected $client;
81
-	/** @var ArrayCache */
82
-	protected $statCache;
83
-	/** @var IClientService */
84
-	protected $httpClientService;
85
-	/** @var ICertificateManager */
86
-	protected $certManager;
87
-
88
-	/**
89
-	 * @param array $params
90
-	 * @throws \Exception
91
-	 */
92
-	public function __construct($params) {
93
-		$this->statCache = new ArrayCache();
94
-		$this->httpClientService = \OC::$server->getHTTPClientService();
95
-		if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
96
-			$host = $params['host'];
97
-			//remove leading http[s], will be generated in createBaseUri()
98
-			if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
99
-			else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
100
-			$this->host = $host;
101
-			$this->user = $params['user'];
102
-			$this->password = $params['password'];
103
-			if (isset($params['authType'])) {
104
-				$this->authType = $params['authType'];
105
-			}
106
-			if (isset($params['secure'])) {
107
-				if (is_string($params['secure'])) {
108
-					$this->secure = ($params['secure'] === 'true');
109
-				} else {
110
-					$this->secure = (bool)$params['secure'];
111
-				}
112
-			} else {
113
-				$this->secure = false;
114
-			}
115
-			if ($this->secure === true) {
116
-				// inject mock for testing
117
-				$this->certManager = \OC::$server->getCertificateManager();
118
-				if (is_null($this->certManager)) { //no user
119
-					$this->certManager = \OC::$server->getCertificateManager(null);
120
-				}
121
-			}
122
-			$this->root = $params['root'] ?? '/';
123
-			$this->root = '/' . ltrim($this->root, '/');
124
-			$this->root = rtrim($this->root, '/') . '/';
125
-		} else {
126
-			throw new \Exception('Invalid webdav storage configuration');
127
-		}
128
-	}
129
-
130
-	protected function init() {
131
-		if ($this->ready) {
132
-			return;
133
-		}
134
-		$this->ready = true;
135
-
136
-		$settings = [
137
-			'baseUri' => $this->createBaseUri(),
138
-			'userName' => $this->user,
139
-			'password' => $this->password,
140
-		];
141
-		if (isset($this->authType)) {
142
-			$settings['authType'] = $this->authType;
143
-		}
144
-
145
-		$proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
146
-		if ($proxy !== '') {
147
-			$settings['proxy'] = $proxy;
148
-		}
149
-
150
-		$this->client = new Client($settings);
151
-		$this->client->setThrowExceptions(true);
152
-
153
-		if($this->secure === true) {
154
-			$certPath = $this->certManager->getAbsoluteBundlePath();
155
-			if (file_exists($certPath)) {
156
-				$this->certPath = $certPath;
157
-			}
158
-			if ($this->certPath) {
159
-				$this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
160
-			}
161
-		}
162
-	}
163
-
164
-	/**
165
-	 * Clear the stat cache
166
-	 */
167
-	public function clearStatCache() {
168
-		$this->statCache->clear();
169
-	}
170
-
171
-	/** {@inheritdoc} */
172
-	public function getId() {
173
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
174
-	}
175
-
176
-	/** {@inheritdoc} */
177
-	public function createBaseUri() {
178
-		$baseUri = 'http';
179
-		if ($this->secure) {
180
-			$baseUri .= 's';
181
-		}
182
-		$baseUri .= '://' . $this->host . $this->root;
183
-		return $baseUri;
184
-	}
185
-
186
-	/** {@inheritdoc} */
187
-	public function mkdir($path) {
188
-		$this->init();
189
-		$path = $this->cleanPath($path);
190
-		$result = $this->simpleResponse('MKCOL', $path, null, 201);
191
-		if ($result) {
192
-			$this->statCache->set($path, true);
193
-		}
194
-		return $result;
195
-	}
196
-
197
-	/** {@inheritdoc} */
198
-	public function rmdir($path) {
199
-		$this->init();
200
-		$path = $this->cleanPath($path);
201
-		// FIXME: some WebDAV impl return 403 when trying to DELETE
202
-		// a non-empty folder
203
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
204
-		$this->statCache->clear($path . '/');
205
-		$this->statCache->remove($path);
206
-		return $result;
207
-	}
208
-
209
-	/** {@inheritdoc} */
210
-	public function opendir($path) {
211
-		$this->init();
212
-		$path = $this->cleanPath($path);
213
-		try {
214
-			$response = $this->client->propFind(
215
-				$this->encodePath($path),
216
-				['{DAV:}getetag'],
217
-				1
218
-			);
219
-			if ($response === false) {
220
-				return false;
221
-			}
222
-			$content = [];
223
-			$files = array_keys($response);
224
-			array_shift($files); //the first entry is the current directory
225
-
226
-			if (!$this->statCache->hasKey($path)) {
227
-				$this->statCache->set($path, true);
228
-			}
229
-			foreach ($files as $file) {
230
-				$file = urldecode($file);
231
-				// do not store the real entry, we might not have all properties
232
-				if (!$this->statCache->hasKey($path)) {
233
-					$this->statCache->set($file, true);
234
-				}
235
-				$file = basename($file);
236
-				$content[] = $file;
237
-			}
238
-			return IteratorDirectory::wrap($content);
239
-		} catch (\Exception $e) {
240
-			$this->convertException($e, $path);
241
-		}
242
-		return false;
243
-	}
244
-
245
-	/**
246
-	 * Propfind call with cache handling.
247
-	 *
248
-	 * First checks if information is cached.
249
-	 * If not, request it from the server then store to cache.
250
-	 *
251
-	 * @param string $path path to propfind
252
-	 *
253
-	 * @return array|boolean propfind response or false if the entry was not found
254
-	 *
255
-	 * @throws ClientHttpException
256
-	 */
257
-	protected function propfind($path) {
258
-		$path = $this->cleanPath($path);
259
-		$cachedResponse = $this->statCache->get($path);
260
-		// we either don't know it, or we know it exists but need more details
261
-		if (is_null($cachedResponse) || $cachedResponse === true) {
262
-			$this->init();
263
-			try {
264
-				$response = $this->client->propFind(
265
-					$this->encodePath($path),
266
-					array(
267
-						'{DAV:}getlastmodified',
268
-						'{DAV:}getcontentlength',
269
-						'{DAV:}getcontenttype',
270
-						'{http://owncloud.org/ns}permissions',
271
-						'{http://open-collaboration-services.org/ns}share-permissions',
272
-						'{DAV:}resourcetype',
273
-						'{DAV:}getetag',
274
-					)
275
-				);
276
-				$this->statCache->set($path, $response);
277
-			} catch (ClientHttpException $e) {
278
-				if ($e->getHttpStatus() === 404 || $e->getHttpStatus() === 405) {
279
-					$this->statCache->clear($path . '/');
280
-					$this->statCache->set($path, false);
281
-					return false;
282
-				}
283
-				$this->convertException($e, $path);
284
-			} catch (\Exception $e) {
285
-				$this->convertException($e, $path);
286
-			}
287
-		} else {
288
-			$response = $cachedResponse;
289
-		}
290
-		return $response;
291
-	}
292
-
293
-	/** {@inheritdoc} */
294
-	public function filetype($path) {
295
-		try {
296
-			$response = $this->propfind($path);
297
-			if ($response === false) {
298
-				return false;
299
-			}
300
-			$responseType = [];
301
-			if (isset($response["{DAV:}resourcetype"])) {
302
-				/** @var ResourceType[] $response */
303
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
304
-			}
305
-			return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
306
-		} catch (\Exception $e) {
307
-			$this->convertException($e, $path);
308
-		}
309
-		return false;
310
-	}
311
-
312
-	/** {@inheritdoc} */
313
-	public function file_exists($path) {
314
-		try {
315
-			$path = $this->cleanPath($path);
316
-			$cachedState = $this->statCache->get($path);
317
-			if ($cachedState === false) {
318
-				// we know the file doesn't exist
319
-				return false;
320
-			} else if (!is_null($cachedState)) {
321
-				return true;
322
-			}
323
-			// need to get from server
324
-			return ($this->propfind($path) !== false);
325
-		} catch (\Exception $e) {
326
-			$this->convertException($e, $path);
327
-		}
328
-		return false;
329
-	}
330
-
331
-	/** {@inheritdoc} */
332
-	public function unlink($path) {
333
-		$this->init();
334
-		$path = $this->cleanPath($path);
335
-		$result = $this->simpleResponse('DELETE', $path, null, 204);
336
-		$this->statCache->clear($path . '/');
337
-		$this->statCache->remove($path);
338
-		return $result;
339
-	}
340
-
341
-	/** {@inheritdoc} */
342
-	public function fopen($path, $mode) {
343
-		$this->init();
344
-		$path = $this->cleanPath($path);
345
-		switch ($mode) {
346
-			case 'r':
347
-			case 'rb':
348
-				try {
349
-					$response = $this->httpClientService
350
-						->newClient()
351
-						->get($this->createBaseUri() . $this->encodePath($path), [
352
-							'auth' => [$this->user, $this->password],
353
-							'stream' => true
354
-						]);
355
-				} catch (\GuzzleHttp\Exception\ClientException $e) {
356
-					if ($e->getResponse() instanceof ResponseInterface
357
-						&& $e->getResponse()->getStatusCode() === 404) {
358
-						return false;
359
-					} else {
360
-						throw $e;
361
-					}
362
-				}
363
-
364
-				if ($response->getStatusCode() !== Http::STATUS_OK) {
365
-					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
366
-						throw new \OCP\Lock\LockedException($path);
367
-					} else {
368
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), ILogger::ERROR);
369
-					}
370
-				}
371
-
372
-				return $response->getBody();
373
-			case 'w':
374
-			case 'wb':
375
-			case 'a':
376
-			case 'ab':
377
-			case 'r+':
378
-			case 'w+':
379
-			case 'wb+':
380
-			case 'a+':
381
-			case 'x':
382
-			case 'x+':
383
-			case 'c':
384
-			case 'c+':
385
-				//emulate these
386
-				$tempManager = \OC::$server->getTempManager();
387
-				if (strrpos($path, '.') !== false) {
388
-					$ext = substr($path, strrpos($path, '.'));
389
-				} else {
390
-					$ext = '';
391
-				}
392
-				if ($this->file_exists($path)) {
393
-					if (!$this->isUpdatable($path)) {
394
-						return false;
395
-					}
396
-					if ($mode === 'w' or $mode === 'w+') {
397
-						$tmpFile = $tempManager->getTemporaryFile($ext);
398
-					} else {
399
-						$tmpFile = $this->getCachedFile($path);
400
-					}
401
-				} else {
402
-					if (!$this->isCreatable(dirname($path))) {
403
-						return false;
404
-					}
405
-					$tmpFile = $tempManager->getTemporaryFile($ext);
406
-				}
407
-				$handle = fopen($tmpFile, $mode);
408
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
409
-					$this->writeBack($tmpFile, $path);
410
-				});
411
-		}
412
-	}
413
-
414
-	/**
415
-	 * @param string $tmpFile
416
-	 */
417
-	public function writeBack($tmpFile, $path) {
418
-		$this->uploadFile($tmpFile, $path);
419
-		unlink($tmpFile);
420
-	}
421
-
422
-	/** {@inheritdoc} */
423
-	public function free_space($path) {
424
-		$this->init();
425
-		$path = $this->cleanPath($path);
426
-		try {
427
-			// TODO: cacheable ?
428
-			$response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
429
-			if ($response === false) {
430
-				return FileInfo::SPACE_UNKNOWN;
431
-			}
432
-			if (isset($response['{DAV:}quota-available-bytes'])) {
433
-				return (int)$response['{DAV:}quota-available-bytes'];
434
-			} else {
435
-				return FileInfo::SPACE_UNKNOWN;
436
-			}
437
-		} catch (\Exception $e) {
438
-			return FileInfo::SPACE_UNKNOWN;
439
-		}
440
-	}
441
-
442
-	/** {@inheritdoc} */
443
-	public function touch($path, $mtime = null) {
444
-		$this->init();
445
-		if (is_null($mtime)) {
446
-			$mtime = time();
447
-		}
448
-		$path = $this->cleanPath($path);
449
-
450
-		// if file exists, update the mtime, else create a new empty file
451
-		if ($this->file_exists($path)) {
452
-			try {
453
-				$this->statCache->remove($path);
454
-				$this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
455
-				// non-owncloud clients might not have accepted the property, need to recheck it
456
-				$response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
457
-				if ($response === false) {
458
-					return false;
459
-				}
460
-				if (isset($response['{DAV:}getlastmodified'])) {
461
-					$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
462
-					if ($remoteMtime !== $mtime) {
463
-						// server has not accepted the mtime
464
-						return false;
465
-					}
466
-				}
467
-			} catch (ClientHttpException $e) {
468
-				if ($e->getHttpStatus() === 501) {
469
-					return false;
470
-				}
471
-				$this->convertException($e, $path);
472
-				return false;
473
-			} catch (\Exception $e) {
474
-				$this->convertException($e, $path);
475
-				return false;
476
-			}
477
-		} else {
478
-			$this->file_put_contents($path, '');
479
-		}
480
-		return true;
481
-	}
482
-
483
-	/**
484
-	 * @param string $path
485
-	 * @param string $data
486
-	 * @return int
487
-	 */
488
-	public function file_put_contents($path, $data) {
489
-		$path = $this->cleanPath($path);
490
-		$result = parent::file_put_contents($path, $data);
491
-		$this->statCache->remove($path);
492
-		return $result;
493
-	}
494
-
495
-	/**
496
-	 * @param string $path
497
-	 * @param string $target
498
-	 */
499
-	protected function uploadFile($path, $target) {
500
-		$this->init();
501
-
502
-		// invalidate
503
-		$target = $this->cleanPath($target);
504
-		$this->statCache->remove($target);
505
-		$source = fopen($path, 'r');
506
-
507
-		$this->httpClientService
508
-			->newClient()
509
-			->put($this->createBaseUri() . $this->encodePath($target), [
510
-				'body' => $source,
511
-				'auth' => [$this->user, $this->password]
512
-			]);
513
-
514
-		$this->removeCachedFile($target);
515
-	}
516
-
517
-	/** {@inheritdoc} */
518
-	public function rename($path1, $path2) {
519
-		$this->init();
520
-		$path1 = $this->cleanPath($path1);
521
-		$path2 = $this->cleanPath($path2);
522
-		try {
523
-			// overwrite directory ?
524
-			if ($this->is_dir($path2)) {
525
-				// needs trailing slash in destination
526
-				$path2 = rtrim($path2, '/') . '/';
527
-			}
528
-			$this->client->request(
529
-				'MOVE',
530
-				$this->encodePath($path1),
531
-				null,
532
-				[
533
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
534
-				]
535
-			);
536
-			$this->statCache->clear($path1 . '/');
537
-			$this->statCache->clear($path2 . '/');
538
-			$this->statCache->set($path1, false);
539
-			$this->statCache->set($path2, true);
540
-			$this->removeCachedFile($path1);
541
-			$this->removeCachedFile($path2);
542
-			return true;
543
-		} catch (\Exception $e) {
544
-			$this->convertException($e);
545
-		}
546
-		return false;
547
-	}
548
-
549
-	/** {@inheritdoc} */
550
-	public function copy($path1, $path2) {
551
-		$this->init();
552
-		$path1 = $this->cleanPath($path1);
553
-		$path2 = $this->cleanPath($path2);
554
-		try {
555
-			// overwrite directory ?
556
-			if ($this->is_dir($path2)) {
557
-				// needs trailing slash in destination
558
-				$path2 = rtrim($path2, '/') . '/';
559
-			}
560
-			$this->client->request(
561
-				'COPY',
562
-				$this->encodePath($path1),
563
-				null,
564
-				[
565
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
566
-				]
567
-			);
568
-			$this->statCache->clear($path2 . '/');
569
-			$this->statCache->set($path2, true);
570
-			$this->removeCachedFile($path2);
571
-			return true;
572
-		} catch (\Exception $e) {
573
-			$this->convertException($e);
574
-		}
575
-		return false;
576
-	}
577
-
578
-	/** {@inheritdoc} */
579
-	public function stat($path) {
580
-		try {
581
-			$response = $this->propfind($path);
582
-			if (!$response) {
583
-				return false;
584
-			}
585
-			return [
586
-				'mtime' => strtotime($response['{DAV:}getlastmodified']),
587
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
588
-			];
589
-		} catch (\Exception $e) {
590
-			$this->convertException($e, $path);
591
-		}
592
-		return array();
593
-	}
594
-
595
-	/** {@inheritdoc} */
596
-	public function getMimeType($path) {
597
-		$remoteMimetype = $this->getMimeTypeFromRemote($path);
598
-		if ($remoteMimetype === 'application/octet-stream') {
599
-			return \OC::$server->getMimeTypeDetector()->detectPath($path);
600
-		} else {
601
-			return $remoteMimetype;
602
-		}
603
-	}
604
-
605
-	public function getMimeTypeFromRemote($path) {
606
-		try {
607
-			$response = $this->propfind($path);
608
-			if ($response === false) {
609
-				return false;
610
-			}
611
-			$responseType = [];
612
-			if (isset($response["{DAV:}resourcetype"])) {
613
-				/** @var ResourceType[] $response */
614
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
615
-			}
616
-			$type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
617
-			if ($type == 'dir') {
618
-				return 'httpd/unix-directory';
619
-			} elseif (isset($response['{DAV:}getcontenttype'])) {
620
-				return $response['{DAV:}getcontenttype'];
621
-			} else {
622
-				return 'application/octet-stream';
623
-			}
624
-		} catch (\Exception $e) {
625
-			return false;
626
-		}
627
-	}
628
-
629
-	/**
630
-	 * @param string $path
631
-	 * @return string
632
-	 */
633
-	public function cleanPath($path) {
634
-		if ($path === '') {
635
-			return $path;
636
-		}
637
-		$path = Filesystem::normalizePath($path);
638
-		// remove leading slash
639
-		return substr($path, 1);
640
-	}
641
-
642
-	/**
643
-	 * URL encodes the given path but keeps the slashes
644
-	 *
645
-	 * @param string $path to encode
646
-	 * @return string encoded path
647
-	 */
648
-	protected function encodePath($path) {
649
-		// slashes need to stay
650
-		return str_replace('%2F', '/', rawurlencode($path));
651
-	}
652
-
653
-	/**
654
-	 * @param string $method
655
-	 * @param string $path
656
-	 * @param string|resource|null $body
657
-	 * @param int $expected
658
-	 * @return bool
659
-	 * @throws StorageInvalidException
660
-	 * @throws StorageNotAvailableException
661
-	 */
662
-	protected function simpleResponse($method, $path, $body, $expected) {
663
-		$path = $this->cleanPath($path);
664
-		try {
665
-			$response = $this->client->request($method, $this->encodePath($path), $body);
666
-			return $response['statusCode'] == $expected;
667
-		} catch (ClientHttpException $e) {
668
-			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
669
-				$this->statCache->clear($path . '/');
670
-				$this->statCache->set($path, false);
671
-				return false;
672
-			}
673
-
674
-			$this->convertException($e, $path);
675
-		} catch (\Exception $e) {
676
-			$this->convertException($e, $path);
677
-		}
678
-		return false;
679
-	}
680
-
681
-	/**
682
-	 * check if curl is installed
683
-	 */
684
-	public static function checkDependencies() {
685
-		return true;
686
-	}
687
-
688
-	/** {@inheritdoc} */
689
-	public function isUpdatable($path) {
690
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
691
-	}
692
-
693
-	/** {@inheritdoc} */
694
-	public function isCreatable($path) {
695
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
696
-	}
697
-
698
-	/** {@inheritdoc} */
699
-	public function isSharable($path) {
700
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
701
-	}
702
-
703
-	/** {@inheritdoc} */
704
-	public function isDeletable($path) {
705
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
706
-	}
707
-
708
-	/** {@inheritdoc} */
709
-	public function getPermissions($path) {
710
-		$this->init();
711
-		$path = $this->cleanPath($path);
712
-		$response = $this->propfind($path);
713
-		if ($response === false) {
714
-			return 0;
715
-		}
716
-		if (isset($response['{http://owncloud.org/ns}permissions'])) {
717
-			return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
718
-		} else if ($this->is_dir($path)) {
719
-			return Constants::PERMISSION_ALL;
720
-		} else if ($this->file_exists($path)) {
721
-			return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
722
-		} else {
723
-			return 0;
724
-		}
725
-	}
726
-
727
-	/** {@inheritdoc} */
728
-	public function getETag($path) {
729
-		$this->init();
730
-		$path = $this->cleanPath($path);
731
-		$response = $this->propfind($path);
732
-		if ($response === false) {
733
-			return null;
734
-		}
735
-		if (isset($response['{DAV:}getetag'])) {
736
-			$etag = trim($response['{DAV:}getetag'], '"');
737
-			if (strlen($etag) > 40) {
738
-				$etag = md5($etag);
739
-			}
740
-			return $etag;
741
-		}
742
-		return parent::getEtag($path);
743
-	}
744
-
745
-	/**
746
-	 * @param string $permissionsString
747
-	 * @return int
748
-	 */
749
-	protected function parsePermissions($permissionsString) {
750
-		$permissions = Constants::PERMISSION_READ;
751
-		if (strpos($permissionsString, 'R') !== false) {
752
-			$permissions |= Constants::PERMISSION_SHARE;
753
-		}
754
-		if (strpos($permissionsString, 'D') !== false) {
755
-			$permissions |= Constants::PERMISSION_DELETE;
756
-		}
757
-		if (strpos($permissionsString, 'W') !== false) {
758
-			$permissions |= Constants::PERMISSION_UPDATE;
759
-		}
760
-		if (strpos($permissionsString, 'CK') !== false) {
761
-			$permissions |= Constants::PERMISSION_CREATE;
762
-			$permissions |= Constants::PERMISSION_UPDATE;
763
-		}
764
-		return $permissions;
765
-	}
766
-
767
-	/**
768
-	 * check if a file or folder has been updated since $time
769
-	 *
770
-	 * @param string $path
771
-	 * @param int $time
772
-	 * @throws \OCP\Files\StorageNotAvailableException
773
-	 * @return bool
774
-	 */
775
-	public function hasUpdated($path, $time) {
776
-		$this->init();
777
-		$path = $this->cleanPath($path);
778
-		try {
779
-			// force refresh for $path
780
-			$this->statCache->remove($path);
781
-			$response = $this->propfind($path);
782
-			if ($response === false) {
783
-				if ($path === '') {
784
-					// if root is gone it means the storage is not available
785
-					throw new StorageNotAvailableException('root is gone');
786
-				}
787
-				return false;
788
-			}
789
-			if (isset($response['{DAV:}getetag'])) {
790
-				$cachedData = $this->getCache()->get($path);
791
-				$etag = null;
792
-				if (isset($response['{DAV:}getetag'])) {
793
-					$etag = trim($response['{DAV:}getetag'], '"');
794
-				}
795
-				if (!empty($etag) && $cachedData['etag'] !== $etag) {
796
-					return true;
797
-				} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
798
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
799
-					return $sharePermissions !== $cachedData['permissions'];
800
-				} else if (isset($response['{http://owncloud.org/ns}permissions'])) {
801
-					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
802
-					return $permissions !== $cachedData['permissions'];
803
-				} else {
804
-					return false;
805
-				}
806
-			} else {
807
-				$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
808
-				return $remoteMtime > $time;
809
-			}
810
-		} catch (ClientHttpException $e) {
811
-			if ($e->getHttpStatus() === 405) {
812
-				if ($path === '') {
813
-					// if root is gone it means the storage is not available
814
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
815
-				}
816
-				return false;
817
-			}
818
-			$this->convertException($e, $path);
819
-			return false;
820
-		} catch (\Exception $e) {
821
-			$this->convertException($e, $path);
822
-			return false;
823
-		}
824
-	}
825
-
826
-	/**
827
-	 * Interpret the given exception and decide whether it is due to an
828
-	 * unavailable storage, invalid storage or other.
829
-	 * This will either throw StorageInvalidException, StorageNotAvailableException
830
-	 * or do nothing.
831
-	 *
832
-	 * @param Exception $e sabre exception
833
-	 * @param string $path optional path from the operation
834
-	 *
835
-	 * @throws StorageInvalidException if the storage is invalid, for example
836
-	 * when the authentication expired or is invalid
837
-	 * @throws StorageNotAvailableException if the storage is not available,
838
-	 * which might be temporary
839
-	 * @throws ForbiddenException if the action is not allowed
840
-	 */
841
-	protected function convertException(Exception $e, $path = '') {
842
-		\OC::$server->getLogger()->logException($e, ['app' => 'files_external', 'level' => ILogger::DEBUG]);
843
-		if ($e instanceof ClientHttpException) {
844
-			if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
845
-				throw new \OCP\Lock\LockedException($path);
846
-			}
847
-			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
848
-				// either password was changed or was invalid all along
849
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
850
-			} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
851
-				// ignore exception for MethodNotAllowed, false will be returned
852
-				return;
853
-			} else if ($e->getHttpStatus() === Http::STATUS_FORBIDDEN){
854
-				// The operation is forbidden. Fail somewhat gracefully
855
-				throw new ForbiddenException(get_class($e) . ':' . $e->getMessage());
856
-			}
857
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
858
-		} else if ($e instanceof ClientException) {
859
-			// connection timeout or refused, server could be temporarily down
860
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
861
-		} else if ($e instanceof \InvalidArgumentException) {
862
-			// parse error because the server returned HTML instead of XML,
863
-			// possibly temporarily down
864
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
865
-		} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
866
-			// rethrow
867
-			throw $e;
868
-		}
869
-
870
-		// TODO: only log for now, but in the future need to wrap/rethrow exception
871
-	}
63
+    /** @var string */
64
+    protected $password;
65
+    /** @var string */
66
+    protected $user;
67
+    /** @var string */
68
+    protected $authType;
69
+    /** @var string */
70
+    protected $host;
71
+    /** @var bool */
72
+    protected $secure;
73
+    /** @var string */
74
+    protected $root;
75
+    /** @var string */
76
+    protected $certPath;
77
+    /** @var bool */
78
+    protected $ready;
79
+    /** @var Client */
80
+    protected $client;
81
+    /** @var ArrayCache */
82
+    protected $statCache;
83
+    /** @var IClientService */
84
+    protected $httpClientService;
85
+    /** @var ICertificateManager */
86
+    protected $certManager;
87
+
88
+    /**
89
+     * @param array $params
90
+     * @throws \Exception
91
+     */
92
+    public function __construct($params) {
93
+        $this->statCache = new ArrayCache();
94
+        $this->httpClientService = \OC::$server->getHTTPClientService();
95
+        if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
96
+            $host = $params['host'];
97
+            //remove leading http[s], will be generated in createBaseUri()
98
+            if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
99
+            else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
100
+            $this->host = $host;
101
+            $this->user = $params['user'];
102
+            $this->password = $params['password'];
103
+            if (isset($params['authType'])) {
104
+                $this->authType = $params['authType'];
105
+            }
106
+            if (isset($params['secure'])) {
107
+                if (is_string($params['secure'])) {
108
+                    $this->secure = ($params['secure'] === 'true');
109
+                } else {
110
+                    $this->secure = (bool)$params['secure'];
111
+                }
112
+            } else {
113
+                $this->secure = false;
114
+            }
115
+            if ($this->secure === true) {
116
+                // inject mock for testing
117
+                $this->certManager = \OC::$server->getCertificateManager();
118
+                if (is_null($this->certManager)) { //no user
119
+                    $this->certManager = \OC::$server->getCertificateManager(null);
120
+                }
121
+            }
122
+            $this->root = $params['root'] ?? '/';
123
+            $this->root = '/' . ltrim($this->root, '/');
124
+            $this->root = rtrim($this->root, '/') . '/';
125
+        } else {
126
+            throw new \Exception('Invalid webdav storage configuration');
127
+        }
128
+    }
129
+
130
+    protected function init() {
131
+        if ($this->ready) {
132
+            return;
133
+        }
134
+        $this->ready = true;
135
+
136
+        $settings = [
137
+            'baseUri' => $this->createBaseUri(),
138
+            'userName' => $this->user,
139
+            'password' => $this->password,
140
+        ];
141
+        if (isset($this->authType)) {
142
+            $settings['authType'] = $this->authType;
143
+        }
144
+
145
+        $proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
146
+        if ($proxy !== '') {
147
+            $settings['proxy'] = $proxy;
148
+        }
149
+
150
+        $this->client = new Client($settings);
151
+        $this->client->setThrowExceptions(true);
152
+
153
+        if($this->secure === true) {
154
+            $certPath = $this->certManager->getAbsoluteBundlePath();
155
+            if (file_exists($certPath)) {
156
+                $this->certPath = $certPath;
157
+            }
158
+            if ($this->certPath) {
159
+                $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
160
+            }
161
+        }
162
+    }
163
+
164
+    /**
165
+     * Clear the stat cache
166
+     */
167
+    public function clearStatCache() {
168
+        $this->statCache->clear();
169
+    }
170
+
171
+    /** {@inheritdoc} */
172
+    public function getId() {
173
+        return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
174
+    }
175
+
176
+    /** {@inheritdoc} */
177
+    public function createBaseUri() {
178
+        $baseUri = 'http';
179
+        if ($this->secure) {
180
+            $baseUri .= 's';
181
+        }
182
+        $baseUri .= '://' . $this->host . $this->root;
183
+        return $baseUri;
184
+    }
185
+
186
+    /** {@inheritdoc} */
187
+    public function mkdir($path) {
188
+        $this->init();
189
+        $path = $this->cleanPath($path);
190
+        $result = $this->simpleResponse('MKCOL', $path, null, 201);
191
+        if ($result) {
192
+            $this->statCache->set($path, true);
193
+        }
194
+        return $result;
195
+    }
196
+
197
+    /** {@inheritdoc} */
198
+    public function rmdir($path) {
199
+        $this->init();
200
+        $path = $this->cleanPath($path);
201
+        // FIXME: some WebDAV impl return 403 when trying to DELETE
202
+        // a non-empty folder
203
+        $result = $this->simpleResponse('DELETE', $path . '/', null, 204);
204
+        $this->statCache->clear($path . '/');
205
+        $this->statCache->remove($path);
206
+        return $result;
207
+    }
208
+
209
+    /** {@inheritdoc} */
210
+    public function opendir($path) {
211
+        $this->init();
212
+        $path = $this->cleanPath($path);
213
+        try {
214
+            $response = $this->client->propFind(
215
+                $this->encodePath($path),
216
+                ['{DAV:}getetag'],
217
+                1
218
+            );
219
+            if ($response === false) {
220
+                return false;
221
+            }
222
+            $content = [];
223
+            $files = array_keys($response);
224
+            array_shift($files); //the first entry is the current directory
225
+
226
+            if (!$this->statCache->hasKey($path)) {
227
+                $this->statCache->set($path, true);
228
+            }
229
+            foreach ($files as $file) {
230
+                $file = urldecode($file);
231
+                // do not store the real entry, we might not have all properties
232
+                if (!$this->statCache->hasKey($path)) {
233
+                    $this->statCache->set($file, true);
234
+                }
235
+                $file = basename($file);
236
+                $content[] = $file;
237
+            }
238
+            return IteratorDirectory::wrap($content);
239
+        } catch (\Exception $e) {
240
+            $this->convertException($e, $path);
241
+        }
242
+        return false;
243
+    }
244
+
245
+    /**
246
+     * Propfind call with cache handling.
247
+     *
248
+     * First checks if information is cached.
249
+     * If not, request it from the server then store to cache.
250
+     *
251
+     * @param string $path path to propfind
252
+     *
253
+     * @return array|boolean propfind response or false if the entry was not found
254
+     *
255
+     * @throws ClientHttpException
256
+     */
257
+    protected function propfind($path) {
258
+        $path = $this->cleanPath($path);
259
+        $cachedResponse = $this->statCache->get($path);
260
+        // we either don't know it, or we know it exists but need more details
261
+        if (is_null($cachedResponse) || $cachedResponse === true) {
262
+            $this->init();
263
+            try {
264
+                $response = $this->client->propFind(
265
+                    $this->encodePath($path),
266
+                    array(
267
+                        '{DAV:}getlastmodified',
268
+                        '{DAV:}getcontentlength',
269
+                        '{DAV:}getcontenttype',
270
+                        '{http://owncloud.org/ns}permissions',
271
+                        '{http://open-collaboration-services.org/ns}share-permissions',
272
+                        '{DAV:}resourcetype',
273
+                        '{DAV:}getetag',
274
+                    )
275
+                );
276
+                $this->statCache->set($path, $response);
277
+            } catch (ClientHttpException $e) {
278
+                if ($e->getHttpStatus() === 404 || $e->getHttpStatus() === 405) {
279
+                    $this->statCache->clear($path . '/');
280
+                    $this->statCache->set($path, false);
281
+                    return false;
282
+                }
283
+                $this->convertException($e, $path);
284
+            } catch (\Exception $e) {
285
+                $this->convertException($e, $path);
286
+            }
287
+        } else {
288
+            $response = $cachedResponse;
289
+        }
290
+        return $response;
291
+    }
292
+
293
+    /** {@inheritdoc} */
294
+    public function filetype($path) {
295
+        try {
296
+            $response = $this->propfind($path);
297
+            if ($response === false) {
298
+                return false;
299
+            }
300
+            $responseType = [];
301
+            if (isset($response["{DAV:}resourcetype"])) {
302
+                /** @var ResourceType[] $response */
303
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
304
+            }
305
+            return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
306
+        } catch (\Exception $e) {
307
+            $this->convertException($e, $path);
308
+        }
309
+        return false;
310
+    }
311
+
312
+    /** {@inheritdoc} */
313
+    public function file_exists($path) {
314
+        try {
315
+            $path = $this->cleanPath($path);
316
+            $cachedState = $this->statCache->get($path);
317
+            if ($cachedState === false) {
318
+                // we know the file doesn't exist
319
+                return false;
320
+            } else if (!is_null($cachedState)) {
321
+                return true;
322
+            }
323
+            // need to get from server
324
+            return ($this->propfind($path) !== false);
325
+        } catch (\Exception $e) {
326
+            $this->convertException($e, $path);
327
+        }
328
+        return false;
329
+    }
330
+
331
+    /** {@inheritdoc} */
332
+    public function unlink($path) {
333
+        $this->init();
334
+        $path = $this->cleanPath($path);
335
+        $result = $this->simpleResponse('DELETE', $path, null, 204);
336
+        $this->statCache->clear($path . '/');
337
+        $this->statCache->remove($path);
338
+        return $result;
339
+    }
340
+
341
+    /** {@inheritdoc} */
342
+    public function fopen($path, $mode) {
343
+        $this->init();
344
+        $path = $this->cleanPath($path);
345
+        switch ($mode) {
346
+            case 'r':
347
+            case 'rb':
348
+                try {
349
+                    $response = $this->httpClientService
350
+                        ->newClient()
351
+                        ->get($this->createBaseUri() . $this->encodePath($path), [
352
+                            'auth' => [$this->user, $this->password],
353
+                            'stream' => true
354
+                        ]);
355
+                } catch (\GuzzleHttp\Exception\ClientException $e) {
356
+                    if ($e->getResponse() instanceof ResponseInterface
357
+                        && $e->getResponse()->getStatusCode() === 404) {
358
+                        return false;
359
+                    } else {
360
+                        throw $e;
361
+                    }
362
+                }
363
+
364
+                if ($response->getStatusCode() !== Http::STATUS_OK) {
365
+                    if ($response->getStatusCode() === Http::STATUS_LOCKED) {
366
+                        throw new \OCP\Lock\LockedException($path);
367
+                    } else {
368
+                        Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), ILogger::ERROR);
369
+                    }
370
+                }
371
+
372
+                return $response->getBody();
373
+            case 'w':
374
+            case 'wb':
375
+            case 'a':
376
+            case 'ab':
377
+            case 'r+':
378
+            case 'w+':
379
+            case 'wb+':
380
+            case 'a+':
381
+            case 'x':
382
+            case 'x+':
383
+            case 'c':
384
+            case 'c+':
385
+                //emulate these
386
+                $tempManager = \OC::$server->getTempManager();
387
+                if (strrpos($path, '.') !== false) {
388
+                    $ext = substr($path, strrpos($path, '.'));
389
+                } else {
390
+                    $ext = '';
391
+                }
392
+                if ($this->file_exists($path)) {
393
+                    if (!$this->isUpdatable($path)) {
394
+                        return false;
395
+                    }
396
+                    if ($mode === 'w' or $mode === 'w+') {
397
+                        $tmpFile = $tempManager->getTemporaryFile($ext);
398
+                    } else {
399
+                        $tmpFile = $this->getCachedFile($path);
400
+                    }
401
+                } else {
402
+                    if (!$this->isCreatable(dirname($path))) {
403
+                        return false;
404
+                    }
405
+                    $tmpFile = $tempManager->getTemporaryFile($ext);
406
+                }
407
+                $handle = fopen($tmpFile, $mode);
408
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
409
+                    $this->writeBack($tmpFile, $path);
410
+                });
411
+        }
412
+    }
413
+
414
+    /**
415
+     * @param string $tmpFile
416
+     */
417
+    public function writeBack($tmpFile, $path) {
418
+        $this->uploadFile($tmpFile, $path);
419
+        unlink($tmpFile);
420
+    }
421
+
422
+    /** {@inheritdoc} */
423
+    public function free_space($path) {
424
+        $this->init();
425
+        $path = $this->cleanPath($path);
426
+        try {
427
+            // TODO: cacheable ?
428
+            $response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
429
+            if ($response === false) {
430
+                return FileInfo::SPACE_UNKNOWN;
431
+            }
432
+            if (isset($response['{DAV:}quota-available-bytes'])) {
433
+                return (int)$response['{DAV:}quota-available-bytes'];
434
+            } else {
435
+                return FileInfo::SPACE_UNKNOWN;
436
+            }
437
+        } catch (\Exception $e) {
438
+            return FileInfo::SPACE_UNKNOWN;
439
+        }
440
+    }
441
+
442
+    /** {@inheritdoc} */
443
+    public function touch($path, $mtime = null) {
444
+        $this->init();
445
+        if (is_null($mtime)) {
446
+            $mtime = time();
447
+        }
448
+        $path = $this->cleanPath($path);
449
+
450
+        // if file exists, update the mtime, else create a new empty file
451
+        if ($this->file_exists($path)) {
452
+            try {
453
+                $this->statCache->remove($path);
454
+                $this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
455
+                // non-owncloud clients might not have accepted the property, need to recheck it
456
+                $response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
457
+                if ($response === false) {
458
+                    return false;
459
+                }
460
+                if (isset($response['{DAV:}getlastmodified'])) {
461
+                    $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
462
+                    if ($remoteMtime !== $mtime) {
463
+                        // server has not accepted the mtime
464
+                        return false;
465
+                    }
466
+                }
467
+            } catch (ClientHttpException $e) {
468
+                if ($e->getHttpStatus() === 501) {
469
+                    return false;
470
+                }
471
+                $this->convertException($e, $path);
472
+                return false;
473
+            } catch (\Exception $e) {
474
+                $this->convertException($e, $path);
475
+                return false;
476
+            }
477
+        } else {
478
+            $this->file_put_contents($path, '');
479
+        }
480
+        return true;
481
+    }
482
+
483
+    /**
484
+     * @param string $path
485
+     * @param string $data
486
+     * @return int
487
+     */
488
+    public function file_put_contents($path, $data) {
489
+        $path = $this->cleanPath($path);
490
+        $result = parent::file_put_contents($path, $data);
491
+        $this->statCache->remove($path);
492
+        return $result;
493
+    }
494
+
495
+    /**
496
+     * @param string $path
497
+     * @param string $target
498
+     */
499
+    protected function uploadFile($path, $target) {
500
+        $this->init();
501
+
502
+        // invalidate
503
+        $target = $this->cleanPath($target);
504
+        $this->statCache->remove($target);
505
+        $source = fopen($path, 'r');
506
+
507
+        $this->httpClientService
508
+            ->newClient()
509
+            ->put($this->createBaseUri() . $this->encodePath($target), [
510
+                'body' => $source,
511
+                'auth' => [$this->user, $this->password]
512
+            ]);
513
+
514
+        $this->removeCachedFile($target);
515
+    }
516
+
517
+    /** {@inheritdoc} */
518
+    public function rename($path1, $path2) {
519
+        $this->init();
520
+        $path1 = $this->cleanPath($path1);
521
+        $path2 = $this->cleanPath($path2);
522
+        try {
523
+            // overwrite directory ?
524
+            if ($this->is_dir($path2)) {
525
+                // needs trailing slash in destination
526
+                $path2 = rtrim($path2, '/') . '/';
527
+            }
528
+            $this->client->request(
529
+                'MOVE',
530
+                $this->encodePath($path1),
531
+                null,
532
+                [
533
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
534
+                ]
535
+            );
536
+            $this->statCache->clear($path1 . '/');
537
+            $this->statCache->clear($path2 . '/');
538
+            $this->statCache->set($path1, false);
539
+            $this->statCache->set($path2, true);
540
+            $this->removeCachedFile($path1);
541
+            $this->removeCachedFile($path2);
542
+            return true;
543
+        } catch (\Exception $e) {
544
+            $this->convertException($e);
545
+        }
546
+        return false;
547
+    }
548
+
549
+    /** {@inheritdoc} */
550
+    public function copy($path1, $path2) {
551
+        $this->init();
552
+        $path1 = $this->cleanPath($path1);
553
+        $path2 = $this->cleanPath($path2);
554
+        try {
555
+            // overwrite directory ?
556
+            if ($this->is_dir($path2)) {
557
+                // needs trailing slash in destination
558
+                $path2 = rtrim($path2, '/') . '/';
559
+            }
560
+            $this->client->request(
561
+                'COPY',
562
+                $this->encodePath($path1),
563
+                null,
564
+                [
565
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
566
+                ]
567
+            );
568
+            $this->statCache->clear($path2 . '/');
569
+            $this->statCache->set($path2, true);
570
+            $this->removeCachedFile($path2);
571
+            return true;
572
+        } catch (\Exception $e) {
573
+            $this->convertException($e);
574
+        }
575
+        return false;
576
+    }
577
+
578
+    /** {@inheritdoc} */
579
+    public function stat($path) {
580
+        try {
581
+            $response = $this->propfind($path);
582
+            if (!$response) {
583
+                return false;
584
+            }
585
+            return [
586
+                'mtime' => strtotime($response['{DAV:}getlastmodified']),
587
+                'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
588
+            ];
589
+        } catch (\Exception $e) {
590
+            $this->convertException($e, $path);
591
+        }
592
+        return array();
593
+    }
594
+
595
+    /** {@inheritdoc} */
596
+    public function getMimeType($path) {
597
+        $remoteMimetype = $this->getMimeTypeFromRemote($path);
598
+        if ($remoteMimetype === 'application/octet-stream') {
599
+            return \OC::$server->getMimeTypeDetector()->detectPath($path);
600
+        } else {
601
+            return $remoteMimetype;
602
+        }
603
+    }
604
+
605
+    public function getMimeTypeFromRemote($path) {
606
+        try {
607
+            $response = $this->propfind($path);
608
+            if ($response === false) {
609
+                return false;
610
+            }
611
+            $responseType = [];
612
+            if (isset($response["{DAV:}resourcetype"])) {
613
+                /** @var ResourceType[] $response */
614
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
615
+            }
616
+            $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
617
+            if ($type == 'dir') {
618
+                return 'httpd/unix-directory';
619
+            } elseif (isset($response['{DAV:}getcontenttype'])) {
620
+                return $response['{DAV:}getcontenttype'];
621
+            } else {
622
+                return 'application/octet-stream';
623
+            }
624
+        } catch (\Exception $e) {
625
+            return false;
626
+        }
627
+    }
628
+
629
+    /**
630
+     * @param string $path
631
+     * @return string
632
+     */
633
+    public function cleanPath($path) {
634
+        if ($path === '') {
635
+            return $path;
636
+        }
637
+        $path = Filesystem::normalizePath($path);
638
+        // remove leading slash
639
+        return substr($path, 1);
640
+    }
641
+
642
+    /**
643
+     * URL encodes the given path but keeps the slashes
644
+     *
645
+     * @param string $path to encode
646
+     * @return string encoded path
647
+     */
648
+    protected function encodePath($path) {
649
+        // slashes need to stay
650
+        return str_replace('%2F', '/', rawurlencode($path));
651
+    }
652
+
653
+    /**
654
+     * @param string $method
655
+     * @param string $path
656
+     * @param string|resource|null $body
657
+     * @param int $expected
658
+     * @return bool
659
+     * @throws StorageInvalidException
660
+     * @throws StorageNotAvailableException
661
+     */
662
+    protected function simpleResponse($method, $path, $body, $expected) {
663
+        $path = $this->cleanPath($path);
664
+        try {
665
+            $response = $this->client->request($method, $this->encodePath($path), $body);
666
+            return $response['statusCode'] == $expected;
667
+        } catch (ClientHttpException $e) {
668
+            if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
669
+                $this->statCache->clear($path . '/');
670
+                $this->statCache->set($path, false);
671
+                return false;
672
+            }
673
+
674
+            $this->convertException($e, $path);
675
+        } catch (\Exception $e) {
676
+            $this->convertException($e, $path);
677
+        }
678
+        return false;
679
+    }
680
+
681
+    /**
682
+     * check if curl is installed
683
+     */
684
+    public static function checkDependencies() {
685
+        return true;
686
+    }
687
+
688
+    /** {@inheritdoc} */
689
+    public function isUpdatable($path) {
690
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
691
+    }
692
+
693
+    /** {@inheritdoc} */
694
+    public function isCreatable($path) {
695
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
696
+    }
697
+
698
+    /** {@inheritdoc} */
699
+    public function isSharable($path) {
700
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
701
+    }
702
+
703
+    /** {@inheritdoc} */
704
+    public function isDeletable($path) {
705
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
706
+    }
707
+
708
+    /** {@inheritdoc} */
709
+    public function getPermissions($path) {
710
+        $this->init();
711
+        $path = $this->cleanPath($path);
712
+        $response = $this->propfind($path);
713
+        if ($response === false) {
714
+            return 0;
715
+        }
716
+        if (isset($response['{http://owncloud.org/ns}permissions'])) {
717
+            return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
718
+        } else if ($this->is_dir($path)) {
719
+            return Constants::PERMISSION_ALL;
720
+        } else if ($this->file_exists($path)) {
721
+            return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
722
+        } else {
723
+            return 0;
724
+        }
725
+    }
726
+
727
+    /** {@inheritdoc} */
728
+    public function getETag($path) {
729
+        $this->init();
730
+        $path = $this->cleanPath($path);
731
+        $response = $this->propfind($path);
732
+        if ($response === false) {
733
+            return null;
734
+        }
735
+        if (isset($response['{DAV:}getetag'])) {
736
+            $etag = trim($response['{DAV:}getetag'], '"');
737
+            if (strlen($etag) > 40) {
738
+                $etag = md5($etag);
739
+            }
740
+            return $etag;
741
+        }
742
+        return parent::getEtag($path);
743
+    }
744
+
745
+    /**
746
+     * @param string $permissionsString
747
+     * @return int
748
+     */
749
+    protected function parsePermissions($permissionsString) {
750
+        $permissions = Constants::PERMISSION_READ;
751
+        if (strpos($permissionsString, 'R') !== false) {
752
+            $permissions |= Constants::PERMISSION_SHARE;
753
+        }
754
+        if (strpos($permissionsString, 'D') !== false) {
755
+            $permissions |= Constants::PERMISSION_DELETE;
756
+        }
757
+        if (strpos($permissionsString, 'W') !== false) {
758
+            $permissions |= Constants::PERMISSION_UPDATE;
759
+        }
760
+        if (strpos($permissionsString, 'CK') !== false) {
761
+            $permissions |= Constants::PERMISSION_CREATE;
762
+            $permissions |= Constants::PERMISSION_UPDATE;
763
+        }
764
+        return $permissions;
765
+    }
766
+
767
+    /**
768
+     * check if a file or folder has been updated since $time
769
+     *
770
+     * @param string $path
771
+     * @param int $time
772
+     * @throws \OCP\Files\StorageNotAvailableException
773
+     * @return bool
774
+     */
775
+    public function hasUpdated($path, $time) {
776
+        $this->init();
777
+        $path = $this->cleanPath($path);
778
+        try {
779
+            // force refresh for $path
780
+            $this->statCache->remove($path);
781
+            $response = $this->propfind($path);
782
+            if ($response === false) {
783
+                if ($path === '') {
784
+                    // if root is gone it means the storage is not available
785
+                    throw new StorageNotAvailableException('root is gone');
786
+                }
787
+                return false;
788
+            }
789
+            if (isset($response['{DAV:}getetag'])) {
790
+                $cachedData = $this->getCache()->get($path);
791
+                $etag = null;
792
+                if (isset($response['{DAV:}getetag'])) {
793
+                    $etag = trim($response['{DAV:}getetag'], '"');
794
+                }
795
+                if (!empty($etag) && $cachedData['etag'] !== $etag) {
796
+                    return true;
797
+                } else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
798
+                    $sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
799
+                    return $sharePermissions !== $cachedData['permissions'];
800
+                } else if (isset($response['{http://owncloud.org/ns}permissions'])) {
801
+                    $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
802
+                    return $permissions !== $cachedData['permissions'];
803
+                } else {
804
+                    return false;
805
+                }
806
+            } else {
807
+                $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
808
+                return $remoteMtime > $time;
809
+            }
810
+        } catch (ClientHttpException $e) {
811
+            if ($e->getHttpStatus() === 405) {
812
+                if ($path === '') {
813
+                    // if root is gone it means the storage is not available
814
+                    throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
815
+                }
816
+                return false;
817
+            }
818
+            $this->convertException($e, $path);
819
+            return false;
820
+        } catch (\Exception $e) {
821
+            $this->convertException($e, $path);
822
+            return false;
823
+        }
824
+    }
825
+
826
+    /**
827
+     * Interpret the given exception and decide whether it is due to an
828
+     * unavailable storage, invalid storage or other.
829
+     * This will either throw StorageInvalidException, StorageNotAvailableException
830
+     * or do nothing.
831
+     *
832
+     * @param Exception $e sabre exception
833
+     * @param string $path optional path from the operation
834
+     *
835
+     * @throws StorageInvalidException if the storage is invalid, for example
836
+     * when the authentication expired or is invalid
837
+     * @throws StorageNotAvailableException if the storage is not available,
838
+     * which might be temporary
839
+     * @throws ForbiddenException if the action is not allowed
840
+     */
841
+    protected function convertException(Exception $e, $path = '') {
842
+        \OC::$server->getLogger()->logException($e, ['app' => 'files_external', 'level' => ILogger::DEBUG]);
843
+        if ($e instanceof ClientHttpException) {
844
+            if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
845
+                throw new \OCP\Lock\LockedException($path);
846
+            }
847
+            if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
848
+                // either password was changed or was invalid all along
849
+                throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
850
+            } else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
851
+                // ignore exception for MethodNotAllowed, false will be returned
852
+                return;
853
+            } else if ($e->getHttpStatus() === Http::STATUS_FORBIDDEN){
854
+                // The operation is forbidden. Fail somewhat gracefully
855
+                throw new ForbiddenException(get_class($e) . ':' . $e->getMessage());
856
+            }
857
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
858
+        } else if ($e instanceof ClientException) {
859
+            // connection timeout or refused, server could be temporarily down
860
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
861
+        } else if ($e instanceof \InvalidArgumentException) {
862
+            // parse error because the server returned HTML instead of XML,
863
+            // possibly temporarily down
864
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
865
+        } else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
866
+            // rethrow
867
+            throw $e;
868
+        }
869
+
870
+        // TODO: only log for now, but in the future need to wrap/rethrow exception
871
+    }
872 872
 }
Please login to merge, or discard this patch.
Spacing   +36 added lines, -36 removed lines patch added patch discarded remove patch
@@ -107,7 +107,7 @@  discard block
 block discarded – undo
107 107
 				if (is_string($params['secure'])) {
108 108
 					$this->secure = ($params['secure'] === 'true');
109 109
 				} else {
110
-					$this->secure = (bool)$params['secure'];
110
+					$this->secure = (bool) $params['secure'];
111 111
 				}
112 112
 			} else {
113 113
 				$this->secure = false;
@@ -120,8 +120,8 @@  discard block
 block discarded – undo
120 120
 				}
121 121
 			}
122 122
 			$this->root = $params['root'] ?? '/';
123
-			$this->root = '/' . ltrim($this->root, '/');
124
-			$this->root = rtrim($this->root, '/') . '/';
123
+			$this->root = '/'.ltrim($this->root, '/');
124
+			$this->root = rtrim($this->root, '/').'/';
125 125
 		} else {
126 126
 			throw new \Exception('Invalid webdav storage configuration');
127 127
 		}
@@ -150,7 +150,7 @@  discard block
 block discarded – undo
150 150
 		$this->client = new Client($settings);
151 151
 		$this->client->setThrowExceptions(true);
152 152
 
153
-		if($this->secure === true) {
153
+		if ($this->secure === true) {
154 154
 			$certPath = $this->certManager->getAbsoluteBundlePath();
155 155
 			if (file_exists($certPath)) {
156 156
 				$this->certPath = $certPath;
@@ -170,7 +170,7 @@  discard block
 block discarded – undo
170 170
 
171 171
 	/** {@inheritdoc} */
172 172
 	public function getId() {
173
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
173
+		return 'webdav::'.$this->user.'@'.$this->host.'/'.$this->root;
174 174
 	}
175 175
 
176 176
 	/** {@inheritdoc} */
@@ -179,7 +179,7 @@  discard block
 block discarded – undo
179 179
 		if ($this->secure) {
180 180
 			$baseUri .= 's';
181 181
 		}
182
-		$baseUri .= '://' . $this->host . $this->root;
182
+		$baseUri .= '://'.$this->host.$this->root;
183 183
 		return $baseUri;
184 184
 	}
185 185
 
@@ -200,8 +200,8 @@  discard block
 block discarded – undo
200 200
 		$path = $this->cleanPath($path);
201 201
 		// FIXME: some WebDAV impl return 403 when trying to DELETE
202 202
 		// a non-empty folder
203
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
204
-		$this->statCache->clear($path . '/');
203
+		$result = $this->simpleResponse('DELETE', $path.'/', null, 204);
204
+		$this->statCache->clear($path.'/');
205 205
 		$this->statCache->remove($path);
206 206
 		return $result;
207 207
 	}
@@ -276,7 +276,7 @@  discard block
 block discarded – undo
276 276
 				$this->statCache->set($path, $response);
277 277
 			} catch (ClientHttpException $e) {
278 278
 				if ($e->getHttpStatus() === 404 || $e->getHttpStatus() === 405) {
279
-					$this->statCache->clear($path . '/');
279
+					$this->statCache->clear($path.'/');
280 280
 					$this->statCache->set($path, false);
281 281
 					return false;
282 282
 				}
@@ -333,7 +333,7 @@  discard block
 block discarded – undo
333 333
 		$this->init();
334 334
 		$path = $this->cleanPath($path);
335 335
 		$result = $this->simpleResponse('DELETE', $path, null, 204);
336
-		$this->statCache->clear($path . '/');
336
+		$this->statCache->clear($path.'/');
337 337
 		$this->statCache->remove($path);
338 338
 		return $result;
339 339
 	}
@@ -348,7 +348,7 @@  discard block
 block discarded – undo
348 348
 				try {
349 349
 					$response = $this->httpClientService
350 350
 						->newClient()
351
-						->get($this->createBaseUri() . $this->encodePath($path), [
351
+						->get($this->createBaseUri().$this->encodePath($path), [
352 352
 							'auth' => [$this->user, $this->password],
353 353
 							'stream' => true
354 354
 						]);
@@ -365,7 +365,7 @@  discard block
 block discarded – undo
365 365
 					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
366 366
 						throw new \OCP\Lock\LockedException($path);
367 367
 					} else {
368
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), ILogger::ERROR);
368
+						Util::writeLog("webdav client", 'Guzzle get returned status code '.$response->getStatusCode(), ILogger::ERROR);
369 369
 					}
370 370
 				}
371 371
 
@@ -405,7 +405,7 @@  discard block
 block discarded – undo
405 405
 					$tmpFile = $tempManager->getTemporaryFile($ext);
406 406
 				}
407 407
 				$handle = fopen($tmpFile, $mode);
408
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
408
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
409 409
 					$this->writeBack($tmpFile, $path);
410 410
 				});
411 411
 		}
@@ -430,7 +430,7 @@  discard block
 block discarded – undo
430 430
 				return FileInfo::SPACE_UNKNOWN;
431 431
 			}
432 432
 			if (isset($response['{DAV:}quota-available-bytes'])) {
433
-				return (int)$response['{DAV:}quota-available-bytes'];
433
+				return (int) $response['{DAV:}quota-available-bytes'];
434 434
 			} else {
435 435
 				return FileInfo::SPACE_UNKNOWN;
436 436
 			}
@@ -506,7 +506,7 @@  discard block
 block discarded – undo
506 506
 
507 507
 		$this->httpClientService
508 508
 			->newClient()
509
-			->put($this->createBaseUri() . $this->encodePath($target), [
509
+			->put($this->createBaseUri().$this->encodePath($target), [
510 510
 				'body' => $source,
511 511
 				'auth' => [$this->user, $this->password]
512 512
 			]);
@@ -523,18 +523,18 @@  discard block
 block discarded – undo
523 523
 			// overwrite directory ?
524 524
 			if ($this->is_dir($path2)) {
525 525
 				// needs trailing slash in destination
526
-				$path2 = rtrim($path2, '/') . '/';
526
+				$path2 = rtrim($path2, '/').'/';
527 527
 			}
528 528
 			$this->client->request(
529 529
 				'MOVE',
530 530
 				$this->encodePath($path1),
531 531
 				null,
532 532
 				[
533
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
533
+					'Destination' => $this->createBaseUri().$this->encodePath($path2),
534 534
 				]
535 535
 			);
536
-			$this->statCache->clear($path1 . '/');
537
-			$this->statCache->clear($path2 . '/');
536
+			$this->statCache->clear($path1.'/');
537
+			$this->statCache->clear($path2.'/');
538 538
 			$this->statCache->set($path1, false);
539 539
 			$this->statCache->set($path2, true);
540 540
 			$this->removeCachedFile($path1);
@@ -555,17 +555,17 @@  discard block
 block discarded – undo
555 555
 			// overwrite directory ?
556 556
 			if ($this->is_dir($path2)) {
557 557
 				// needs trailing slash in destination
558
-				$path2 = rtrim($path2, '/') . '/';
558
+				$path2 = rtrim($path2, '/').'/';
559 559
 			}
560 560
 			$this->client->request(
561 561
 				'COPY',
562 562
 				$this->encodePath($path1),
563 563
 				null,
564 564
 				[
565
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
565
+					'Destination' => $this->createBaseUri().$this->encodePath($path2),
566 566
 				]
567 567
 			);
568
-			$this->statCache->clear($path2 . '/');
568
+			$this->statCache->clear($path2.'/');
569 569
 			$this->statCache->set($path2, true);
570 570
 			$this->removeCachedFile($path2);
571 571
 			return true;
@@ -584,7 +584,7 @@  discard block
 block discarded – undo
584 584
 			}
585 585
 			return [
586 586
 				'mtime' => strtotime($response['{DAV:}getlastmodified']),
587
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
587
+				'size' => (int) isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
588 588
 			];
589 589
 		} catch (\Exception $e) {
590 590
 			$this->convertException($e, $path);
@@ -666,7 +666,7 @@  discard block
 block discarded – undo
666 666
 			return $response['statusCode'] == $expected;
667 667
 		} catch (ClientHttpException $e) {
668 668
 			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
669
-				$this->statCache->clear($path . '/');
669
+				$this->statCache->clear($path.'/');
670 670
 				$this->statCache->set($path, false);
671 671
 				return false;
672 672
 			}
@@ -687,22 +687,22 @@  discard block
 block discarded – undo
687 687
 
688 688
 	/** {@inheritdoc} */
689 689
 	public function isUpdatable($path) {
690
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
690
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
691 691
 	}
692 692
 
693 693
 	/** {@inheritdoc} */
694 694
 	public function isCreatable($path) {
695
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
695
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_CREATE);
696 696
 	}
697 697
 
698 698
 	/** {@inheritdoc} */
699 699
 	public function isSharable($path) {
700
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
700
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_SHARE);
701 701
 	}
702 702
 
703 703
 	/** {@inheritdoc} */
704 704
 	public function isDeletable($path) {
705
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
705
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_DELETE);
706 706
 	}
707 707
 
708 708
 	/** {@inheritdoc} */
@@ -795,7 +795,7 @@  discard block
 block discarded – undo
795 795
 				if (!empty($etag) && $cachedData['etag'] !== $etag) {
796 796
 					return true;
797 797
 				} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
798
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
798
+					$sharePermissions = (int) $response['{http://open-collaboration-services.org/ns}share-permissions'];
799 799
 					return $sharePermissions !== $cachedData['permissions'];
800 800
 				} else if (isset($response['{http://owncloud.org/ns}permissions'])) {
801 801
 					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
@@ -811,7 +811,7 @@  discard block
 block discarded – undo
811 811
 			if ($e->getHttpStatus() === 405) {
812 812
 				if ($path === '') {
813 813
 					// if root is gone it means the storage is not available
814
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
814
+					throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
815 815
 				}
816 816
 				return false;
817 817
 			}
@@ -846,22 +846,22 @@  discard block
 block discarded – undo
846 846
 			}
847 847
 			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
848 848
 				// either password was changed or was invalid all along
849
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
849
+				throw new StorageInvalidException(get_class($e).': '.$e->getMessage());
850 850
 			} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
851 851
 				// ignore exception for MethodNotAllowed, false will be returned
852 852
 				return;
853
-			} else if ($e->getHttpStatus() === Http::STATUS_FORBIDDEN){
853
+			} else if ($e->getHttpStatus() === Http::STATUS_FORBIDDEN) {
854 854
 				// The operation is forbidden. Fail somewhat gracefully
855
-				throw new ForbiddenException(get_class($e) . ':' . $e->getMessage());
855
+				throw new ForbiddenException(get_class($e).':'.$e->getMessage());
856 856
 			}
857
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
857
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
858 858
 		} else if ($e instanceof ClientException) {
859 859
 			// connection timeout or refused, server could be temporarily down
860
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
860
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
861 861
 		} else if ($e instanceof \InvalidArgumentException) {
862 862
 			// parse error because the server returned HTML instead of XML,
863 863
 			// possibly temporarily down
864
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
864
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
865 865
 		} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
866 866
 			// rethrow
867 867
 			throw $e;
Please login to merge, or discard this patch.
lib/private/Files/Storage/Temporary.php 2 patches
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -29,20 +29,20 @@
 block discarded – undo
29 29
  * local storage backend in temporary folder for testing purpose
30 30
  */
31 31
 class Temporary extends Local{
32
-	public function __construct($arguments = null) {
33
-		parent::__construct(array('datadir' => \OC::$server->getTempManager()->getTemporaryFolder()));
34
-	}
32
+    public function __construct($arguments = null) {
33
+        parent::__construct(array('datadir' => \OC::$server->getTempManager()->getTemporaryFolder()));
34
+    }
35 35
 
36
-	public function cleanUp() {
37
-		\OC_Helper::rmdirr($this->datadir);
38
-	}
36
+    public function cleanUp() {
37
+        \OC_Helper::rmdirr($this->datadir);
38
+    }
39 39
 
40
-	public function __destruct() {
41
-		parent::__destruct();
42
-		$this->cleanUp();
43
-	}
40
+    public function __destruct() {
41
+        parent::__destruct();
42
+        $this->cleanUp();
43
+    }
44 44
 
45
-	public function getDataDir() {
46
-		return $this->datadir;
47
-	}
45
+    public function getDataDir() {
46
+        return $this->datadir;
47
+    }
48 48
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -28,7 +28,7 @@
 block discarded – undo
28 28
 /**
29 29
  * local storage backend in temporary folder for testing purpose
30 30
  */
31
-class Temporary extends Local{
31
+class Temporary extends Local {
32 32
 	public function __construct($arguments = null) {
33 33
 		parent::__construct(array('datadir' => \OC::$server->getTempManager()->getTemporaryFolder()));
34 34
 	}
Please login to merge, or discard this patch.
lib/private/Files/Storage/Local.php 3 patches
Braces   +3 added lines, -2 removed lines patch added patch discarded remove patch
@@ -317,8 +317,9 @@
 block discarded – undo
317 317
 		$files = array();
318 318
 		$physicalDir = $this->getSourcePath($dir);
319 319
 		foreach (scandir($physicalDir) as $item) {
320
-			if (\OC\Files\Filesystem::isIgnoredDir($item))
321
-				continue;
320
+			if (\OC\Files\Filesystem::isIgnoredDir($item)) {
321
+							continue;
322
+			}
322 323
 			$physicalItem = $physicalDir . '/' . $item;
323 324
 
324 325
 			if (strstr(strtolower($item), strtolower($query)) !== false) {
Please login to merge, or discard this patch.
Indentation   +452 added lines, -452 removed lines patch added patch discarded remove patch
@@ -49,456 +49,456 @@
 block discarded – undo
49 49
  * for local filestore, we only have to map the paths
50 50
  */
51 51
 class Local extends \OC\Files\Storage\Common {
52
-	protected $datadir;
53
-
54
-	protected $dataDirLength;
55
-
56
-	protected $allowSymlinks = false;
57
-
58
-	protected $realDataDir;
59
-
60
-	public function __construct($arguments) {
61
-		if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) {
62
-			throw new \InvalidArgumentException('No data directory set for local storage');
63
-		}
64
-		$this->datadir = str_replace('//', '/', $arguments['datadir']);
65
-		// some crazy code uses a local storage on root...
66
-		if ($this->datadir === '/') {
67
-			$this->realDataDir = $this->datadir;
68
-		} else {
69
-			$realPath = realpath($this->datadir) ?: $this->datadir;
70
-			$this->realDataDir = rtrim($realPath, '/') . '/';
71
-		}
72
-		if (substr($this->datadir, -1) !== '/') {
73
-			$this->datadir .= '/';
74
-		}
75
-		$this->dataDirLength = strlen($this->realDataDir);
76
-	}
77
-
78
-	public function __destruct() {
79
-	}
80
-
81
-	public function getId() {
82
-		return 'local::' . $this->datadir;
83
-	}
84
-
85
-	public function mkdir($path) {
86
-		return @mkdir($this->getSourcePath($path), 0777, true);
87
-	}
88
-
89
-	public function rmdir($path) {
90
-		if (!$this->isDeletable($path)) {
91
-			return false;
92
-		}
93
-		try {
94
-			$it = new \RecursiveIteratorIterator(
95
-				new \RecursiveDirectoryIterator($this->getSourcePath($path)),
96
-				\RecursiveIteratorIterator::CHILD_FIRST
97
-			);
98
-			/**
99
-			 * RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
100
-			 * This bug is fixed in PHP 5.5.9 or before
101
-			 * See #8376
102
-			 */
103
-			$it->rewind();
104
-			while ($it->valid()) {
105
-				/**
106
-				 * @var \SplFileInfo $file
107
-				 */
108
-				$file = $it->current();
109
-				if (in_array($file->getBasename(), array('.', '..'))) {
110
-					$it->next();
111
-					continue;
112
-				} elseif ($file->isDir()) {
113
-					rmdir($file->getPathname());
114
-				} elseif ($file->isFile() || $file->isLink()) {
115
-					unlink($file->getPathname());
116
-				}
117
-				$it->next();
118
-			}
119
-			return rmdir($this->getSourcePath($path));
120
-		} catch (\UnexpectedValueException $e) {
121
-			return false;
122
-		}
123
-	}
124
-
125
-	public function opendir($path) {
126
-		return opendir($this->getSourcePath($path));
127
-	}
128
-
129
-	public function is_dir($path) {
130
-		if (substr($path, -1) == '/') {
131
-			$path = substr($path, 0, -1);
132
-		}
133
-		return is_dir($this->getSourcePath($path));
134
-	}
135
-
136
-	public function is_file($path) {
137
-		return is_file($this->getSourcePath($path));
138
-	}
139
-
140
-	public function stat($path) {
141
-		clearstatcache();
142
-		$fullPath = $this->getSourcePath($path);
143
-		$statResult = stat($fullPath);
144
-		if (PHP_INT_SIZE === 4 && !$this->is_dir($path)) {
145
-			$filesize = $this->filesize($path);
146
-			$statResult['size'] = $filesize;
147
-			$statResult[7] = $filesize;
148
-		}
149
-		return $statResult;
150
-	}
151
-
152
-	public function filetype($path) {
153
-		$filetype = filetype($this->getSourcePath($path));
154
-		if ($filetype == 'link') {
155
-			$filetype = filetype(realpath($this->getSourcePath($path)));
156
-		}
157
-		return $filetype;
158
-	}
159
-
160
-	public function filesize($path) {
161
-		if ($this->is_dir($path)) {
162
-			return 0;
163
-		}
164
-		$fullPath = $this->getSourcePath($path);
165
-		if (PHP_INT_SIZE === 4) {
166
-			$helper = new \OC\LargeFileHelper;
167
-			return $helper->getFileSize($fullPath);
168
-		}
169
-		return filesize($fullPath);
170
-	}
171
-
172
-	public function isReadable($path) {
173
-		return is_readable($this->getSourcePath($path));
174
-	}
175
-
176
-	public function isUpdatable($path) {
177
-		return is_writable($this->getSourcePath($path));
178
-	}
179
-
180
-	public function file_exists($path) {
181
-		return file_exists($this->getSourcePath($path));
182
-	}
183
-
184
-	public function filemtime($path) {
185
-		$fullPath = $this->getSourcePath($path);
186
-		clearstatcache(true, $fullPath);
187
-		if (!$this->file_exists($path)) {
188
-			return false;
189
-		}
190
-		if (PHP_INT_SIZE === 4) {
191
-			$helper = new \OC\LargeFileHelper();
192
-			return $helper->getFileMtime($fullPath);
193
-		}
194
-		return filemtime($fullPath);
195
-	}
196
-
197
-	public function touch($path, $mtime = null) {
198
-		// sets the modification time of the file to the given value.
199
-		// If mtime is nil the current time is set.
200
-		// note that the access time of the file always changes to the current time.
201
-		if ($this->file_exists($path) and !$this->isUpdatable($path)) {
202
-			return false;
203
-		}
204
-		if (!is_null($mtime)) {
205
-			$result = @touch($this->getSourcePath($path), $mtime);
206
-		} else {
207
-			$result = @touch($this->getSourcePath($path));
208
-		}
209
-		if ($result) {
210
-			clearstatcache(true, $this->getSourcePath($path));
211
-		}
212
-
213
-		return $result;
214
-	}
215
-
216
-	public function file_get_contents($path) {
217
-		return file_get_contents($this->getSourcePath($path));
218
-	}
219
-
220
-	public function file_put_contents($path, $data) {
221
-		return file_put_contents($this->getSourcePath($path), $data);
222
-	}
223
-
224
-	public function unlink($path) {
225
-		if ($this->is_dir($path)) {
226
-			return $this->rmdir($path);
227
-		} else if ($this->is_file($path)) {
228
-			return unlink($this->getSourcePath($path));
229
-		} else {
230
-			return false;
231
-		}
232
-
233
-	}
234
-
235
-	private function treeContainsBlacklistedFile(string $path): bool {
236
-		$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
237
-		foreach ($iterator as $file) {
238
-			/** @var \SplFileInfo $file */
239
-			if (Filesystem::isFileBlacklisted($file->getBasename())) {
240
-				return true;
241
-			}
242
-		}
243
-
244
-		return false;
245
-	}
246
-
247
-	public function rename($path1, $path2) {
248
-		$srcParent = dirname($path1);
249
-		$dstParent = dirname($path2);
250
-
251
-		if (!$this->isUpdatable($srcParent)) {
252
-			\OCP\Util::writeLog('core', 'unable to rename, source directory is not writable : ' . $srcParent, ILogger::ERROR);
253
-			return false;
254
-		}
255
-
256
-		if (!$this->isUpdatable($dstParent)) {
257
-			\OCP\Util::writeLog('core', 'unable to rename, destination directory is not writable : ' . $dstParent, ILogger::ERROR);
258
-			return false;
259
-		}
260
-
261
-		if (!$this->file_exists($path1)) {
262
-			\OCP\Util::writeLog('core', 'unable to rename, file does not exists : ' . $path1, ILogger::ERROR);
263
-			return false;
264
-		}
265
-
266
-		if ($this->is_dir($path2)) {
267
-			$this->rmdir($path2);
268
-		} else if ($this->is_file($path2)) {
269
-			$this->unlink($path2);
270
-		}
271
-
272
-		if ($this->is_dir($path1)) {
273
-			// we can't move folders across devices, use copy instead
274
-			$stat1 = stat(dirname($this->getSourcePath($path1)));
275
-			$stat2 = stat(dirname($this->getSourcePath($path2)));
276
-			if ($stat1['dev'] !== $stat2['dev']) {
277
-				$result = $this->copy($path1, $path2);
278
-				if ($result) {
279
-					$result &= $this->rmdir($path1);
280
-				}
281
-				return $result;
282
-			}
283
-
284
-			if ($this->treeContainsBlacklistedFile($this->getSourcePath($path1))) {
285
-				throw new ForbiddenException('Invalid path', false);
286
-			}
287
-		}
288
-
289
-		return rename($this->getSourcePath($path1), $this->getSourcePath($path2));
290
-	}
291
-
292
-	public function copy($path1, $path2) {
293
-		if ($this->is_dir($path1)) {
294
-			return parent::copy($path1, $path2);
295
-		} else {
296
-			return copy($this->getSourcePath($path1), $this->getSourcePath($path2));
297
-		}
298
-	}
299
-
300
-	public function fopen($path, $mode) {
301
-		return fopen($this->getSourcePath($path), $mode);
302
-	}
303
-
304
-	public function hash($type, $path, $raw = false) {
305
-		return hash_file($type, $this->getSourcePath($path), $raw);
306
-	}
307
-
308
-	public function free_space($path) {
309
-		$sourcePath = $this->getSourcePath($path);
310
-		// using !is_dir because $sourcePath might be a part file or
311
-		// non-existing file, so we'd still want to use the parent dir
312
-		// in such cases
313
-		if (!is_dir($sourcePath)) {
314
-			// disk_free_space doesn't work on files
315
-			$sourcePath = dirname($sourcePath);
316
-		}
317
-		$space = @disk_free_space($sourcePath);
318
-		if ($space === false || is_null($space)) {
319
-			return \OCP\Files\FileInfo::SPACE_UNKNOWN;
320
-		}
321
-		return $space;
322
-	}
323
-
324
-	public function search($query) {
325
-		return $this->searchInDir($query);
326
-	}
327
-
328
-	public function getLocalFile($path) {
329
-		return $this->getSourcePath($path);
330
-	}
331
-
332
-	public function getLocalFolder($path) {
333
-		return $this->getSourcePath($path);
334
-	}
335
-
336
-	/**
337
-	 * @param string $query
338
-	 * @param string $dir
339
-	 * @return array
340
-	 */
341
-	protected function searchInDir($query, $dir = '') {
342
-		$files = array();
343
-		$physicalDir = $this->getSourcePath($dir);
344
-		foreach (scandir($physicalDir) as $item) {
345
-			if (\OC\Files\Filesystem::isIgnoredDir($item))
346
-				continue;
347
-			$physicalItem = $physicalDir . '/' . $item;
348
-
349
-			if (strstr(strtolower($item), strtolower($query)) !== false) {
350
-				$files[] = $dir . '/' . $item;
351
-			}
352
-			if (is_dir($physicalItem)) {
353
-				$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
354
-			}
355
-		}
356
-		return $files;
357
-	}
358
-
359
-	/**
360
-	 * check if a file or folder has been updated since $time
361
-	 *
362
-	 * @param string $path
363
-	 * @param int $time
364
-	 * @return bool
365
-	 */
366
-	public function hasUpdated($path, $time) {
367
-		if ($this->file_exists($path)) {
368
-			return $this->filemtime($path) > $time;
369
-		} else {
370
-			return true;
371
-		}
372
-	}
373
-
374
-	/**
375
-	 * Get the source path (on disk) of a given path
376
-	 *
377
-	 * @param string $path
378
-	 * @return string
379
-	 * @throws ForbiddenException
380
-	 */
381
-	public function getSourcePath($path) {
382
-		if (Filesystem::isFileBlacklisted($path)) {
383
-			throw new ForbiddenException('Invalid path', false);
384
-		}
385
-
386
-		$fullPath = $this->datadir . $path;
387
-		$currentPath = $path;
388
-		if ($this->allowSymlinks || $currentPath === '') {
389
-			return $fullPath;
390
-		}
391
-		$pathToResolve = $fullPath;
392
-		$realPath = realpath($pathToResolve);
393
-		while ($realPath === false) { // for non existing files check the parent directory
394
-			$currentPath = dirname($currentPath);
395
-			if ($currentPath === '' || $currentPath === '.') {
396
-				return $fullPath;
397
-			}
398
-			$realPath = realpath($this->datadir . $currentPath);
399
-		}
400
-		if ($realPath) {
401
-			$realPath = $realPath . '/';
402
-		}
403
-		if (substr($realPath, 0, $this->dataDirLength) === $this->realDataDir) {
404
-			return $fullPath;
405
-		}
406
-
407
-		\OCP\Util::writeLog('core', "Following symlinks is not allowed ('$fullPath' -> '$realPath' not inside '{$this->realDataDir}')", ILogger::ERROR);
408
-		throw new ForbiddenException('Following symlinks is not allowed', false);
409
-	}
410
-
411
-	/**
412
-	 * {@inheritdoc}
413
-	 */
414
-	public function isLocal() {
415
-		return true;
416
-	}
417
-
418
-	/**
419
-	 * get the ETag for a file or folder
420
-	 *
421
-	 * @param string $path
422
-	 * @return string
423
-	 */
424
-	public function getETag($path) {
425
-		if ($this->is_file($path)) {
426
-			$stat = $this->stat($path);
427
-
428
-			if ($stat === false) {
429
-				return md5('');
430
-			}
431
-
432
-			$toHash = '';
433
-			if (isset($stat['mtime'])) {
434
-				$toHash .= $stat['mtime'];
435
-			}
436
-			if (isset($stat['ino'])) {
437
-				$toHash .= $stat['ino'];
438
-			}
439
-			if (isset($stat['dev'])) {
440
-				$toHash .= $stat['dev'];
441
-			}
442
-			if (isset($stat['size'])) {
443
-				$toHash .= $stat['size'];
444
-			}
445
-
446
-			return md5($toHash);
447
-		} else {
448
-			return parent::getETag($path);
449
-		}
450
-	}
451
-
452
-	/**
453
-	 * @param IStorage $sourceStorage
454
-	 * @param string $sourceInternalPath
455
-	 * @param string $targetInternalPath
456
-	 * @param bool $preserveMtime
457
-	 * @return bool
458
-	 */
459
-	public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
460
-		if ($sourceStorage->instanceOfStorage(Local::class)) {
461
-			if ($sourceStorage->instanceOfStorage(Jail::class)) {
462
-				/**
463
-				 * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage
464
-				 */
465
-				$sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
466
-			}
467
-			/**
468
-			 * @var \OC\Files\Storage\Local $sourceStorage
469
-			 */
470
-			$rootStorage = new Local(['datadir' => '/']);
471
-			return $rootStorage->copy($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
472
-		} else {
473
-			return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
474
-		}
475
-	}
476
-
477
-	/**
478
-	 * @param IStorage $sourceStorage
479
-	 * @param string $sourceInternalPath
480
-	 * @param string $targetInternalPath
481
-	 * @return bool
482
-	 */
483
-	public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
484
-		if ($sourceStorage->instanceOfStorage(Local::class)) {
485
-			if ($sourceStorage->instanceOfStorage(Jail::class)) {
486
-				/**
487
-				 * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage
488
-				 */
489
-				$sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
490
-			}
491
-			/**
492
-			 * @var \OC\Files\Storage\Local $sourceStorage
493
-			 */
494
-			$rootStorage = new Local(['datadir' => '/']);
495
-			return $rootStorage->rename($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
496
-		} else {
497
-			return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
498
-		}
499
-	}
500
-
501
-	public function writeStream(string $path, $stream, int $size = null): int {
502
-		return (int)file_put_contents($this->getSourcePath($path), $stream);
503
-	}
52
+    protected $datadir;
53
+
54
+    protected $dataDirLength;
55
+
56
+    protected $allowSymlinks = false;
57
+
58
+    protected $realDataDir;
59
+
60
+    public function __construct($arguments) {
61
+        if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) {
62
+            throw new \InvalidArgumentException('No data directory set for local storage');
63
+        }
64
+        $this->datadir = str_replace('//', '/', $arguments['datadir']);
65
+        // some crazy code uses a local storage on root...
66
+        if ($this->datadir === '/') {
67
+            $this->realDataDir = $this->datadir;
68
+        } else {
69
+            $realPath = realpath($this->datadir) ?: $this->datadir;
70
+            $this->realDataDir = rtrim($realPath, '/') . '/';
71
+        }
72
+        if (substr($this->datadir, -1) !== '/') {
73
+            $this->datadir .= '/';
74
+        }
75
+        $this->dataDirLength = strlen($this->realDataDir);
76
+    }
77
+
78
+    public function __destruct() {
79
+    }
80
+
81
+    public function getId() {
82
+        return 'local::' . $this->datadir;
83
+    }
84
+
85
+    public function mkdir($path) {
86
+        return @mkdir($this->getSourcePath($path), 0777, true);
87
+    }
88
+
89
+    public function rmdir($path) {
90
+        if (!$this->isDeletable($path)) {
91
+            return false;
92
+        }
93
+        try {
94
+            $it = new \RecursiveIteratorIterator(
95
+                new \RecursiveDirectoryIterator($this->getSourcePath($path)),
96
+                \RecursiveIteratorIterator::CHILD_FIRST
97
+            );
98
+            /**
99
+             * RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
100
+             * This bug is fixed in PHP 5.5.9 or before
101
+             * See #8376
102
+             */
103
+            $it->rewind();
104
+            while ($it->valid()) {
105
+                /**
106
+                 * @var \SplFileInfo $file
107
+                 */
108
+                $file = $it->current();
109
+                if (in_array($file->getBasename(), array('.', '..'))) {
110
+                    $it->next();
111
+                    continue;
112
+                } elseif ($file->isDir()) {
113
+                    rmdir($file->getPathname());
114
+                } elseif ($file->isFile() || $file->isLink()) {
115
+                    unlink($file->getPathname());
116
+                }
117
+                $it->next();
118
+            }
119
+            return rmdir($this->getSourcePath($path));
120
+        } catch (\UnexpectedValueException $e) {
121
+            return false;
122
+        }
123
+    }
124
+
125
+    public function opendir($path) {
126
+        return opendir($this->getSourcePath($path));
127
+    }
128
+
129
+    public function is_dir($path) {
130
+        if (substr($path, -1) == '/') {
131
+            $path = substr($path, 0, -1);
132
+        }
133
+        return is_dir($this->getSourcePath($path));
134
+    }
135
+
136
+    public function is_file($path) {
137
+        return is_file($this->getSourcePath($path));
138
+    }
139
+
140
+    public function stat($path) {
141
+        clearstatcache();
142
+        $fullPath = $this->getSourcePath($path);
143
+        $statResult = stat($fullPath);
144
+        if (PHP_INT_SIZE === 4 && !$this->is_dir($path)) {
145
+            $filesize = $this->filesize($path);
146
+            $statResult['size'] = $filesize;
147
+            $statResult[7] = $filesize;
148
+        }
149
+        return $statResult;
150
+    }
151
+
152
+    public function filetype($path) {
153
+        $filetype = filetype($this->getSourcePath($path));
154
+        if ($filetype == 'link') {
155
+            $filetype = filetype(realpath($this->getSourcePath($path)));
156
+        }
157
+        return $filetype;
158
+    }
159
+
160
+    public function filesize($path) {
161
+        if ($this->is_dir($path)) {
162
+            return 0;
163
+        }
164
+        $fullPath = $this->getSourcePath($path);
165
+        if (PHP_INT_SIZE === 4) {
166
+            $helper = new \OC\LargeFileHelper;
167
+            return $helper->getFileSize($fullPath);
168
+        }
169
+        return filesize($fullPath);
170
+    }
171
+
172
+    public function isReadable($path) {
173
+        return is_readable($this->getSourcePath($path));
174
+    }
175
+
176
+    public function isUpdatable($path) {
177
+        return is_writable($this->getSourcePath($path));
178
+    }
179
+
180
+    public function file_exists($path) {
181
+        return file_exists($this->getSourcePath($path));
182
+    }
183
+
184
+    public function filemtime($path) {
185
+        $fullPath = $this->getSourcePath($path);
186
+        clearstatcache(true, $fullPath);
187
+        if (!$this->file_exists($path)) {
188
+            return false;
189
+        }
190
+        if (PHP_INT_SIZE === 4) {
191
+            $helper = new \OC\LargeFileHelper();
192
+            return $helper->getFileMtime($fullPath);
193
+        }
194
+        return filemtime($fullPath);
195
+    }
196
+
197
+    public function touch($path, $mtime = null) {
198
+        // sets the modification time of the file to the given value.
199
+        // If mtime is nil the current time is set.
200
+        // note that the access time of the file always changes to the current time.
201
+        if ($this->file_exists($path) and !$this->isUpdatable($path)) {
202
+            return false;
203
+        }
204
+        if (!is_null($mtime)) {
205
+            $result = @touch($this->getSourcePath($path), $mtime);
206
+        } else {
207
+            $result = @touch($this->getSourcePath($path));
208
+        }
209
+        if ($result) {
210
+            clearstatcache(true, $this->getSourcePath($path));
211
+        }
212
+
213
+        return $result;
214
+    }
215
+
216
+    public function file_get_contents($path) {
217
+        return file_get_contents($this->getSourcePath($path));
218
+    }
219
+
220
+    public function file_put_contents($path, $data) {
221
+        return file_put_contents($this->getSourcePath($path), $data);
222
+    }
223
+
224
+    public function unlink($path) {
225
+        if ($this->is_dir($path)) {
226
+            return $this->rmdir($path);
227
+        } else if ($this->is_file($path)) {
228
+            return unlink($this->getSourcePath($path));
229
+        } else {
230
+            return false;
231
+        }
232
+
233
+    }
234
+
235
+    private function treeContainsBlacklistedFile(string $path): bool {
236
+        $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
237
+        foreach ($iterator as $file) {
238
+            /** @var \SplFileInfo $file */
239
+            if (Filesystem::isFileBlacklisted($file->getBasename())) {
240
+                return true;
241
+            }
242
+        }
243
+
244
+        return false;
245
+    }
246
+
247
+    public function rename($path1, $path2) {
248
+        $srcParent = dirname($path1);
249
+        $dstParent = dirname($path2);
250
+
251
+        if (!$this->isUpdatable($srcParent)) {
252
+            \OCP\Util::writeLog('core', 'unable to rename, source directory is not writable : ' . $srcParent, ILogger::ERROR);
253
+            return false;
254
+        }
255
+
256
+        if (!$this->isUpdatable($dstParent)) {
257
+            \OCP\Util::writeLog('core', 'unable to rename, destination directory is not writable : ' . $dstParent, ILogger::ERROR);
258
+            return false;
259
+        }
260
+
261
+        if (!$this->file_exists($path1)) {
262
+            \OCP\Util::writeLog('core', 'unable to rename, file does not exists : ' . $path1, ILogger::ERROR);
263
+            return false;
264
+        }
265
+
266
+        if ($this->is_dir($path2)) {
267
+            $this->rmdir($path2);
268
+        } else if ($this->is_file($path2)) {
269
+            $this->unlink($path2);
270
+        }
271
+
272
+        if ($this->is_dir($path1)) {
273
+            // we can't move folders across devices, use copy instead
274
+            $stat1 = stat(dirname($this->getSourcePath($path1)));
275
+            $stat2 = stat(dirname($this->getSourcePath($path2)));
276
+            if ($stat1['dev'] !== $stat2['dev']) {
277
+                $result = $this->copy($path1, $path2);
278
+                if ($result) {
279
+                    $result &= $this->rmdir($path1);
280
+                }
281
+                return $result;
282
+            }
283
+
284
+            if ($this->treeContainsBlacklistedFile($this->getSourcePath($path1))) {
285
+                throw new ForbiddenException('Invalid path', false);
286
+            }
287
+        }
288
+
289
+        return rename($this->getSourcePath($path1), $this->getSourcePath($path2));
290
+    }
291
+
292
+    public function copy($path1, $path2) {
293
+        if ($this->is_dir($path1)) {
294
+            return parent::copy($path1, $path2);
295
+        } else {
296
+            return copy($this->getSourcePath($path1), $this->getSourcePath($path2));
297
+        }
298
+    }
299
+
300
+    public function fopen($path, $mode) {
301
+        return fopen($this->getSourcePath($path), $mode);
302
+    }
303
+
304
+    public function hash($type, $path, $raw = false) {
305
+        return hash_file($type, $this->getSourcePath($path), $raw);
306
+    }
307
+
308
+    public function free_space($path) {
309
+        $sourcePath = $this->getSourcePath($path);
310
+        // using !is_dir because $sourcePath might be a part file or
311
+        // non-existing file, so we'd still want to use the parent dir
312
+        // in such cases
313
+        if (!is_dir($sourcePath)) {
314
+            // disk_free_space doesn't work on files
315
+            $sourcePath = dirname($sourcePath);
316
+        }
317
+        $space = @disk_free_space($sourcePath);
318
+        if ($space === false || is_null($space)) {
319
+            return \OCP\Files\FileInfo::SPACE_UNKNOWN;
320
+        }
321
+        return $space;
322
+    }
323
+
324
+    public function search($query) {
325
+        return $this->searchInDir($query);
326
+    }
327
+
328
+    public function getLocalFile($path) {
329
+        return $this->getSourcePath($path);
330
+    }
331
+
332
+    public function getLocalFolder($path) {
333
+        return $this->getSourcePath($path);
334
+    }
335
+
336
+    /**
337
+     * @param string $query
338
+     * @param string $dir
339
+     * @return array
340
+     */
341
+    protected function searchInDir($query, $dir = '') {
342
+        $files = array();
343
+        $physicalDir = $this->getSourcePath($dir);
344
+        foreach (scandir($physicalDir) as $item) {
345
+            if (\OC\Files\Filesystem::isIgnoredDir($item))
346
+                continue;
347
+            $physicalItem = $physicalDir . '/' . $item;
348
+
349
+            if (strstr(strtolower($item), strtolower($query)) !== false) {
350
+                $files[] = $dir . '/' . $item;
351
+            }
352
+            if (is_dir($physicalItem)) {
353
+                $files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
354
+            }
355
+        }
356
+        return $files;
357
+    }
358
+
359
+    /**
360
+     * check if a file or folder has been updated since $time
361
+     *
362
+     * @param string $path
363
+     * @param int $time
364
+     * @return bool
365
+     */
366
+    public function hasUpdated($path, $time) {
367
+        if ($this->file_exists($path)) {
368
+            return $this->filemtime($path) > $time;
369
+        } else {
370
+            return true;
371
+        }
372
+    }
373
+
374
+    /**
375
+     * Get the source path (on disk) of a given path
376
+     *
377
+     * @param string $path
378
+     * @return string
379
+     * @throws ForbiddenException
380
+     */
381
+    public function getSourcePath($path) {
382
+        if (Filesystem::isFileBlacklisted($path)) {
383
+            throw new ForbiddenException('Invalid path', false);
384
+        }
385
+
386
+        $fullPath = $this->datadir . $path;
387
+        $currentPath = $path;
388
+        if ($this->allowSymlinks || $currentPath === '') {
389
+            return $fullPath;
390
+        }
391
+        $pathToResolve = $fullPath;
392
+        $realPath = realpath($pathToResolve);
393
+        while ($realPath === false) { // for non existing files check the parent directory
394
+            $currentPath = dirname($currentPath);
395
+            if ($currentPath === '' || $currentPath === '.') {
396
+                return $fullPath;
397
+            }
398
+            $realPath = realpath($this->datadir . $currentPath);
399
+        }
400
+        if ($realPath) {
401
+            $realPath = $realPath . '/';
402
+        }
403
+        if (substr($realPath, 0, $this->dataDirLength) === $this->realDataDir) {
404
+            return $fullPath;
405
+        }
406
+
407
+        \OCP\Util::writeLog('core', "Following symlinks is not allowed ('$fullPath' -> '$realPath' not inside '{$this->realDataDir}')", ILogger::ERROR);
408
+        throw new ForbiddenException('Following symlinks is not allowed', false);
409
+    }
410
+
411
+    /**
412
+     * {@inheritdoc}
413
+     */
414
+    public function isLocal() {
415
+        return true;
416
+    }
417
+
418
+    /**
419
+     * get the ETag for a file or folder
420
+     *
421
+     * @param string $path
422
+     * @return string
423
+     */
424
+    public function getETag($path) {
425
+        if ($this->is_file($path)) {
426
+            $stat = $this->stat($path);
427
+
428
+            if ($stat === false) {
429
+                return md5('');
430
+            }
431
+
432
+            $toHash = '';
433
+            if (isset($stat['mtime'])) {
434
+                $toHash .= $stat['mtime'];
435
+            }
436
+            if (isset($stat['ino'])) {
437
+                $toHash .= $stat['ino'];
438
+            }
439
+            if (isset($stat['dev'])) {
440
+                $toHash .= $stat['dev'];
441
+            }
442
+            if (isset($stat['size'])) {
443
+                $toHash .= $stat['size'];
444
+            }
445
+
446
+            return md5($toHash);
447
+        } else {
448
+            return parent::getETag($path);
449
+        }
450
+    }
451
+
452
+    /**
453
+     * @param IStorage $sourceStorage
454
+     * @param string $sourceInternalPath
455
+     * @param string $targetInternalPath
456
+     * @param bool $preserveMtime
457
+     * @return bool
458
+     */
459
+    public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
460
+        if ($sourceStorage->instanceOfStorage(Local::class)) {
461
+            if ($sourceStorage->instanceOfStorage(Jail::class)) {
462
+                /**
463
+                 * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage
464
+                 */
465
+                $sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
466
+            }
467
+            /**
468
+             * @var \OC\Files\Storage\Local $sourceStorage
469
+             */
470
+            $rootStorage = new Local(['datadir' => '/']);
471
+            return $rootStorage->copy($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
472
+        } else {
473
+            return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
474
+        }
475
+    }
476
+
477
+    /**
478
+     * @param IStorage $sourceStorage
479
+     * @param string $sourceInternalPath
480
+     * @param string $targetInternalPath
481
+     * @return bool
482
+     */
483
+    public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
484
+        if ($sourceStorage->instanceOfStorage(Local::class)) {
485
+            if ($sourceStorage->instanceOfStorage(Jail::class)) {
486
+                /**
487
+                 * @var \OC\Files\Storage\Wrapper\Jail $sourceStorage
488
+                 */
489
+                $sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
490
+            }
491
+            /**
492
+             * @var \OC\Files\Storage\Local $sourceStorage
493
+             */
494
+            $rootStorage = new Local(['datadir' => '/']);
495
+            return $rootStorage->rename($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath));
496
+        } else {
497
+            return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
498
+        }
499
+    }
500
+
501
+    public function writeStream(string $path, $stream, int $size = null): int {
502
+        return (int)file_put_contents($this->getSourcePath($path), $stream);
503
+    }
504 504
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -67,7 +67,7 @@  discard block
 block discarded – undo
67 67
 			$this->realDataDir = $this->datadir;
68 68
 		} else {
69 69
 			$realPath = realpath($this->datadir) ?: $this->datadir;
70
-			$this->realDataDir = rtrim($realPath, '/') . '/';
70
+			$this->realDataDir = rtrim($realPath, '/').'/';
71 71
 		}
72 72
 		if (substr($this->datadir, -1) !== '/') {
73 73
 			$this->datadir .= '/';
@@ -79,7 +79,7 @@  discard block
 block discarded – undo
79 79
 	}
80 80
 
81 81
 	public function getId() {
82
-		return 'local::' . $this->datadir;
82
+		return 'local::'.$this->datadir;
83 83
 	}
84 84
 
85 85
 	public function mkdir($path) {
@@ -249,17 +249,17 @@  discard block
 block discarded – undo
249 249
 		$dstParent = dirname($path2);
250 250
 
251 251
 		if (!$this->isUpdatable($srcParent)) {
252
-			\OCP\Util::writeLog('core', 'unable to rename, source directory is not writable : ' . $srcParent, ILogger::ERROR);
252
+			\OCP\Util::writeLog('core', 'unable to rename, source directory is not writable : '.$srcParent, ILogger::ERROR);
253 253
 			return false;
254 254
 		}
255 255
 
256 256
 		if (!$this->isUpdatable($dstParent)) {
257
-			\OCP\Util::writeLog('core', 'unable to rename, destination directory is not writable : ' . $dstParent, ILogger::ERROR);
257
+			\OCP\Util::writeLog('core', 'unable to rename, destination directory is not writable : '.$dstParent, ILogger::ERROR);
258 258
 			return false;
259 259
 		}
260 260
 
261 261
 		if (!$this->file_exists($path1)) {
262
-			\OCP\Util::writeLog('core', 'unable to rename, file does not exists : ' . $path1, ILogger::ERROR);
262
+			\OCP\Util::writeLog('core', 'unable to rename, file does not exists : '.$path1, ILogger::ERROR);
263 263
 			return false;
264 264
 		}
265 265
 
@@ -344,13 +344,13 @@  discard block
 block discarded – undo
344 344
 		foreach (scandir($physicalDir) as $item) {
345 345
 			if (\OC\Files\Filesystem::isIgnoredDir($item))
346 346
 				continue;
347
-			$physicalItem = $physicalDir . '/' . $item;
347
+			$physicalItem = $physicalDir.'/'.$item;
348 348
 
349 349
 			if (strstr(strtolower($item), strtolower($query)) !== false) {
350
-				$files[] = $dir . '/' . $item;
350
+				$files[] = $dir.'/'.$item;
351 351
 			}
352 352
 			if (is_dir($physicalItem)) {
353
-				$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
353
+				$files = array_merge($files, $this->searchInDir($query, $dir.'/'.$item));
354 354
 			}
355 355
 		}
356 356
 		return $files;
@@ -383,7 +383,7 @@  discard block
 block discarded – undo
383 383
 			throw new ForbiddenException('Invalid path', false);
384 384
 		}
385 385
 
386
-		$fullPath = $this->datadir . $path;
386
+		$fullPath = $this->datadir.$path;
387 387
 		$currentPath = $path;
388 388
 		if ($this->allowSymlinks || $currentPath === '') {
389 389
 			return $fullPath;
@@ -395,10 +395,10 @@  discard block
 block discarded – undo
395 395
 			if ($currentPath === '' || $currentPath === '.') {
396 396
 				return $fullPath;
397 397
 			}
398
-			$realPath = realpath($this->datadir . $currentPath);
398
+			$realPath = realpath($this->datadir.$currentPath);
399 399
 		}
400 400
 		if ($realPath) {
401
-			$realPath = $realPath . '/';
401
+			$realPath = $realPath.'/';
402 402
 		}
403 403
 		if (substr($realPath, 0, $this->dataDirLength) === $this->realDataDir) {
404 404
 			return $fullPath;
@@ -499,6 +499,6 @@  discard block
 block discarded – undo
499 499
 	}
500 500
 
501 501
 	public function writeStream(string $path, $stream, int $size = null): int {
502
-		return (int)file_put_contents($this->getSourcePath($path), $stream);
502
+		return (int) file_put_contents($this->getSourcePath($path), $stream);
503 503
 	}
504 504
 }
Please login to merge, or discard this patch.
lib/private/Files/Storage/Storage.php 1 patch
Indentation   +75 added lines, -75 removed lines patch added patch discarded remove patch
@@ -32,90 +32,90 @@
 block discarded – undo
32 32
  */
33 33
 interface Storage extends \OCP\Files\Storage {
34 34
 
35
-	/**
36
-	 * get a cache instance for the storage
37
-	 *
38
-	 * @param string $path
39
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache
40
-	 * @return \OC\Files\Cache\Cache
41
-	 */
42
-	public function getCache($path = '', $storage = null);
35
+    /**
36
+     * get a cache instance for the storage
37
+     *
38
+     * @param string $path
39
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache
40
+     * @return \OC\Files\Cache\Cache
41
+     */
42
+    public function getCache($path = '', $storage = null);
43 43
 
44
-	/**
45
-	 * get a scanner instance for the storage
46
-	 *
47
-	 * @param string $path
48
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
49
-	 * @return \OC\Files\Cache\Scanner
50
-	 */
51
-	public function getScanner($path = '', $storage = null);
44
+    /**
45
+     * get a scanner instance for the storage
46
+     *
47
+     * @param string $path
48
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
49
+     * @return \OC\Files\Cache\Scanner
50
+     */
51
+    public function getScanner($path = '', $storage = null);
52 52
 
53 53
 
54
-	/**
55
-	 * get the user id of the owner of a file or folder
56
-	 *
57
-	 * @param string $path
58
-	 * @return string
59
-	 */
60
-	public function getOwner($path);
54
+    /**
55
+     * get the user id of the owner of a file or folder
56
+     *
57
+     * @param string $path
58
+     * @return string
59
+     */
60
+    public function getOwner($path);
61 61
 
62
-	/**
63
-	 * get a watcher instance for the cache
64
-	 *
65
-	 * @param string $path
66
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
67
-	 * @return \OC\Files\Cache\Watcher
68
-	 */
69
-	public function getWatcher($path = '', $storage = null);
62
+    /**
63
+     * get a watcher instance for the cache
64
+     *
65
+     * @param string $path
66
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
67
+     * @return \OC\Files\Cache\Watcher
68
+     */
69
+    public function getWatcher($path = '', $storage = null);
70 70
 
71
-	/**
72
-	 * get a propagator instance for the cache
73
-	 *
74
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
75
-	 * @return \OC\Files\Cache\Propagator
76
-	 */
77
-	public function getPropagator($storage = null);
71
+    /**
72
+     * get a propagator instance for the cache
73
+     *
74
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
75
+     * @return \OC\Files\Cache\Propagator
76
+     */
77
+    public function getPropagator($storage = null);
78 78
 
79
-	/**
80
-	 * get a updater instance for the cache
81
-	 *
82
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
83
-	 * @return \OC\Files\Cache\Updater
84
-	 */
85
-	public function getUpdater($storage = null);
79
+    /**
80
+     * get a updater instance for the cache
81
+     *
82
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
83
+     * @return \OC\Files\Cache\Updater
84
+     */
85
+    public function getUpdater($storage = null);
86 86
 
87
-	/**
88
-	 * @return \OC\Files\Cache\Storage
89
-	 */
90
-	public function getStorageCache();
87
+    /**
88
+     * @return \OC\Files\Cache\Storage
89
+     */
90
+    public function getStorageCache();
91 91
 
92
-	/**
93
-	 * @param string $path
94
-	 * @return array
95
-	 */
96
-	public function getMetaData($path);
92
+    /**
93
+     * @param string $path
94
+     * @return array
95
+     */
96
+    public function getMetaData($path);
97 97
 
98
-	/**
99
-	 * @param string $path The path of the file to acquire the lock for
100
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
101
-	 * @param \OCP\Lock\ILockingProvider $provider
102
-	 * @throws \OCP\Lock\LockedException
103
-	 */
104
-	public function acquireLock($path, $type, ILockingProvider $provider);
98
+    /**
99
+     * @param string $path The path of the file to acquire the lock for
100
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
101
+     * @param \OCP\Lock\ILockingProvider $provider
102
+     * @throws \OCP\Lock\LockedException
103
+     */
104
+    public function acquireLock($path, $type, ILockingProvider $provider);
105 105
 
106
-	/**
107
-	 * @param string $path The path of the file to release the lock for
108
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
109
-	 * @param \OCP\Lock\ILockingProvider $provider
110
-	 * @throws \OCP\Lock\LockedException
111
-	 */
112
-	public function releaseLock($path, $type, ILockingProvider $provider);
106
+    /**
107
+     * @param string $path The path of the file to release the lock for
108
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
109
+     * @param \OCP\Lock\ILockingProvider $provider
110
+     * @throws \OCP\Lock\LockedException
111
+     */
112
+    public function releaseLock($path, $type, ILockingProvider $provider);
113 113
 
114
-	/**
115
-	 * @param string $path The path of the file to change the lock for
116
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
117
-	 * @param \OCP\Lock\ILockingProvider $provider
118
-	 * @throws \OCP\Lock\LockedException
119
-	 */
120
-	public function changeLock($path, $type, ILockingProvider $provider);
114
+    /**
115
+     * @param string $path The path of the file to change the lock for
116
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
117
+     * @param \OCP\Lock\ILockingProvider $provider
118
+     * @throws \OCP\Lock\LockedException
119
+     */
120
+    public function changeLock($path, $type, ILockingProvider $provider);
121 121
 }
Please login to merge, or discard this patch.
lib/private/Files/Storage/LocalTempFileTrait.php 1 patch
Indentation   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -37,45 +37,45 @@
 block discarded – undo
37 37
  */
38 38
 trait LocalTempFileTrait {
39 39
 
40
-	/** @var string[] */
41
-	protected $cachedFiles = [];
40
+    /** @var string[] */
41
+    protected $cachedFiles = [];
42 42
 
43
-	/**
44
-	 * @param string $path
45
-	 * @return string
46
-	 */
47
-	protected function getCachedFile($path) {
48
-		if (!isset($this->cachedFiles[$path])) {
49
-			$this->cachedFiles[$path] = $this->toTmpFile($path);
50
-		}
51
-		return $this->cachedFiles[$path];
52
-	}
43
+    /**
44
+     * @param string $path
45
+     * @return string
46
+     */
47
+    protected function getCachedFile($path) {
48
+        if (!isset($this->cachedFiles[$path])) {
49
+            $this->cachedFiles[$path] = $this->toTmpFile($path);
50
+        }
51
+        return $this->cachedFiles[$path];
52
+    }
53 53
 
54
-	/**
55
-	 * @param string $path
56
-	 */
57
-	protected function removeCachedFile($path) {
58
-		unset($this->cachedFiles[$path]);
59
-	}
54
+    /**
55
+     * @param string $path
56
+     */
57
+    protected function removeCachedFile($path) {
58
+        unset($this->cachedFiles[$path]);
59
+    }
60 60
 
61
-	/**
62
-	 * @param string $path
63
-	 * @return string
64
-	 */
65
-	protected function toTmpFile($path) { //no longer in the storage api, still useful here
66
-		$source = $this->fopen($path, 'r');
67
-		if (!$source) {
68
-			return false;
69
-		}
70
-		if ($pos = strrpos($path, '.')) {
71
-			$extension = substr($path, $pos);
72
-		} else {
73
-			$extension = '';
74
-		}
75
-		$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension);
76
-		$target = fopen($tmpFile, 'w');
77
-		\OC_Helper::streamCopy($source, $target);
78
-		fclose($target);
79
-		return $tmpFile;
80
-	}
61
+    /**
62
+     * @param string $path
63
+     * @return string
64
+     */
65
+    protected function toTmpFile($path) { //no longer in the storage api, still useful here
66
+        $source = $this->fopen($path, 'r');
67
+        if (!$source) {
68
+            return false;
69
+        }
70
+        if ($pos = strrpos($path, '.')) {
71
+            $extension = substr($path, $pos);
72
+        } else {
73
+            $extension = '';
74
+        }
75
+        $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension);
76
+        $target = fopen($tmpFile, 'w');
77
+        \OC_Helper::streamCopy($source, $target);
78
+        fclose($target);
79
+        return $tmpFile;
80
+    }
81 81
 }
Please login to merge, or discard this patch.
lib/private/Files/Storage/Common.php 3 patches
Braces   +3 added lines, -1 removed lines patch added patch discarded remove patch
@@ -289,7 +289,9 @@
 block discarded – undo
289 289
 		$dh = $this->opendir($dir);
290 290
 		if (is_resource($dh)) {
291 291
 			while (($item = readdir($dh)) !== false) {
292
-				if (\OC\Files\Filesystem::isIgnoredDir($item)) continue;
292
+				if (\OC\Files\Filesystem::isIgnoredDir($item)) {
293
+				    continue;
294
+				}
293 295
 				if (strstr(strtolower($item), strtolower($query)) !== false) {
294 296
 					$files[] = $dir . '/' . $item;
295 297
 				}
Please login to merge, or discard this patch.
Spacing   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -220,7 +220,7 @@  discard block
 block discarded – undo
220 220
 			$this->mkdir($path2);
221 221
 			while ($file = readdir($dir)) {
222 222
 				if (!Filesystem::isIgnoredDir($file)) {
223
-					if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
223
+					if (!$this->copy($path1.'/'.$file, $path2.'/'.$file)) {
224 224
 						return false;
225 225
 					}
226 226
 				}
@@ -274,12 +274,12 @@  discard block
 block discarded – undo
274 274
 		if (is_resource($dh)) {
275 275
 			while (($file = readdir($dh)) !== false) {
276 276
 				if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
277
-					if ($this->is_dir($path . '/' . $file)) {
278
-						mkdir($target . '/' . $file);
279
-						$this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
277
+					if ($this->is_dir($path.'/'.$file)) {
278
+						mkdir($target.'/'.$file);
279
+						$this->addLocalFolder($path.'/'.$file, $target.'/'.$file);
280 280
 					} else {
281
-						$tmp = $this->toTmpFile($path . '/' . $file);
282
-						rename($tmp, $target . '/' . $file);
281
+						$tmp = $this->toTmpFile($path.'/'.$file);
282
+						rename($tmp, $target.'/'.$file);
283 283
 					}
284 284
 				}
285 285
 			}
@@ -298,10 +298,10 @@  discard block
 block discarded – undo
298 298
 			while (($item = readdir($dh)) !== false) {
299 299
 				if (\OC\Files\Filesystem::isIgnoredDir($item)) continue;
300 300
 				if (strstr(strtolower($item), strtolower($query)) !== false) {
301
-					$files[] = $dir . '/' . $item;
301
+					$files[] = $dir.'/'.$item;
302 302
 				}
303
-				if ($this->is_dir($dir . '/' . $item)) {
304
-					$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
303
+				if ($this->is_dir($dir.'/'.$item)) {
304
+					$files = array_merge($files, $this->searchInDir($query, $dir.'/'.$item));
305 305
 				}
306 306
 			}
307 307
 		}
@@ -352,7 +352,7 @@  discard block
 block discarded – undo
352 352
 		if (!isset($this->watcher)) {
353 353
 			$this->watcher = new Watcher($storage);
354 354
 			$globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
355
-			$this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
355
+			$this->watcher->setPolicy((int) $this->getMountOption('filesystem_check_changes', $globalPolicy));
356 356
 		}
357 357
 		return $this->watcher;
358 358
 	}
@@ -369,7 +369,7 @@  discard block
 block discarded – undo
369 369
 		}
370 370
 		if (!isset($storage->propagator)) {
371 371
 			$config = \OC::$server->getSystemConfig();
372
-			$storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection(), ['appdata_' . $config->getValue('instanceid')]);
372
+			$storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection(), ['appdata_'.$config->getValue('instanceid')]);
373 373
 		}
374 374
 		return $storage->propagator;
375 375
 	}
@@ -427,7 +427,7 @@  discard block
 block discarded – undo
427 427
 	 */
428 428
 	public function cleanPath($path) {
429 429
 		if (strlen($path) == 0 or $path[0] != '/') {
430
-			$path = '/' . $path;
430
+			$path = '/'.$path;
431 431
 		}
432 432
 
433 433
 		$output = array();
@@ -455,7 +455,7 @@  discard block
 block discarded – undo
455 455
 			\OC::$server->getLogger()->info("External storage not available: stat() failed");
456 456
 			return false;
457 457
 		} catch (\Exception $e) {
458
-			\OC::$server->getLogger()->info("External storage not available: " . $e->getMessage());
458
+			\OC::$server->getLogger()->info("External storage not available: ".$e->getMessage());
459 459
 			\OC::$server->getLogger()->logException($e, ['level' => ILogger::DEBUG]);
460 460
 			return false;
461 461
 		}
@@ -606,7 +606,7 @@  discard block
 block discarded – undo
606 606
 			if (is_resource($dh)) {
607 607
 				while ($result and ($file = readdir($dh)) !== false) {
608 608
 					if (!Filesystem::isIgnoredDir($file)) {
609
-						$result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
609
+						$result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath.'/'.$file, $targetInternalPath.'/'.$file);
610 610
 					}
611 611
 				}
612 612
 			}
@@ -632,7 +632,7 @@  discard block
 block discarded – undo
632 632
 				$this->getCache()->remove($targetInternalPath);
633 633
 			}
634 634
 		}
635
-		return (bool)$result;
635
+		return (bool) $result;
636 636
 	}
637 637
 
638 638
 	/**
@@ -712,7 +712,7 @@  discard block
 block discarded – undo
712 712
 			);
713 713
 		}
714 714
 		try {
715
-			$provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
715
+			$provider->acquireLock('files/'.md5($this->getId().'::'.trim($path, '/')), $type);
716 716
 		} catch (LockedException $e) {
717 717
 			if ($logger) {
718 718
 				$logger->logException($e, ['level' => ILogger::INFO]);
@@ -744,7 +744,7 @@  discard block
 block discarded – undo
744 744
 			);
745 745
 		}
746 746
 		try {
747
-			$provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
747
+			$provider->releaseLock('files/'.md5($this->getId().'::'.trim($path, '/')), $type);
748 748
 		} catch (LockedException $e) {
749 749
 			if ($logger) {
750 750
 				$logger->logException($e, ['level' => ILogger::INFO]);
@@ -776,7 +776,7 @@  discard block
 block discarded – undo
776 776
 			);
777 777
 		}
778 778
 		try {
779
-			$provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
779
+			$provider->changeLock('files/'.md5($this->getId().'::'.trim($path, '/')), $type);
780 780
 		} catch (LockedException $e) {
781 781
 			\OC::$server->getLogger()->logException($e, ['level' => ILogger::INFO]);
782 782
 			throw $e;
Please login to merge, or discard this patch.
Indentation   +782 added lines, -782 removed lines patch added patch discarded remove patch
@@ -74,790 +74,790 @@
 block discarded – undo
74 74
  */
75 75
 abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage {
76 76
 
77
-	use LocalTempFileTrait;
78
-
79
-	protected $cache;
80
-	protected $scanner;
81
-	protected $watcher;
82
-	protected $propagator;
83
-	protected $storageCache;
84
-	protected $updater;
85
-
86
-	protected $mountOptions = [];
87
-	protected $owner = null;
88
-
89
-	private $shouldLogLocks = null;
90
-	private $logger;
91
-
92
-	public function __construct($parameters) {
93
-	}
94
-
95
-	/**
96
-	 * Remove a file or folder
97
-	 *
98
-	 * @param string $path
99
-	 * @return bool
100
-	 */
101
-	protected function remove($path) {
102
-		if ($this->is_dir($path)) {
103
-			return $this->rmdir($path);
104
-		} else if ($this->is_file($path)) {
105
-			return $this->unlink($path);
106
-		} else {
107
-			return false;
108
-		}
109
-	}
110
-
111
-	public function is_dir($path) {
112
-		return $this->filetype($path) === 'dir';
113
-	}
114
-
115
-	public function is_file($path) {
116
-		return $this->filetype($path) === 'file';
117
-	}
118
-
119
-	public function filesize($path) {
120
-		if ($this->is_dir($path)) {
121
-			return 0; //by definition
122
-		} else {
123
-			$stat = $this->stat($path);
124
-			if (isset($stat['size'])) {
125
-				return $stat['size'];
126
-			} else {
127
-				return 0;
128
-			}
129
-		}
130
-	}
131
-
132
-	public function isReadable($path) {
133
-		// at least check whether it exists
134
-		// subclasses might want to implement this more thoroughly
135
-		return $this->file_exists($path);
136
-	}
137
-
138
-	public function isUpdatable($path) {
139
-		// at least check whether it exists
140
-		// subclasses might want to implement this more thoroughly
141
-		// a non-existing file/folder isn't updatable
142
-		return $this->file_exists($path);
143
-	}
144
-
145
-	public function isCreatable($path) {
146
-		if ($this->is_dir($path) && $this->isUpdatable($path)) {
147
-			return true;
148
-		}
149
-		return false;
150
-	}
151
-
152
-	public function isDeletable($path) {
153
-		if ($path === '' || $path === '/') {
154
-			return false;
155
-		}
156
-		$parent = dirname($path);
157
-		return $this->isUpdatable($parent) && $this->isUpdatable($path);
158
-	}
159
-
160
-	public function isSharable($path) {
161
-		return $this->isReadable($path);
162
-	}
163
-
164
-	public function getPermissions($path) {
165
-		$permissions = 0;
166
-		if ($this->isCreatable($path)) {
167
-			$permissions |= \OCP\Constants::PERMISSION_CREATE;
168
-		}
169
-		if ($this->isReadable($path)) {
170
-			$permissions |= \OCP\Constants::PERMISSION_READ;
171
-		}
172
-		if ($this->isUpdatable($path)) {
173
-			$permissions |= \OCP\Constants::PERMISSION_UPDATE;
174
-		}
175
-		if ($this->isDeletable($path)) {
176
-			$permissions |= \OCP\Constants::PERMISSION_DELETE;
177
-		}
178
-		if ($this->isSharable($path)) {
179
-			$permissions |= \OCP\Constants::PERMISSION_SHARE;
180
-		}
181
-		return $permissions;
182
-	}
183
-
184
-	public function filemtime($path) {
185
-		$stat = $this->stat($path);
186
-		if (isset($stat['mtime']) && $stat['mtime'] > 0) {
187
-			return $stat['mtime'];
188
-		} else {
189
-			return 0;
190
-		}
191
-	}
192
-
193
-	public function file_get_contents($path) {
194
-		$handle = $this->fopen($path, "r");
195
-		if (!$handle) {
196
-			return false;
197
-		}
198
-		$data = stream_get_contents($handle);
199
-		fclose($handle);
200
-		return $data;
201
-	}
202
-
203
-	public function file_put_contents($path, $data) {
204
-		$handle = $this->fopen($path, "w");
205
-		$this->removeCachedFile($path);
206
-		$count = fwrite($handle, $data);
207
-		fclose($handle);
208
-		return $count;
209
-	}
210
-
211
-	public function rename($path1, $path2) {
212
-		$this->remove($path2);
213
-
214
-		$this->removeCachedFile($path1);
215
-		return $this->copy($path1, $path2) and $this->remove($path1);
216
-	}
217
-
218
-	public function copy($path1, $path2) {
219
-		if ($this->is_dir($path1)) {
220
-			$this->remove($path2);
221
-			$dir = $this->opendir($path1);
222
-			$this->mkdir($path2);
223
-			while ($file = readdir($dir)) {
224
-				if (!Filesystem::isIgnoredDir($file)) {
225
-					if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
226
-						return false;
227
-					}
228
-				}
229
-			}
230
-			closedir($dir);
231
-			return true;
232
-		} else {
233
-			$source = $this->fopen($path1, 'r');
234
-			$target = $this->fopen($path2, 'w');
235
-			list(, $result) = \OC_Helper::streamCopy($source, $target);
236
-			if (!$result) {
237
-				\OC::$server->getLogger()->warning("Failed to write data while copying $path1 to $path2");
238
-			}
239
-			$this->removeCachedFile($path2);
240
-			return $result;
241
-		}
242
-	}
243
-
244
-	public function getMimeType($path) {
245
-		if ($this->is_dir($path)) {
246
-			return 'httpd/unix-directory';
247
-		} elseif ($this->file_exists($path)) {
248
-			return \OC::$server->getMimeTypeDetector()->detectPath($path);
249
-		} else {
250
-			return false;
251
-		}
252
-	}
253
-
254
-	public function hash($type, $path, $raw = false) {
255
-		$fh = $this->fopen($path, 'rb');
256
-		$ctx = hash_init($type);
257
-		hash_update_stream($ctx, $fh);
258
-		fclose($fh);
259
-		return hash_final($ctx, $raw);
260
-	}
261
-
262
-	public function search($query) {
263
-		return $this->searchInDir($query);
264
-	}
265
-
266
-	public function getLocalFile($path) {
267
-		return $this->getCachedFile($path);
268
-	}
269
-
270
-	/**
271
-	 * @param string $path
272
-	 * @param string $target
273
-	 */
274
-	private function addLocalFolder($path, $target) {
275
-		$dh = $this->opendir($path);
276
-		if (is_resource($dh)) {
277
-			while (($file = readdir($dh)) !== false) {
278
-				if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
279
-					if ($this->is_dir($path . '/' . $file)) {
280
-						mkdir($target . '/' . $file);
281
-						$this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
282
-					} else {
283
-						$tmp = $this->toTmpFile($path . '/' . $file);
284
-						rename($tmp, $target . '/' . $file);
285
-					}
286
-				}
287
-			}
288
-		}
289
-	}
290
-
291
-	/**
292
-	 * @param string $query
293
-	 * @param string $dir
294
-	 * @return array
295
-	 */
296
-	protected function searchInDir($query, $dir = '') {
297
-		$files = array();
298
-		$dh = $this->opendir($dir);
299
-		if (is_resource($dh)) {
300
-			while (($item = readdir($dh)) !== false) {
301
-				if (\OC\Files\Filesystem::isIgnoredDir($item)) continue;
302
-				if (strstr(strtolower($item), strtolower($query)) !== false) {
303
-					$files[] = $dir . '/' . $item;
304
-				}
305
-				if ($this->is_dir($dir . '/' . $item)) {
306
-					$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
307
-				}
308
-			}
309
-		}
310
-		closedir($dh);
311
-		return $files;
312
-	}
313
-
314
-	/**
315
-	 * check if a file or folder has been updated since $time
316
-	 *
317
-	 * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking
318
-	 * the mtime should always return false here. As a result storage implementations that always return false expect
319
-	 * exclusive access to the backend and will not pick up files that have been added in a way that circumvents
320
-	 * ownClouds filesystem.
321
-	 *
322
-	 * @param string $path
323
-	 * @param int $time
324
-	 * @return bool
325
-	 */
326
-	public function hasUpdated($path, $time) {
327
-		return $this->filemtime($path) > $time;
328
-	}
329
-
330
-	public function getCache($path = '', $storage = null) {
331
-		if (!$storage) {
332
-			$storage = $this;
333
-		}
334
-		if (!isset($storage->cache)) {
335
-			$storage->cache = new Cache($storage);
336
-		}
337
-		return $storage->cache;
338
-	}
339
-
340
-	public function getScanner($path = '', $storage = null) {
341
-		if (!$storage) {
342
-			$storage = $this;
343
-		}
344
-		if (!isset($storage->scanner)) {
345
-			$storage->scanner = new Scanner($storage);
346
-		}
347
-		return $storage->scanner;
348
-	}
349
-
350
-	public function getWatcher($path = '', $storage = null) {
351
-		if (!$storage) {
352
-			$storage = $this;
353
-		}
354
-		if (!isset($this->watcher)) {
355
-			$this->watcher = new Watcher($storage);
356
-			$globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
357
-			$this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
358
-		}
359
-		return $this->watcher;
360
-	}
361
-
362
-	/**
363
-	 * get a propagator instance for the cache
364
-	 *
365
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
366
-	 * @return \OC\Files\Cache\Propagator
367
-	 */
368
-	public function getPropagator($storage = null) {
369
-		if (!$storage) {
370
-			$storage = $this;
371
-		}
372
-		if (!isset($storage->propagator)) {
373
-			$config = \OC::$server->getSystemConfig();
374
-			$storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection(), ['appdata_' . $config->getValue('instanceid')]);
375
-		}
376
-		return $storage->propagator;
377
-	}
378
-
379
-	public function getUpdater($storage = null) {
380
-		if (!$storage) {
381
-			$storage = $this;
382
-		}
383
-		if (!isset($storage->updater)) {
384
-			$storage->updater = new Updater($storage);
385
-		}
386
-		return $storage->updater;
387
-	}
388
-
389
-	public function getStorageCache($storage = null) {
390
-		if (!$storage) {
391
-			$storage = $this;
392
-		}
393
-		if (!isset($this->storageCache)) {
394
-			$this->storageCache = new \OC\Files\Cache\Storage($storage);
395
-		}
396
-		return $this->storageCache;
397
-	}
398
-
399
-	/**
400
-	 * get the owner of a path
401
-	 *
402
-	 * @param string $path The path to get the owner
403
-	 * @return string|false uid or false
404
-	 */
405
-	public function getOwner($path) {
406
-		if ($this->owner === null) {
407
-			$this->owner = \OC_User::getUser();
408
-		}
409
-
410
-		return $this->owner;
411
-	}
412
-
413
-	/**
414
-	 * get the ETag for a file or folder
415
-	 *
416
-	 * @param string $path
417
-	 * @return string
418
-	 */
419
-	public function getETag($path) {
420
-		return uniqid();
421
-	}
422
-
423
-	/**
424
-	 * clean a path, i.e. remove all redundant '.' and '..'
425
-	 * making sure that it can't point to higher than '/'
426
-	 *
427
-	 * @param string $path The path to clean
428
-	 * @return string cleaned path
429
-	 */
430
-	public function cleanPath($path) {
431
-		if (strlen($path) == 0 or $path[0] != '/') {
432
-			$path = '/' . $path;
433
-		}
434
-
435
-		$output = array();
436
-		foreach (explode('/', $path) as $chunk) {
437
-			if ($chunk == '..') {
438
-				array_pop($output);
439
-			} else if ($chunk == '.') {
440
-			} else {
441
-				$output[] = $chunk;
442
-			}
443
-		}
444
-		return implode('/', $output);
445
-	}
446
-
447
-	/**
448
-	 * Test a storage for availability
449
-	 *
450
-	 * @return bool
451
-	 */
452
-	public function test() {
453
-		try {
454
-			if ($this->stat('')) {
455
-				return true;
456
-			}
457
-			\OC::$server->getLogger()->info("External storage not available: stat() failed");
458
-			return false;
459
-		} catch (\Exception $e) {
460
-			\OC::$server->getLogger()->info("External storage not available: " . $e->getMessage());
461
-			\OC::$server->getLogger()->logException($e, ['level' => ILogger::DEBUG]);
462
-			return false;
463
-		}
464
-	}
465
-
466
-	/**
467
-	 * get the free space in the storage
468
-	 *
469
-	 * @param string $path
470
-	 * @return int|false
471
-	 */
472
-	public function free_space($path) {
473
-		return \OCP\Files\FileInfo::SPACE_UNKNOWN;
474
-	}
475
-
476
-	/**
477
-	 * {@inheritdoc}
478
-	 */
479
-	public function isLocal() {
480
-		// the common implementation returns a temporary file by
481
-		// default, which is not local
482
-		return false;
483
-	}
484
-
485
-	/**
486
-	 * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
487
-	 *
488
-	 * @param string $class
489
-	 * @return bool
490
-	 */
491
-	public function instanceOfStorage($class) {
492
-		if (ltrim($class, '\\') === 'OC\Files\Storage\Shared') {
493
-			// FIXME Temporary fix to keep existing checks working
494
-			$class = '\OCA\Files_Sharing\SharedStorage';
495
-		}
496
-		return is_a($this, $class);
497
-	}
498
-
499
-	/**
500
-	 * A custom storage implementation can return an url for direct download of a give file.
501
-	 *
502
-	 * For now the returned array can hold the parameter url - in future more attributes might follow.
503
-	 *
504
-	 * @param string $path
505
-	 * @return array|false
506
-	 */
507
-	public function getDirectDownload($path) {
508
-		return [];
509
-	}
510
-
511
-	/**
512
-	 * @inheritdoc
513
-	 * @throws InvalidPathException
514
-	 */
515
-	public function verifyPath($path, $fileName) {
516
-
517
-		// verify empty and dot files
518
-		$trimmed = trim($fileName);
519
-		if ($trimmed === '') {
520
-			throw new EmptyFileNameException();
521
-		}
522
-
523
-		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
524
-			throw new InvalidDirectoryException();
525
-		}
526
-
527
-		if (!\OC::$server->getDatabaseConnection()->supports4ByteText()) {
528
-			// verify database - e.g. mysql only 3-byte chars
529
-			if (preg_match('%(?:
77
+    use LocalTempFileTrait;
78
+
79
+    protected $cache;
80
+    protected $scanner;
81
+    protected $watcher;
82
+    protected $propagator;
83
+    protected $storageCache;
84
+    protected $updater;
85
+
86
+    protected $mountOptions = [];
87
+    protected $owner = null;
88
+
89
+    private $shouldLogLocks = null;
90
+    private $logger;
91
+
92
+    public function __construct($parameters) {
93
+    }
94
+
95
+    /**
96
+     * Remove a file or folder
97
+     *
98
+     * @param string $path
99
+     * @return bool
100
+     */
101
+    protected function remove($path) {
102
+        if ($this->is_dir($path)) {
103
+            return $this->rmdir($path);
104
+        } else if ($this->is_file($path)) {
105
+            return $this->unlink($path);
106
+        } else {
107
+            return false;
108
+        }
109
+    }
110
+
111
+    public function is_dir($path) {
112
+        return $this->filetype($path) === 'dir';
113
+    }
114
+
115
+    public function is_file($path) {
116
+        return $this->filetype($path) === 'file';
117
+    }
118
+
119
+    public function filesize($path) {
120
+        if ($this->is_dir($path)) {
121
+            return 0; //by definition
122
+        } else {
123
+            $stat = $this->stat($path);
124
+            if (isset($stat['size'])) {
125
+                return $stat['size'];
126
+            } else {
127
+                return 0;
128
+            }
129
+        }
130
+    }
131
+
132
+    public function isReadable($path) {
133
+        // at least check whether it exists
134
+        // subclasses might want to implement this more thoroughly
135
+        return $this->file_exists($path);
136
+    }
137
+
138
+    public function isUpdatable($path) {
139
+        // at least check whether it exists
140
+        // subclasses might want to implement this more thoroughly
141
+        // a non-existing file/folder isn't updatable
142
+        return $this->file_exists($path);
143
+    }
144
+
145
+    public function isCreatable($path) {
146
+        if ($this->is_dir($path) && $this->isUpdatable($path)) {
147
+            return true;
148
+        }
149
+        return false;
150
+    }
151
+
152
+    public function isDeletable($path) {
153
+        if ($path === '' || $path === '/') {
154
+            return false;
155
+        }
156
+        $parent = dirname($path);
157
+        return $this->isUpdatable($parent) && $this->isUpdatable($path);
158
+    }
159
+
160
+    public function isSharable($path) {
161
+        return $this->isReadable($path);
162
+    }
163
+
164
+    public function getPermissions($path) {
165
+        $permissions = 0;
166
+        if ($this->isCreatable($path)) {
167
+            $permissions |= \OCP\Constants::PERMISSION_CREATE;
168
+        }
169
+        if ($this->isReadable($path)) {
170
+            $permissions |= \OCP\Constants::PERMISSION_READ;
171
+        }
172
+        if ($this->isUpdatable($path)) {
173
+            $permissions |= \OCP\Constants::PERMISSION_UPDATE;
174
+        }
175
+        if ($this->isDeletable($path)) {
176
+            $permissions |= \OCP\Constants::PERMISSION_DELETE;
177
+        }
178
+        if ($this->isSharable($path)) {
179
+            $permissions |= \OCP\Constants::PERMISSION_SHARE;
180
+        }
181
+        return $permissions;
182
+    }
183
+
184
+    public function filemtime($path) {
185
+        $stat = $this->stat($path);
186
+        if (isset($stat['mtime']) && $stat['mtime'] > 0) {
187
+            return $stat['mtime'];
188
+        } else {
189
+            return 0;
190
+        }
191
+    }
192
+
193
+    public function file_get_contents($path) {
194
+        $handle = $this->fopen($path, "r");
195
+        if (!$handle) {
196
+            return false;
197
+        }
198
+        $data = stream_get_contents($handle);
199
+        fclose($handle);
200
+        return $data;
201
+    }
202
+
203
+    public function file_put_contents($path, $data) {
204
+        $handle = $this->fopen($path, "w");
205
+        $this->removeCachedFile($path);
206
+        $count = fwrite($handle, $data);
207
+        fclose($handle);
208
+        return $count;
209
+    }
210
+
211
+    public function rename($path1, $path2) {
212
+        $this->remove($path2);
213
+
214
+        $this->removeCachedFile($path1);
215
+        return $this->copy($path1, $path2) and $this->remove($path1);
216
+    }
217
+
218
+    public function copy($path1, $path2) {
219
+        if ($this->is_dir($path1)) {
220
+            $this->remove($path2);
221
+            $dir = $this->opendir($path1);
222
+            $this->mkdir($path2);
223
+            while ($file = readdir($dir)) {
224
+                if (!Filesystem::isIgnoredDir($file)) {
225
+                    if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
226
+                        return false;
227
+                    }
228
+                }
229
+            }
230
+            closedir($dir);
231
+            return true;
232
+        } else {
233
+            $source = $this->fopen($path1, 'r');
234
+            $target = $this->fopen($path2, 'w');
235
+            list(, $result) = \OC_Helper::streamCopy($source, $target);
236
+            if (!$result) {
237
+                \OC::$server->getLogger()->warning("Failed to write data while copying $path1 to $path2");
238
+            }
239
+            $this->removeCachedFile($path2);
240
+            return $result;
241
+        }
242
+    }
243
+
244
+    public function getMimeType($path) {
245
+        if ($this->is_dir($path)) {
246
+            return 'httpd/unix-directory';
247
+        } elseif ($this->file_exists($path)) {
248
+            return \OC::$server->getMimeTypeDetector()->detectPath($path);
249
+        } else {
250
+            return false;
251
+        }
252
+    }
253
+
254
+    public function hash($type, $path, $raw = false) {
255
+        $fh = $this->fopen($path, 'rb');
256
+        $ctx = hash_init($type);
257
+        hash_update_stream($ctx, $fh);
258
+        fclose($fh);
259
+        return hash_final($ctx, $raw);
260
+    }
261
+
262
+    public function search($query) {
263
+        return $this->searchInDir($query);
264
+    }
265
+
266
+    public function getLocalFile($path) {
267
+        return $this->getCachedFile($path);
268
+    }
269
+
270
+    /**
271
+     * @param string $path
272
+     * @param string $target
273
+     */
274
+    private function addLocalFolder($path, $target) {
275
+        $dh = $this->opendir($path);
276
+        if (is_resource($dh)) {
277
+            while (($file = readdir($dh)) !== false) {
278
+                if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
279
+                    if ($this->is_dir($path . '/' . $file)) {
280
+                        mkdir($target . '/' . $file);
281
+                        $this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
282
+                    } else {
283
+                        $tmp = $this->toTmpFile($path . '/' . $file);
284
+                        rename($tmp, $target . '/' . $file);
285
+                    }
286
+                }
287
+            }
288
+        }
289
+    }
290
+
291
+    /**
292
+     * @param string $query
293
+     * @param string $dir
294
+     * @return array
295
+     */
296
+    protected function searchInDir($query, $dir = '') {
297
+        $files = array();
298
+        $dh = $this->opendir($dir);
299
+        if (is_resource($dh)) {
300
+            while (($item = readdir($dh)) !== false) {
301
+                if (\OC\Files\Filesystem::isIgnoredDir($item)) continue;
302
+                if (strstr(strtolower($item), strtolower($query)) !== false) {
303
+                    $files[] = $dir . '/' . $item;
304
+                }
305
+                if ($this->is_dir($dir . '/' . $item)) {
306
+                    $files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
307
+                }
308
+            }
309
+        }
310
+        closedir($dh);
311
+        return $files;
312
+    }
313
+
314
+    /**
315
+     * check if a file or folder has been updated since $time
316
+     *
317
+     * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking
318
+     * the mtime should always return false here. As a result storage implementations that always return false expect
319
+     * exclusive access to the backend and will not pick up files that have been added in a way that circumvents
320
+     * ownClouds filesystem.
321
+     *
322
+     * @param string $path
323
+     * @param int $time
324
+     * @return bool
325
+     */
326
+    public function hasUpdated($path, $time) {
327
+        return $this->filemtime($path) > $time;
328
+    }
329
+
330
+    public function getCache($path = '', $storage = null) {
331
+        if (!$storage) {
332
+            $storage = $this;
333
+        }
334
+        if (!isset($storage->cache)) {
335
+            $storage->cache = new Cache($storage);
336
+        }
337
+        return $storage->cache;
338
+    }
339
+
340
+    public function getScanner($path = '', $storage = null) {
341
+        if (!$storage) {
342
+            $storage = $this;
343
+        }
344
+        if (!isset($storage->scanner)) {
345
+            $storage->scanner = new Scanner($storage);
346
+        }
347
+        return $storage->scanner;
348
+    }
349
+
350
+    public function getWatcher($path = '', $storage = null) {
351
+        if (!$storage) {
352
+            $storage = $this;
353
+        }
354
+        if (!isset($this->watcher)) {
355
+            $this->watcher = new Watcher($storage);
356
+            $globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
357
+            $this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
358
+        }
359
+        return $this->watcher;
360
+    }
361
+
362
+    /**
363
+     * get a propagator instance for the cache
364
+     *
365
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
366
+     * @return \OC\Files\Cache\Propagator
367
+     */
368
+    public function getPropagator($storage = null) {
369
+        if (!$storage) {
370
+            $storage = $this;
371
+        }
372
+        if (!isset($storage->propagator)) {
373
+            $config = \OC::$server->getSystemConfig();
374
+            $storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection(), ['appdata_' . $config->getValue('instanceid')]);
375
+        }
376
+        return $storage->propagator;
377
+    }
378
+
379
+    public function getUpdater($storage = null) {
380
+        if (!$storage) {
381
+            $storage = $this;
382
+        }
383
+        if (!isset($storage->updater)) {
384
+            $storage->updater = new Updater($storage);
385
+        }
386
+        return $storage->updater;
387
+    }
388
+
389
+    public function getStorageCache($storage = null) {
390
+        if (!$storage) {
391
+            $storage = $this;
392
+        }
393
+        if (!isset($this->storageCache)) {
394
+            $this->storageCache = new \OC\Files\Cache\Storage($storage);
395
+        }
396
+        return $this->storageCache;
397
+    }
398
+
399
+    /**
400
+     * get the owner of a path
401
+     *
402
+     * @param string $path The path to get the owner
403
+     * @return string|false uid or false
404
+     */
405
+    public function getOwner($path) {
406
+        if ($this->owner === null) {
407
+            $this->owner = \OC_User::getUser();
408
+        }
409
+
410
+        return $this->owner;
411
+    }
412
+
413
+    /**
414
+     * get the ETag for a file or folder
415
+     *
416
+     * @param string $path
417
+     * @return string
418
+     */
419
+    public function getETag($path) {
420
+        return uniqid();
421
+    }
422
+
423
+    /**
424
+     * clean a path, i.e. remove all redundant '.' and '..'
425
+     * making sure that it can't point to higher than '/'
426
+     *
427
+     * @param string $path The path to clean
428
+     * @return string cleaned path
429
+     */
430
+    public function cleanPath($path) {
431
+        if (strlen($path) == 0 or $path[0] != '/') {
432
+            $path = '/' . $path;
433
+        }
434
+
435
+        $output = array();
436
+        foreach (explode('/', $path) as $chunk) {
437
+            if ($chunk == '..') {
438
+                array_pop($output);
439
+            } else if ($chunk == '.') {
440
+            } else {
441
+                $output[] = $chunk;
442
+            }
443
+        }
444
+        return implode('/', $output);
445
+    }
446
+
447
+    /**
448
+     * Test a storage for availability
449
+     *
450
+     * @return bool
451
+     */
452
+    public function test() {
453
+        try {
454
+            if ($this->stat('')) {
455
+                return true;
456
+            }
457
+            \OC::$server->getLogger()->info("External storage not available: stat() failed");
458
+            return false;
459
+        } catch (\Exception $e) {
460
+            \OC::$server->getLogger()->info("External storage not available: " . $e->getMessage());
461
+            \OC::$server->getLogger()->logException($e, ['level' => ILogger::DEBUG]);
462
+            return false;
463
+        }
464
+    }
465
+
466
+    /**
467
+     * get the free space in the storage
468
+     *
469
+     * @param string $path
470
+     * @return int|false
471
+     */
472
+    public function free_space($path) {
473
+        return \OCP\Files\FileInfo::SPACE_UNKNOWN;
474
+    }
475
+
476
+    /**
477
+     * {@inheritdoc}
478
+     */
479
+    public function isLocal() {
480
+        // the common implementation returns a temporary file by
481
+        // default, which is not local
482
+        return false;
483
+    }
484
+
485
+    /**
486
+     * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
487
+     *
488
+     * @param string $class
489
+     * @return bool
490
+     */
491
+    public function instanceOfStorage($class) {
492
+        if (ltrim($class, '\\') === 'OC\Files\Storage\Shared') {
493
+            // FIXME Temporary fix to keep existing checks working
494
+            $class = '\OCA\Files_Sharing\SharedStorage';
495
+        }
496
+        return is_a($this, $class);
497
+    }
498
+
499
+    /**
500
+     * A custom storage implementation can return an url for direct download of a give file.
501
+     *
502
+     * For now the returned array can hold the parameter url - in future more attributes might follow.
503
+     *
504
+     * @param string $path
505
+     * @return array|false
506
+     */
507
+    public function getDirectDownload($path) {
508
+        return [];
509
+    }
510
+
511
+    /**
512
+     * @inheritdoc
513
+     * @throws InvalidPathException
514
+     */
515
+    public function verifyPath($path, $fileName) {
516
+
517
+        // verify empty and dot files
518
+        $trimmed = trim($fileName);
519
+        if ($trimmed === '') {
520
+            throw new EmptyFileNameException();
521
+        }
522
+
523
+        if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
524
+            throw new InvalidDirectoryException();
525
+        }
526
+
527
+        if (!\OC::$server->getDatabaseConnection()->supports4ByteText()) {
528
+            // verify database - e.g. mysql only 3-byte chars
529
+            if (preg_match('%(?:
530 530
       \xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
531 531
     | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
532 532
     | \xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16
533 533
 )%xs', $fileName)) {
534
-				throw new InvalidCharacterInPathException();
535
-			}
536
-		}
537
-
538
-		// 255 characters is the limit on common file systems (ext/xfs)
539
-		// oc_filecache has a 250 char length limit for the filename
540
-		if (isset($fileName[250])) {
541
-			throw new FileNameTooLongException();
542
-		}
543
-
544
-		// NOTE: $path will remain unverified for now
545
-		$this->verifyPosixPath($fileName);
546
-	}
547
-
548
-	/**
549
-	 * @param string $fileName
550
-	 * @throws InvalidPathException
551
-	 */
552
-	protected function verifyPosixPath($fileName) {
553
-		$fileName = trim($fileName);
554
-		$this->scanForInvalidCharacters($fileName, "\\/");
555
-		$reservedNames = ['*'];
556
-		if (in_array($fileName, $reservedNames)) {
557
-			throw new ReservedWordException();
558
-		}
559
-	}
560
-
561
-	/**
562
-	 * @param string $fileName
563
-	 * @param string $invalidChars
564
-	 * @throws InvalidPathException
565
-	 */
566
-	private function scanForInvalidCharacters($fileName, $invalidChars) {
567
-		foreach (str_split($invalidChars) as $char) {
568
-			if (strpos($fileName, $char) !== false) {
569
-				throw new InvalidCharacterInPathException();
570
-			}
571
-		}
572
-
573
-		$sanitizedFileName = filter_var($fileName, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
574
-		if ($sanitizedFileName !== $fileName) {
575
-			throw new InvalidCharacterInPathException();
576
-		}
577
-	}
578
-
579
-	/**
580
-	 * @param array $options
581
-	 */
582
-	public function setMountOptions(array $options) {
583
-		$this->mountOptions = $options;
584
-	}
585
-
586
-	/**
587
-	 * @param string $name
588
-	 * @param mixed $default
589
-	 * @return mixed
590
-	 */
591
-	public function getMountOption($name, $default = null) {
592
-		return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default;
593
-	}
594
-
595
-	/**
596
-	 * @param IStorage $sourceStorage
597
-	 * @param string $sourceInternalPath
598
-	 * @param string $targetInternalPath
599
-	 * @param bool $preserveMtime
600
-	 * @return bool
601
-	 */
602
-	public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
603
-		if ($sourceStorage === $this) {
604
-			return $this->copy($sourceInternalPath, $targetInternalPath);
605
-		}
606
-
607
-		if ($sourceStorage->is_dir($sourceInternalPath)) {
608
-			$dh = $sourceStorage->opendir($sourceInternalPath);
609
-			$result = $this->mkdir($targetInternalPath);
610
-			if (is_resource($dh)) {
611
-				while ($result and ($file = readdir($dh)) !== false) {
612
-					if (!Filesystem::isIgnoredDir($file)) {
613
-						$result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
614
-					}
615
-				}
616
-			}
617
-		} else {
618
-			$source = $sourceStorage->fopen($sourceInternalPath, 'r');
619
-			// TODO: call fopen in a way that we execute again all storage wrappers
620
-			// to avoid that we bypass storage wrappers which perform important actions
621
-			// for this operation. Same is true for all other operations which
622
-			// are not the same as the original one.Once this is fixed we also
623
-			// need to adjust the encryption wrapper.
624
-			$target = $this->fopen($targetInternalPath, 'w');
625
-			list(, $result) = \OC_Helper::streamCopy($source, $target);
626
-			if ($result and $preserveMtime) {
627
-				$this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath));
628
-			}
629
-			fclose($source);
630
-			fclose($target);
631
-
632
-			if (!$result) {
633
-				// delete partially written target file
634
-				$this->unlink($targetInternalPath);
635
-				// delete cache entry that was created by fopen
636
-				$this->getCache()->remove($targetInternalPath);
637
-			}
638
-		}
639
-		return (bool)$result;
640
-	}
641
-
642
-	/**
643
-	 * Check if a storage is the same as the current one, including wrapped storages
644
-	 *
645
-	 * @param IStorage $storage
646
-	 * @return bool
647
-	 */
648
-	private function isSameStorage(IStorage $storage): bool {
649
-		while ($storage->instanceOfStorage(Wrapper::class)) {
650
-			/**
651
-			 * @var Wrapper $sourceStorage
652
-			 */
653
-			$storage = $storage->getWrapperStorage();
654
-		}
655
-
656
-		return $storage === $this;
657
-	}
658
-
659
-	/**
660
-	 * @param IStorage $sourceStorage
661
-	 * @param string $sourceInternalPath
662
-	 * @param string $targetInternalPath
663
-	 * @return bool
664
-	 */
665
-	public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
666
-		if ($this->isSameStorage($sourceStorage)) {
667
-			// resolve any jailed paths
668
-			while ($sourceStorage->instanceOfStorage(Jail::class)) {
669
-				/**
670
-				 * @var Jail $sourceStorage
671
-				 */
672
-				$sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
673
-				$sourceStorage = $sourceStorage->getUnjailedStorage();
674
-			}
675
-
676
-			return $this->rename($sourceInternalPath, $targetInternalPath);
677
-		}
678
-
679
-		if (!$sourceStorage->isDeletable($sourceInternalPath)) {
680
-			return false;
681
-		}
682
-
683
-		$result = $this->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true);
684
-		if ($result) {
685
-			if ($sourceStorage->is_dir($sourceInternalPath)) {
686
-				$result &= $sourceStorage->rmdir($sourceInternalPath);
687
-			} else {
688
-				$result &= $sourceStorage->unlink($sourceInternalPath);
689
-			}
690
-		}
691
-		return $result;
692
-	}
693
-
694
-	/**
695
-	 * @inheritdoc
696
-	 */
697
-	public function getMetaData($path) {
698
-		$permissions = $this->getPermissions($path);
699
-		if (!$permissions & \OCP\Constants::PERMISSION_READ) {
700
-			//can't read, nothing we can do
701
-			return null;
702
-		}
703
-
704
-		$data = [];
705
-		$data['mimetype'] = $this->getMimeType($path);
706
-		$data['mtime'] = $this->filemtime($path);
707
-		if ($data['mtime'] === false) {
708
-			$data['mtime'] = time();
709
-		}
710
-		if ($data['mimetype'] == 'httpd/unix-directory') {
711
-			$data['size'] = -1; //unknown
712
-		} else {
713
-			$data['size'] = $this->filesize($path);
714
-		}
715
-		$data['etag'] = $this->getETag($path);
716
-		$data['storage_mtime'] = $data['mtime'];
717
-		$data['permissions'] = $permissions;
718
-
719
-		return $data;
720
-	}
721
-
722
-	/**
723
-	 * @param string $path
724
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
725
-	 * @param \OCP\Lock\ILockingProvider $provider
726
-	 * @throws \OCP\Lock\LockedException
727
-	 */
728
-	public function acquireLock($path, $type, ILockingProvider $provider) {
729
-		$logger = $this->getLockLogger();
730
-		if ($logger) {
731
-			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
732
-			$logger->info(
733
-				sprintf(
734
-					'acquire %s lock on "%s" on storage "%s"',
735
-					$typeString,
736
-					$path,
737
-					$this->getId()
738
-				),
739
-				[
740
-					'app' => 'locking',
741
-				]
742
-			);
743
-		}
744
-		try {
745
-			$provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
746
-		} catch (LockedException $e) {
747
-			if ($logger) {
748
-				$logger->logException($e, ['level' => ILogger::INFO]);
749
-			}
750
-			throw $e;
751
-		}
752
-	}
753
-
754
-	/**
755
-	 * @param string $path
756
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
757
-	 * @param \OCP\Lock\ILockingProvider $provider
758
-	 * @throws \OCP\Lock\LockedException
759
-	 */
760
-	public function releaseLock($path, $type, ILockingProvider $provider) {
761
-		$logger = $this->getLockLogger();
762
-		if ($logger) {
763
-			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
764
-			$logger->info(
765
-				sprintf(
766
-					'release %s lock on "%s" on storage "%s"',
767
-					$typeString,
768
-					$path,
769
-					$this->getId()
770
-				),
771
-				[
772
-					'app' => 'locking',
773
-				]
774
-			);
775
-		}
776
-		try {
777
-			$provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
778
-		} catch (LockedException $e) {
779
-			if ($logger) {
780
-				$logger->logException($e, ['level' => ILogger::INFO]);
781
-			}
782
-			throw $e;
783
-		}
784
-	}
785
-
786
-	/**
787
-	 * @param string $path
788
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
789
-	 * @param \OCP\Lock\ILockingProvider $provider
790
-	 * @throws \OCP\Lock\LockedException
791
-	 */
792
-	public function changeLock($path, $type, ILockingProvider $provider) {
793
-		$logger = $this->getLockLogger();
794
-		if ($logger) {
795
-			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
796
-			$logger->info(
797
-				sprintf(
798
-					'change lock on "%s" to %s on storage "%s"',
799
-					$path,
800
-					$typeString,
801
-					$this->getId()
802
-				),
803
-				[
804
-					'app' => 'locking',
805
-				]
806
-			);
807
-		}
808
-		try {
809
-			$provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
810
-		} catch (LockedException $e) {
811
-			\OC::$server->getLogger()->logException($e, ['level' => ILogger::INFO]);
812
-			throw $e;
813
-		}
814
-	}
815
-
816
-	private function getLockLogger() {
817
-		if (is_null($this->shouldLogLocks)) {
818
-			$this->shouldLogLocks = \OC::$server->getConfig()->getSystemValue('filelocking.debug', false);
819
-			$this->logger = $this->shouldLogLocks ? \OC::$server->getLogger() : null;
820
-		}
821
-		return $this->logger;
822
-	}
823
-
824
-	/**
825
-	 * @return array [ available, last_checked ]
826
-	 */
827
-	public function getAvailability() {
828
-		return $this->getStorageCache()->getAvailability();
829
-	}
830
-
831
-	/**
832
-	 * @param bool $isAvailable
833
-	 */
834
-	public function setAvailability($isAvailable) {
835
-		$this->getStorageCache()->setAvailability($isAvailable);
836
-	}
837
-
838
-	/**
839
-	 * @return bool
840
-	 */
841
-	public function needsPartFile() {
842
-		return true;
843
-	}
844
-
845
-	/**
846
-	 * fallback implementation
847
-	 *
848
-	 * @param string $path
849
-	 * @param resource $stream
850
-	 * @param int $size
851
-	 * @return int
852
-	 */
853
-	public function writeStream(string $path, $stream, int $size = null): int {
854
-		$target = $this->fopen($path, 'w');
855
-		if (!$target) {
856
-			return 0;
857
-		}
858
-		list($count, $result) = \OC_Helper::streamCopy($stream, $target);
859
-		fclose($stream);
860
-		fclose($target);
861
-		return $count;
862
-	}
534
+                throw new InvalidCharacterInPathException();
535
+            }
536
+        }
537
+
538
+        // 255 characters is the limit on common file systems (ext/xfs)
539
+        // oc_filecache has a 250 char length limit for the filename
540
+        if (isset($fileName[250])) {
541
+            throw new FileNameTooLongException();
542
+        }
543
+
544
+        // NOTE: $path will remain unverified for now
545
+        $this->verifyPosixPath($fileName);
546
+    }
547
+
548
+    /**
549
+     * @param string $fileName
550
+     * @throws InvalidPathException
551
+     */
552
+    protected function verifyPosixPath($fileName) {
553
+        $fileName = trim($fileName);
554
+        $this->scanForInvalidCharacters($fileName, "\\/");
555
+        $reservedNames = ['*'];
556
+        if (in_array($fileName, $reservedNames)) {
557
+            throw new ReservedWordException();
558
+        }
559
+    }
560
+
561
+    /**
562
+     * @param string $fileName
563
+     * @param string $invalidChars
564
+     * @throws InvalidPathException
565
+     */
566
+    private function scanForInvalidCharacters($fileName, $invalidChars) {
567
+        foreach (str_split($invalidChars) as $char) {
568
+            if (strpos($fileName, $char) !== false) {
569
+                throw new InvalidCharacterInPathException();
570
+            }
571
+        }
572
+
573
+        $sanitizedFileName = filter_var($fileName, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
574
+        if ($sanitizedFileName !== $fileName) {
575
+            throw new InvalidCharacterInPathException();
576
+        }
577
+    }
578
+
579
+    /**
580
+     * @param array $options
581
+     */
582
+    public function setMountOptions(array $options) {
583
+        $this->mountOptions = $options;
584
+    }
585
+
586
+    /**
587
+     * @param string $name
588
+     * @param mixed $default
589
+     * @return mixed
590
+     */
591
+    public function getMountOption($name, $default = null) {
592
+        return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default;
593
+    }
594
+
595
+    /**
596
+     * @param IStorage $sourceStorage
597
+     * @param string $sourceInternalPath
598
+     * @param string $targetInternalPath
599
+     * @param bool $preserveMtime
600
+     * @return bool
601
+     */
602
+    public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
603
+        if ($sourceStorage === $this) {
604
+            return $this->copy($sourceInternalPath, $targetInternalPath);
605
+        }
606
+
607
+        if ($sourceStorage->is_dir($sourceInternalPath)) {
608
+            $dh = $sourceStorage->opendir($sourceInternalPath);
609
+            $result = $this->mkdir($targetInternalPath);
610
+            if (is_resource($dh)) {
611
+                while ($result and ($file = readdir($dh)) !== false) {
612
+                    if (!Filesystem::isIgnoredDir($file)) {
613
+                        $result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
614
+                    }
615
+                }
616
+            }
617
+        } else {
618
+            $source = $sourceStorage->fopen($sourceInternalPath, 'r');
619
+            // TODO: call fopen in a way that we execute again all storage wrappers
620
+            // to avoid that we bypass storage wrappers which perform important actions
621
+            // for this operation. Same is true for all other operations which
622
+            // are not the same as the original one.Once this is fixed we also
623
+            // need to adjust the encryption wrapper.
624
+            $target = $this->fopen($targetInternalPath, 'w');
625
+            list(, $result) = \OC_Helper::streamCopy($source, $target);
626
+            if ($result and $preserveMtime) {
627
+                $this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath));
628
+            }
629
+            fclose($source);
630
+            fclose($target);
631
+
632
+            if (!$result) {
633
+                // delete partially written target file
634
+                $this->unlink($targetInternalPath);
635
+                // delete cache entry that was created by fopen
636
+                $this->getCache()->remove($targetInternalPath);
637
+            }
638
+        }
639
+        return (bool)$result;
640
+    }
641
+
642
+    /**
643
+     * Check if a storage is the same as the current one, including wrapped storages
644
+     *
645
+     * @param IStorage $storage
646
+     * @return bool
647
+     */
648
+    private function isSameStorage(IStorage $storage): bool {
649
+        while ($storage->instanceOfStorage(Wrapper::class)) {
650
+            /**
651
+             * @var Wrapper $sourceStorage
652
+             */
653
+            $storage = $storage->getWrapperStorage();
654
+        }
655
+
656
+        return $storage === $this;
657
+    }
658
+
659
+    /**
660
+     * @param IStorage $sourceStorage
661
+     * @param string $sourceInternalPath
662
+     * @param string $targetInternalPath
663
+     * @return bool
664
+     */
665
+    public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
666
+        if ($this->isSameStorage($sourceStorage)) {
667
+            // resolve any jailed paths
668
+            while ($sourceStorage->instanceOfStorage(Jail::class)) {
669
+                /**
670
+                 * @var Jail $sourceStorage
671
+                 */
672
+                $sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
673
+                $sourceStorage = $sourceStorage->getUnjailedStorage();
674
+            }
675
+
676
+            return $this->rename($sourceInternalPath, $targetInternalPath);
677
+        }
678
+
679
+        if (!$sourceStorage->isDeletable($sourceInternalPath)) {
680
+            return false;
681
+        }
682
+
683
+        $result = $this->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true);
684
+        if ($result) {
685
+            if ($sourceStorage->is_dir($sourceInternalPath)) {
686
+                $result &= $sourceStorage->rmdir($sourceInternalPath);
687
+            } else {
688
+                $result &= $sourceStorage->unlink($sourceInternalPath);
689
+            }
690
+        }
691
+        return $result;
692
+    }
693
+
694
+    /**
695
+     * @inheritdoc
696
+     */
697
+    public function getMetaData($path) {
698
+        $permissions = $this->getPermissions($path);
699
+        if (!$permissions & \OCP\Constants::PERMISSION_READ) {
700
+            //can't read, nothing we can do
701
+            return null;
702
+        }
703
+
704
+        $data = [];
705
+        $data['mimetype'] = $this->getMimeType($path);
706
+        $data['mtime'] = $this->filemtime($path);
707
+        if ($data['mtime'] === false) {
708
+            $data['mtime'] = time();
709
+        }
710
+        if ($data['mimetype'] == 'httpd/unix-directory') {
711
+            $data['size'] = -1; //unknown
712
+        } else {
713
+            $data['size'] = $this->filesize($path);
714
+        }
715
+        $data['etag'] = $this->getETag($path);
716
+        $data['storage_mtime'] = $data['mtime'];
717
+        $data['permissions'] = $permissions;
718
+
719
+        return $data;
720
+    }
721
+
722
+    /**
723
+     * @param string $path
724
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
725
+     * @param \OCP\Lock\ILockingProvider $provider
726
+     * @throws \OCP\Lock\LockedException
727
+     */
728
+    public function acquireLock($path, $type, ILockingProvider $provider) {
729
+        $logger = $this->getLockLogger();
730
+        if ($logger) {
731
+            $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
732
+            $logger->info(
733
+                sprintf(
734
+                    'acquire %s lock on "%s" on storage "%s"',
735
+                    $typeString,
736
+                    $path,
737
+                    $this->getId()
738
+                ),
739
+                [
740
+                    'app' => 'locking',
741
+                ]
742
+            );
743
+        }
744
+        try {
745
+            $provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
746
+        } catch (LockedException $e) {
747
+            if ($logger) {
748
+                $logger->logException($e, ['level' => ILogger::INFO]);
749
+            }
750
+            throw $e;
751
+        }
752
+    }
753
+
754
+    /**
755
+     * @param string $path
756
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
757
+     * @param \OCP\Lock\ILockingProvider $provider
758
+     * @throws \OCP\Lock\LockedException
759
+     */
760
+    public function releaseLock($path, $type, ILockingProvider $provider) {
761
+        $logger = $this->getLockLogger();
762
+        if ($logger) {
763
+            $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
764
+            $logger->info(
765
+                sprintf(
766
+                    'release %s lock on "%s" on storage "%s"',
767
+                    $typeString,
768
+                    $path,
769
+                    $this->getId()
770
+                ),
771
+                [
772
+                    'app' => 'locking',
773
+                ]
774
+            );
775
+        }
776
+        try {
777
+            $provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
778
+        } catch (LockedException $e) {
779
+            if ($logger) {
780
+                $logger->logException($e, ['level' => ILogger::INFO]);
781
+            }
782
+            throw $e;
783
+        }
784
+    }
785
+
786
+    /**
787
+     * @param string $path
788
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
789
+     * @param \OCP\Lock\ILockingProvider $provider
790
+     * @throws \OCP\Lock\LockedException
791
+     */
792
+    public function changeLock($path, $type, ILockingProvider $provider) {
793
+        $logger = $this->getLockLogger();
794
+        if ($logger) {
795
+            $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
796
+            $logger->info(
797
+                sprintf(
798
+                    'change lock on "%s" to %s on storage "%s"',
799
+                    $path,
800
+                    $typeString,
801
+                    $this->getId()
802
+                ),
803
+                [
804
+                    'app' => 'locking',
805
+                ]
806
+            );
807
+        }
808
+        try {
809
+            $provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
810
+        } catch (LockedException $e) {
811
+            \OC::$server->getLogger()->logException($e, ['level' => ILogger::INFO]);
812
+            throw $e;
813
+        }
814
+    }
815
+
816
+    private function getLockLogger() {
817
+        if (is_null($this->shouldLogLocks)) {
818
+            $this->shouldLogLocks = \OC::$server->getConfig()->getSystemValue('filelocking.debug', false);
819
+            $this->logger = $this->shouldLogLocks ? \OC::$server->getLogger() : null;
820
+        }
821
+        return $this->logger;
822
+    }
823
+
824
+    /**
825
+     * @return array [ available, last_checked ]
826
+     */
827
+    public function getAvailability() {
828
+        return $this->getStorageCache()->getAvailability();
829
+    }
830
+
831
+    /**
832
+     * @param bool $isAvailable
833
+     */
834
+    public function setAvailability($isAvailable) {
835
+        $this->getStorageCache()->setAvailability($isAvailable);
836
+    }
837
+
838
+    /**
839
+     * @return bool
840
+     */
841
+    public function needsPartFile() {
842
+        return true;
843
+    }
844
+
845
+    /**
846
+     * fallback implementation
847
+     *
848
+     * @param string $path
849
+     * @param resource $stream
850
+     * @param int $size
851
+     * @return int
852
+     */
853
+    public function writeStream(string $path, $stream, int $size = null): int {
854
+        $target = $this->fopen($path, 'w');
855
+        if (!$target) {
856
+            return 0;
857
+        }
858
+        list($count, $result) = \OC_Helper::streamCopy($stream, $target);
859
+        fclose($stream);
860
+        fclose($target);
861
+        return $count;
862
+    }
863 863
 }
Please login to merge, or discard this patch.
lib/private/Files/Storage/StorageFactory.php 2 patches
Indentation   +70 added lines, -70 removed lines patch added patch discarded remove patch
@@ -29,80 +29,80 @@
 block discarded – undo
29 29
 use OCP\Files\Storage\IStorageFactory;
30 30
 
31 31
 class StorageFactory implements IStorageFactory {
32
-	/**
33
-	 * @var array[] [$name=>['priority'=>$priority, 'wrapper'=>$callable] $storageWrappers
34
-	 */
35
-	private $storageWrappers = [];
32
+    /**
33
+     * @var array[] [$name=>['priority'=>$priority, 'wrapper'=>$callable] $storageWrappers
34
+     */
35
+    private $storageWrappers = [];
36 36
 
37
-	/**
38
-	 * allow modifier storage behaviour by adding wrappers around storages
39
-	 *
40
-	 * $callback should be a function of type (string $mountPoint, Storage $storage) => Storage
41
-	 *
42
-	 * @param string $wrapperName name of the wrapper
43
-	 * @param callable $callback callback
44
-	 * @param int $priority wrappers with the lower priority are applied last (meaning they get called first)
45
-	 * @param \OCP\Files\Mount\IMountPoint[] $existingMounts existing mount points to apply the wrapper to
46
-	 * @return bool true if the wrapper was added, false if there was already a wrapper with this
47
-	 * name registered
48
-	 */
49
-	public function addStorageWrapper($wrapperName, $callback, $priority = 50, $existingMounts = []) {
50
-		if (isset($this->storageWrappers[$wrapperName])) {
51
-			return false;
52
-		}
37
+    /**
38
+     * allow modifier storage behaviour by adding wrappers around storages
39
+     *
40
+     * $callback should be a function of type (string $mountPoint, Storage $storage) => Storage
41
+     *
42
+     * @param string $wrapperName name of the wrapper
43
+     * @param callable $callback callback
44
+     * @param int $priority wrappers with the lower priority are applied last (meaning they get called first)
45
+     * @param \OCP\Files\Mount\IMountPoint[] $existingMounts existing mount points to apply the wrapper to
46
+     * @return bool true if the wrapper was added, false if there was already a wrapper with this
47
+     * name registered
48
+     */
49
+    public function addStorageWrapper($wrapperName, $callback, $priority = 50, $existingMounts = []) {
50
+        if (isset($this->storageWrappers[$wrapperName])) {
51
+            return false;
52
+        }
53 53
 
54
-		// apply to existing mounts before registering it to prevent applying it double in MountPoint::createStorage
55
-		foreach ($existingMounts as $mount) {
56
-			$mount->wrapStorage($callback);
57
-		}
54
+        // apply to existing mounts before registering it to prevent applying it double in MountPoint::createStorage
55
+        foreach ($existingMounts as $mount) {
56
+            $mount->wrapStorage($callback);
57
+        }
58 58
 
59
-		$this->storageWrappers[$wrapperName] = ['wrapper' => $callback, 'priority' => $priority];
60
-		return true;
61
-	}
59
+        $this->storageWrappers[$wrapperName] = ['wrapper' => $callback, 'priority' => $priority];
60
+        return true;
61
+    }
62 62
 
63
-	/**
64
-	 * Remove a storage wrapper by name.
65
-	 * Note: internal method only to be used for cleanup
66
-	 *
67
-	 * @param string $wrapperName name of the wrapper
68
-	 * @internal
69
-	 */
70
-	public function removeStorageWrapper($wrapperName) {
71
-		unset($this->storageWrappers[$wrapperName]);
72
-	}
63
+    /**
64
+     * Remove a storage wrapper by name.
65
+     * Note: internal method only to be used for cleanup
66
+     *
67
+     * @param string $wrapperName name of the wrapper
68
+     * @internal
69
+     */
70
+    public function removeStorageWrapper($wrapperName) {
71
+        unset($this->storageWrappers[$wrapperName]);
72
+    }
73 73
 
74
-	/**
75
-	 * Create an instance of a storage and apply the registered storage wrappers
76
-	 *
77
-	 * @param \OCP\Files\Mount\IMountPoint $mountPoint
78
-	 * @param string $class
79
-	 * @param array $arguments
80
-	 * @return \OCP\Files\Storage
81
-	 */
82
-	public function getInstance(IMountPoint $mountPoint, $class, $arguments) {
83
-		return $this->wrap($mountPoint, new $class($arguments));
84
-	}
74
+    /**
75
+     * Create an instance of a storage and apply the registered storage wrappers
76
+     *
77
+     * @param \OCP\Files\Mount\IMountPoint $mountPoint
78
+     * @param string $class
79
+     * @param array $arguments
80
+     * @return \OCP\Files\Storage
81
+     */
82
+    public function getInstance(IMountPoint $mountPoint, $class, $arguments) {
83
+        return $this->wrap($mountPoint, new $class($arguments));
84
+    }
85 85
 
86
-	/**
87
-	 * @param \OCP\Files\Mount\IMountPoint $mountPoint
88
-	 * @param \OCP\Files\Storage $storage
89
-	 * @return \OCP\Files\Storage
90
-	 */
91
-	public function wrap(IMountPoint $mountPoint, $storage) {
92
-		$wrappers = array_values($this->storageWrappers);
93
-		usort($wrappers, function ($a, $b) {
94
-			return $b['priority'] - $a['priority'];
95
-		});
96
-		/** @var callable[] $wrappers */
97
-		$wrappers = array_map(function ($wrapper) {
98
-			return $wrapper['wrapper'];
99
-		}, $wrappers);
100
-		foreach ($wrappers as $wrapper) {
101
-			$storage = $wrapper($mountPoint->getMountPoint(), $storage, $mountPoint);
102
-			if (!($storage instanceof \OCP\Files\Storage)) {
103
-				throw new \Exception('Invalid result from storage wrapper');
104
-			}
105
-		}
106
-		return $storage;
107
-	}
86
+    /**
87
+     * @param \OCP\Files\Mount\IMountPoint $mountPoint
88
+     * @param \OCP\Files\Storage $storage
89
+     * @return \OCP\Files\Storage
90
+     */
91
+    public function wrap(IMountPoint $mountPoint, $storage) {
92
+        $wrappers = array_values($this->storageWrappers);
93
+        usort($wrappers, function ($a, $b) {
94
+            return $b['priority'] - $a['priority'];
95
+        });
96
+        /** @var callable[] $wrappers */
97
+        $wrappers = array_map(function ($wrapper) {
98
+            return $wrapper['wrapper'];
99
+        }, $wrappers);
100
+        foreach ($wrappers as $wrapper) {
101
+            $storage = $wrapper($mountPoint->getMountPoint(), $storage, $mountPoint);
102
+            if (!($storage instanceof \OCP\Files\Storage)) {
103
+                throw new \Exception('Invalid result from storage wrapper');
104
+            }
105
+        }
106
+        return $storage;
107
+    }
108 108
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -90,11 +90,11 @@
 block discarded – undo
90 90
 	 */
91 91
 	public function wrap(IMountPoint $mountPoint, $storage) {
92 92
 		$wrappers = array_values($this->storageWrappers);
93
-		usort($wrappers, function ($a, $b) {
93
+		usort($wrappers, function($a, $b) {
94 94
 			return $b['priority'] - $a['priority'];
95 95
 		});
96 96
 		/** @var callable[] $wrappers */
97
-		$wrappers = array_map(function ($wrapper) {
97
+		$wrappers = array_map(function($wrapper) {
98 98
 			return $wrapper['wrapper'];
99 99
 		}, $wrappers);
100 100
 		foreach ($wrappers as $wrapper) {
Please login to merge, or discard this patch.
lib/private/Files/Storage/PolyFill/CopyDirectory.php 2 patches
Indentation   +71 added lines, -71 removed lines patch added patch discarded remove patch
@@ -25,81 +25,81 @@
 block discarded – undo
25 25
 namespace OC\Files\Storage\PolyFill;
26 26
 
27 27
 trait CopyDirectory {
28
-	/**
29
-	 * Check if a path is a directory
30
-	 *
31
-	 * @param string $path
32
-	 * @return bool
33
-	 */
34
-	abstract public function is_dir($path);
28
+    /**
29
+     * Check if a path is a directory
30
+     *
31
+     * @param string $path
32
+     * @return bool
33
+     */
34
+    abstract public function is_dir($path);
35 35
 
36
-	/**
37
-	 * Check if a file or folder exists
38
-	 *
39
-	 * @param string $path
40
-	 * @return bool
41
-	 */
42
-	abstract public function file_exists($path);
36
+    /**
37
+     * Check if a file or folder exists
38
+     *
39
+     * @param string $path
40
+     * @return bool
41
+     */
42
+    abstract public function file_exists($path);
43 43
 
44
-	/**
45
-	 * Delete a file or folder
46
-	 *
47
-	 * @param string $path
48
-	 * @return bool
49
-	 */
50
-	abstract public function unlink($path);
44
+    /**
45
+     * Delete a file or folder
46
+     *
47
+     * @param string $path
48
+     * @return bool
49
+     */
50
+    abstract public function unlink($path);
51 51
 
52
-	/**
53
-	 * Open a directory handle for a folder
54
-	 *
55
-	 * @param string $path
56
-	 * @return resource | bool
57
-	 */
58
-	abstract public function opendir($path);
52
+    /**
53
+     * Open a directory handle for a folder
54
+     *
55
+     * @param string $path
56
+     * @return resource | bool
57
+     */
58
+    abstract public function opendir($path);
59 59
 
60
-	/**
61
-	 * Create a new folder
62
-	 *
63
-	 * @param string $path
64
-	 * @return bool
65
-	 */
66
-	abstract public function mkdir($path);
60
+    /**
61
+     * Create a new folder
62
+     *
63
+     * @param string $path
64
+     * @return bool
65
+     */
66
+    abstract public function mkdir($path);
67 67
 
68
-	public function copy($source, $target) {
69
-		if ($this->is_dir($source)) {
70
-			if ($this->file_exists($target)) {
71
-				$this->unlink($target);
72
-			}
73
-			$this->mkdir($target);
74
-			return $this->copyRecursive($source, $target);
75
-		} else {
76
-			return parent::copy($source, $target);
77
-		}
78
-	}
68
+    public function copy($source, $target) {
69
+        if ($this->is_dir($source)) {
70
+            if ($this->file_exists($target)) {
71
+                $this->unlink($target);
72
+            }
73
+            $this->mkdir($target);
74
+            return $this->copyRecursive($source, $target);
75
+        } else {
76
+            return parent::copy($source, $target);
77
+        }
78
+    }
79 79
 
80
-	/**
81
-	 * For adapters that don't support copying folders natively
82
-	 *
83
-	 * @param $source
84
-	 * @param $target
85
-	 * @return bool
86
-	 */
87
-	protected function copyRecursive($source, $target) {
88
-		$dh = $this->opendir($source);
89
-		$result = true;
90
-		while ($file = readdir($dh)) {
91
-			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
92
-				if ($this->is_dir($source . '/' . $file)) {
93
-					$this->mkdir($target . '/' . $file);
94
-					$result = $this->copyRecursive($source . '/' . $file, $target . '/' . $file);
95
-				} else {
96
-					$result = parent::copy($source . '/' . $file, $target . '/' . $file);
97
-				}
98
-				if (!$result) {
99
-					break;
100
-				}
101
-			}
102
-		}
103
-		return $result;
104
-	}
80
+    /**
81
+     * For adapters that don't support copying folders natively
82
+     *
83
+     * @param $source
84
+     * @param $target
85
+     * @return bool
86
+     */
87
+    protected function copyRecursive($source, $target) {
88
+        $dh = $this->opendir($source);
89
+        $result = true;
90
+        while ($file = readdir($dh)) {
91
+            if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
92
+                if ($this->is_dir($source . '/' . $file)) {
93
+                    $this->mkdir($target . '/' . $file);
94
+                    $result = $this->copyRecursive($source . '/' . $file, $target . '/' . $file);
95
+                } else {
96
+                    $result = parent::copy($source . '/' . $file, $target . '/' . $file);
97
+                }
98
+                if (!$result) {
99
+                    break;
100
+                }
101
+            }
102
+        }
103
+        return $result;
104
+    }
105 105
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -89,11 +89,11 @@
 block discarded – undo
89 89
 		$result = true;
90 90
 		while ($file = readdir($dh)) {
91 91
 			if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
92
-				if ($this->is_dir($source . '/' . $file)) {
93
-					$this->mkdir($target . '/' . $file);
94
-					$result = $this->copyRecursive($source . '/' . $file, $target . '/' . $file);
92
+				if ($this->is_dir($source.'/'.$file)) {
93
+					$this->mkdir($target.'/'.$file);
94
+					$result = $this->copyRecursive($source.'/'.$file, $target.'/'.$file);
95 95
 				} else {
96
-					$result = parent::copy($source . '/' . $file, $target . '/' . $file);
96
+					$result = parent::copy($source.'/'.$file, $target.'/'.$file);
97 97
 				}
98 98
 				if (!$result) {
99 99
 					break;
Please login to merge, or discard this patch.
lib/private/Files/Storage/CommonTest.php 2 patches
Indentation   +47 added lines, -47 removed lines patch added patch discarded remove patch
@@ -33,53 +33,53 @@
 block discarded – undo
33 33
 namespace OC\Files\Storage;
34 34
 
35 35
 class CommonTest extends \OC\Files\Storage\Common{
36
-	/**
37
-	 * underlying local storage used for missing functions
38
-	 * @var \OC\Files\Storage\Local
39
-	 */
40
-	private $storage;
36
+    /**
37
+     * underlying local storage used for missing functions
38
+     * @var \OC\Files\Storage\Local
39
+     */
40
+    private $storage;
41 41
 
42
-	public function __construct($params) {
43
-		$this->storage=new \OC\Files\Storage\Local($params);
44
-	}
42
+    public function __construct($params) {
43
+        $this->storage=new \OC\Files\Storage\Local($params);
44
+    }
45 45
 
46
-	public function getId(){
47
-		return 'test::'.$this->storage->getId();
48
-	}
49
-	public function mkdir($path) {
50
-		return $this->storage->mkdir($path);
51
-	}
52
-	public function rmdir($path) {
53
-		return $this->storage->rmdir($path);
54
-	}
55
-	public function opendir($path) {
56
-		return $this->storage->opendir($path);
57
-	}
58
-	public function stat($path) {
59
-		return $this->storage->stat($path);
60
-	}
61
-	public function filetype($path) {
62
-		return @$this->storage->filetype($path);
63
-	}
64
-	public function isReadable($path) {
65
-		return $this->storage->isReadable($path);
66
-	}
67
-	public function isUpdatable($path) {
68
-		return $this->storage->isUpdatable($path);
69
-	}
70
-	public function file_exists($path) {
71
-		return $this->storage->file_exists($path);
72
-	}
73
-	public function unlink($path) {
74
-		return $this->storage->unlink($path);
75
-	}
76
-	public function fopen($path, $mode) {
77
-		return $this->storage->fopen($path, $mode);
78
-	}
79
-	public function free_space($path) {
80
-		return $this->storage->free_space($path);
81
-	}
82
-	public function touch($path, $mtime=null) {
83
-		return $this->storage->touch($path, $mtime);
84
-	}
46
+    public function getId(){
47
+        return 'test::'.$this->storage->getId();
48
+    }
49
+    public function mkdir($path) {
50
+        return $this->storage->mkdir($path);
51
+    }
52
+    public function rmdir($path) {
53
+        return $this->storage->rmdir($path);
54
+    }
55
+    public function opendir($path) {
56
+        return $this->storage->opendir($path);
57
+    }
58
+    public function stat($path) {
59
+        return $this->storage->stat($path);
60
+    }
61
+    public function filetype($path) {
62
+        return @$this->storage->filetype($path);
63
+    }
64
+    public function isReadable($path) {
65
+        return $this->storage->isReadable($path);
66
+    }
67
+    public function isUpdatable($path) {
68
+        return $this->storage->isUpdatable($path);
69
+    }
70
+    public function file_exists($path) {
71
+        return $this->storage->file_exists($path);
72
+    }
73
+    public function unlink($path) {
74
+        return $this->storage->unlink($path);
75
+    }
76
+    public function fopen($path, $mode) {
77
+        return $this->storage->fopen($path, $mode);
78
+    }
79
+    public function free_space($path) {
80
+        return $this->storage->free_space($path);
81
+    }
82
+    public function touch($path, $mtime=null) {
83
+        return $this->storage->touch($path, $mtime);
84
+    }
85 85
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -32,7 +32,7 @@  discard block
 block discarded – undo
32 32
 
33 33
 namespace OC\Files\Storage;
34 34
 
35
-class CommonTest extends \OC\Files\Storage\Common{
35
+class CommonTest extends \OC\Files\Storage\Common {
36 36
 	/**
37 37
 	 * underlying local storage used for missing functions
38 38
 	 * @var \OC\Files\Storage\Local
@@ -40,10 +40,10 @@  discard block
 block discarded – undo
40 40
 	private $storage;
41 41
 
42 42
 	public function __construct($params) {
43
-		$this->storage=new \OC\Files\Storage\Local($params);
43
+		$this->storage = new \OC\Files\Storage\Local($params);
44 44
 	}
45 45
 
46
-	public function getId(){
46
+	public function getId() {
47 47
 		return 'test::'.$this->storage->getId();
48 48
 	}
49 49
 	public function mkdir($path) {
@@ -79,7 +79,7 @@  discard block
 block discarded – undo
79 79
 	public function free_space($path) {
80 80
 		return $this->storage->free_space($path);
81 81
 	}
82
-	public function touch($path, $mtime=null) {
82
+	public function touch($path, $mtime = null) {
83 83
 		return $this->storage->touch($path, $mtime);
84 84
 	}
85 85
 }
Please login to merge, or discard this patch.