Passed
Push — master ( a44671...ccc348 )
by Robin
13:31 queued 19s
created
lib/private/legacy/OC_Helper.php 1 patch
Indentation   +569 added lines, -569 removed lines patch added patch discarded remove patch
@@ -52,573 +52,573 @@
 block discarded – undo
52 52
  * Collection of useful functions
53 53
  */
54 54
 class OC_Helper {
55
-	private static $templateManager;
56
-
57
-	/**
58
-	 * Make a human file size
59
-	 * @param int $bytes file size in bytes
60
-	 * @return string a human readable file size
61
-	 *
62
-	 * Makes 2048 to 2 kB.
63
-	 */
64
-	public static function humanFileSize($bytes) {
65
-		if ($bytes < 0) {
66
-			return "?";
67
-		}
68
-		if ($bytes < 1024) {
69
-			return "$bytes B";
70
-		}
71
-		$bytes = round($bytes / 1024, 0);
72
-		if ($bytes < 1024) {
73
-			return "$bytes KB";
74
-		}
75
-		$bytes = round($bytes / 1024, 1);
76
-		if ($bytes < 1024) {
77
-			return "$bytes MB";
78
-		}
79
-		$bytes = round($bytes / 1024, 1);
80
-		if ($bytes < 1024) {
81
-			return "$bytes GB";
82
-		}
83
-		$bytes = round($bytes / 1024, 1);
84
-		if ($bytes < 1024) {
85
-			return "$bytes TB";
86
-		}
87
-
88
-		$bytes = round($bytes / 1024, 1);
89
-		return "$bytes PB";
90
-	}
91
-
92
-	/**
93
-	 * Make a computer file size
94
-	 * @param string $str file size in human readable format
95
-	 * @return float|bool a file size in bytes
96
-	 *
97
-	 * Makes 2kB to 2048.
98
-	 *
99
-	 * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418
100
-	 */
101
-	public static function computerFileSize($str) {
102
-		$str = strtolower($str);
103
-		if (is_numeric($str)) {
104
-			return (float)$str;
105
-		}
106
-
107
-		$bytes_array = [
108
-			'b' => 1,
109
-			'k' => 1024,
110
-			'kb' => 1024,
111
-			'mb' => 1024 * 1024,
112
-			'm' => 1024 * 1024,
113
-			'gb' => 1024 * 1024 * 1024,
114
-			'g' => 1024 * 1024 * 1024,
115
-			'tb' => 1024 * 1024 * 1024 * 1024,
116
-			't' => 1024 * 1024 * 1024 * 1024,
117
-			'pb' => 1024 * 1024 * 1024 * 1024 * 1024,
118
-			'p' => 1024 * 1024 * 1024 * 1024 * 1024,
119
-		];
120
-
121
-		$bytes = (float)$str;
122
-
123
-		if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
124
-			$bytes *= $bytes_array[$matches[1]];
125
-		} else {
126
-			return false;
127
-		}
128
-
129
-		$bytes = round($bytes);
130
-
131
-		return $bytes;
132
-	}
133
-
134
-	/**
135
-	 * Recursive copying of folders
136
-	 * @param string $src source folder
137
-	 * @param string $dest target folder
138
-	 *
139
-	 */
140
-	public static function copyr($src, $dest) {
141
-		if (is_dir($src)) {
142
-			if (!is_dir($dest)) {
143
-				mkdir($dest);
144
-			}
145
-			$files = scandir($src);
146
-			foreach ($files as $file) {
147
-				if ($file != "." && $file != "..") {
148
-					self::copyr("$src/$file", "$dest/$file");
149
-				}
150
-			}
151
-		} elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) {
152
-			copy($src, $dest);
153
-		}
154
-	}
155
-
156
-	/**
157
-	 * Recursive deletion of folders
158
-	 * @param string $dir path to the folder
159
-	 * @param bool $deleteSelf if set to false only the content of the folder will be deleted
160
-	 * @return bool
161
-	 */
162
-	public static function rmdirr($dir, $deleteSelf = true) {
163
-		if (is_dir($dir)) {
164
-			$files = new RecursiveIteratorIterator(
165
-				new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
166
-				RecursiveIteratorIterator::CHILD_FIRST
167
-			);
168
-
169
-			foreach ($files as $fileInfo) {
170
-				/** @var SplFileInfo $fileInfo */
171
-				if ($fileInfo->isLink()) {
172
-					unlink($fileInfo->getPathname());
173
-				} elseif ($fileInfo->isDir()) {
174
-					rmdir($fileInfo->getRealPath());
175
-				} else {
176
-					unlink($fileInfo->getRealPath());
177
-				}
178
-			}
179
-			if ($deleteSelf) {
180
-				rmdir($dir);
181
-			}
182
-		} elseif (file_exists($dir)) {
183
-			if ($deleteSelf) {
184
-				unlink($dir);
185
-			}
186
-		}
187
-		if (!$deleteSelf) {
188
-			return true;
189
-		}
190
-
191
-		return !file_exists($dir);
192
-	}
193
-
194
-	/**
195
-	 * @deprecated 18.0.0
196
-	 * @return \OC\Files\Type\TemplateManager
197
-	 */
198
-	public static function getFileTemplateManager() {
199
-		if (!self::$templateManager) {
200
-			self::$templateManager = new \OC\Files\Type\TemplateManager();
201
-		}
202
-		return self::$templateManager;
203
-	}
204
-
205
-	/**
206
-	 * detect if a given program is found in the search PATH
207
-	 *
208
-	 * @param string $name
209
-	 * @param bool $path
210
-	 * @internal param string $program name
211
-	 * @internal param string $optional search path, defaults to $PATH
212
-	 * @return bool    true if executable program found in path
213
-	 */
214
-	public static function canExecute($name, $path = false) {
215
-		// path defaults to PATH from environment if not set
216
-		if ($path === false) {
217
-			$path = getenv("PATH");
218
-		}
219
-		// we look for an executable file of that name
220
-		$exts = [""];
221
-		$check_fn = "is_executable";
222
-		// Default check will be done with $path directories :
223
-		$dirs = explode(PATH_SEPARATOR, $path);
224
-		// WARNING : We have to check if open_basedir is enabled :
225
-		$obd = OC::$server->get(IniGetWrapper::class)->getString('open_basedir');
226
-		if ($obd != "none") {
227
-			$obd_values = explode(PATH_SEPARATOR, $obd);
228
-			if (count($obd_values) > 0 and $obd_values[0]) {
229
-				// open_basedir is in effect !
230
-				// We need to check if the program is in one of these dirs :
231
-				$dirs = $obd_values;
232
-			}
233
-		}
234
-		foreach ($dirs as $dir) {
235
-			foreach ($exts as $ext) {
236
-				if ($check_fn("$dir/$name" . $ext)) {
237
-					return true;
238
-				}
239
-			}
240
-		}
241
-		return false;
242
-	}
243
-
244
-	/**
245
-	 * copy the contents of one stream to another
246
-	 *
247
-	 * @param resource $source
248
-	 * @param resource $target
249
-	 * @return array the number of bytes copied and result
250
-	 */
251
-	public static function streamCopy($source, $target) {
252
-		if (!$source or !$target) {
253
-			return [0, false];
254
-		}
255
-		$bufSize = 8192;
256
-		$result = true;
257
-		$count = 0;
258
-		while (!feof($source)) {
259
-			$buf = fread($source, $bufSize);
260
-			$bytesWritten = fwrite($target, $buf);
261
-			if ($bytesWritten !== false) {
262
-				$count += $bytesWritten;
263
-			}
264
-			// note: strlen is expensive so only use it when necessary,
265
-			// on the last block
266
-			if ($bytesWritten === false
267
-				|| ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
268
-			) {
269
-				// write error, could be disk full ?
270
-				$result = false;
271
-				break;
272
-			}
273
-		}
274
-		return [$count, $result];
275
-	}
276
-
277
-	/**
278
-	 * Adds a suffix to the name in case the file exists
279
-	 *
280
-	 * @param string $path
281
-	 * @param string $filename
282
-	 * @return string
283
-	 */
284
-	public static function buildNotExistingFileName($path, $filename) {
285
-		$view = \OC\Files\Filesystem::getView();
286
-		return self::buildNotExistingFileNameForView($path, $filename, $view);
287
-	}
288
-
289
-	/**
290
-	 * Adds a suffix to the name in case the file exists
291
-	 *
292
-	 * @param string $path
293
-	 * @param string $filename
294
-	 * @return string
295
-	 */
296
-	public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
297
-		if ($path === '/') {
298
-			$path = '';
299
-		}
300
-		if ($pos = strrpos($filename, '.')) {
301
-			$name = substr($filename, 0, $pos);
302
-			$ext = substr($filename, $pos);
303
-		} else {
304
-			$name = $filename;
305
-			$ext = '';
306
-		}
307
-
308
-		$newpath = $path . '/' . $filename;
309
-		if ($view->file_exists($newpath)) {
310
-			if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
311
-				//Replace the last "(number)" with "(number+1)"
312
-				$last_match = count($matches[0]) - 1;
313
-				$counter = $matches[1][$last_match][0] + 1;
314
-				$offset = $matches[0][$last_match][1];
315
-				$match_length = strlen($matches[0][$last_match][0]);
316
-			} else {
317
-				$counter = 2;
318
-				$match_length = 0;
319
-				$offset = false;
320
-			}
321
-			do {
322
-				if ($offset) {
323
-					//Replace the last "(number)" with "(number+1)"
324
-					$newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
325
-				} else {
326
-					$newname = $name . ' (' . $counter . ')';
327
-				}
328
-				$newpath = $path . '/' . $newname . $ext;
329
-				$counter++;
330
-			} while ($view->file_exists($newpath));
331
-		}
332
-
333
-		return $newpath;
334
-	}
335
-
336
-	/**
337
-	 * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
338
-	 *
339
-	 * @param array $input The array to work on
340
-	 * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
341
-	 * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
342
-	 * @return array
343
-	 *
344
-	 * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
345
-	 * based on https://www.php.net/manual/en/function.array-change-key-case.php#107715
346
-	 *
347
-	 */
348
-	public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
349
-		$case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
350
-		$ret = [];
351
-		foreach ($input as $k => $v) {
352
-			$ret[mb_convert_case($k, $case, $encoding)] = $v;
353
-		}
354
-		return $ret;
355
-	}
356
-
357
-	/**
358
-	 * performs a search in a nested array
359
-	 * @param array $haystack the array to be searched
360
-	 * @param string $needle the search string
361
-	 * @param mixed $index optional, only search this key name
362
-	 * @return mixed the key of the matching field, otherwise false
363
-	 *
364
-	 * performs a search in a nested array
365
-	 *
366
-	 * taken from https://www.php.net/manual/en/function.array-search.php#97645
367
-	 */
368
-	public static function recursiveArraySearch($haystack, $needle, $index = null) {
369
-		$aIt = new RecursiveArrayIterator($haystack);
370
-		$it = new RecursiveIteratorIterator($aIt);
371
-
372
-		while ($it->valid()) {
373
-			if (((isset($index) and ($it->key() == $index)) or !isset($index)) and ($it->current() == $needle)) {
374
-				return $aIt->key();
375
-			}
376
-
377
-			$it->next();
378
-		}
379
-
380
-		return false;
381
-	}
382
-
383
-	/**
384
-	 * calculates the maximum upload size respecting system settings, free space and user quota
385
-	 *
386
-	 * @param string $dir the current folder where the user currently operates
387
-	 * @param int $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
388
-	 * @return int number of bytes representing
389
-	 */
390
-	public static function maxUploadFilesize($dir, $freeSpace = null) {
391
-		if (is_null($freeSpace) || $freeSpace < 0) {
392
-			$freeSpace = self::freeSpace($dir);
393
-		}
394
-		return min($freeSpace, self::uploadLimit());
395
-	}
396
-
397
-	/**
398
-	 * Calculate free space left within user quota
399
-	 *
400
-	 * @param string $dir the current folder where the user currently operates
401
-	 * @return int number of bytes representing
402
-	 */
403
-	public static function freeSpace($dir) {
404
-		$freeSpace = \OC\Files\Filesystem::free_space($dir);
405
-		if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
406
-			$freeSpace = max($freeSpace, 0);
407
-			return $freeSpace;
408
-		} else {
409
-			return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
410
-		}
411
-	}
412
-
413
-	/**
414
-	 * Calculate PHP upload limit
415
-	 *
416
-	 * @return int PHP upload file size limit
417
-	 */
418
-	public static function uploadLimit() {
419
-		$ini = \OC::$server->get(IniGetWrapper::class);
420
-		$upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize'));
421
-		$post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size'));
422
-		if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) {
423
-			return INF;
424
-		} elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) {
425
-			return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
426
-		} else {
427
-			return min($upload_max_filesize, $post_max_size);
428
-		}
429
-	}
430
-
431
-	/**
432
-	 * Checks if a function is available
433
-	 *
434
-	 * @param string $function_name
435
-	 * @return bool
436
-	 */
437
-	public static function is_function_enabled($function_name) {
438
-		if (!function_exists($function_name)) {
439
-			return false;
440
-		}
441
-		$ini = \OC::$server->get(IniGetWrapper::class);
442
-		$disabled = explode(',', $ini->get('disable_functions') ?: '');
443
-		$disabled = array_map('trim', $disabled);
444
-		if (in_array($function_name, $disabled)) {
445
-			return false;
446
-		}
447
-		$disabled = explode(',', $ini->get('suhosin.executor.func.blacklist') ?: '');
448
-		$disabled = array_map('trim', $disabled);
449
-		if (in_array($function_name, $disabled)) {
450
-			return false;
451
-		}
452
-		return true;
453
-	}
454
-
455
-	/**
456
-	 * Try to find a program
457
-	 *
458
-	 * @param string $program
459
-	 * @return null|string
460
-	 */
461
-	public static function findBinaryPath($program) {
462
-		$memcache = \OC::$server->getMemCacheFactory()->createDistributed('findBinaryPath');
463
-		if ($memcache->hasKey($program)) {
464
-			return $memcache->get($program);
465
-		}
466
-		$result = null;
467
-		if (self::is_function_enabled('exec')) {
468
-			$exeSniffer = new ExecutableFinder();
469
-			// Returns null if nothing is found
470
-			$result = $exeSniffer->find($program, null, ['/usr/local/sbin', '/usr/local/bin', '/usr/sbin', '/usr/bin', '/sbin', '/bin', '/opt/bin']);
471
-		}
472
-		// store the value for 5 minutes
473
-		$memcache->set($program, $result, 300);
474
-		return $result;
475
-	}
476
-
477
-	/**
478
-	 * Calculate the disc space for the given path
479
-	 *
480
-	 * BEWARE: this requires that Util::setupFS() was called
481
-	 * already !
482
-	 *
483
-	 * @param string $path
484
-	 * @param \OCP\Files\FileInfo $rootInfo (optional)
485
-	 * @return array
486
-	 * @throws \OCP\Files\NotFoundException
487
-	 */
488
-	public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true) {
489
-		// return storage info without adding mount points
490
-		$includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
491
-
492
-		if (!$rootInfo) {
493
-			$rootInfo = \OC\Files\Filesystem::getFileInfo($path, $includeExtStorage ? 'ext' : false);
494
-		}
495
-		if (!$rootInfo instanceof \OCP\Files\FileInfo) {
496
-			throw new \OCP\Files\NotFoundException();
497
-		}
498
-		$used = $rootInfo->getSize($includeMountPoints);
499
-		if ($used < 0) {
500
-			$used = 0;
501
-		}
502
-		$quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
503
-		$mount = $rootInfo->getMountPoint();
504
-		$storage = $mount->getStorage();
505
-		$sourceStorage = $storage;
506
-		if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
507
-			$includeExtStorage = false;
508
-			$sourceStorage = $storage->getSourceStorage();
509
-			$internalPath = $storage->getUnjailedPath($rootInfo->getInternalPath());
510
-		} else {
511
-			$internalPath = $rootInfo->getInternalPath();
512
-		}
513
-		if ($includeExtStorage) {
514
-			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
515
-				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
516
-			) {
517
-				/** @var \OC\Files\Storage\Home $storage */
518
-				$user = $storage->getUser();
519
-			} else {
520
-				$user = \OC::$server->getUserSession()->getUser();
521
-			}
522
-			$quota = OC_Util::getUserQuota($user);
523
-			if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
524
-				// always get free space / total space from root + mount points
525
-				return self::getGlobalStorageInfo($quota, $user, $mount);
526
-			}
527
-		}
528
-
529
-		// TODO: need a better way to get total space from storage
530
-		if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
531
-			/** @var \OC\Files\Storage\Wrapper\Quota $storage */
532
-			$quota = $sourceStorage->getQuota();
533
-		}
534
-		$free = $sourceStorage->free_space($internalPath);
535
-		if ($free >= 0) {
536
-			$total = $free + $used;
537
-		} else {
538
-			$total = $free; //either unknown or unlimited
539
-		}
540
-		if ($total > 0) {
541
-			if ($quota > 0 && $total > $quota) {
542
-				$total = $quota;
543
-			}
544
-			// prevent division by zero or error codes (negative values)
545
-			$relative = round(($used / $total) * 10000) / 100;
546
-		} else {
547
-			$relative = 0;
548
-		}
549
-
550
-		$ownerId = $storage->getOwner($path);
551
-		$ownerDisplayName = '';
552
-		$owner = \OC::$server->getUserManager()->get($ownerId);
553
-		if ($owner) {
554
-			$ownerDisplayName = $owner->getDisplayName();
555
-		}
556
-		if (substr_count($mount->getMountPoint(), '/') < 3) {
557
-			$mountPoint = '';
558
-		} else {
559
-			[,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
560
-		}
561
-
562
-		return [
563
-			'free' => $free,
564
-			'used' => $used,
565
-			'quota' => $quota,
566
-			'total' => $total,
567
-			'relative' => $relative,
568
-			'owner' => $ownerId,
569
-			'ownerDisplayName' => $ownerDisplayName,
570
-			'mountType' => $mount->getMountType(),
571
-			'mountPoint' => trim($mountPoint, '/'),
572
-		];
573
-	}
574
-
575
-	/**
576
-	 * Get storage info including all mount points and quota
577
-	 */
578
-	private static function getGlobalStorageInfo(int $quota, IUser $user, IMountPoint $mount): array {
579
-		$rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
580
-		$used = $rootInfo['size'];
581
-		if ($used < 0) {
582
-			$used = 0;
583
-		}
584
-
585
-		$total = $quota;
586
-		$free = $quota - $used;
587
-
588
-		if ($total > 0) {
589
-			if ($quota > 0 && $total > $quota) {
590
-				$total = $quota;
591
-			}
592
-			// prevent division by zero or error codes (negative values)
593
-			$relative = round(($used / $total) * 10000) / 100;
594
-		} else {
595
-			$relative = 0;
596
-		}
597
-
598
-		if (substr_count($mount->getMountPoint(), '/') < 3) {
599
-			$mountPoint = '';
600
-		} else {
601
-			[,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
602
-		}
603
-
604
-		return [
605
-			'free' => $free,
606
-			'used' => $used,
607
-			'total' => $total,
608
-			'relative' => $relative,
609
-			'quota' => $quota,
610
-			'owner' => $user->getUID(),
611
-			'ownerDisplayName' => $user->getDisplayName(),
612
-			'mountType' => $mount->getMountType(),
613
-			'mountPoint' => trim($mountPoint, '/'),
614
-		];
615
-	}
616
-
617
-	/**
618
-	 * Returns whether the config file is set manually to read-only
619
-	 * @return bool
620
-	 */
621
-	public static function isReadOnlyConfigEnabled() {
622
-		return \OC::$server->getConfig()->getSystemValue('config_is_read_only', false);
623
-	}
55
+    private static $templateManager;
56
+
57
+    /**
58
+     * Make a human file size
59
+     * @param int $bytes file size in bytes
60
+     * @return string a human readable file size
61
+     *
62
+     * Makes 2048 to 2 kB.
63
+     */
64
+    public static function humanFileSize($bytes) {
65
+        if ($bytes < 0) {
66
+            return "?";
67
+        }
68
+        if ($bytes < 1024) {
69
+            return "$bytes B";
70
+        }
71
+        $bytes = round($bytes / 1024, 0);
72
+        if ($bytes < 1024) {
73
+            return "$bytes KB";
74
+        }
75
+        $bytes = round($bytes / 1024, 1);
76
+        if ($bytes < 1024) {
77
+            return "$bytes MB";
78
+        }
79
+        $bytes = round($bytes / 1024, 1);
80
+        if ($bytes < 1024) {
81
+            return "$bytes GB";
82
+        }
83
+        $bytes = round($bytes / 1024, 1);
84
+        if ($bytes < 1024) {
85
+            return "$bytes TB";
86
+        }
87
+
88
+        $bytes = round($bytes / 1024, 1);
89
+        return "$bytes PB";
90
+    }
91
+
92
+    /**
93
+     * Make a computer file size
94
+     * @param string $str file size in human readable format
95
+     * @return float|bool a file size in bytes
96
+     *
97
+     * Makes 2kB to 2048.
98
+     *
99
+     * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418
100
+     */
101
+    public static function computerFileSize($str) {
102
+        $str = strtolower($str);
103
+        if (is_numeric($str)) {
104
+            return (float)$str;
105
+        }
106
+
107
+        $bytes_array = [
108
+            'b' => 1,
109
+            'k' => 1024,
110
+            'kb' => 1024,
111
+            'mb' => 1024 * 1024,
112
+            'm' => 1024 * 1024,
113
+            'gb' => 1024 * 1024 * 1024,
114
+            'g' => 1024 * 1024 * 1024,
115
+            'tb' => 1024 * 1024 * 1024 * 1024,
116
+            't' => 1024 * 1024 * 1024 * 1024,
117
+            'pb' => 1024 * 1024 * 1024 * 1024 * 1024,
118
+            'p' => 1024 * 1024 * 1024 * 1024 * 1024,
119
+        ];
120
+
121
+        $bytes = (float)$str;
122
+
123
+        if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
124
+            $bytes *= $bytes_array[$matches[1]];
125
+        } else {
126
+            return false;
127
+        }
128
+
129
+        $bytes = round($bytes);
130
+
131
+        return $bytes;
132
+    }
133
+
134
+    /**
135
+     * Recursive copying of folders
136
+     * @param string $src source folder
137
+     * @param string $dest target folder
138
+     *
139
+     */
140
+    public static function copyr($src, $dest) {
141
+        if (is_dir($src)) {
142
+            if (!is_dir($dest)) {
143
+                mkdir($dest);
144
+            }
145
+            $files = scandir($src);
146
+            foreach ($files as $file) {
147
+                if ($file != "." && $file != "..") {
148
+                    self::copyr("$src/$file", "$dest/$file");
149
+                }
150
+            }
151
+        } elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) {
152
+            copy($src, $dest);
153
+        }
154
+    }
155
+
156
+    /**
157
+     * Recursive deletion of folders
158
+     * @param string $dir path to the folder
159
+     * @param bool $deleteSelf if set to false only the content of the folder will be deleted
160
+     * @return bool
161
+     */
162
+    public static function rmdirr($dir, $deleteSelf = true) {
163
+        if (is_dir($dir)) {
164
+            $files = new RecursiveIteratorIterator(
165
+                new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
166
+                RecursiveIteratorIterator::CHILD_FIRST
167
+            );
168
+
169
+            foreach ($files as $fileInfo) {
170
+                /** @var SplFileInfo $fileInfo */
171
+                if ($fileInfo->isLink()) {
172
+                    unlink($fileInfo->getPathname());
173
+                } elseif ($fileInfo->isDir()) {
174
+                    rmdir($fileInfo->getRealPath());
175
+                } else {
176
+                    unlink($fileInfo->getRealPath());
177
+                }
178
+            }
179
+            if ($deleteSelf) {
180
+                rmdir($dir);
181
+            }
182
+        } elseif (file_exists($dir)) {
183
+            if ($deleteSelf) {
184
+                unlink($dir);
185
+            }
186
+        }
187
+        if (!$deleteSelf) {
188
+            return true;
189
+        }
190
+
191
+        return !file_exists($dir);
192
+    }
193
+
194
+    /**
195
+     * @deprecated 18.0.0
196
+     * @return \OC\Files\Type\TemplateManager
197
+     */
198
+    public static function getFileTemplateManager() {
199
+        if (!self::$templateManager) {
200
+            self::$templateManager = new \OC\Files\Type\TemplateManager();
201
+        }
202
+        return self::$templateManager;
203
+    }
204
+
205
+    /**
206
+     * detect if a given program is found in the search PATH
207
+     *
208
+     * @param string $name
209
+     * @param bool $path
210
+     * @internal param string $program name
211
+     * @internal param string $optional search path, defaults to $PATH
212
+     * @return bool    true if executable program found in path
213
+     */
214
+    public static function canExecute($name, $path = false) {
215
+        // path defaults to PATH from environment if not set
216
+        if ($path === false) {
217
+            $path = getenv("PATH");
218
+        }
219
+        // we look for an executable file of that name
220
+        $exts = [""];
221
+        $check_fn = "is_executable";
222
+        // Default check will be done with $path directories :
223
+        $dirs = explode(PATH_SEPARATOR, $path);
224
+        // WARNING : We have to check if open_basedir is enabled :
225
+        $obd = OC::$server->get(IniGetWrapper::class)->getString('open_basedir');
226
+        if ($obd != "none") {
227
+            $obd_values = explode(PATH_SEPARATOR, $obd);
228
+            if (count($obd_values) > 0 and $obd_values[0]) {
229
+                // open_basedir is in effect !
230
+                // We need to check if the program is in one of these dirs :
231
+                $dirs = $obd_values;
232
+            }
233
+        }
234
+        foreach ($dirs as $dir) {
235
+            foreach ($exts as $ext) {
236
+                if ($check_fn("$dir/$name" . $ext)) {
237
+                    return true;
238
+                }
239
+            }
240
+        }
241
+        return false;
242
+    }
243
+
244
+    /**
245
+     * copy the contents of one stream to another
246
+     *
247
+     * @param resource $source
248
+     * @param resource $target
249
+     * @return array the number of bytes copied and result
250
+     */
251
+    public static function streamCopy($source, $target) {
252
+        if (!$source or !$target) {
253
+            return [0, false];
254
+        }
255
+        $bufSize = 8192;
256
+        $result = true;
257
+        $count = 0;
258
+        while (!feof($source)) {
259
+            $buf = fread($source, $bufSize);
260
+            $bytesWritten = fwrite($target, $buf);
261
+            if ($bytesWritten !== false) {
262
+                $count += $bytesWritten;
263
+            }
264
+            // note: strlen is expensive so only use it when necessary,
265
+            // on the last block
266
+            if ($bytesWritten === false
267
+                || ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
268
+            ) {
269
+                // write error, could be disk full ?
270
+                $result = false;
271
+                break;
272
+            }
273
+        }
274
+        return [$count, $result];
275
+    }
276
+
277
+    /**
278
+     * Adds a suffix to the name in case the file exists
279
+     *
280
+     * @param string $path
281
+     * @param string $filename
282
+     * @return string
283
+     */
284
+    public static function buildNotExistingFileName($path, $filename) {
285
+        $view = \OC\Files\Filesystem::getView();
286
+        return self::buildNotExistingFileNameForView($path, $filename, $view);
287
+    }
288
+
289
+    /**
290
+     * Adds a suffix to the name in case the file exists
291
+     *
292
+     * @param string $path
293
+     * @param string $filename
294
+     * @return string
295
+     */
296
+    public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
297
+        if ($path === '/') {
298
+            $path = '';
299
+        }
300
+        if ($pos = strrpos($filename, '.')) {
301
+            $name = substr($filename, 0, $pos);
302
+            $ext = substr($filename, $pos);
303
+        } else {
304
+            $name = $filename;
305
+            $ext = '';
306
+        }
307
+
308
+        $newpath = $path . '/' . $filename;
309
+        if ($view->file_exists($newpath)) {
310
+            if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
311
+                //Replace the last "(number)" with "(number+1)"
312
+                $last_match = count($matches[0]) - 1;
313
+                $counter = $matches[1][$last_match][0] + 1;
314
+                $offset = $matches[0][$last_match][1];
315
+                $match_length = strlen($matches[0][$last_match][0]);
316
+            } else {
317
+                $counter = 2;
318
+                $match_length = 0;
319
+                $offset = false;
320
+            }
321
+            do {
322
+                if ($offset) {
323
+                    //Replace the last "(number)" with "(number+1)"
324
+                    $newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
325
+                } else {
326
+                    $newname = $name . ' (' . $counter . ')';
327
+                }
328
+                $newpath = $path . '/' . $newname . $ext;
329
+                $counter++;
330
+            } while ($view->file_exists($newpath));
331
+        }
332
+
333
+        return $newpath;
334
+    }
335
+
336
+    /**
337
+     * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
338
+     *
339
+     * @param array $input The array to work on
340
+     * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
341
+     * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
342
+     * @return array
343
+     *
344
+     * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
345
+     * based on https://www.php.net/manual/en/function.array-change-key-case.php#107715
346
+     *
347
+     */
348
+    public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
349
+        $case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
350
+        $ret = [];
351
+        foreach ($input as $k => $v) {
352
+            $ret[mb_convert_case($k, $case, $encoding)] = $v;
353
+        }
354
+        return $ret;
355
+    }
356
+
357
+    /**
358
+     * performs a search in a nested array
359
+     * @param array $haystack the array to be searched
360
+     * @param string $needle the search string
361
+     * @param mixed $index optional, only search this key name
362
+     * @return mixed the key of the matching field, otherwise false
363
+     *
364
+     * performs a search in a nested array
365
+     *
366
+     * taken from https://www.php.net/manual/en/function.array-search.php#97645
367
+     */
368
+    public static function recursiveArraySearch($haystack, $needle, $index = null) {
369
+        $aIt = new RecursiveArrayIterator($haystack);
370
+        $it = new RecursiveIteratorIterator($aIt);
371
+
372
+        while ($it->valid()) {
373
+            if (((isset($index) and ($it->key() == $index)) or !isset($index)) and ($it->current() == $needle)) {
374
+                return $aIt->key();
375
+            }
376
+
377
+            $it->next();
378
+        }
379
+
380
+        return false;
381
+    }
382
+
383
+    /**
384
+     * calculates the maximum upload size respecting system settings, free space and user quota
385
+     *
386
+     * @param string $dir the current folder where the user currently operates
387
+     * @param int $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
388
+     * @return int number of bytes representing
389
+     */
390
+    public static function maxUploadFilesize($dir, $freeSpace = null) {
391
+        if (is_null($freeSpace) || $freeSpace < 0) {
392
+            $freeSpace = self::freeSpace($dir);
393
+        }
394
+        return min($freeSpace, self::uploadLimit());
395
+    }
396
+
397
+    /**
398
+     * Calculate free space left within user quota
399
+     *
400
+     * @param string $dir the current folder where the user currently operates
401
+     * @return int number of bytes representing
402
+     */
403
+    public static function freeSpace($dir) {
404
+        $freeSpace = \OC\Files\Filesystem::free_space($dir);
405
+        if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
406
+            $freeSpace = max($freeSpace, 0);
407
+            return $freeSpace;
408
+        } else {
409
+            return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
410
+        }
411
+    }
412
+
413
+    /**
414
+     * Calculate PHP upload limit
415
+     *
416
+     * @return int PHP upload file size limit
417
+     */
418
+    public static function uploadLimit() {
419
+        $ini = \OC::$server->get(IniGetWrapper::class);
420
+        $upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize'));
421
+        $post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size'));
422
+        if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) {
423
+            return INF;
424
+        } elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) {
425
+            return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
426
+        } else {
427
+            return min($upload_max_filesize, $post_max_size);
428
+        }
429
+    }
430
+
431
+    /**
432
+     * Checks if a function is available
433
+     *
434
+     * @param string $function_name
435
+     * @return bool
436
+     */
437
+    public static function is_function_enabled($function_name) {
438
+        if (!function_exists($function_name)) {
439
+            return false;
440
+        }
441
+        $ini = \OC::$server->get(IniGetWrapper::class);
442
+        $disabled = explode(',', $ini->get('disable_functions') ?: '');
443
+        $disabled = array_map('trim', $disabled);
444
+        if (in_array($function_name, $disabled)) {
445
+            return false;
446
+        }
447
+        $disabled = explode(',', $ini->get('suhosin.executor.func.blacklist') ?: '');
448
+        $disabled = array_map('trim', $disabled);
449
+        if (in_array($function_name, $disabled)) {
450
+            return false;
451
+        }
452
+        return true;
453
+    }
454
+
455
+    /**
456
+     * Try to find a program
457
+     *
458
+     * @param string $program
459
+     * @return null|string
460
+     */
461
+    public static function findBinaryPath($program) {
462
+        $memcache = \OC::$server->getMemCacheFactory()->createDistributed('findBinaryPath');
463
+        if ($memcache->hasKey($program)) {
464
+            return $memcache->get($program);
465
+        }
466
+        $result = null;
467
+        if (self::is_function_enabled('exec')) {
468
+            $exeSniffer = new ExecutableFinder();
469
+            // Returns null if nothing is found
470
+            $result = $exeSniffer->find($program, null, ['/usr/local/sbin', '/usr/local/bin', '/usr/sbin', '/usr/bin', '/sbin', '/bin', '/opt/bin']);
471
+        }
472
+        // store the value for 5 minutes
473
+        $memcache->set($program, $result, 300);
474
+        return $result;
475
+    }
476
+
477
+    /**
478
+     * Calculate the disc space for the given path
479
+     *
480
+     * BEWARE: this requires that Util::setupFS() was called
481
+     * already !
482
+     *
483
+     * @param string $path
484
+     * @param \OCP\Files\FileInfo $rootInfo (optional)
485
+     * @return array
486
+     * @throws \OCP\Files\NotFoundException
487
+     */
488
+    public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true) {
489
+        // return storage info without adding mount points
490
+        $includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
491
+
492
+        if (!$rootInfo) {
493
+            $rootInfo = \OC\Files\Filesystem::getFileInfo($path, $includeExtStorage ? 'ext' : false);
494
+        }
495
+        if (!$rootInfo instanceof \OCP\Files\FileInfo) {
496
+            throw new \OCP\Files\NotFoundException();
497
+        }
498
+        $used = $rootInfo->getSize($includeMountPoints);
499
+        if ($used < 0) {
500
+            $used = 0;
501
+        }
502
+        $quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
503
+        $mount = $rootInfo->getMountPoint();
504
+        $storage = $mount->getStorage();
505
+        $sourceStorage = $storage;
506
+        if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
507
+            $includeExtStorage = false;
508
+            $sourceStorage = $storage->getSourceStorage();
509
+            $internalPath = $storage->getUnjailedPath($rootInfo->getInternalPath());
510
+        } else {
511
+            $internalPath = $rootInfo->getInternalPath();
512
+        }
513
+        if ($includeExtStorage) {
514
+            if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
515
+                || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
516
+            ) {
517
+                /** @var \OC\Files\Storage\Home $storage */
518
+                $user = $storage->getUser();
519
+            } else {
520
+                $user = \OC::$server->getUserSession()->getUser();
521
+            }
522
+            $quota = OC_Util::getUserQuota($user);
523
+            if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
524
+                // always get free space / total space from root + mount points
525
+                return self::getGlobalStorageInfo($quota, $user, $mount);
526
+            }
527
+        }
528
+
529
+        // TODO: need a better way to get total space from storage
530
+        if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
531
+            /** @var \OC\Files\Storage\Wrapper\Quota $storage */
532
+            $quota = $sourceStorage->getQuota();
533
+        }
534
+        $free = $sourceStorage->free_space($internalPath);
535
+        if ($free >= 0) {
536
+            $total = $free + $used;
537
+        } else {
538
+            $total = $free; //either unknown or unlimited
539
+        }
540
+        if ($total > 0) {
541
+            if ($quota > 0 && $total > $quota) {
542
+                $total = $quota;
543
+            }
544
+            // prevent division by zero or error codes (negative values)
545
+            $relative = round(($used / $total) * 10000) / 100;
546
+        } else {
547
+            $relative = 0;
548
+        }
549
+
550
+        $ownerId = $storage->getOwner($path);
551
+        $ownerDisplayName = '';
552
+        $owner = \OC::$server->getUserManager()->get($ownerId);
553
+        if ($owner) {
554
+            $ownerDisplayName = $owner->getDisplayName();
555
+        }
556
+        if (substr_count($mount->getMountPoint(), '/') < 3) {
557
+            $mountPoint = '';
558
+        } else {
559
+            [,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
560
+        }
561
+
562
+        return [
563
+            'free' => $free,
564
+            'used' => $used,
565
+            'quota' => $quota,
566
+            'total' => $total,
567
+            'relative' => $relative,
568
+            'owner' => $ownerId,
569
+            'ownerDisplayName' => $ownerDisplayName,
570
+            'mountType' => $mount->getMountType(),
571
+            'mountPoint' => trim($mountPoint, '/'),
572
+        ];
573
+    }
574
+
575
+    /**
576
+     * Get storage info including all mount points and quota
577
+     */
578
+    private static function getGlobalStorageInfo(int $quota, IUser $user, IMountPoint $mount): array {
579
+        $rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
580
+        $used = $rootInfo['size'];
581
+        if ($used < 0) {
582
+            $used = 0;
583
+        }
584
+
585
+        $total = $quota;
586
+        $free = $quota - $used;
587
+
588
+        if ($total > 0) {
589
+            if ($quota > 0 && $total > $quota) {
590
+                $total = $quota;
591
+            }
592
+            // prevent division by zero or error codes (negative values)
593
+            $relative = round(($used / $total) * 10000) / 100;
594
+        } else {
595
+            $relative = 0;
596
+        }
597
+
598
+        if (substr_count($mount->getMountPoint(), '/') < 3) {
599
+            $mountPoint = '';
600
+        } else {
601
+            [,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
602
+        }
603
+
604
+        return [
605
+            'free' => $free,
606
+            'used' => $used,
607
+            'total' => $total,
608
+            'relative' => $relative,
609
+            'quota' => $quota,
610
+            'owner' => $user->getUID(),
611
+            'ownerDisplayName' => $user->getDisplayName(),
612
+            'mountType' => $mount->getMountType(),
613
+            'mountPoint' => trim($mountPoint, '/'),
614
+        ];
615
+    }
616
+
617
+    /**
618
+     * Returns whether the config file is set manually to read-only
619
+     * @return bool
620
+     */
621
+    public static function isReadOnlyConfigEnabled() {
622
+        return \OC::$server->getConfig()->getSystemValue('config_is_read_only', false);
623
+    }
624 624
 }
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/Directory.php 1 patch
Indentation   +421 added lines, -421 removed lines patch added patch discarded remove patch
@@ -53,425 +53,425 @@
 block discarded – undo
53 53
 
54 54
 class Directory extends \OCA\DAV\Connector\Sabre\Node implements \Sabre\DAV\ICollection, \Sabre\DAV\IQuota, \Sabre\DAV\IMoveTarget, \Sabre\DAV\ICopyTarget {
55 55
 
56
-	/**
57
-	 * Cached directory content
58
-	 *
59
-	 * @var \OCP\Files\FileInfo[]
60
-	 */
61
-	private $dirContent;
62
-
63
-	/**
64
-	 * Cached quota info
65
-	 *
66
-	 * @var array
67
-	 */
68
-	private $quotaInfo;
69
-
70
-	/**
71
-	 * @var ObjectTree|null
72
-	 */
73
-	private $tree;
74
-
75
-	/**
76
-	 * Sets up the node, expects a full path name
77
-	 *
78
-	 * @param \OC\Files\View $view
79
-	 * @param \OCP\Files\FileInfo $info
80
-	 * @param ObjectTree|null $tree
81
-	 * @param \OCP\Share\IManager $shareManager
82
-	 */
83
-	public function __construct(View $view, FileInfo $info, $tree = null, $shareManager = null) {
84
-		parent::__construct($view, $info, $shareManager);
85
-		$this->tree = $tree;
86
-	}
87
-
88
-	/**
89
-	 * Creates a new file in the directory
90
-	 *
91
-	 * Data will either be supplied as a stream resource, or in certain cases
92
-	 * as a string. Keep in mind that you may have to support either.
93
-	 *
94
-	 * After successful creation of the file, you may choose to return the ETag
95
-	 * of the new file here.
96
-	 *
97
-	 * The returned ETag must be surrounded by double-quotes (The quotes should
98
-	 * be part of the actual string).
99
-	 *
100
-	 * If you cannot accurately determine the ETag, you should not return it.
101
-	 * If you don't store the file exactly as-is (you're transforming it
102
-	 * somehow) you should also not return an ETag.
103
-	 *
104
-	 * This means that if a subsequent GET to this new file does not exactly
105
-	 * return the same contents of what was submitted here, you are strongly
106
-	 * recommended to omit the ETag.
107
-	 *
108
-	 * @param string $name Name of the file
109
-	 * @param resource|string $data Initial payload
110
-	 * @return null|string
111
-	 * @throws Exception\EntityTooLarge
112
-	 * @throws Exception\UnsupportedMediaType
113
-	 * @throws FileLocked
114
-	 * @throws InvalidPath
115
-	 * @throws \Sabre\DAV\Exception
116
-	 * @throws \Sabre\DAV\Exception\BadRequest
117
-	 * @throws \Sabre\DAV\Exception\Forbidden
118
-	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
119
-	 */
120
-	public function createFile($name, $data = null) {
121
-		try {
122
-			// for chunked upload also updating a existing file is a "createFile"
123
-			// because we create all the chunks before re-assemble them to the existing file.
124
-			if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
125
-
126
-				// exit if we can't create a new file and we don't updatable existing file
127
-				$chunkInfo = \OC_FileChunking::decodeName($name);
128
-				if (!$this->fileView->isCreatable($this->path) &&
129
-					!$this->fileView->isUpdatable($this->path . '/' . $chunkInfo['name'])
130
-				) {
131
-					throw new \Sabre\DAV\Exception\Forbidden();
132
-				}
133
-			} else {
134
-				// For non-chunked upload it is enough to check if we can create a new file
135
-				if (!$this->fileView->isCreatable($this->path)) {
136
-					throw new \Sabre\DAV\Exception\Forbidden();
137
-				}
138
-			}
139
-
140
-			$this->fileView->verifyPath($this->path, $name);
141
-
142
-			$path = $this->fileView->getAbsolutePath($this->path) . '/' . $name;
143
-			// in case the file already exists/overwriting
144
-			$info = $this->fileView->getFileInfo($this->path . '/' . $name);
145
-			if (!$info) {
146
-				// use a dummy FileInfo which is acceptable here since it will be refreshed after the put is complete
147
-				$info = new \OC\Files\FileInfo($path, null, null, [], null);
148
-			}
149
-			$node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info);
150
-
151
-			// only allow 1 process to upload a file at once but still allow reading the file while writing the part file
152
-			$node->acquireLock(ILockingProvider::LOCK_SHARED);
153
-			$this->fileView->lockFile($path . '.upload.part', ILockingProvider::LOCK_EXCLUSIVE);
154
-
155
-			$result = $node->put($data);
156
-
157
-			$this->fileView->unlockFile($path . '.upload.part', ILockingProvider::LOCK_EXCLUSIVE);
158
-			$node->releaseLock(ILockingProvider::LOCK_SHARED);
159
-			return $result;
160
-		} catch (\OCP\Files\StorageNotAvailableException $e) {
161
-			throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage(), $e->getCode(), $e);
162
-		} catch (InvalidPathException $ex) {
163
-			throw new InvalidPath($ex->getMessage(), false, $ex);
164
-		} catch (ForbiddenException $ex) {
165
-			throw new Forbidden($ex->getMessage(), $ex->getRetry(), $ex);
166
-		} catch (LockedException $e) {
167
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
168
-		}
169
-	}
170
-
171
-	/**
172
-	 * Creates a new subdirectory
173
-	 *
174
-	 * @param string $name
175
-	 * @throws FileLocked
176
-	 * @throws InvalidPath
177
-	 * @throws \Sabre\DAV\Exception\Forbidden
178
-	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
179
-	 */
180
-	public function createDirectory($name) {
181
-		try {
182
-			if (!$this->info->isCreatable()) {
183
-				throw new \Sabre\DAV\Exception\Forbidden();
184
-			}
185
-
186
-			$this->fileView->verifyPath($this->path, $name);
187
-			$newPath = $this->path . '/' . $name;
188
-			if (!$this->fileView->mkdir($newPath)) {
189
-				throw new \Sabre\DAV\Exception\Forbidden('Could not create directory ' . $newPath);
190
-			}
191
-		} catch (\OCP\Files\StorageNotAvailableException $e) {
192
-			throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
193
-		} catch (InvalidPathException $ex) {
194
-			throw new InvalidPath($ex->getMessage());
195
-		} catch (ForbiddenException $ex) {
196
-			throw new Forbidden($ex->getMessage(), $ex->getRetry());
197
-		} catch (LockedException $e) {
198
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
199
-		}
200
-	}
201
-
202
-	/**
203
-	 * Returns a specific child node, referenced by its name
204
-	 *
205
-	 * @param string $name
206
-	 * @param \OCP\Files\FileInfo $info
207
-	 * @return \Sabre\DAV\INode
208
-	 * @throws InvalidPath
209
-	 * @throws \Sabre\DAV\Exception\NotFound
210
-	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
211
-	 */
212
-	public function getChild($name, $info = null) {
213
-		if (!$this->info->isReadable()) {
214
-			// avoid detecting files through this way
215
-			throw new NotFound();
216
-		}
217
-
218
-		$path = $this->path . '/' . $name;
219
-		if (is_null($info)) {
220
-			try {
221
-				$this->fileView->verifyPath($this->path, $name);
222
-				$info = $this->fileView->getFileInfo($path);
223
-			} catch (\OCP\Files\StorageNotAvailableException $e) {
224
-				throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
225
-			} catch (InvalidPathException $ex) {
226
-				throw new InvalidPath($ex->getMessage());
227
-			} catch (ForbiddenException $e) {
228
-				throw new \Sabre\DAV\Exception\Forbidden();
229
-			}
230
-		}
231
-
232
-		if (!$info) {
233
-			throw new \Sabre\DAV\Exception\NotFound('File with name ' . $path . ' could not be located');
234
-		}
235
-
236
-		if ($info['mimetype'] === 'httpd/unix-directory') {
237
-			$node = new \OCA\DAV\Connector\Sabre\Directory($this->fileView, $info, $this->tree, $this->shareManager);
238
-		} else {
239
-			$node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info, $this->shareManager);
240
-		}
241
-		if ($this->tree) {
242
-			$this->tree->cacheNode($node);
243
-		}
244
-		return $node;
245
-	}
246
-
247
-	/**
248
-	 * Returns an array with all the child nodes
249
-	 *
250
-	 * @return \Sabre\DAV\INode[]
251
-	 * @throws \Sabre\DAV\Exception\Locked
252
-	 * @throws \OCA\DAV\Connector\Sabre\Exception\Forbidden
253
-	 */
254
-	public function getChildren() {
255
-		if (!is_null($this->dirContent)) {
256
-			return $this->dirContent;
257
-		}
258
-		try {
259
-			if (!$this->info->isReadable()) {
260
-				// return 403 instead of 404 because a 404 would make
261
-				// the caller believe that the collection itself does not exist
262
-				throw new Forbidden('No read permissions');
263
-			}
264
-			$folderContent = $this->fileView->getDirectoryContent($this->path);
265
-		} catch (LockedException $e) {
266
-			throw new Locked();
267
-		}
268
-
269
-		$nodes = [];
270
-		foreach ($folderContent as $info) {
271
-			$node = $this->getChild($info->getName(), $info);
272
-			$nodes[] = $node;
273
-		}
274
-		$this->dirContent = $nodes;
275
-		return $this->dirContent;
276
-	}
277
-
278
-	/**
279
-	 * Checks if a child exists.
280
-	 *
281
-	 * @param string $name
282
-	 * @return bool
283
-	 */
284
-	public function childExists($name) {
285
-		// note: here we do NOT resolve the chunk file name to the real file name
286
-		// to make sure we return false when checking for file existence with a chunk
287
-		// file name.
288
-		// This is to make sure that "createFile" is still triggered
289
-		// (required old code) instead of "updateFile".
290
-		//
291
-		// TODO: resolve chunk file name here and implement "updateFile"
292
-		$path = $this->path . '/' . $name;
293
-		return $this->fileView->file_exists($path);
294
-	}
295
-
296
-	/**
297
-	 * Deletes all files in this directory, and then itself
298
-	 *
299
-	 * @return void
300
-	 * @throws FileLocked
301
-	 * @throws \Sabre\DAV\Exception\Forbidden
302
-	 */
303
-	public function delete() {
304
-		if ($this->path === '' || $this->path === '/' || !$this->info->isDeletable()) {
305
-			throw new \Sabre\DAV\Exception\Forbidden();
306
-		}
307
-
308
-		try {
309
-			if (!$this->fileView->rmdir($this->path)) {
310
-				// assume it wasn't possible to remove due to permission issue
311
-				throw new \Sabre\DAV\Exception\Forbidden();
312
-			}
313
-		} catch (ForbiddenException $ex) {
314
-			throw new Forbidden($ex->getMessage(), $ex->getRetry());
315
-		} catch (LockedException $e) {
316
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
317
-		}
318
-	}
319
-
320
-	/**
321
-	 * Returns available diskspace information
322
-	 *
323
-	 * @return array
324
-	 */
325
-	public function getQuotaInfo() {
326
-		if ($this->quotaInfo) {
327
-			return $this->quotaInfo;
328
-		}
329
-		try {
330
-			$storageInfo = \OC_Helper::getStorageInfo($this->info->getPath(), $this->info, false);
331
-			if ($storageInfo['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED) {
332
-				$free = \OCP\Files\FileInfo::SPACE_UNLIMITED;
333
-			} else {
334
-				$free = $storageInfo['free'];
335
-			}
336
-			$this->quotaInfo = [
337
-				$storageInfo['used'],
338
-				$free
339
-			];
340
-			return $this->quotaInfo;
341
-		} catch (\OCP\Files\NotFoundException $e) {
342
-			return [0, 0];
343
-		} catch (\OCP\Files\StorageNotAvailableException $e) {
344
-			return [0, 0];
345
-		} catch (NotPermittedException $e) {
346
-			return [0, 0];
347
-		}
348
-	}
349
-
350
-	/**
351
-	 * Moves a node into this collection.
352
-	 *
353
-	 * It is up to the implementors to:
354
-	 *   1. Create the new resource.
355
-	 *   2. Remove the old resource.
356
-	 *   3. Transfer any properties or other data.
357
-	 *
358
-	 * Generally you should make very sure that your collection can easily move
359
-	 * the move.
360
-	 *
361
-	 * If you don't, just return false, which will trigger sabre/dav to handle
362
-	 * the move itself. If you return true from this function, the assumption
363
-	 * is that the move was successful.
364
-	 *
365
-	 * @param string $targetName New local file/collection name.
366
-	 * @param string $fullSourcePath Full path to source node
367
-	 * @param INode $sourceNode Source node itself
368
-	 * @return bool
369
-	 * @throws BadRequest
370
-	 * @throws ServiceUnavailable
371
-	 * @throws Forbidden
372
-	 * @throws FileLocked
373
-	 * @throws \Sabre\DAV\Exception\Forbidden
374
-	 */
375
-	public function moveInto($targetName, $fullSourcePath, INode $sourceNode) {
376
-		if (!$sourceNode instanceof Node) {
377
-			// it's a file of another kind, like FutureFile
378
-			if ($sourceNode instanceof IFile) {
379
-				// fallback to default copy+delete handling
380
-				return false;
381
-			}
382
-			throw new BadRequest('Incompatible node types');
383
-		}
384
-
385
-		if (!$this->fileView) {
386
-			throw new ServiceUnavailable('filesystem not setup');
387
-		}
388
-
389
-		$destinationPath = $this->getPath() . '/' . $targetName;
390
-
391
-
392
-		$targetNodeExists = $this->childExists($targetName);
393
-
394
-		// at getNodeForPath we also check the path for isForbiddenFileOrDir
395
-		// with that we have covered both source and destination
396
-		if ($sourceNode instanceof Directory && $targetNodeExists) {
397
-			throw new \Sabre\DAV\Exception\Forbidden('Could not copy directory ' . $sourceNode->getName() . ', target exists');
398
-		}
399
-
400
-		[$sourceDir,] = \Sabre\Uri\split($sourceNode->getPath());
401
-		$destinationDir = $this->getPath();
402
-
403
-		$sourcePath = $sourceNode->getPath();
404
-
405
-		$isMovableMount = false;
406
-		$sourceMount = \OC::$server->getMountManager()->find($this->fileView->getAbsolutePath($sourcePath));
407
-		$internalPath = $sourceMount->getInternalPath($this->fileView->getAbsolutePath($sourcePath));
408
-		if ($sourceMount instanceof MoveableMount && $internalPath === '') {
409
-			$isMovableMount = true;
410
-		}
411
-
412
-		try {
413
-			$sameFolder = ($sourceDir === $destinationDir);
414
-			// if we're overwriting or same folder
415
-			if ($targetNodeExists || $sameFolder) {
416
-				// note that renaming a share mount point is always allowed
417
-				if (!$this->fileView->isUpdatable($destinationDir) && !$isMovableMount) {
418
-					throw new \Sabre\DAV\Exception\Forbidden();
419
-				}
420
-			} else {
421
-				if (!$this->fileView->isCreatable($destinationDir)) {
422
-					throw new \Sabre\DAV\Exception\Forbidden();
423
-				}
424
-			}
425
-
426
-			if (!$sameFolder) {
427
-				// moving to a different folder, source will be gone, like a deletion
428
-				// note that moving a share mount point is always allowed
429
-				if (!$this->fileView->isDeletable($sourcePath) && !$isMovableMount) {
430
-					throw new \Sabre\DAV\Exception\Forbidden();
431
-				}
432
-			}
433
-
434
-			$fileName = basename($destinationPath);
435
-			try {
436
-				$this->fileView->verifyPath($destinationDir, $fileName);
437
-			} catch (InvalidPathException $ex) {
438
-				throw new InvalidPath($ex->getMessage());
439
-			}
440
-
441
-			$renameOkay = $this->fileView->rename($sourcePath, $destinationPath);
442
-			if (!$renameOkay) {
443
-				throw new \Sabre\DAV\Exception\Forbidden('');
444
-			}
445
-		} catch (StorageNotAvailableException $e) {
446
-			throw new ServiceUnavailable($e->getMessage());
447
-		} catch (ForbiddenException $ex) {
448
-			throw new Forbidden($ex->getMessage(), $ex->getRetry());
449
-		} catch (LockedException $e) {
450
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
451
-		}
452
-
453
-		return true;
454
-	}
455
-
456
-
457
-	public function copyInto($targetName, $sourcePath, INode $sourceNode) {
458
-		if ($sourceNode instanceof File || $sourceNode instanceof Directory) {
459
-			$destinationPath = $this->getPath() . '/' . $targetName;
460
-			$sourcePath = $sourceNode->getPath();
461
-
462
-			if (!$this->fileView->isCreatable($this->getPath())) {
463
-				throw new \Sabre\DAV\Exception\Forbidden();
464
-			}
465
-
466
-			try {
467
-				$this->fileView->verifyPath($this->getPath(), $targetName);
468
-			} catch (InvalidPathException $ex) {
469
-				throw new InvalidPath($ex->getMessage());
470
-			}
471
-
472
-			return $this->fileView->copy($sourcePath, $destinationPath);
473
-		}
474
-
475
-		return false;
476
-	}
56
+    /**
57
+     * Cached directory content
58
+     *
59
+     * @var \OCP\Files\FileInfo[]
60
+     */
61
+    private $dirContent;
62
+
63
+    /**
64
+     * Cached quota info
65
+     *
66
+     * @var array
67
+     */
68
+    private $quotaInfo;
69
+
70
+    /**
71
+     * @var ObjectTree|null
72
+     */
73
+    private $tree;
74
+
75
+    /**
76
+     * Sets up the node, expects a full path name
77
+     *
78
+     * @param \OC\Files\View $view
79
+     * @param \OCP\Files\FileInfo $info
80
+     * @param ObjectTree|null $tree
81
+     * @param \OCP\Share\IManager $shareManager
82
+     */
83
+    public function __construct(View $view, FileInfo $info, $tree = null, $shareManager = null) {
84
+        parent::__construct($view, $info, $shareManager);
85
+        $this->tree = $tree;
86
+    }
87
+
88
+    /**
89
+     * Creates a new file in the directory
90
+     *
91
+     * Data will either be supplied as a stream resource, or in certain cases
92
+     * as a string. Keep in mind that you may have to support either.
93
+     *
94
+     * After successful creation of the file, you may choose to return the ETag
95
+     * of the new file here.
96
+     *
97
+     * The returned ETag must be surrounded by double-quotes (The quotes should
98
+     * be part of the actual string).
99
+     *
100
+     * If you cannot accurately determine the ETag, you should not return it.
101
+     * If you don't store the file exactly as-is (you're transforming it
102
+     * somehow) you should also not return an ETag.
103
+     *
104
+     * This means that if a subsequent GET to this new file does not exactly
105
+     * return the same contents of what was submitted here, you are strongly
106
+     * recommended to omit the ETag.
107
+     *
108
+     * @param string $name Name of the file
109
+     * @param resource|string $data Initial payload
110
+     * @return null|string
111
+     * @throws Exception\EntityTooLarge
112
+     * @throws Exception\UnsupportedMediaType
113
+     * @throws FileLocked
114
+     * @throws InvalidPath
115
+     * @throws \Sabre\DAV\Exception
116
+     * @throws \Sabre\DAV\Exception\BadRequest
117
+     * @throws \Sabre\DAV\Exception\Forbidden
118
+     * @throws \Sabre\DAV\Exception\ServiceUnavailable
119
+     */
120
+    public function createFile($name, $data = null) {
121
+        try {
122
+            // for chunked upload also updating a existing file is a "createFile"
123
+            // because we create all the chunks before re-assemble them to the existing file.
124
+            if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
125
+
126
+                // exit if we can't create a new file and we don't updatable existing file
127
+                $chunkInfo = \OC_FileChunking::decodeName($name);
128
+                if (!$this->fileView->isCreatable($this->path) &&
129
+                    !$this->fileView->isUpdatable($this->path . '/' . $chunkInfo['name'])
130
+                ) {
131
+                    throw new \Sabre\DAV\Exception\Forbidden();
132
+                }
133
+            } else {
134
+                // For non-chunked upload it is enough to check if we can create a new file
135
+                if (!$this->fileView->isCreatable($this->path)) {
136
+                    throw new \Sabre\DAV\Exception\Forbidden();
137
+                }
138
+            }
139
+
140
+            $this->fileView->verifyPath($this->path, $name);
141
+
142
+            $path = $this->fileView->getAbsolutePath($this->path) . '/' . $name;
143
+            // in case the file already exists/overwriting
144
+            $info = $this->fileView->getFileInfo($this->path . '/' . $name);
145
+            if (!$info) {
146
+                // use a dummy FileInfo which is acceptable here since it will be refreshed after the put is complete
147
+                $info = new \OC\Files\FileInfo($path, null, null, [], null);
148
+            }
149
+            $node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info);
150
+
151
+            // only allow 1 process to upload a file at once but still allow reading the file while writing the part file
152
+            $node->acquireLock(ILockingProvider::LOCK_SHARED);
153
+            $this->fileView->lockFile($path . '.upload.part', ILockingProvider::LOCK_EXCLUSIVE);
154
+
155
+            $result = $node->put($data);
156
+
157
+            $this->fileView->unlockFile($path . '.upload.part', ILockingProvider::LOCK_EXCLUSIVE);
158
+            $node->releaseLock(ILockingProvider::LOCK_SHARED);
159
+            return $result;
160
+        } catch (\OCP\Files\StorageNotAvailableException $e) {
161
+            throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage(), $e->getCode(), $e);
162
+        } catch (InvalidPathException $ex) {
163
+            throw new InvalidPath($ex->getMessage(), false, $ex);
164
+        } catch (ForbiddenException $ex) {
165
+            throw new Forbidden($ex->getMessage(), $ex->getRetry(), $ex);
166
+        } catch (LockedException $e) {
167
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
168
+        }
169
+    }
170
+
171
+    /**
172
+     * Creates a new subdirectory
173
+     *
174
+     * @param string $name
175
+     * @throws FileLocked
176
+     * @throws InvalidPath
177
+     * @throws \Sabre\DAV\Exception\Forbidden
178
+     * @throws \Sabre\DAV\Exception\ServiceUnavailable
179
+     */
180
+    public function createDirectory($name) {
181
+        try {
182
+            if (!$this->info->isCreatable()) {
183
+                throw new \Sabre\DAV\Exception\Forbidden();
184
+            }
185
+
186
+            $this->fileView->verifyPath($this->path, $name);
187
+            $newPath = $this->path . '/' . $name;
188
+            if (!$this->fileView->mkdir($newPath)) {
189
+                throw new \Sabre\DAV\Exception\Forbidden('Could not create directory ' . $newPath);
190
+            }
191
+        } catch (\OCP\Files\StorageNotAvailableException $e) {
192
+            throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
193
+        } catch (InvalidPathException $ex) {
194
+            throw new InvalidPath($ex->getMessage());
195
+        } catch (ForbiddenException $ex) {
196
+            throw new Forbidden($ex->getMessage(), $ex->getRetry());
197
+        } catch (LockedException $e) {
198
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
199
+        }
200
+    }
201
+
202
+    /**
203
+     * Returns a specific child node, referenced by its name
204
+     *
205
+     * @param string $name
206
+     * @param \OCP\Files\FileInfo $info
207
+     * @return \Sabre\DAV\INode
208
+     * @throws InvalidPath
209
+     * @throws \Sabre\DAV\Exception\NotFound
210
+     * @throws \Sabre\DAV\Exception\ServiceUnavailable
211
+     */
212
+    public function getChild($name, $info = null) {
213
+        if (!$this->info->isReadable()) {
214
+            // avoid detecting files through this way
215
+            throw new NotFound();
216
+        }
217
+
218
+        $path = $this->path . '/' . $name;
219
+        if (is_null($info)) {
220
+            try {
221
+                $this->fileView->verifyPath($this->path, $name);
222
+                $info = $this->fileView->getFileInfo($path);
223
+            } catch (\OCP\Files\StorageNotAvailableException $e) {
224
+                throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
225
+            } catch (InvalidPathException $ex) {
226
+                throw new InvalidPath($ex->getMessage());
227
+            } catch (ForbiddenException $e) {
228
+                throw new \Sabre\DAV\Exception\Forbidden();
229
+            }
230
+        }
231
+
232
+        if (!$info) {
233
+            throw new \Sabre\DAV\Exception\NotFound('File with name ' . $path . ' could not be located');
234
+        }
235
+
236
+        if ($info['mimetype'] === 'httpd/unix-directory') {
237
+            $node = new \OCA\DAV\Connector\Sabre\Directory($this->fileView, $info, $this->tree, $this->shareManager);
238
+        } else {
239
+            $node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info, $this->shareManager);
240
+        }
241
+        if ($this->tree) {
242
+            $this->tree->cacheNode($node);
243
+        }
244
+        return $node;
245
+    }
246
+
247
+    /**
248
+     * Returns an array with all the child nodes
249
+     *
250
+     * @return \Sabre\DAV\INode[]
251
+     * @throws \Sabre\DAV\Exception\Locked
252
+     * @throws \OCA\DAV\Connector\Sabre\Exception\Forbidden
253
+     */
254
+    public function getChildren() {
255
+        if (!is_null($this->dirContent)) {
256
+            return $this->dirContent;
257
+        }
258
+        try {
259
+            if (!$this->info->isReadable()) {
260
+                // return 403 instead of 404 because a 404 would make
261
+                // the caller believe that the collection itself does not exist
262
+                throw new Forbidden('No read permissions');
263
+            }
264
+            $folderContent = $this->fileView->getDirectoryContent($this->path);
265
+        } catch (LockedException $e) {
266
+            throw new Locked();
267
+        }
268
+
269
+        $nodes = [];
270
+        foreach ($folderContent as $info) {
271
+            $node = $this->getChild($info->getName(), $info);
272
+            $nodes[] = $node;
273
+        }
274
+        $this->dirContent = $nodes;
275
+        return $this->dirContent;
276
+    }
277
+
278
+    /**
279
+     * Checks if a child exists.
280
+     *
281
+     * @param string $name
282
+     * @return bool
283
+     */
284
+    public function childExists($name) {
285
+        // note: here we do NOT resolve the chunk file name to the real file name
286
+        // to make sure we return false when checking for file existence with a chunk
287
+        // file name.
288
+        // This is to make sure that "createFile" is still triggered
289
+        // (required old code) instead of "updateFile".
290
+        //
291
+        // TODO: resolve chunk file name here and implement "updateFile"
292
+        $path = $this->path . '/' . $name;
293
+        return $this->fileView->file_exists($path);
294
+    }
295
+
296
+    /**
297
+     * Deletes all files in this directory, and then itself
298
+     *
299
+     * @return void
300
+     * @throws FileLocked
301
+     * @throws \Sabre\DAV\Exception\Forbidden
302
+     */
303
+    public function delete() {
304
+        if ($this->path === '' || $this->path === '/' || !$this->info->isDeletable()) {
305
+            throw new \Sabre\DAV\Exception\Forbidden();
306
+        }
307
+
308
+        try {
309
+            if (!$this->fileView->rmdir($this->path)) {
310
+                // assume it wasn't possible to remove due to permission issue
311
+                throw new \Sabre\DAV\Exception\Forbidden();
312
+            }
313
+        } catch (ForbiddenException $ex) {
314
+            throw new Forbidden($ex->getMessage(), $ex->getRetry());
315
+        } catch (LockedException $e) {
316
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
317
+        }
318
+    }
319
+
320
+    /**
321
+     * Returns available diskspace information
322
+     *
323
+     * @return array
324
+     */
325
+    public function getQuotaInfo() {
326
+        if ($this->quotaInfo) {
327
+            return $this->quotaInfo;
328
+        }
329
+        try {
330
+            $storageInfo = \OC_Helper::getStorageInfo($this->info->getPath(), $this->info, false);
331
+            if ($storageInfo['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED) {
332
+                $free = \OCP\Files\FileInfo::SPACE_UNLIMITED;
333
+            } else {
334
+                $free = $storageInfo['free'];
335
+            }
336
+            $this->quotaInfo = [
337
+                $storageInfo['used'],
338
+                $free
339
+            ];
340
+            return $this->quotaInfo;
341
+        } catch (\OCP\Files\NotFoundException $e) {
342
+            return [0, 0];
343
+        } catch (\OCP\Files\StorageNotAvailableException $e) {
344
+            return [0, 0];
345
+        } catch (NotPermittedException $e) {
346
+            return [0, 0];
347
+        }
348
+    }
349
+
350
+    /**
351
+     * Moves a node into this collection.
352
+     *
353
+     * It is up to the implementors to:
354
+     *   1. Create the new resource.
355
+     *   2. Remove the old resource.
356
+     *   3. Transfer any properties or other data.
357
+     *
358
+     * Generally you should make very sure that your collection can easily move
359
+     * the move.
360
+     *
361
+     * If you don't, just return false, which will trigger sabre/dav to handle
362
+     * the move itself. If you return true from this function, the assumption
363
+     * is that the move was successful.
364
+     *
365
+     * @param string $targetName New local file/collection name.
366
+     * @param string $fullSourcePath Full path to source node
367
+     * @param INode $sourceNode Source node itself
368
+     * @return bool
369
+     * @throws BadRequest
370
+     * @throws ServiceUnavailable
371
+     * @throws Forbidden
372
+     * @throws FileLocked
373
+     * @throws \Sabre\DAV\Exception\Forbidden
374
+     */
375
+    public function moveInto($targetName, $fullSourcePath, INode $sourceNode) {
376
+        if (!$sourceNode instanceof Node) {
377
+            // it's a file of another kind, like FutureFile
378
+            if ($sourceNode instanceof IFile) {
379
+                // fallback to default copy+delete handling
380
+                return false;
381
+            }
382
+            throw new BadRequest('Incompatible node types');
383
+        }
384
+
385
+        if (!$this->fileView) {
386
+            throw new ServiceUnavailable('filesystem not setup');
387
+        }
388
+
389
+        $destinationPath = $this->getPath() . '/' . $targetName;
390
+
391
+
392
+        $targetNodeExists = $this->childExists($targetName);
393
+
394
+        // at getNodeForPath we also check the path for isForbiddenFileOrDir
395
+        // with that we have covered both source and destination
396
+        if ($sourceNode instanceof Directory && $targetNodeExists) {
397
+            throw new \Sabre\DAV\Exception\Forbidden('Could not copy directory ' . $sourceNode->getName() . ', target exists');
398
+        }
399
+
400
+        [$sourceDir,] = \Sabre\Uri\split($sourceNode->getPath());
401
+        $destinationDir = $this->getPath();
402
+
403
+        $sourcePath = $sourceNode->getPath();
404
+
405
+        $isMovableMount = false;
406
+        $sourceMount = \OC::$server->getMountManager()->find($this->fileView->getAbsolutePath($sourcePath));
407
+        $internalPath = $sourceMount->getInternalPath($this->fileView->getAbsolutePath($sourcePath));
408
+        if ($sourceMount instanceof MoveableMount && $internalPath === '') {
409
+            $isMovableMount = true;
410
+        }
411
+
412
+        try {
413
+            $sameFolder = ($sourceDir === $destinationDir);
414
+            // if we're overwriting or same folder
415
+            if ($targetNodeExists || $sameFolder) {
416
+                // note that renaming a share mount point is always allowed
417
+                if (!$this->fileView->isUpdatable($destinationDir) && !$isMovableMount) {
418
+                    throw new \Sabre\DAV\Exception\Forbidden();
419
+                }
420
+            } else {
421
+                if (!$this->fileView->isCreatable($destinationDir)) {
422
+                    throw new \Sabre\DAV\Exception\Forbidden();
423
+                }
424
+            }
425
+
426
+            if (!$sameFolder) {
427
+                // moving to a different folder, source will be gone, like a deletion
428
+                // note that moving a share mount point is always allowed
429
+                if (!$this->fileView->isDeletable($sourcePath) && !$isMovableMount) {
430
+                    throw new \Sabre\DAV\Exception\Forbidden();
431
+                }
432
+            }
433
+
434
+            $fileName = basename($destinationPath);
435
+            try {
436
+                $this->fileView->verifyPath($destinationDir, $fileName);
437
+            } catch (InvalidPathException $ex) {
438
+                throw new InvalidPath($ex->getMessage());
439
+            }
440
+
441
+            $renameOkay = $this->fileView->rename($sourcePath, $destinationPath);
442
+            if (!$renameOkay) {
443
+                throw new \Sabre\DAV\Exception\Forbidden('');
444
+            }
445
+        } catch (StorageNotAvailableException $e) {
446
+            throw new ServiceUnavailable($e->getMessage());
447
+        } catch (ForbiddenException $ex) {
448
+            throw new Forbidden($ex->getMessage(), $ex->getRetry());
449
+        } catch (LockedException $e) {
450
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
451
+        }
452
+
453
+        return true;
454
+    }
455
+
456
+
457
+    public function copyInto($targetName, $sourcePath, INode $sourceNode) {
458
+        if ($sourceNode instanceof File || $sourceNode instanceof Directory) {
459
+            $destinationPath = $this->getPath() . '/' . $targetName;
460
+            $sourcePath = $sourceNode->getPath();
461
+
462
+            if (!$this->fileView->isCreatable($this->getPath())) {
463
+                throw new \Sabre\DAV\Exception\Forbidden();
464
+            }
465
+
466
+            try {
467
+                $this->fileView->verifyPath($this->getPath(), $targetName);
468
+            } catch (InvalidPathException $ex) {
469
+                throw new InvalidPath($ex->getMessage());
470
+            }
471
+
472
+            return $this->fileView->copy($sourcePath, $destinationPath);
473
+        }
474
+
475
+        return false;
476
+    }
477 477
 }
Please login to merge, or discard this patch.