Completed
Push — master ( e4992c...6d0a35 )
by
unknown
10:42
created

Common::searchInDir()   A

Complexity

Conditions 6
Paths 2

Size

Total Lines 19

Duplication

Lines 3
Ratio 15.79 %

Importance

Changes 0
Metric Value
cc 6
nc 2
nop 2
dl 3
loc 19
rs 9.0111
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Bart Visscher <[email protected]>
5
 * @author Björn Schießle <[email protected]>
6
 * @author hkjolhede <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Jörn Friedrich Dreyer <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author Martin Mattel <[email protected]>
11
 * @author Michael Gapczynski <[email protected]>
12
 * @author Morris Jobke <[email protected]>
13
 * @author Robin Appelman <[email protected]>
14
 * @author Robin McCorkell <[email protected]>
15
 * @author Roeland Jago Douma <[email protected]>
16
 * @author Sam Tuke <[email protected]>
17
 * @author scambra <[email protected]>
18
 * @author Stefan Weil <[email protected]>
19
 * @author Thomas Müller <[email protected]>
20
 * @author Vincent Petry <[email protected]>
21
 *
22
 * @copyright Copyright (c) 2018, ownCloud GmbH
23
 * @license AGPL-3.0
24
 *
25
 * This code is free software: you can redistribute it and/or modify
26
 * it under the terms of the GNU Affero General Public License, version 3,
27
 * as published by the Free Software Foundation.
28
 *
29
 * This program is distributed in the hope that it will be useful,
30
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
 * GNU Affero General Public License for more details.
33
 *
34
 * You should have received a copy of the GNU Affero General Public License, version 3,
35
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
36
 *
37
 */
38
39
namespace OC\Files\Storage;
40
41
use OC\Files\Cache\Cache;
42
use OC\Files\Cache\Propagator;
43
use OC\Files\Cache\Scanner;
44
use OC\Files\Cache\Updater;
45
use OC\Files\Filesystem;
46
use OC\Files\Cache\Watcher;
47
use OCP\Constants;
48
use OCP\Files\FileInfo;
49
use OCP\Files\FileNameTooLongException;
50
use OCP\Files\InvalidCharacterInPathException;
51
use OCP\Files\InvalidPathException;
52
use OCP\Files\ReservedWordException;
53
use OCP\Files\Storage\ILockingStorage;
54
use OCP\Files\Storage\IVersionedStorage;
55
use OCP\Lock\ILockingProvider;
56
57
/**
58
 * Storage backend class for providing common filesystem operation methods
59
 * which are not storage-backend specific.
60
 *
61
 * \OC\Files\Storage\Common is never used directly; it is extended by all other
62
 * storage backends, where its methods may be overridden, and additional
63
 * (backend-specific) methods are defined.
64
 *
65
 * Some \OC\Files\Storage\Common methods call functions which are first defined
66
 * in classes which extend it, e.g. $this->stat() .
67
 */
68
abstract class Common implements Storage, ILockingStorage, IVersionedStorage {
69
	use LocalTempFileTrait;
70
71
	protected $cache;
72
	protected $scanner;
73
	protected $watcher;
74
	protected $propagator;
75
	protected $storageCache;
76
	protected $updater;
77
78
	protected $mountOptions = [];
79
	protected $owner = null;
80
81
	public function __construct($parameters) {
82
	}
83
84
	/**
85
	 * Remove a file or folder
86
	 *
87
	 * @param string $path
88
	 * @return bool
89
	 */
90 View Code Duplication
	protected function remove($path) {
91
		if ($this->is_dir($path)) {
92
			return $this->rmdir($path);
93
		} elseif ($this->is_file($path)) {
94
			return $this->unlink($path);
95
		} else {
96
			return false;
97
		}
98
	}
99
100
	public function is_dir($path) {
101
		return $this->filetype($path) === 'dir';
102
	}
103
104
	public function is_file($path) {
105
		return $this->filetype($path) === 'file';
106
	}
107
108
	public function filesize($path) {
109
		if ($this->is_dir($path)) {
110
			return 0; //by definition
111
		} else {
112
			$stat = $this->stat($path);
113
			if (isset($stat['size'])) {
114
				return $stat['size'];
115
			} else {
116
				return 0;
117
			}
118
		}
119
	}
120
121
	public function isReadable($path) {
122
		// at least check whether it exists
123
		// subclasses might want to implement this more thoroughly
124
		return $this->file_exists($path);
125
	}
126
127
	public function isUpdatable($path) {
128
		// at least check whether it exists
129
		// subclasses might want to implement this more thoroughly
130
		// a non-existing file/folder isn't updatable
131
		return $this->file_exists($path);
132
	}
133
134
	public function isCreatable($path) {
135
		if ($this->is_dir($path) && $this->isUpdatable($path)) {
136
			return true;
137
		}
138
		return false;
139
	}
140
141
	public function isDeletable($path) {
142
		if ($path === '' || $path === '/') {
143
			return false;
144
		}
145
		$parent = \dirname($path);
146
		return $this->isUpdatable($parent) && $this->isUpdatable($path);
147
	}
148
149
	public function isSharable($path) {
150
		return $this->isReadable($path);
151
	}
152
153
	public function getPermissions($path) {
154
		$permissions = 0;
155
		if ($this->isCreatable($path)) {
156
			$permissions |= Constants::PERMISSION_CREATE;
157
		}
158
		if ($this->isReadable($path)) {
159
			$permissions |= Constants::PERMISSION_READ;
160
		}
161
		if ($this->isUpdatable($path)) {
162
			$permissions |= Constants::PERMISSION_UPDATE;
163
		}
164
		if ($this->isDeletable($path)) {
165
			$permissions |= Constants::PERMISSION_DELETE;
166
		}
167
		if ($this->isSharable($path)) {
168
			$permissions |= Constants::PERMISSION_SHARE;
169
		}
170
		return $permissions;
171
	}
172
173
	public function filemtime($path) {
174
		$stat = $this->stat($path);
175
		if (isset($stat['mtime'])) {
176
			return $stat['mtime'];
177
		} else {
178
			return 0;
179
		}
180
	}
181
182
	public function file_get_contents($path) {
183
		$handle = $this->fopen($path, "r");
184
		if (!$handle) {
185
			return false;
186
		}
187
		$data = \stream_get_contents($handle);
188
		\fclose($handle);
189
		return $data;
190
	}
191
192
	public function file_put_contents($path, $data) {
193
		$handle = $this->fopen($path, "w");
194
		$this->removeCachedFile($path);
195
		$count = \fwrite($handle, $data);
196
		\fclose($handle);
197
		return $count;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $count; (integer) is incompatible with the return type declared by the interface OCP\Files\Storage::file_put_contents of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
198
	}
199
200
	public function rename($path1, $path2) {
201
		$this->remove($path2);
202
203
		$this->removeCachedFile($path1);
204
		return $this->copy($path1, $path2) and $this->remove($path1);
205
	}
206
207
	public function copy($path1, $path2) {
208
		if ($this->is_dir($path1)) {
209
			$this->remove($path2);
210
			$dir = $this->opendir($path1);
211
			$this->mkdir($path2);
212
			while ($file = \readdir($dir)) {
213
				if (!Filesystem::isIgnoredDir($file) && !Filesystem::isForbiddenFileOrDir($file)) {
214
					if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
215
						return false;
216
					}
217
				}
218
			}
219
			\closedir($dir);
220
			return true;
221
		} else {
222
			$source = $this->fopen($path1, 'r');
223
			$target = $this->fopen($path2, 'w');
224
			list(, $result) = \OC_Helper::streamCopy($source, $target);
0 ignored issues
show
Security Bug introduced by
It seems like $source can also be of type false; however, OC_Helper::streamCopy() does only seem to accept resource, did you maybe forget to handle an error condition?
Loading history...
Security Bug introduced by
It seems like $target can also be of type false; however, OC_Helper::streamCopy() does only seem to accept resource, did you maybe forget to handle an error condition?
Loading history...
225
			$this->removeCachedFile($path2);
226
			return $result;
227
		}
228
	}
229
230
	public function getMimeType($path) {
231
		if ($this->is_dir($path)) {
232
			return 'httpd/unix-directory';
233
		} elseif ($this->file_exists($path)) {
234
			return \OC::$server->getMimeTypeDetector()->detectPath($path);
235
		} else {
236
			return false;
237
		}
238
	}
239
240 View Code Duplication
	public function hash($type, $path, $raw = false) {
241
		$fh = $this->fopen($path, 'rb');
242
		$ctx = \hash_init($type);
243
		\hash_update_stream($ctx, $fh);
244
		\fclose($fh);
245
		return \hash_final($ctx, $raw);
246
	}
247
248
	public function search($query) {
249
		return $this->searchInDir($query);
250
	}
251
252
	public function getLocalFile($path) {
253
		return $this->getCachedFile($path);
254
	}
255
256
	/**
257
	 * @param string $path
258
	 * @param string $target
259
	 */
260
	private function addLocalFolder($path, $target) {
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
261
		$dh = $this->opendir($path);
262
		if (\is_resource($dh)) {
263
			while (($file = \readdir($dh)) !== false) {
264
				if (!Filesystem::isIgnoredDir($file)) {
265
					if ($this->is_dir($path . '/' . $file)) {
266
						\mkdir($target . '/' . $file);
267
						$this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
268
					} else {
269
						$tmp = $this->toTmpFile($path . '/' . $file);
270
						\rename($tmp, $target . '/' . $file);
271
					}
272
				}
273
			}
274
		}
275
	}
276
277
	/**
278
	 * @param string $query
279
	 * @param string $dir
280
	 * @return array
281
	 */
282
	protected function searchInDir($query, $dir = '') {
283
		$files = [];
284
		$dh = $this->opendir($dir);
285
		if (\is_resource($dh)) {
286
			while (($item = \readdir($dh)) !== false) {
287
				if (Filesystem::isIgnoredDir($item)) {
288
					continue;
289
				}
290 View Code Duplication
				if (\strstr(\strtolower($item), \strtolower($query)) !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
291
					$files[] = $dir . '/' . $item;
292
				}
293
				if ($this->is_dir($dir . '/' . $item)) {
294
					$files = \array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
295
				}
296
			}
297
		}
298
		\closedir($dh);
299
		return $files;
300
	}
301
302
	/**
303
	 * check if a file or folder has been updated since $time
304
	 *
305
	 * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking
306
	 * the mtime should always return false here. As a result storage implementations that always return false expect
307
	 * exclusive access to the backend and will not pick up files that have been added in a way that circumvents
308
	 * ownClouds filesystem.
309
	 *
310
	 * @param string $path
311
	 * @param int $time
312
	 * @return bool
313
	 */
314
	public function hasUpdated($path, $time) {
315
		return $this->filemtime($path) > $time;
316
	}
317
318
	public function getCache($path = '', $storage = null) {
319
		if (!$storage) {
320
			$storage = $this;
321
		}
322
		if (!isset($storage->cache)) {
323
			$storage->cache = new Cache($storage);
324
		}
325
		return $storage->cache;
326
	}
327
328
	public function getScanner($path = '', $storage = null) {
329
		if (!$storage) {
330
			$storage = $this;
331
		}
332
		if (!isset($storage->scanner)) {
333
			$storage->scanner = new Scanner($storage);
334
		}
335
		return $storage->scanner;
336
	}
337
338
	public function getWatcher($path = '', $storage = null) {
339
		if (!$storage) {
340
			$storage = $this;
341
		}
342
		if (!isset($this->watcher)) {
343
			$this->watcher = new Watcher($storage);
344
			$globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
345
			$this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
346
		}
347
		return $this->watcher;
348
	}
349
350
	/**
351
	 * get a propagator instance for the cache
352
	 *
353
	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
354
	 * @return \OC\Files\Cache\Propagator
355
	 */
356
	public function getPropagator($storage = null) {
357
		if (!$storage) {
358
			$storage = $this;
359
		}
360
		if (!isset($storage->propagator)) {
361
			$storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection());
362
		}
363
		return $storage->propagator;
364
	}
365
366
	public function getUpdater($storage = null) {
367
		if (!$storage) {
368
			$storage = $this;
369
		}
370
		if (!isset($storage->updater)) {
371
			$storage->updater = new Updater($storage);
372
		}
373
		return $storage->updater;
374
	}
375
376
	public function getStorageCache($storage = null) {
377
		if (!$storage) {
378
			$storage = $this;
379
		}
380
		if (!isset($this->storageCache)) {
381
			$this->storageCache = new \OC\Files\Cache\Storage($storage);
382
		}
383
		return $this->storageCache;
384
	}
385
386
	/**
387
	 * get the owner of a path
388
	 *
389
	 * @param string $path The path to get the owner
390
	 * @return string|false uid or false
391
	 */
392
	public function getOwner($path) {
393
		if ($this->owner === null) {
394
			$this->owner = \OC_User::getUser();
395
		}
396
397
		return $this->owner;
398
	}
399
400
	/**
401
	 * get the ETag for a file or folder
402
	 *
403
	 * @param string $path
404
	 * @return string
405
	 */
406
	public function getETag($path) {
407
		return \uniqid();
408
	}
409
410
	/**
411
	 * clean a path, i.e. remove all redundant '.' and '..'
412
	 * making sure that it can't point to higher than '/'
413
	 *
414
	 * @param string $path The path to clean
415
	 * @return string cleaned path
416
	 */
417
	public function cleanPath($path) {
418
		if (\strlen($path) == 0 or $path[0] != '/') {
419
			$path = '/' . $path;
420
		}
421
422
		$output = [];
423
		foreach (\explode('/', $path) as $chunk) {
424
			if ($chunk == '..') {
425
				\array_pop($output);
426
			} elseif ($chunk == '.') {
427
			} else {
428
				$output[] = $chunk;
429
			}
430
		}
431
		return \implode('/', $output);
432
	}
433
434
	/**
435
	 * Test a storage for availability
436
	 *
437
	 * @return bool
438
	 */
439
	public function test() {
440
		if ($this->stat('')) {
441
			return true;
442
		}
443
		return false;
444
	}
445
446
	/**
447
	 * get the free space in the storage
448
	 *
449
	 * @param string $path
450
	 * @return int|false
451
	 */
452
	public function free_space($path) {
453
		return FileInfo::SPACE_UNKNOWN;
454
	}
455
456
	/**
457
	 * {@inheritdoc}
458
	 */
459
	public function isLocal() {
460
		// the common implementation returns a temporary file by
461
		// default, which is not local
462
		return false;
463
	}
464
465
	/**
466
	 * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
467
	 *
468
	 * @param string $class
469
	 * @return bool
470
	 */
471
	public function instanceOfStorage($class) {
472
		if (\ltrim($class, '\\') === 'OC\Files\Storage\Shared') {
473
			// FIXME Temporary fix to keep existing checks working
474
			$class = '\OCA\Files_Sharing\SharedStorage';
475
		}
476
		return \is_a($this, $class);
477
	}
478
479
	/**
480
	 * A custom storage implementation can return an url for direct download of a give file.
481
	 *
482
	 * For now the returned array can hold the parameter url - in future more attributes might follow.
483
	 *
484
	 * @param string $path
485
	 * @return array|false
486
	 */
487
	public function getDirectDownload($path) {
488
		return [];
489
	}
490
491
	/**
492
	 * @inheritdoc
493
	 */
494
	public function verifyPath($path, $fileName) {
495
		if (isset($fileName[255])) {
496
			throw new FileNameTooLongException();
497
		}
498
499
		$this->verifyPosixPath($fileName);
500
	}
501
502
	/**
503
	 * @param string $fileName
504
	 * @throws InvalidPathException
505
	 */
506
	protected function verifyPosixPath($fileName) {
507
		$fileName = \trim($fileName);
508
		$this->scanForInvalidCharacters($fileName, "\\/");
509
		$reservedNames = ['*'];
510
		if (\in_array($fileName, $reservedNames)) {
511
			throw new ReservedWordException();
512
		}
513
	}
514
515
	/**
516
	 * @param string $fileName
517
	 * @param string $invalidChars
518
	 * @throws InvalidPathException
519
	 */
520
	private function scanForInvalidCharacters($fileName, $invalidChars) {
521
		foreach (\str_split($invalidChars) as $char) {
522
			if (\strpos($fileName, $char) !== false) {
523
				throw new InvalidCharacterInPathException();
524
			}
525
		}
526
527
		$sanitizedFileName = \filter_var($fileName, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
528
		if ($sanitizedFileName !== $fileName) {
529
			throw new InvalidCharacterInPathException();
530
		}
531
	}
532
533
	/**
534
	 * @param array $options
535
	 */
536
	public function setMountOptions(array $options) {
537
		$this->mountOptions = $options;
538
	}
539
540
	/**
541
	 * @param string $name
542
	 * @param mixed $default
543
	 * @return mixed
544
	 */
545
	public function getMountOption($name, $default = null) {
546
		return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default;
547
	}
548
549
	/**
550
	 * @param \OCP\Files\Storage $sourceStorage
551
	 * @param string $sourceInternalPath
552
	 * @param string $targetInternalPath
553
	 * @param bool $preserveMtime
554
	 * @return bool
555
	 */
556
	public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
557
		if ($sourceStorage === $this) {
558
			return $this->copy($sourceInternalPath, $targetInternalPath);
559
		}
560
561
		if ($sourceStorage->is_dir($sourceInternalPath)) {
562
			$dh = $sourceStorage->opendir($sourceInternalPath);
563
			$result = $this->mkdir($targetInternalPath);
564 View Code Duplication
			if (\is_resource($dh)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
565
				while ($result and ($file = \readdir($dh)) !== false) {
566
					if (!Filesystem::isIgnoredDir($file) && !Filesystem::isForbiddenFileOrDir($file)) {
567
						$result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
568
					}
569
				}
570
			}
571
		} else {
572
			$source = $sourceStorage->fopen($sourceInternalPath, 'r');
573
			// TODO: call fopen in a way that we execute again all storage wrappers
574
			// to avoid that we bypass storage wrappers which perform important actions
575
			// for this operation. Same is true for all other operations which
576
			// are not the same as the original one.Once this is fixed we also
577
			// need to adjust the encryption wrapper.
578
			$target = $this->fopen($targetInternalPath, 'w');
579
			list(, $result) = \OC_Helper::streamCopy($source, $target);
0 ignored issues
show
Security Bug introduced by
It seems like $source can also be of type false; however, OC_Helper::streamCopy() does only seem to accept resource, did you maybe forget to handle an error condition?
Loading history...
Security Bug introduced by
It seems like $target can also be of type false; however, OC_Helper::streamCopy() does only seem to accept resource, did you maybe forget to handle an error condition?
Loading history...
580
			if ($result and $preserveMtime) {
581
				$this->touch($targetInternalPath, $sourceStorage->filemtime($sourceInternalPath));
0 ignored issues
show
Security Bug introduced by
It seems like $sourceStorage->filemtime($sourceInternalPath) targeting OCP\Files\Storage::filemtime() can also be of type false; however, OCP\Files\Storage::touch() does only seem to accept integer|null, did you maybe forget to handle an error condition?
Loading history...
582
			}
583
			\fclose($source);
584
			\fclose($target);
585
586
			if (!$result) {
587
				// delete partially written target file
588
				$this->unlink($targetInternalPath);
589
				// delete cache entry that was created by fopen
590
				$this->getCache()->remove($targetInternalPath);
591
			}
592
		}
593
		return (bool)$result;
594
	}
595
596
	/**
597
	 * @param \OCP\Files\Storage $sourceStorage
598
	 * @param string $sourceInternalPath
599
	 * @param string $targetInternalPath
600
	 * @return bool
601
	 */
602
	public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
603
		if ($sourceStorage === $this) {
604
			return $this->rename($sourceInternalPath, $targetInternalPath);
605
		}
606
607
		if (!$sourceStorage->isDeletable($sourceInternalPath)) {
608
			return false;
609
		}
610
611
		$result = $this->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true);
612
		if ($result) {
613
			if ($sourceStorage->is_dir($sourceInternalPath)) {
614
				$result &= $sourceStorage->rmdir($sourceInternalPath);
615
			} else {
616
				$result &= $sourceStorage->unlink($sourceInternalPath);
617
			}
618
		}
619
		return $result;
620
	}
621
622
	/**
623
	 * @inheritdoc
624
	 */
625
	public function getMetaData($path) {
626
		$permissions = $this->getPermissions($path);
627
		if (!$permissions & Constants::PERMISSION_READ) {
628
			//can't read, nothing we can do
629
			return null;
630
		}
631
632
		$data = [];
633
		$data['mimetype'] = $this->getMimeType($path);
634
		$data['mtime'] = $this->filemtime($path);
635
		if ($data['mtime'] === false) {
636
			$data['mtime'] = \time();
637
		}
638
		if ($data['mimetype'] == 'httpd/unix-directory') {
639
			$data['size'] = -1; //unknown
640
		} else {
641
			$data['size'] = $this->filesize($path);
642
		}
643
		$data['etag'] = $this->getETag($path);
644
		$data['storage_mtime'] = $data['mtime'];
645
		$data['permissions'] = $permissions;
646
647
		return $data;
648
	}
649
650
	/**
651
	 * @param string $path
652
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
653
	 * @param \OCP\Lock\ILockingProvider $provider
654
	 * @throws \OCP\Lock\LockedException
655
	 */
656
	public function acquireLock($path, $type, ILockingProvider $provider) {
657
		$provider->acquireLock('files/' . \md5($this->getId() . '::' . \trim($path, '/')), $type);
658
	}
659
660
	/**
661
	 * @param string $path
662
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
663
	 * @param \OCP\Lock\ILockingProvider $provider
664
	 */
665
	public function releaseLock($path, $type, ILockingProvider $provider) {
666
		$provider->releaseLock('files/' . \md5($this->getId() . '::' . \trim($path, '/')), $type);
667
	}
668
669
	/**
670
	 * @param string $path
671
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
672
	 * @param \OCP\Lock\ILockingProvider $provider
673
	 * @throws \OCP\Lock\LockedException
674
	 */
675
	public function changeLock($path, $type, ILockingProvider $provider) {
676
		$provider->changeLock('files/' . \md5($this->getId() . '::' . \trim($path, '/')), $type);
677
	}
678
679
	/**
680
	 * @return array [ available, last_checked ]
681
	 */
682
	public function getAvailability() {
683
		return $this->getStorageCache()->getAvailability();
684
	}
685
686
	/**
687
	 * @param bool $isAvailable
688
	 */
689
	public function setAvailability($isAvailable) {
690
		$this->getStorageCache()->setAvailability($isAvailable);
691
	}
692
	public function getVersions($internalPath) {
693
		// KISS implementation
694
		if (!\OC_App::isEnabled('files_versions')) {
695
			return [];
696
		}
697
		list($uid, $filename) =  $this->convertInternalPathToGlobalPath($internalPath);
698
699
		return \array_map(function ($version) use ($internalPath) {
700
			$version['mimetype'] = $this->getMimeType($internalPath);
701
			return $version;
702
		}, \array_values(
703
			\OCA\Files_Versions\Storage::getVersions($uid, $filename)));
704
	}
705
706
	/**
707
	 * @param $internalPath
708
	 * @return array
709
	 */
710
	protected function convertInternalPathToGlobalPath($internalPath) {
711
		$mounts = \OC::$server->getMountManager()->findByStorageId($this->getId());
712
		$mount = \end($mounts);
713
		$p = $mount->getMountPoint() . $internalPath;
714
		$p = \explode('/', \ltrim($p, '/'));
715
		\array_shift($p);
716
		\array_shift($p);
717
		$p = \implode('/', $p);
718
		$o = \explode('/', $mount->getMountPoint());
719
		return [$o[1], $p];
720
	}
721
722
	public function getVersion($internalPath, $versionId) {
723
		$versions = $this->getVersions($internalPath);
724
		$versions = \array_filter($versions, function ($version) use ($versionId) {
725
			return $version['version'] === $versionId;
726
		});
727
		return \array_shift($versions);
728
	}
729
730
	public function getContentOfVersion($internalPath, $versionId) {
731
		$v = $this->getVersion($internalPath, $versionId);
732
		return \OCA\Files_Versions\Storage::getContentOfVersion($v['owner'], $v['storage_location']);
733
	}
734
735
	public function restoreVersion($internalPath, $versionId) {
736
		// KISS implementation
737
		if (!\OC_App::isEnabled('files_versions')) {
738
			return false;
739
		}
740
		$v = $this->getVersion($internalPath, $versionId);
741
		return \OCA\Files_Versions\Storage::restoreVersion($v['owner'], $v['path'], $v['storage_location'], $versionId);
742
	}
743
744
	public function saveVersion($internalPath) {
745
		// returning false here will trigger the fallback implementation
746
		return false;
747
	}
748
}
749