Passed
Push — master ( 9a1a14...3f5510 )
by John
38:03 queued 24:21
created
lib/private/legacy/OC_Helper.php 2 patches
Indentation   +584 added lines, -584 removed lines patch added patch discarded remove patch
@@ -54,588 +54,588 @@
 block discarded – undo
54 54
  * Collection of useful functions
55 55
  */
56 56
 class OC_Helper {
57
-	private static $templateManager;
58
-
59
-	/**
60
-	 * Make a human file size
61
-	 * @param int $bytes file size in bytes
62
-	 * @return string a human readable file size
63
-	 *
64
-	 * Makes 2048 to 2 kB.
65
-	 */
66
-	public static function humanFileSize($bytes) {
67
-		if ($bytes < 0) {
68
-			return "?";
69
-		}
70
-		if ($bytes < 1024) {
71
-			return "$bytes B";
72
-		}
73
-		$bytes = round($bytes / 1024, 0);
74
-		if ($bytes < 1024) {
75
-			return "$bytes KB";
76
-		}
77
-		$bytes = round($bytes / 1024, 1);
78
-		if ($bytes < 1024) {
79
-			return "$bytes MB";
80
-		}
81
-		$bytes = round($bytes / 1024, 1);
82
-		if ($bytes < 1024) {
83
-			return "$bytes GB";
84
-		}
85
-		$bytes = round($bytes / 1024, 1);
86
-		if ($bytes < 1024) {
87
-			return "$bytes TB";
88
-		}
89
-
90
-		$bytes = round($bytes / 1024, 1);
91
-		return "$bytes PB";
92
-	}
93
-
94
-	/**
95
-	 * Make a computer file size
96
-	 * @param string $str file size in human readable format
97
-	 * @return float|bool a file size in bytes
98
-	 *
99
-	 * Makes 2kB to 2048.
100
-	 *
101
-	 * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418
102
-	 */
103
-	public static function computerFileSize($str) {
104
-		$str = strtolower($str);
105
-		if (is_numeric($str)) {
106
-			return (float)$str;
107
-		}
108
-
109
-		$bytes_array = [
110
-			'b' => 1,
111
-			'k' => 1024,
112
-			'kb' => 1024,
113
-			'mb' => 1024 * 1024,
114
-			'm' => 1024 * 1024,
115
-			'gb' => 1024 * 1024 * 1024,
116
-			'g' => 1024 * 1024 * 1024,
117
-			'tb' => 1024 * 1024 * 1024 * 1024,
118
-			't' => 1024 * 1024 * 1024 * 1024,
119
-			'pb' => 1024 * 1024 * 1024 * 1024 * 1024,
120
-			'p' => 1024 * 1024 * 1024 * 1024 * 1024,
121
-		];
122
-
123
-		$bytes = (float)$str;
124
-
125
-		if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
126
-			$bytes *= $bytes_array[$matches[1]];
127
-		} else {
128
-			return false;
129
-		}
130
-
131
-		$bytes = round($bytes);
132
-
133
-		return $bytes;
134
-	}
135
-
136
-	/**
137
-	 * Recursive copying of folders
138
-	 * @param string $src source folder
139
-	 * @param string $dest target folder
140
-	 *
141
-	 */
142
-	public static function copyr($src, $dest) {
143
-		if (is_dir($src)) {
144
-			if (!is_dir($dest)) {
145
-				mkdir($dest);
146
-			}
147
-			$files = scandir($src);
148
-			foreach ($files as $file) {
149
-				if ($file != "." && $file != "..") {
150
-					self::copyr("$src/$file", "$dest/$file");
151
-				}
152
-			}
153
-		} elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) {
154
-			copy($src, $dest);
155
-		}
156
-	}
157
-
158
-	/**
159
-	 * Recursive deletion of folders
160
-	 * @param string $dir path to the folder
161
-	 * @param bool $deleteSelf if set to false only the content of the folder will be deleted
162
-	 * @return bool
163
-	 */
164
-	public static function rmdirr($dir, $deleteSelf = true) {
165
-		if (is_dir($dir)) {
166
-			$files = new RecursiveIteratorIterator(
167
-				new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
168
-				RecursiveIteratorIterator::CHILD_FIRST
169
-			);
170
-
171
-			foreach ($files as $fileInfo) {
172
-				/** @var SplFileInfo $fileInfo */
173
-				if ($fileInfo->isLink()) {
174
-					unlink($fileInfo->getPathname());
175
-				} elseif ($fileInfo->isDir()) {
176
-					rmdir($fileInfo->getRealPath());
177
-				} else {
178
-					unlink($fileInfo->getRealPath());
179
-				}
180
-			}
181
-			if ($deleteSelf) {
182
-				rmdir($dir);
183
-			}
184
-		} elseif (file_exists($dir)) {
185
-			if ($deleteSelf) {
186
-				unlink($dir);
187
-			}
188
-		}
189
-		if (!$deleteSelf) {
190
-			return true;
191
-		}
192
-
193
-		return !file_exists($dir);
194
-	}
195
-
196
-	/**
197
-	 * @deprecated 18.0.0
198
-	 * @return \OC\Files\Type\TemplateManager
199
-	 */
200
-	public static function getFileTemplateManager() {
201
-		if (!self::$templateManager) {
202
-			self::$templateManager = new \OC\Files\Type\TemplateManager();
203
-		}
204
-		return self::$templateManager;
205
-	}
206
-
207
-	/**
208
-	 * detect if a given program is found in the search PATH
209
-	 *
210
-	 * @param string $name
211
-	 * @param bool $path
212
-	 * @internal param string $program name
213
-	 * @internal param string $optional search path, defaults to $PATH
214
-	 * @return bool    true if executable program found in path
215
-	 */
216
-	public static function canExecute($name, $path = false) {
217
-		// path defaults to PATH from environment if not set
218
-		if ($path === false) {
219
-			$path = getenv("PATH");
220
-		}
221
-		// we look for an executable file of that name
222
-		$exts = [""];
223
-		$check_fn = "is_executable";
224
-		// Default check will be done with $path directories :
225
-		$dirs = explode(PATH_SEPARATOR, $path);
226
-		// WARNING : We have to check if open_basedir is enabled :
227
-		$obd = OC::$server->get(IniGetWrapper::class)->getString('open_basedir');
228
-		if ($obd != "none") {
229
-			$obd_values = explode(PATH_SEPARATOR, $obd);
230
-			if (count($obd_values) > 0 and $obd_values[0]) {
231
-				// open_basedir is in effect !
232
-				// We need to check if the program is in one of these dirs :
233
-				$dirs = $obd_values;
234
-			}
235
-		}
236
-		foreach ($dirs as $dir) {
237
-			foreach ($exts as $ext) {
238
-				if ($check_fn("$dir/$name" . $ext)) {
239
-					return true;
240
-				}
241
-			}
242
-		}
243
-		return false;
244
-	}
245
-
246
-	/**
247
-	 * copy the contents of one stream to another
248
-	 *
249
-	 * @param resource $source
250
-	 * @param resource $target
251
-	 * @return array the number of bytes copied and result
252
-	 */
253
-	public static function streamCopy($source, $target) {
254
-		if (!$source or !$target) {
255
-			return [0, false];
256
-		}
257
-		$bufSize = 8192;
258
-		$result = true;
259
-		$count = 0;
260
-		while (!feof($source)) {
261
-			$buf = fread($source, $bufSize);
262
-			$bytesWritten = fwrite($target, $buf);
263
-			if ($bytesWritten !== false) {
264
-				$count += $bytesWritten;
265
-			}
266
-			// note: strlen is expensive so only use it when necessary,
267
-			// on the last block
268
-			if ($bytesWritten === false
269
-				|| ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
270
-			) {
271
-				// write error, could be disk full ?
272
-				$result = false;
273
-				break;
274
-			}
275
-		}
276
-		return [$count, $result];
277
-	}
278
-
279
-	/**
280
-	 * Adds a suffix to the name in case the file exists
281
-	 *
282
-	 * @param string $path
283
-	 * @param string $filename
284
-	 * @return string
285
-	 */
286
-	public static function buildNotExistingFileName($path, $filename) {
287
-		$view = \OC\Files\Filesystem::getView();
288
-		return self::buildNotExistingFileNameForView($path, $filename, $view);
289
-	}
290
-
291
-	/**
292
-	 * Adds a suffix to the name in case the file exists
293
-	 *
294
-	 * @param string $path
295
-	 * @param string $filename
296
-	 * @return string
297
-	 */
298
-	public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
299
-		if ($path === '/') {
300
-			$path = '';
301
-		}
302
-		if ($pos = strrpos($filename, '.')) {
303
-			$name = substr($filename, 0, $pos);
304
-			$ext = substr($filename, $pos);
305
-		} else {
306
-			$name = $filename;
307
-			$ext = '';
308
-		}
309
-
310
-		$newpath = $path . '/' . $filename;
311
-		if ($view->file_exists($newpath)) {
312
-			if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
313
-				//Replace the last "(number)" with "(number+1)"
314
-				$last_match = count($matches[0]) - 1;
315
-				$counter = $matches[1][$last_match][0] + 1;
316
-				$offset = $matches[0][$last_match][1];
317
-				$match_length = strlen($matches[0][$last_match][0]);
318
-			} else {
319
-				$counter = 2;
320
-				$match_length = 0;
321
-				$offset = false;
322
-			}
323
-			do {
324
-				if ($offset) {
325
-					//Replace the last "(number)" with "(number+1)"
326
-					$newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
327
-				} else {
328
-					$newname = $name . ' (' . $counter . ')';
329
-				}
330
-				$newpath = $path . '/' . $newname . $ext;
331
-				$counter++;
332
-			} while ($view->file_exists($newpath));
333
-		}
334
-
335
-		return $newpath;
336
-	}
337
-
338
-	/**
339
-	 * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
340
-	 *
341
-	 * @param array $input The array to work on
342
-	 * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
343
-	 * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
344
-	 * @return array
345
-	 *
346
-	 * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
347
-	 * based on https://www.php.net/manual/en/function.array-change-key-case.php#107715
348
-	 *
349
-	 */
350
-	public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
351
-		$case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
352
-		$ret = [];
353
-		foreach ($input as $k => $v) {
354
-			$ret[mb_convert_case($k, $case, $encoding)] = $v;
355
-		}
356
-		return $ret;
357
-	}
358
-
359
-	/**
360
-	 * performs a search in a nested array
361
-	 * @param array $haystack the array to be searched
362
-	 * @param string $needle the search string
363
-	 * @param mixed $index optional, only search this key name
364
-	 * @return mixed the key of the matching field, otherwise false
365
-	 *
366
-	 * performs a search in a nested array
367
-	 *
368
-	 * taken from https://www.php.net/manual/en/function.array-search.php#97645
369
-	 */
370
-	public static function recursiveArraySearch($haystack, $needle, $index = null) {
371
-		$aIt = new RecursiveArrayIterator($haystack);
372
-		$it = new RecursiveIteratorIterator($aIt);
373
-
374
-		while ($it->valid()) {
375
-			if (((isset($index) and ($it->key() == $index)) or !isset($index)) and ($it->current() == $needle)) {
376
-				return $aIt->key();
377
-			}
378
-
379
-			$it->next();
380
-		}
381
-
382
-		return false;
383
-	}
384
-
385
-	/**
386
-	 * calculates the maximum upload size respecting system settings, free space and user quota
387
-	 *
388
-	 * @param string $dir the current folder where the user currently operates
389
-	 * @param int $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
390
-	 * @return int number of bytes representing
391
-	 */
392
-	public static function maxUploadFilesize($dir, $freeSpace = null) {
393
-		if (is_null($freeSpace) || $freeSpace < 0) {
394
-			$freeSpace = self::freeSpace($dir);
395
-		}
396
-		return min($freeSpace, self::uploadLimit());
397
-	}
398
-
399
-	/**
400
-	 * Calculate free space left within user quota
401
-	 *
402
-	 * @param string $dir the current folder where the user currently operates
403
-	 * @return int number of bytes representing
404
-	 */
405
-	public static function freeSpace($dir) {
406
-		$freeSpace = \OC\Files\Filesystem::free_space($dir);
407
-		if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
408
-			$freeSpace = max($freeSpace, 0);
409
-			return $freeSpace;
410
-		} else {
411
-			return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
412
-		}
413
-	}
414
-
415
-	/**
416
-	 * Calculate PHP upload limit
417
-	 *
418
-	 * @return int PHP upload file size limit
419
-	 */
420
-	public static function uploadLimit() {
421
-		$ini = \OC::$server->get(IniGetWrapper::class);
422
-		$upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize'));
423
-		$post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size'));
424
-		if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) {
425
-			return INF;
426
-		} elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) {
427
-			return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
428
-		} else {
429
-			return min($upload_max_filesize, $post_max_size);
430
-		}
431
-	}
432
-
433
-	/**
434
-	 * Checks if a function is available
435
-	 *
436
-	 * @param string $function_name
437
-	 * @return bool
438
-	 */
439
-	public static function is_function_enabled($function_name) {
440
-		if (!function_exists($function_name)) {
441
-			return false;
442
-		}
443
-		$ini = \OC::$server->get(IniGetWrapper::class);
444
-		$disabled = explode(',', $ini->get('disable_functions') ?: '');
445
-		$disabled = array_map('trim', $disabled);
446
-		if (in_array($function_name, $disabled)) {
447
-			return false;
448
-		}
449
-		$disabled = explode(',', $ini->get('suhosin.executor.func.blacklist') ?: '');
450
-		$disabled = array_map('trim', $disabled);
451
-		if (in_array($function_name, $disabled)) {
452
-			return false;
453
-		}
454
-		return true;
455
-	}
456
-
457
-	/**
458
-	 * Try to find a program
459
-	 *
460
-	 * @param string $program
461
-	 * @return null|string
462
-	 */
463
-	public static function findBinaryPath($program) {
464
-		$memcache = \OC::$server->getMemCacheFactory()->createDistributed('findBinaryPath');
465
-		if ($memcache->hasKey($program)) {
466
-			return $memcache->get($program);
467
-		}
468
-		$result = null;
469
-		if (self::is_function_enabled('exec')) {
470
-			$exeSniffer = new ExecutableFinder();
471
-			// Returns null if nothing is found
472
-			$result = $exeSniffer->find($program, null, ['/usr/local/sbin', '/usr/local/bin', '/usr/sbin', '/usr/bin', '/sbin', '/bin', '/opt/bin']);
473
-		}
474
-		// store the value for 5 minutes
475
-		$memcache->set($program, $result, 300);
476
-		return $result;
477
-	}
478
-
479
-	/**
480
-	 * Calculate the disc space for the given path
481
-	 *
482
-	 * BEWARE: this requires that Util::setupFS() was called
483
-	 * already !
484
-	 *
485
-	 * @param string $path
486
-	 * @param \OCP\Files\FileInfo $rootInfo (optional)
487
-	 * @return array
488
-	 * @throws \OCP\Files\NotFoundException
489
-	 */
490
-	public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true) {
491
-		/** @var ICacheFactory $cacheFactory */
492
-		$cacheFactory = \OC::$server->get(ICacheFactory::class);
493
-		$memcache = $cacheFactory->createLocal('storage_info');
494
-
495
-		// return storage info without adding mount points
496
-		$includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
497
-
498
-		$fullPath = Filesystem::getView()->getAbsolutePath($path);
499
-		$cacheKey = $fullPath. '::' . ($includeMountPoints ? 'include' : 'exclude');
500
-		$cached = $memcache->get($cacheKey);
501
-		if ($cached) {
502
-			return $cached;
503
-		}
504
-
505
-		if (!$rootInfo) {
506
-			$rootInfo = \OC\Files\Filesystem::getFileInfo($path, $includeExtStorage ? 'ext' : false);
507
-		}
508
-		if (!$rootInfo instanceof \OCP\Files\FileInfo) {
509
-			throw new \OCP\Files\NotFoundException();
510
-		}
511
-		$used = $rootInfo->getSize($includeMountPoints);
512
-		if ($used < 0) {
513
-			$used = 0;
514
-		}
515
-		$quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
516
-		$mount = $rootInfo->getMountPoint();
517
-		$storage = $mount->getStorage();
518
-		$sourceStorage = $storage;
519
-		if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
520
-			$includeExtStorage = false;
521
-			$sourceStorage = $storage->getSourceStorage();
522
-			$internalPath = $storage->getUnjailedPath($rootInfo->getInternalPath());
523
-		} else {
524
-			$internalPath = $rootInfo->getInternalPath();
525
-		}
526
-		if ($includeExtStorage) {
527
-			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
528
-				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
529
-			) {
530
-				/** @var \OC\Files\Storage\Home $storage */
531
-				$user = $storage->getUser();
532
-			} else {
533
-				$user = \OC::$server->getUserSession()->getUser();
534
-			}
535
-			$quota = OC_Util::getUserQuota($user);
536
-			if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
537
-				// always get free space / total space from root + mount points
538
-				return self::getGlobalStorageInfo($quota, $user, $mount);
539
-			}
540
-		}
541
-
542
-		// TODO: need a better way to get total space from storage
543
-		if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
544
-			/** @var \OC\Files\Storage\Wrapper\Quota $storage */
545
-			$quota = $sourceStorage->getQuota();
546
-		}
547
-		$free = $sourceStorage->free_space($internalPath);
548
-		if ($free >= 0) {
549
-			$total = $free + $used;
550
-		} else {
551
-			$total = $free; //either unknown or unlimited
552
-		}
553
-		if ($total > 0) {
554
-			if ($quota > 0 && $total > $quota) {
555
-				$total = $quota;
556
-			}
557
-			// prevent division by zero or error codes (negative values)
558
-			$relative = round(($used / $total) * 10000) / 100;
559
-		} else {
560
-			$relative = 0;
561
-		}
562
-
563
-		$ownerId = $storage->getOwner($path);
564
-		$ownerDisplayName = '';
565
-		$owner = \OC::$server->getUserManager()->get($ownerId);
566
-		if ($owner) {
567
-			$ownerDisplayName = $owner->getDisplayName();
568
-		}
569
-		if (substr_count($mount->getMountPoint(), '/') < 3) {
570
-			$mountPoint = '';
571
-		} else {
572
-			[,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
573
-		}
574
-
575
-		$info = [
576
-			'free' => $free,
577
-			'used' => $used,
578
-			'quota' => $quota,
579
-			'total' => $total,
580
-			'relative' => $relative,
581
-			'owner' => $ownerId,
582
-			'ownerDisplayName' => $ownerDisplayName,
583
-			'mountType' => $mount->getMountType(),
584
-			'mountPoint' => trim($mountPoint, '/'),
585
-		];
586
-
587
-		$memcache->set($cacheKey, $info, 5 * 60);
588
-
589
-		return $info;
590
-	}
591
-
592
-	/**
593
-	 * Get storage info including all mount points and quota
594
-	 */
595
-	private static function getGlobalStorageInfo(int $quota, IUser $user, IMountPoint $mount): array {
596
-		$rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
597
-		$used = $rootInfo['size'];
598
-		if ($used < 0) {
599
-			$used = 0;
600
-		}
601
-
602
-		$total = $quota;
603
-		$free = $quota - $used;
604
-
605
-		if ($total > 0) {
606
-			if ($quota > 0 && $total > $quota) {
607
-				$total = $quota;
608
-			}
609
-			// prevent division by zero or error codes (negative values)
610
-			$relative = round(($used / $total) * 10000) / 100;
611
-		} else {
612
-			$relative = 0;
613
-		}
614
-
615
-		if (substr_count($mount->getMountPoint(), '/') < 3) {
616
-			$mountPoint = '';
617
-		} else {
618
-			[,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
619
-		}
620
-
621
-		return [
622
-			'free' => $free,
623
-			'used' => $used,
624
-			'total' => $total,
625
-			'relative' => $relative,
626
-			'quota' => $quota,
627
-			'owner' => $user->getUID(),
628
-			'ownerDisplayName' => $user->getDisplayName(),
629
-			'mountType' => $mount->getMountType(),
630
-			'mountPoint' => trim($mountPoint, '/'),
631
-		];
632
-	}
633
-
634
-	/**
635
-	 * Returns whether the config file is set manually to read-only
636
-	 * @return bool
637
-	 */
638
-	public static function isReadOnlyConfigEnabled() {
639
-		return \OC::$server->getConfig()->getSystemValue('config_is_read_only', false);
640
-	}
57
+    private static $templateManager;
58
+
59
+    /**
60
+     * Make a human file size
61
+     * @param int $bytes file size in bytes
62
+     * @return string a human readable file size
63
+     *
64
+     * Makes 2048 to 2 kB.
65
+     */
66
+    public static function humanFileSize($bytes) {
67
+        if ($bytes < 0) {
68
+            return "?";
69
+        }
70
+        if ($bytes < 1024) {
71
+            return "$bytes B";
72
+        }
73
+        $bytes = round($bytes / 1024, 0);
74
+        if ($bytes < 1024) {
75
+            return "$bytes KB";
76
+        }
77
+        $bytes = round($bytes / 1024, 1);
78
+        if ($bytes < 1024) {
79
+            return "$bytes MB";
80
+        }
81
+        $bytes = round($bytes / 1024, 1);
82
+        if ($bytes < 1024) {
83
+            return "$bytes GB";
84
+        }
85
+        $bytes = round($bytes / 1024, 1);
86
+        if ($bytes < 1024) {
87
+            return "$bytes TB";
88
+        }
89
+
90
+        $bytes = round($bytes / 1024, 1);
91
+        return "$bytes PB";
92
+    }
93
+
94
+    /**
95
+     * Make a computer file size
96
+     * @param string $str file size in human readable format
97
+     * @return float|bool a file size in bytes
98
+     *
99
+     * Makes 2kB to 2048.
100
+     *
101
+     * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418
102
+     */
103
+    public static function computerFileSize($str) {
104
+        $str = strtolower($str);
105
+        if (is_numeric($str)) {
106
+            return (float)$str;
107
+        }
108
+
109
+        $bytes_array = [
110
+            'b' => 1,
111
+            'k' => 1024,
112
+            'kb' => 1024,
113
+            'mb' => 1024 * 1024,
114
+            'm' => 1024 * 1024,
115
+            'gb' => 1024 * 1024 * 1024,
116
+            'g' => 1024 * 1024 * 1024,
117
+            'tb' => 1024 * 1024 * 1024 * 1024,
118
+            't' => 1024 * 1024 * 1024 * 1024,
119
+            'pb' => 1024 * 1024 * 1024 * 1024 * 1024,
120
+            'p' => 1024 * 1024 * 1024 * 1024 * 1024,
121
+        ];
122
+
123
+        $bytes = (float)$str;
124
+
125
+        if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
126
+            $bytes *= $bytes_array[$matches[1]];
127
+        } else {
128
+            return false;
129
+        }
130
+
131
+        $bytes = round($bytes);
132
+
133
+        return $bytes;
134
+    }
135
+
136
+    /**
137
+     * Recursive copying of folders
138
+     * @param string $src source folder
139
+     * @param string $dest target folder
140
+     *
141
+     */
142
+    public static function copyr($src, $dest) {
143
+        if (is_dir($src)) {
144
+            if (!is_dir($dest)) {
145
+                mkdir($dest);
146
+            }
147
+            $files = scandir($src);
148
+            foreach ($files as $file) {
149
+                if ($file != "." && $file != "..") {
150
+                    self::copyr("$src/$file", "$dest/$file");
151
+                }
152
+            }
153
+        } elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) {
154
+            copy($src, $dest);
155
+        }
156
+    }
157
+
158
+    /**
159
+     * Recursive deletion of folders
160
+     * @param string $dir path to the folder
161
+     * @param bool $deleteSelf if set to false only the content of the folder will be deleted
162
+     * @return bool
163
+     */
164
+    public static function rmdirr($dir, $deleteSelf = true) {
165
+        if (is_dir($dir)) {
166
+            $files = new RecursiveIteratorIterator(
167
+                new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
168
+                RecursiveIteratorIterator::CHILD_FIRST
169
+            );
170
+
171
+            foreach ($files as $fileInfo) {
172
+                /** @var SplFileInfo $fileInfo */
173
+                if ($fileInfo->isLink()) {
174
+                    unlink($fileInfo->getPathname());
175
+                } elseif ($fileInfo->isDir()) {
176
+                    rmdir($fileInfo->getRealPath());
177
+                } else {
178
+                    unlink($fileInfo->getRealPath());
179
+                }
180
+            }
181
+            if ($deleteSelf) {
182
+                rmdir($dir);
183
+            }
184
+        } elseif (file_exists($dir)) {
185
+            if ($deleteSelf) {
186
+                unlink($dir);
187
+            }
188
+        }
189
+        if (!$deleteSelf) {
190
+            return true;
191
+        }
192
+
193
+        return !file_exists($dir);
194
+    }
195
+
196
+    /**
197
+     * @deprecated 18.0.0
198
+     * @return \OC\Files\Type\TemplateManager
199
+     */
200
+    public static function getFileTemplateManager() {
201
+        if (!self::$templateManager) {
202
+            self::$templateManager = new \OC\Files\Type\TemplateManager();
203
+        }
204
+        return self::$templateManager;
205
+    }
206
+
207
+    /**
208
+     * detect if a given program is found in the search PATH
209
+     *
210
+     * @param string $name
211
+     * @param bool $path
212
+     * @internal param string $program name
213
+     * @internal param string $optional search path, defaults to $PATH
214
+     * @return bool    true if executable program found in path
215
+     */
216
+    public static function canExecute($name, $path = false) {
217
+        // path defaults to PATH from environment if not set
218
+        if ($path === false) {
219
+            $path = getenv("PATH");
220
+        }
221
+        // we look for an executable file of that name
222
+        $exts = [""];
223
+        $check_fn = "is_executable";
224
+        // Default check will be done with $path directories :
225
+        $dirs = explode(PATH_SEPARATOR, $path);
226
+        // WARNING : We have to check if open_basedir is enabled :
227
+        $obd = OC::$server->get(IniGetWrapper::class)->getString('open_basedir');
228
+        if ($obd != "none") {
229
+            $obd_values = explode(PATH_SEPARATOR, $obd);
230
+            if (count($obd_values) > 0 and $obd_values[0]) {
231
+                // open_basedir is in effect !
232
+                // We need to check if the program is in one of these dirs :
233
+                $dirs = $obd_values;
234
+            }
235
+        }
236
+        foreach ($dirs as $dir) {
237
+            foreach ($exts as $ext) {
238
+                if ($check_fn("$dir/$name" . $ext)) {
239
+                    return true;
240
+                }
241
+            }
242
+        }
243
+        return false;
244
+    }
245
+
246
+    /**
247
+     * copy the contents of one stream to another
248
+     *
249
+     * @param resource $source
250
+     * @param resource $target
251
+     * @return array the number of bytes copied and result
252
+     */
253
+    public static function streamCopy($source, $target) {
254
+        if (!$source or !$target) {
255
+            return [0, false];
256
+        }
257
+        $bufSize = 8192;
258
+        $result = true;
259
+        $count = 0;
260
+        while (!feof($source)) {
261
+            $buf = fread($source, $bufSize);
262
+            $bytesWritten = fwrite($target, $buf);
263
+            if ($bytesWritten !== false) {
264
+                $count += $bytesWritten;
265
+            }
266
+            // note: strlen is expensive so only use it when necessary,
267
+            // on the last block
268
+            if ($bytesWritten === false
269
+                || ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
270
+            ) {
271
+                // write error, could be disk full ?
272
+                $result = false;
273
+                break;
274
+            }
275
+        }
276
+        return [$count, $result];
277
+    }
278
+
279
+    /**
280
+     * Adds a suffix to the name in case the file exists
281
+     *
282
+     * @param string $path
283
+     * @param string $filename
284
+     * @return string
285
+     */
286
+    public static function buildNotExistingFileName($path, $filename) {
287
+        $view = \OC\Files\Filesystem::getView();
288
+        return self::buildNotExistingFileNameForView($path, $filename, $view);
289
+    }
290
+
291
+    /**
292
+     * Adds a suffix to the name in case the file exists
293
+     *
294
+     * @param string $path
295
+     * @param string $filename
296
+     * @return string
297
+     */
298
+    public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
299
+        if ($path === '/') {
300
+            $path = '';
301
+        }
302
+        if ($pos = strrpos($filename, '.')) {
303
+            $name = substr($filename, 0, $pos);
304
+            $ext = substr($filename, $pos);
305
+        } else {
306
+            $name = $filename;
307
+            $ext = '';
308
+        }
309
+
310
+        $newpath = $path . '/' . $filename;
311
+        if ($view->file_exists($newpath)) {
312
+            if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
313
+                //Replace the last "(number)" with "(number+1)"
314
+                $last_match = count($matches[0]) - 1;
315
+                $counter = $matches[1][$last_match][0] + 1;
316
+                $offset = $matches[0][$last_match][1];
317
+                $match_length = strlen($matches[0][$last_match][0]);
318
+            } else {
319
+                $counter = 2;
320
+                $match_length = 0;
321
+                $offset = false;
322
+            }
323
+            do {
324
+                if ($offset) {
325
+                    //Replace the last "(number)" with "(number+1)"
326
+                    $newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
327
+                } else {
328
+                    $newname = $name . ' (' . $counter . ')';
329
+                }
330
+                $newpath = $path . '/' . $newname . $ext;
331
+                $counter++;
332
+            } while ($view->file_exists($newpath));
333
+        }
334
+
335
+        return $newpath;
336
+    }
337
+
338
+    /**
339
+     * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
340
+     *
341
+     * @param array $input The array to work on
342
+     * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
343
+     * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
344
+     * @return array
345
+     *
346
+     * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
347
+     * based on https://www.php.net/manual/en/function.array-change-key-case.php#107715
348
+     *
349
+     */
350
+    public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
351
+        $case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
352
+        $ret = [];
353
+        foreach ($input as $k => $v) {
354
+            $ret[mb_convert_case($k, $case, $encoding)] = $v;
355
+        }
356
+        return $ret;
357
+    }
358
+
359
+    /**
360
+     * performs a search in a nested array
361
+     * @param array $haystack the array to be searched
362
+     * @param string $needle the search string
363
+     * @param mixed $index optional, only search this key name
364
+     * @return mixed the key of the matching field, otherwise false
365
+     *
366
+     * performs a search in a nested array
367
+     *
368
+     * taken from https://www.php.net/manual/en/function.array-search.php#97645
369
+     */
370
+    public static function recursiveArraySearch($haystack, $needle, $index = null) {
371
+        $aIt = new RecursiveArrayIterator($haystack);
372
+        $it = new RecursiveIteratorIterator($aIt);
373
+
374
+        while ($it->valid()) {
375
+            if (((isset($index) and ($it->key() == $index)) or !isset($index)) and ($it->current() == $needle)) {
376
+                return $aIt->key();
377
+            }
378
+
379
+            $it->next();
380
+        }
381
+
382
+        return false;
383
+    }
384
+
385
+    /**
386
+     * calculates the maximum upload size respecting system settings, free space and user quota
387
+     *
388
+     * @param string $dir the current folder where the user currently operates
389
+     * @param int $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
390
+     * @return int number of bytes representing
391
+     */
392
+    public static function maxUploadFilesize($dir, $freeSpace = null) {
393
+        if (is_null($freeSpace) || $freeSpace < 0) {
394
+            $freeSpace = self::freeSpace($dir);
395
+        }
396
+        return min($freeSpace, self::uploadLimit());
397
+    }
398
+
399
+    /**
400
+     * Calculate free space left within user quota
401
+     *
402
+     * @param string $dir the current folder where the user currently operates
403
+     * @return int number of bytes representing
404
+     */
405
+    public static function freeSpace($dir) {
406
+        $freeSpace = \OC\Files\Filesystem::free_space($dir);
407
+        if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
408
+            $freeSpace = max($freeSpace, 0);
409
+            return $freeSpace;
410
+        } else {
411
+            return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
412
+        }
413
+    }
414
+
415
+    /**
416
+     * Calculate PHP upload limit
417
+     *
418
+     * @return int PHP upload file size limit
419
+     */
420
+    public static function uploadLimit() {
421
+        $ini = \OC::$server->get(IniGetWrapper::class);
422
+        $upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize'));
423
+        $post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size'));
424
+        if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) {
425
+            return INF;
426
+        } elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) {
427
+            return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
428
+        } else {
429
+            return min($upload_max_filesize, $post_max_size);
430
+        }
431
+    }
432
+
433
+    /**
434
+     * Checks if a function is available
435
+     *
436
+     * @param string $function_name
437
+     * @return bool
438
+     */
439
+    public static function is_function_enabled($function_name) {
440
+        if (!function_exists($function_name)) {
441
+            return false;
442
+        }
443
+        $ini = \OC::$server->get(IniGetWrapper::class);
444
+        $disabled = explode(',', $ini->get('disable_functions') ?: '');
445
+        $disabled = array_map('trim', $disabled);
446
+        if (in_array($function_name, $disabled)) {
447
+            return false;
448
+        }
449
+        $disabled = explode(',', $ini->get('suhosin.executor.func.blacklist') ?: '');
450
+        $disabled = array_map('trim', $disabled);
451
+        if (in_array($function_name, $disabled)) {
452
+            return false;
453
+        }
454
+        return true;
455
+    }
456
+
457
+    /**
458
+     * Try to find a program
459
+     *
460
+     * @param string $program
461
+     * @return null|string
462
+     */
463
+    public static function findBinaryPath($program) {
464
+        $memcache = \OC::$server->getMemCacheFactory()->createDistributed('findBinaryPath');
465
+        if ($memcache->hasKey($program)) {
466
+            return $memcache->get($program);
467
+        }
468
+        $result = null;
469
+        if (self::is_function_enabled('exec')) {
470
+            $exeSniffer = new ExecutableFinder();
471
+            // Returns null if nothing is found
472
+            $result = $exeSniffer->find($program, null, ['/usr/local/sbin', '/usr/local/bin', '/usr/sbin', '/usr/bin', '/sbin', '/bin', '/opt/bin']);
473
+        }
474
+        // store the value for 5 minutes
475
+        $memcache->set($program, $result, 300);
476
+        return $result;
477
+    }
478
+
479
+    /**
480
+     * Calculate the disc space for the given path
481
+     *
482
+     * BEWARE: this requires that Util::setupFS() was called
483
+     * already !
484
+     *
485
+     * @param string $path
486
+     * @param \OCP\Files\FileInfo $rootInfo (optional)
487
+     * @return array
488
+     * @throws \OCP\Files\NotFoundException
489
+     */
490
+    public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true) {
491
+        /** @var ICacheFactory $cacheFactory */
492
+        $cacheFactory = \OC::$server->get(ICacheFactory::class);
493
+        $memcache = $cacheFactory->createLocal('storage_info');
494
+
495
+        // return storage info without adding mount points
496
+        $includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
497
+
498
+        $fullPath = Filesystem::getView()->getAbsolutePath($path);
499
+        $cacheKey = $fullPath. '::' . ($includeMountPoints ? 'include' : 'exclude');
500
+        $cached = $memcache->get($cacheKey);
501
+        if ($cached) {
502
+            return $cached;
503
+        }
504
+
505
+        if (!$rootInfo) {
506
+            $rootInfo = \OC\Files\Filesystem::getFileInfo($path, $includeExtStorage ? 'ext' : false);
507
+        }
508
+        if (!$rootInfo instanceof \OCP\Files\FileInfo) {
509
+            throw new \OCP\Files\NotFoundException();
510
+        }
511
+        $used = $rootInfo->getSize($includeMountPoints);
512
+        if ($used < 0) {
513
+            $used = 0;
514
+        }
515
+        $quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
516
+        $mount = $rootInfo->getMountPoint();
517
+        $storage = $mount->getStorage();
518
+        $sourceStorage = $storage;
519
+        if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
520
+            $includeExtStorage = false;
521
+            $sourceStorage = $storage->getSourceStorage();
522
+            $internalPath = $storage->getUnjailedPath($rootInfo->getInternalPath());
523
+        } else {
524
+            $internalPath = $rootInfo->getInternalPath();
525
+        }
526
+        if ($includeExtStorage) {
527
+            if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
528
+                || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
529
+            ) {
530
+                /** @var \OC\Files\Storage\Home $storage */
531
+                $user = $storage->getUser();
532
+            } else {
533
+                $user = \OC::$server->getUserSession()->getUser();
534
+            }
535
+            $quota = OC_Util::getUserQuota($user);
536
+            if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
537
+                // always get free space / total space from root + mount points
538
+                return self::getGlobalStorageInfo($quota, $user, $mount);
539
+            }
540
+        }
541
+
542
+        // TODO: need a better way to get total space from storage
543
+        if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
544
+            /** @var \OC\Files\Storage\Wrapper\Quota $storage */
545
+            $quota = $sourceStorage->getQuota();
546
+        }
547
+        $free = $sourceStorage->free_space($internalPath);
548
+        if ($free >= 0) {
549
+            $total = $free + $used;
550
+        } else {
551
+            $total = $free; //either unknown or unlimited
552
+        }
553
+        if ($total > 0) {
554
+            if ($quota > 0 && $total > $quota) {
555
+                $total = $quota;
556
+            }
557
+            // prevent division by zero or error codes (negative values)
558
+            $relative = round(($used / $total) * 10000) / 100;
559
+        } else {
560
+            $relative = 0;
561
+        }
562
+
563
+        $ownerId = $storage->getOwner($path);
564
+        $ownerDisplayName = '';
565
+        $owner = \OC::$server->getUserManager()->get($ownerId);
566
+        if ($owner) {
567
+            $ownerDisplayName = $owner->getDisplayName();
568
+        }
569
+        if (substr_count($mount->getMountPoint(), '/') < 3) {
570
+            $mountPoint = '';
571
+        } else {
572
+            [,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
573
+        }
574
+
575
+        $info = [
576
+            'free' => $free,
577
+            'used' => $used,
578
+            'quota' => $quota,
579
+            'total' => $total,
580
+            'relative' => $relative,
581
+            'owner' => $ownerId,
582
+            'ownerDisplayName' => $ownerDisplayName,
583
+            'mountType' => $mount->getMountType(),
584
+            'mountPoint' => trim($mountPoint, '/'),
585
+        ];
586
+
587
+        $memcache->set($cacheKey, $info, 5 * 60);
588
+
589
+        return $info;
590
+    }
591
+
592
+    /**
593
+     * Get storage info including all mount points and quota
594
+     */
595
+    private static function getGlobalStorageInfo(int $quota, IUser $user, IMountPoint $mount): array {
596
+        $rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
597
+        $used = $rootInfo['size'];
598
+        if ($used < 0) {
599
+            $used = 0;
600
+        }
601
+
602
+        $total = $quota;
603
+        $free = $quota - $used;
604
+
605
+        if ($total > 0) {
606
+            if ($quota > 0 && $total > $quota) {
607
+                $total = $quota;
608
+            }
609
+            // prevent division by zero or error codes (negative values)
610
+            $relative = round(($used / $total) * 10000) / 100;
611
+        } else {
612
+            $relative = 0;
613
+        }
614
+
615
+        if (substr_count($mount->getMountPoint(), '/') < 3) {
616
+            $mountPoint = '';
617
+        } else {
618
+            [,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
619
+        }
620
+
621
+        return [
622
+            'free' => $free,
623
+            'used' => $used,
624
+            'total' => $total,
625
+            'relative' => $relative,
626
+            'quota' => $quota,
627
+            'owner' => $user->getUID(),
628
+            'ownerDisplayName' => $user->getDisplayName(),
629
+            'mountType' => $mount->getMountType(),
630
+            'mountPoint' => trim($mountPoint, '/'),
631
+        ];
632
+    }
633
+
634
+    /**
635
+     * Returns whether the config file is set manually to read-only
636
+     * @return bool
637
+     */
638
+    public static function isReadOnlyConfigEnabled() {
639
+        return \OC::$server->getConfig()->getSystemValue('config_is_read_only', false);
640
+    }
641 641
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -103,7 +103,7 @@  discard block
 block discarded – undo
103 103
 	public static function computerFileSize($str) {
104 104
 		$str = strtolower($str);
105 105
 		if (is_numeric($str)) {
106
-			return (float)$str;
106
+			return (float) $str;
107 107
 		}
108 108
 
109 109
 		$bytes_array = [
@@ -120,7 +120,7 @@  discard block
 block discarded – undo
120 120
 			'p' => 1024 * 1024 * 1024 * 1024 * 1024,
121 121
 		];
122 122
 
123
-		$bytes = (float)$str;
123
+		$bytes = (float) $str;
124 124
 
125 125
 		if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
126 126
 			$bytes *= $bytes_array[$matches[1]];
@@ -235,7 +235,7 @@  discard block
 block discarded – undo
235 235
 		}
236 236
 		foreach ($dirs as $dir) {
237 237
 			foreach ($exts as $ext) {
238
-				if ($check_fn("$dir/$name" . $ext)) {
238
+				if ($check_fn("$dir/$name".$ext)) {
239 239
 					return true;
240 240
 				}
241 241
 			}
@@ -307,7 +307,7 @@  discard block
 block discarded – undo
307 307
 			$ext = '';
308 308
 		}
309 309
 
310
-		$newpath = $path . '/' . $filename;
310
+		$newpath = $path.'/'.$filename;
311 311
 		if ($view->file_exists($newpath)) {
312 312
 			if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
313 313
 				//Replace the last "(number)" with "(number+1)"
@@ -323,11 +323,11 @@  discard block
 block discarded – undo
323 323
 			do {
324 324
 				if ($offset) {
325 325
 					//Replace the last "(number)" with "(number+1)"
326
-					$newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
326
+					$newname = substr_replace($name, '('.$counter.')', $offset, $match_length);
327 327
 				} else {
328
-					$newname = $name . ' (' . $counter . ')';
328
+					$newname = $name.' ('.$counter.')';
329 329
 				}
330
-				$newpath = $path . '/' . $newname . $ext;
330
+				$newpath = $path.'/'.$newname.$ext;
331 331
 				$counter++;
332 332
 			} while ($view->file_exists($newpath));
333 333
 		}
@@ -408,7 +408,7 @@  discard block
 block discarded – undo
408 408
 			$freeSpace = max($freeSpace, 0);
409 409
 			return $freeSpace;
410 410
 		} else {
411
-			return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
411
+			return (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
412 412
 		}
413 413
 	}
414 414
 
@@ -421,9 +421,9 @@  discard block
 block discarded – undo
421 421
 		$ini = \OC::$server->get(IniGetWrapper::class);
422 422
 		$upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize'));
423 423
 		$post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size'));
424
-		if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) {
424
+		if ((int) $upload_max_filesize === 0 and (int) $post_max_size === 0) {
425 425
 			return INF;
426
-		} elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) {
426
+		} elseif ((int) $upload_max_filesize === 0 or (int) $post_max_size === 0) {
427 427
 			return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
428 428
 		} else {
429 429
 			return min($upload_max_filesize, $post_max_size);
@@ -496,7 +496,7 @@  discard block
 block discarded – undo
496 496
 		$includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
497 497
 
498 498
 		$fullPath = Filesystem::getView()->getAbsolutePath($path);
499
-		$cacheKey = $fullPath. '::' . ($includeMountPoints ? 'include' : 'exclude');
499
+		$cacheKey = $fullPath.'::'.($includeMountPoints ? 'include' : 'exclude');
500 500
 		$cached = $memcache->get($cacheKey);
501 501
 		if ($cached) {
502 502
 			return $cached;
Please login to merge, or discard this patch.