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