Completed
Push — master ( 1b72dd...b3b630 )
by John
26:15 queued 14s
created
lib/public/Files.php 1 patch
Indentation   +94 added lines, -94 removed lines patch added patch discarded remove patch
@@ -16,105 +16,105 @@
 block discarded – undo
16 16
  * @deprecated 14.0.0
17 17
  */
18 18
 class Files {
19
-	/**
20
-	 * Recursive deletion of folders
21
-	 *
22
-	 * @param string $dir path to the folder
23
-	 * @param bool $deleteSelf if set to false only the content of the folder will be deleted
24
-	 * @return bool
25
-	 * @since 5.0.0
26
-	 * @since 32.0.0 added the $deleteSelf parameter
27
-	 * @deprecated 14.0.0
28
-	 */
29
-	public static function rmdirr($dir, bool $deleteSelf = true) {
30
-		if (is_dir($dir)) {
31
-			$files = new \RecursiveIteratorIterator(
32
-				new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
33
-				\RecursiveIteratorIterator::CHILD_FIRST
34
-			);
19
+    /**
20
+     * Recursive deletion of folders
21
+     *
22
+     * @param string $dir path to the folder
23
+     * @param bool $deleteSelf if set to false only the content of the folder will be deleted
24
+     * @return bool
25
+     * @since 5.0.0
26
+     * @since 32.0.0 added the $deleteSelf parameter
27
+     * @deprecated 14.0.0
28
+     */
29
+    public static function rmdirr($dir, bool $deleteSelf = true) {
30
+        if (is_dir($dir)) {
31
+            $files = new \RecursiveIteratorIterator(
32
+                new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
33
+                \RecursiveIteratorIterator::CHILD_FIRST
34
+            );
35 35
 
36
-			foreach ($files as $fileInfo) {
37
-				/** @var \SplFileInfo $fileInfo */
38
-				if ($fileInfo->isLink()) {
39
-					unlink($fileInfo->getPathname());
40
-				} elseif ($fileInfo->isDir()) {
41
-					rmdir($fileInfo->getRealPath());
42
-				} else {
43
-					unlink($fileInfo->getRealPath());
44
-				}
45
-			}
46
-			if ($deleteSelf) {
47
-				rmdir($dir);
48
-			}
49
-		} elseif (file_exists($dir)) {
50
-			if ($deleteSelf) {
51
-				unlink($dir);
52
-			}
53
-		}
54
-		if (!$deleteSelf) {
55
-			return true;
56
-		}
36
+            foreach ($files as $fileInfo) {
37
+                /** @var \SplFileInfo $fileInfo */
38
+                if ($fileInfo->isLink()) {
39
+                    unlink($fileInfo->getPathname());
40
+                } elseif ($fileInfo->isDir()) {
41
+                    rmdir($fileInfo->getRealPath());
42
+                } else {
43
+                    unlink($fileInfo->getRealPath());
44
+                }
45
+            }
46
+            if ($deleteSelf) {
47
+                rmdir($dir);
48
+            }
49
+        } elseif (file_exists($dir)) {
50
+            if ($deleteSelf) {
51
+                unlink($dir);
52
+            }
53
+        }
54
+        if (!$deleteSelf) {
55
+            return true;
56
+        }
57 57
 
58
-		return !file_exists($dir);
59
-	}
58
+        return !file_exists($dir);
59
+    }
60 60
 
61
-	/**
62
-	 * Get the mimetype form a local file
63
-	 * @param string $path
64
-	 * @return string
65
-	 *                does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead
66
-	 * @since 5.0.0
67
-	 * @deprecated 14.0.0
68
-	 */
69
-	public static function getMimeType($path) {
70
-		return \OC::$server->getMimeTypeDetector()->detect($path);
71
-	}
61
+    /**
62
+     * Get the mimetype form a local file
63
+     * @param string $path
64
+     * @return string
65
+     *                does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead
66
+     * @since 5.0.0
67
+     * @deprecated 14.0.0
68
+     */
69
+    public static function getMimeType($path) {
70
+        return \OC::$server->getMimeTypeDetector()->detect($path);
71
+    }
72 72
 
73
-	/**
74
-	 * Search for files by mimetype
75
-	 * @param string $mimetype
76
-	 * @return array
77
-	 * @since 6.0.0
78
-	 * @deprecated 14.0.0
79
-	 */
80
-	public static function searchByMime($mimetype) {
81
-		return \OC\Files\Filesystem::searchByMime($mimetype);
82
-	}
73
+    /**
74
+     * Search for files by mimetype
75
+     * @param string $mimetype
76
+     * @return array
77
+     * @since 6.0.0
78
+     * @deprecated 14.0.0
79
+     */
80
+    public static function searchByMime($mimetype) {
81
+        return \OC\Files\Filesystem::searchByMime($mimetype);
82
+    }
83 83
 
84
-	/**
85
-	 * Copy the contents of one stream to another
86
-	 * @param resource $source
87
-	 * @param resource $target
88
-	 * @return int the number of bytes copied
89
-	 * @since 5.0.0
90
-	 * @deprecated 14.0.0
91
-	 */
92
-	public static function streamCopy($source, $target) {
93
-		[$count, ] = \OC_Helper::streamCopy($source, $target);
94
-		return $count;
95
-	}
84
+    /**
85
+     * Copy the contents of one stream to another
86
+     * @param resource $source
87
+     * @param resource $target
88
+     * @return int the number of bytes copied
89
+     * @since 5.0.0
90
+     * @deprecated 14.0.0
91
+     */
92
+    public static function streamCopy($source, $target) {
93
+        [$count, ] = \OC_Helper::streamCopy($source, $target);
94
+        return $count;
95
+    }
96 96
 
97
-	/**
98
-	 * Adds a suffix to the name in case the file exists
99
-	 * @param string $path
100
-	 * @param string $filename
101
-	 * @return string
102
-	 * @since 5.0.0
103
-	 * @deprecated 14.0.0 use getNonExistingName of the OCP\Files\Folder object
104
-	 */
105
-	public static function buildNotExistingFileName($path, $filename) {
106
-		return \OC_Helper::buildNotExistingFileName($path, $filename);
107
-	}
97
+    /**
98
+     * Adds a suffix to the name in case the file exists
99
+     * @param string $path
100
+     * @param string $filename
101
+     * @return string
102
+     * @since 5.0.0
103
+     * @deprecated 14.0.0 use getNonExistingName of the OCP\Files\Folder object
104
+     */
105
+    public static function buildNotExistingFileName($path, $filename) {
106
+        return \OC_Helper::buildNotExistingFileName($path, $filename);
107
+    }
108 108
 
109
-	/**
110
-	 * Gets the Storage for an app - creates the needed folder if they are not
111
-	 * existent
112
-	 * @param string $app
113
-	 * @return \OC\Files\View
114
-	 * @since 5.0.0
115
-	 * @deprecated 14.0.0 use IAppData instead
116
-	 */
117
-	public static function getStorage($app) {
118
-		return \OC_App::getStorage($app);
119
-	}
109
+    /**
110
+     * Gets the Storage for an app - creates the needed folder if they are not
111
+     * existent
112
+     * @param string $app
113
+     * @return \OC\Files\View
114
+     * @since 5.0.0
115
+     * @deprecated 14.0.0 use IAppData instead
116
+     */
117
+    public static function getStorage($app) {
118
+        return \OC_App::getStorage($app);
119
+    }
120 120
 }
Please login to merge, or discard this patch.
lib/private/legacy/OC_Helper.php 1 patch
Indentation   +536 added lines, -536 removed lines patch added patch discarded remove patch
@@ -31,543 +31,543 @@
 block discarded – undo
31 31
  * }
32 32
  */
33 33
 class OC_Helper {
34
-	private static $templateManager;
35
-	private static ?ICacheFactory $cacheFactory = null;
36
-	private static ?bool $quotaIncludeExternalStorage = null;
37
-
38
-	/**
39
-	 * Make a human file size
40
-	 * @param int|float $bytes file size in bytes
41
-	 * @return string a human readable file size
42
-	 * @deprecated 4.0.0 replaced with \OCP\Util::humanFileSize
43
-	 *
44
-	 * Makes 2048 to 2 kB.
45
-	 */
46
-	public static function humanFileSize(int|float $bytes): string {
47
-		return \OCP\Util::humanFileSize($bytes);
48
-	}
49
-
50
-	/**
51
-	 * Make a computer file size
52
-	 * @param string $str file size in human readable format
53
-	 * @return false|int|float a file size in bytes
54
-	 * @deprecated 4.0.0 Use \OCP\Util::computerFileSize
55
-	 *
56
-	 * Makes 2kB to 2048.
57
-	 *
58
-	 * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418
59
-	 */
60
-	public static function computerFileSize(string $str): false|int|float {
61
-		return \OCP\Util::computerFileSize($str);
62
-	}
63
-
64
-	/**
65
-	 * Recursive copying of folders
66
-	 * @param string $src source folder
67
-	 * @param string $dest target folder
68
-	 * @return void
69
-	 */
70
-	public static function copyr($src, $dest) {
71
-		if (!file_exists($src)) {
72
-			return;
73
-		}
74
-
75
-		if (is_dir($src)) {
76
-			if (!is_dir($dest)) {
77
-				mkdir($dest);
78
-			}
79
-			$files = scandir($src);
80
-			foreach ($files as $file) {
81
-				if ($file != '.' && $file != '..') {
82
-					self::copyr("$src/$file", "$dest/$file");
83
-				}
84
-			}
85
-		} else {
86
-			$validator = \OCP\Server::get(FilenameValidator::class);
87
-			if (!$validator->isForbidden($src)) {
88
-				copy($src, $dest);
89
-			}
90
-		}
91
-	}
92
-
93
-	/**
94
-	 * Recursive deletion of folders
95
-	 * @param string $dir path to the folder
96
-	 * @param bool $deleteSelf if set to false only the content of the folder will be deleted
97
-	 * @return bool
98
-	 * @deprecated 5.0.0 use \OCP\Files::rmdirr instead
99
-	 */
100
-	public static function rmdirr($dir, $deleteSelf = true) {
101
-		return \OCP\Files::rmdirr($dir, $deleteSelf);
102
-	}
103
-
104
-	/**
105
-	 * @deprecated 18.0.0
106
-	 * @return \OC\Files\Type\TemplateManager
107
-	 */
108
-	public static function getFileTemplateManager() {
109
-		if (!self::$templateManager) {
110
-			self::$templateManager = new \OC\Files\Type\TemplateManager();
111
-		}
112
-		return self::$templateManager;
113
-	}
114
-
115
-	/**
116
-	 * detect if a given program is found in the search PATH
117
-	 *
118
-	 * @param string $name
119
-	 * @param bool $path
120
-	 * @internal param string $program name
121
-	 * @internal param string $optional search path, defaults to $PATH
122
-	 * @return bool true if executable program found in path
123
-	 * @deprecated 32.0.0 use the \OCP\IBinaryFinder
124
-	 */
125
-	public static function canExecute($name, $path = false) {
126
-		// path defaults to PATH from environment if not set
127
-		if ($path === false) {
128
-			$path = getenv('PATH');
129
-		}
130
-		// we look for an executable file of that name
131
-		$exts = [''];
132
-		$check_fn = 'is_executable';
133
-		// Default check will be done with $path directories :
134
-		$dirs = explode(PATH_SEPARATOR, $path);
135
-		// WARNING : We have to check if open_basedir is enabled :
136
-		$obd = OC::$server->get(IniGetWrapper::class)->getString('open_basedir');
137
-		if ($obd != 'none') {
138
-			$obd_values = explode(PATH_SEPARATOR, $obd);
139
-			if (count($obd_values) > 0 and $obd_values[0]) {
140
-				// open_basedir is in effect !
141
-				// We need to check if the program is in one of these dirs :
142
-				$dirs = $obd_values;
143
-			}
144
-		}
145
-		foreach ($dirs as $dir) {
146
-			foreach ($exts as $ext) {
147
-				if ($check_fn("$dir/$name" . $ext)) {
148
-					return true;
149
-				}
150
-			}
151
-		}
152
-		return false;
153
-	}
154
-
155
-	/**
156
-	 * copy the contents of one stream to another
157
-	 *
158
-	 * @param resource $source
159
-	 * @param resource $target
160
-	 * @return array the number of bytes copied and result
161
-	 */
162
-	public static function streamCopy($source, $target) {
163
-		if (!$source or !$target) {
164
-			return [0, false];
165
-		}
166
-		$bufSize = 8192;
167
-		$result = true;
168
-		$count = 0;
169
-		while (!feof($source)) {
170
-			$buf = fread($source, $bufSize);
171
-			$bytesWritten = fwrite($target, $buf);
172
-			if ($bytesWritten !== false) {
173
-				$count += $bytesWritten;
174
-			}
175
-			// note: strlen is expensive so only use it when necessary,
176
-			// on the last block
177
-			if ($bytesWritten === false
178
-				|| ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
179
-			) {
180
-				// write error, could be disk full ?
181
-				$result = false;
182
-				break;
183
-			}
184
-		}
185
-		return [$count, $result];
186
-	}
187
-
188
-	/**
189
-	 * Adds a suffix to the name in case the file exists
190
-	 *
191
-	 * @param string $path
192
-	 * @param string $filename
193
-	 * @return string
194
-	 */
195
-	public static function buildNotExistingFileName($path, $filename) {
196
-		$view = \OC\Files\Filesystem::getView();
197
-		return self::buildNotExistingFileNameForView($path, $filename, $view);
198
-	}
199
-
200
-	/**
201
-	 * Adds a suffix to the name in case the file exists
202
-	 *
203
-	 * @param string $path
204
-	 * @param string $filename
205
-	 * @return string
206
-	 */
207
-	public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
208
-		if ($path === '/') {
209
-			$path = '';
210
-		}
211
-		if ($pos = strrpos($filename, '.')) {
212
-			$name = substr($filename, 0, $pos);
213
-			$ext = substr($filename, $pos);
214
-		} else {
215
-			$name = $filename;
216
-			$ext = '';
217
-		}
218
-
219
-		$newpath = $path . '/' . $filename;
220
-		if ($view->file_exists($newpath)) {
221
-			if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
222
-				//Replace the last "(number)" with "(number+1)"
223
-				$last_match = count($matches[0]) - 1;
224
-				$counter = $matches[1][$last_match][0] + 1;
225
-				$offset = $matches[0][$last_match][1];
226
-				$match_length = strlen($matches[0][$last_match][0]);
227
-			} else {
228
-				$counter = 2;
229
-				$match_length = 0;
230
-				$offset = false;
231
-			}
232
-			do {
233
-				if ($offset) {
234
-					//Replace the last "(number)" with "(number+1)"
235
-					$newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
236
-				} else {
237
-					$newname = $name . ' (' . $counter . ')';
238
-				}
239
-				$newpath = $path . '/' . $newname . $ext;
240
-				$counter++;
241
-			} while ($view->file_exists($newpath));
242
-		}
243
-
244
-		return $newpath;
245
-	}
246
-
247
-	/**
248
-	 * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
249
-	 *
250
-	 * @param array $input The array to work on
251
-	 * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
252
-	 * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
253
-	 * @return array
254
-	 *
255
-	 * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
256
-	 * based on https://www.php.net/manual/en/function.array-change-key-case.php#107715
257
-	 *
258
-	 */
259
-	public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
260
-		$case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
261
-		$ret = [];
262
-		foreach ($input as $k => $v) {
263
-			$ret[mb_convert_case($k, $case, $encoding)] = $v;
264
-		}
265
-		return $ret;
266
-	}
267
-
268
-	/**
269
-	 * performs a search in a nested array
270
-	 * @param array $haystack the array to be searched
271
-	 * @param string $needle the search string
272
-	 * @param mixed $index optional, only search this key name
273
-	 * @return mixed the key of the matching field, otherwise false
274
-	 *
275
-	 * performs a search in a nested array
276
-	 *
277
-	 * taken from https://www.php.net/manual/en/function.array-search.php#97645
278
-	 */
279
-	public static function recursiveArraySearch($haystack, $needle, $index = null) {
280
-		$aIt = new RecursiveArrayIterator($haystack);
281
-		$it = new RecursiveIteratorIterator($aIt);
282
-
283
-		while ($it->valid()) {
284
-			if (((isset($index) and ($it->key() == $index)) or !isset($index)) and ($it->current() == $needle)) {
285
-				return $aIt->key();
286
-			}
287
-
288
-			$it->next();
289
-		}
290
-
291
-		return false;
292
-	}
293
-
294
-	/**
295
-	 * calculates the maximum upload size respecting system settings, free space and user quota
296
-	 *
297
-	 * @param string $dir the current folder where the user currently operates
298
-	 * @param int|float $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
299
-	 * @return int|float number of bytes representing
300
-	 */
301
-	public static function maxUploadFilesize($dir, $freeSpace = null) {
302
-		if (is_null($freeSpace) || $freeSpace < 0) {
303
-			$freeSpace = self::freeSpace($dir);
304
-		}
305
-		return min($freeSpace, self::uploadLimit());
306
-	}
307
-
308
-	/**
309
-	 * Calculate free space left within user quota
310
-	 *
311
-	 * @param string $dir the current folder where the user currently operates
312
-	 * @return int|float number of bytes representing
313
-	 */
314
-	public static function freeSpace($dir) {
315
-		$freeSpace = \OC\Files\Filesystem::free_space($dir);
316
-		if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
317
-			$freeSpace = max($freeSpace, 0);
318
-			return $freeSpace;
319
-		} else {
320
-			return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
321
-		}
322
-	}
323
-
324
-	/**
325
-	 * Calculate PHP upload limit
326
-	 *
327
-	 * @return int|float PHP upload file size limit
328
-	 */
329
-	public static function uploadLimit() {
330
-		$ini = \OC::$server->get(IniGetWrapper::class);
331
-		$upload_max_filesize = Util::computerFileSize($ini->get('upload_max_filesize')) ?: 0;
332
-		$post_max_size = Util::computerFileSize($ini->get('post_max_size')) ?: 0;
333
-		if ($upload_max_filesize === 0 && $post_max_size === 0) {
334
-			return INF;
335
-		} elseif ($upload_max_filesize === 0 || $post_max_size === 0) {
336
-			return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
337
-		} else {
338
-			return min($upload_max_filesize, $post_max_size);
339
-		}
340
-	}
341
-
342
-	/**
343
-	 * Checks if a function is available
344
-	 *
345
-	 * @deprecated 25.0.0 use \OCP\Util::isFunctionEnabled instead
346
-	 */
347
-	public static function is_function_enabled(string $function_name): bool {
348
-		return \OCP\Util::isFunctionEnabled($function_name);
349
-	}
350
-
351
-	/**
352
-	 * Try to find a program
353
-	 * @deprecated 25.0.0 Use \OC\BinaryFinder directly
354
-	 */
355
-	public static function findBinaryPath(string $program): ?string {
356
-		$result = \OCP\Server::get(IBinaryFinder::class)->findBinaryPath($program);
357
-		return $result !== false ? $result : null;
358
-	}
359
-
360
-	/**
361
-	 * Calculate the disc space for the given path
362
-	 *
363
-	 * BEWARE: this requires that Util::setupFS() was called
364
-	 * already !
365
-	 *
366
-	 * @param string $path
367
-	 * @param \OCP\Files\FileInfo $rootInfo (optional)
368
-	 * @param bool $includeMountPoints whether to include mount points in the size calculation
369
-	 * @param bool $useCache whether to use the cached quota values
370
-	 * @psalm-suppress LessSpecificReturnStatement Legacy code outputs weird types - manually validated that they are correct
371
-	 * @return StorageInfo
372
-	 * @throws \OCP\Files\NotFoundException
373
-	 */
374
-	public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true, $useCache = true) {
375
-		if (!self::$cacheFactory) {
376
-			self::$cacheFactory = \OC::$server->get(ICacheFactory::class);
377
-		}
378
-		$memcache = self::$cacheFactory->createLocal('storage_info');
379
-
380
-		// return storage info without adding mount points
381
-		if (self::$quotaIncludeExternalStorage === null) {
382
-			self::$quotaIncludeExternalStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
383
-		}
384
-
385
-		$view = Filesystem::getView();
386
-		if (!$view) {
387
-			throw new \OCP\Files\NotFoundException();
388
-		}
389
-		$fullPath = Filesystem::normalizePath($view->getAbsolutePath($path));
390
-
391
-		$cacheKey = $fullPath . '::' . ($includeMountPoints ? 'include' : 'exclude');
392
-		if ($useCache) {
393
-			$cached = $memcache->get($cacheKey);
394
-			if ($cached) {
395
-				return $cached;
396
-			}
397
-		}
398
-
399
-		if (!$rootInfo) {
400
-			$rootInfo = \OC\Files\Filesystem::getFileInfo($path, self::$quotaIncludeExternalStorage ? 'ext' : false);
401
-		}
402
-		if (!$rootInfo instanceof \OCP\Files\FileInfo) {
403
-			throw new \OCP\Files\NotFoundException('The root directory of the user\'s files is missing');
404
-		}
405
-		$used = $rootInfo->getSize($includeMountPoints);
406
-		if ($used < 0) {
407
-			$used = 0.0;
408
-		}
409
-		/** @var int|float $quota */
410
-		$quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
411
-		$mount = $rootInfo->getMountPoint();
412
-		$storage = $mount->getStorage();
413
-		$sourceStorage = $storage;
414
-		if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
415
-			self::$quotaIncludeExternalStorage = false;
416
-		}
417
-		if (self::$quotaIncludeExternalStorage) {
418
-			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
419
-				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
420
-			) {
421
-				/** @var \OC\Files\Storage\Home $storage */
422
-				$user = $storage->getUser();
423
-			} else {
424
-				$user = \OC::$server->getUserSession()->getUser();
425
-			}
426
-			$quota = OC_Util::getUserQuota($user);
427
-			if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
428
-				// always get free space / total space from root + mount points
429
-				return self::getGlobalStorageInfo($quota, $user, $mount);
430
-			}
431
-		}
432
-
433
-		// TODO: need a better way to get total space from storage
434
-		if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
435
-			/** @var \OC\Files\Storage\Wrapper\Quota $storage */
436
-			$quota = $sourceStorage->getQuota();
437
-		}
438
-		try {
439
-			$free = $sourceStorage->free_space($rootInfo->getInternalPath());
440
-			if (is_bool($free)) {
441
-				$free = 0.0;
442
-			}
443
-		} catch (\Exception $e) {
444
-			if ($path === '') {
445
-				throw $e;
446
-			}
447
-			/** @var LoggerInterface $logger */
448
-			$logger = \OC::$server->get(LoggerInterface::class);
449
-			$logger->warning('Error while getting quota info, using root quota', ['exception' => $e]);
450
-			$rootInfo = self::getStorageInfo('');
451
-			$memcache->set($cacheKey, $rootInfo, 5 * 60);
452
-			return $rootInfo;
453
-		}
454
-		if ($free >= 0) {
455
-			$total = $free + $used;
456
-		} else {
457
-			$total = $free; //either unknown or unlimited
458
-		}
459
-		if ($total > 0) {
460
-			if ($quota > 0 && $total > $quota) {
461
-				$total = $quota;
462
-			}
463
-			// prevent division by zero or error codes (negative values)
464
-			$relative = round(($used / $total) * 10000) / 100;
465
-		} else {
466
-			$relative = 0;
467
-		}
468
-
469
-		/*
34
+    private static $templateManager;
35
+    private static ?ICacheFactory $cacheFactory = null;
36
+    private static ?bool $quotaIncludeExternalStorage = null;
37
+
38
+    /**
39
+     * Make a human file size
40
+     * @param int|float $bytes file size in bytes
41
+     * @return string a human readable file size
42
+     * @deprecated 4.0.0 replaced with \OCP\Util::humanFileSize
43
+     *
44
+     * Makes 2048 to 2 kB.
45
+     */
46
+    public static function humanFileSize(int|float $bytes): string {
47
+        return \OCP\Util::humanFileSize($bytes);
48
+    }
49
+
50
+    /**
51
+     * Make a computer file size
52
+     * @param string $str file size in human readable format
53
+     * @return false|int|float a file size in bytes
54
+     * @deprecated 4.0.0 Use \OCP\Util::computerFileSize
55
+     *
56
+     * Makes 2kB to 2048.
57
+     *
58
+     * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418
59
+     */
60
+    public static function computerFileSize(string $str): false|int|float {
61
+        return \OCP\Util::computerFileSize($str);
62
+    }
63
+
64
+    /**
65
+     * Recursive copying of folders
66
+     * @param string $src source folder
67
+     * @param string $dest target folder
68
+     * @return void
69
+     */
70
+    public static function copyr($src, $dest) {
71
+        if (!file_exists($src)) {
72
+            return;
73
+        }
74
+
75
+        if (is_dir($src)) {
76
+            if (!is_dir($dest)) {
77
+                mkdir($dest);
78
+            }
79
+            $files = scandir($src);
80
+            foreach ($files as $file) {
81
+                if ($file != '.' && $file != '..') {
82
+                    self::copyr("$src/$file", "$dest/$file");
83
+                }
84
+            }
85
+        } else {
86
+            $validator = \OCP\Server::get(FilenameValidator::class);
87
+            if (!$validator->isForbidden($src)) {
88
+                copy($src, $dest);
89
+            }
90
+        }
91
+    }
92
+
93
+    /**
94
+     * Recursive deletion of folders
95
+     * @param string $dir path to the folder
96
+     * @param bool $deleteSelf if set to false only the content of the folder will be deleted
97
+     * @return bool
98
+     * @deprecated 5.0.0 use \OCP\Files::rmdirr instead
99
+     */
100
+    public static function rmdirr($dir, $deleteSelf = true) {
101
+        return \OCP\Files::rmdirr($dir, $deleteSelf);
102
+    }
103
+
104
+    /**
105
+     * @deprecated 18.0.0
106
+     * @return \OC\Files\Type\TemplateManager
107
+     */
108
+    public static function getFileTemplateManager() {
109
+        if (!self::$templateManager) {
110
+            self::$templateManager = new \OC\Files\Type\TemplateManager();
111
+        }
112
+        return self::$templateManager;
113
+    }
114
+
115
+    /**
116
+     * detect if a given program is found in the search PATH
117
+     *
118
+     * @param string $name
119
+     * @param bool $path
120
+     * @internal param string $program name
121
+     * @internal param string $optional search path, defaults to $PATH
122
+     * @return bool true if executable program found in path
123
+     * @deprecated 32.0.0 use the \OCP\IBinaryFinder
124
+     */
125
+    public static function canExecute($name, $path = false) {
126
+        // path defaults to PATH from environment if not set
127
+        if ($path === false) {
128
+            $path = getenv('PATH');
129
+        }
130
+        // we look for an executable file of that name
131
+        $exts = [''];
132
+        $check_fn = 'is_executable';
133
+        // Default check will be done with $path directories :
134
+        $dirs = explode(PATH_SEPARATOR, $path);
135
+        // WARNING : We have to check if open_basedir is enabled :
136
+        $obd = OC::$server->get(IniGetWrapper::class)->getString('open_basedir');
137
+        if ($obd != 'none') {
138
+            $obd_values = explode(PATH_SEPARATOR, $obd);
139
+            if (count($obd_values) > 0 and $obd_values[0]) {
140
+                // open_basedir is in effect !
141
+                // We need to check if the program is in one of these dirs :
142
+                $dirs = $obd_values;
143
+            }
144
+        }
145
+        foreach ($dirs as $dir) {
146
+            foreach ($exts as $ext) {
147
+                if ($check_fn("$dir/$name" . $ext)) {
148
+                    return true;
149
+                }
150
+            }
151
+        }
152
+        return false;
153
+    }
154
+
155
+    /**
156
+     * copy the contents of one stream to another
157
+     *
158
+     * @param resource $source
159
+     * @param resource $target
160
+     * @return array the number of bytes copied and result
161
+     */
162
+    public static function streamCopy($source, $target) {
163
+        if (!$source or !$target) {
164
+            return [0, false];
165
+        }
166
+        $bufSize = 8192;
167
+        $result = true;
168
+        $count = 0;
169
+        while (!feof($source)) {
170
+            $buf = fread($source, $bufSize);
171
+            $bytesWritten = fwrite($target, $buf);
172
+            if ($bytesWritten !== false) {
173
+                $count += $bytesWritten;
174
+            }
175
+            // note: strlen is expensive so only use it when necessary,
176
+            // on the last block
177
+            if ($bytesWritten === false
178
+                || ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
179
+            ) {
180
+                // write error, could be disk full ?
181
+                $result = false;
182
+                break;
183
+            }
184
+        }
185
+        return [$count, $result];
186
+    }
187
+
188
+    /**
189
+     * Adds a suffix to the name in case the file exists
190
+     *
191
+     * @param string $path
192
+     * @param string $filename
193
+     * @return string
194
+     */
195
+    public static function buildNotExistingFileName($path, $filename) {
196
+        $view = \OC\Files\Filesystem::getView();
197
+        return self::buildNotExistingFileNameForView($path, $filename, $view);
198
+    }
199
+
200
+    /**
201
+     * Adds a suffix to the name in case the file exists
202
+     *
203
+     * @param string $path
204
+     * @param string $filename
205
+     * @return string
206
+     */
207
+    public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
208
+        if ($path === '/') {
209
+            $path = '';
210
+        }
211
+        if ($pos = strrpos($filename, '.')) {
212
+            $name = substr($filename, 0, $pos);
213
+            $ext = substr($filename, $pos);
214
+        } else {
215
+            $name = $filename;
216
+            $ext = '';
217
+        }
218
+
219
+        $newpath = $path . '/' . $filename;
220
+        if ($view->file_exists($newpath)) {
221
+            if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
222
+                //Replace the last "(number)" with "(number+1)"
223
+                $last_match = count($matches[0]) - 1;
224
+                $counter = $matches[1][$last_match][0] + 1;
225
+                $offset = $matches[0][$last_match][1];
226
+                $match_length = strlen($matches[0][$last_match][0]);
227
+            } else {
228
+                $counter = 2;
229
+                $match_length = 0;
230
+                $offset = false;
231
+            }
232
+            do {
233
+                if ($offset) {
234
+                    //Replace the last "(number)" with "(number+1)"
235
+                    $newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
236
+                } else {
237
+                    $newname = $name . ' (' . $counter . ')';
238
+                }
239
+                $newpath = $path . '/' . $newname . $ext;
240
+                $counter++;
241
+            } while ($view->file_exists($newpath));
242
+        }
243
+
244
+        return $newpath;
245
+    }
246
+
247
+    /**
248
+     * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
249
+     *
250
+     * @param array $input The array to work on
251
+     * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
252
+     * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
253
+     * @return array
254
+     *
255
+     * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
256
+     * based on https://www.php.net/manual/en/function.array-change-key-case.php#107715
257
+     *
258
+     */
259
+    public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
260
+        $case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
261
+        $ret = [];
262
+        foreach ($input as $k => $v) {
263
+            $ret[mb_convert_case($k, $case, $encoding)] = $v;
264
+        }
265
+        return $ret;
266
+    }
267
+
268
+    /**
269
+     * performs a search in a nested array
270
+     * @param array $haystack the array to be searched
271
+     * @param string $needle the search string
272
+     * @param mixed $index optional, only search this key name
273
+     * @return mixed the key of the matching field, otherwise false
274
+     *
275
+     * performs a search in a nested array
276
+     *
277
+     * taken from https://www.php.net/manual/en/function.array-search.php#97645
278
+     */
279
+    public static function recursiveArraySearch($haystack, $needle, $index = null) {
280
+        $aIt = new RecursiveArrayIterator($haystack);
281
+        $it = new RecursiveIteratorIterator($aIt);
282
+
283
+        while ($it->valid()) {
284
+            if (((isset($index) and ($it->key() == $index)) or !isset($index)) and ($it->current() == $needle)) {
285
+                return $aIt->key();
286
+            }
287
+
288
+            $it->next();
289
+        }
290
+
291
+        return false;
292
+    }
293
+
294
+    /**
295
+     * calculates the maximum upload size respecting system settings, free space and user quota
296
+     *
297
+     * @param string $dir the current folder where the user currently operates
298
+     * @param int|float $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
299
+     * @return int|float number of bytes representing
300
+     */
301
+    public static function maxUploadFilesize($dir, $freeSpace = null) {
302
+        if (is_null($freeSpace) || $freeSpace < 0) {
303
+            $freeSpace = self::freeSpace($dir);
304
+        }
305
+        return min($freeSpace, self::uploadLimit());
306
+    }
307
+
308
+    /**
309
+     * Calculate free space left within user quota
310
+     *
311
+     * @param string $dir the current folder where the user currently operates
312
+     * @return int|float number of bytes representing
313
+     */
314
+    public static function freeSpace($dir) {
315
+        $freeSpace = \OC\Files\Filesystem::free_space($dir);
316
+        if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
317
+            $freeSpace = max($freeSpace, 0);
318
+            return $freeSpace;
319
+        } else {
320
+            return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
321
+        }
322
+    }
323
+
324
+    /**
325
+     * Calculate PHP upload limit
326
+     *
327
+     * @return int|float PHP upload file size limit
328
+     */
329
+    public static function uploadLimit() {
330
+        $ini = \OC::$server->get(IniGetWrapper::class);
331
+        $upload_max_filesize = Util::computerFileSize($ini->get('upload_max_filesize')) ?: 0;
332
+        $post_max_size = Util::computerFileSize($ini->get('post_max_size')) ?: 0;
333
+        if ($upload_max_filesize === 0 && $post_max_size === 0) {
334
+            return INF;
335
+        } elseif ($upload_max_filesize === 0 || $post_max_size === 0) {
336
+            return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
337
+        } else {
338
+            return min($upload_max_filesize, $post_max_size);
339
+        }
340
+    }
341
+
342
+    /**
343
+     * Checks if a function is available
344
+     *
345
+     * @deprecated 25.0.0 use \OCP\Util::isFunctionEnabled instead
346
+     */
347
+    public static function is_function_enabled(string $function_name): bool {
348
+        return \OCP\Util::isFunctionEnabled($function_name);
349
+    }
350
+
351
+    /**
352
+     * Try to find a program
353
+     * @deprecated 25.0.0 Use \OC\BinaryFinder directly
354
+     */
355
+    public static function findBinaryPath(string $program): ?string {
356
+        $result = \OCP\Server::get(IBinaryFinder::class)->findBinaryPath($program);
357
+        return $result !== false ? $result : null;
358
+    }
359
+
360
+    /**
361
+     * Calculate the disc space for the given path
362
+     *
363
+     * BEWARE: this requires that Util::setupFS() was called
364
+     * already !
365
+     *
366
+     * @param string $path
367
+     * @param \OCP\Files\FileInfo $rootInfo (optional)
368
+     * @param bool $includeMountPoints whether to include mount points in the size calculation
369
+     * @param bool $useCache whether to use the cached quota values
370
+     * @psalm-suppress LessSpecificReturnStatement Legacy code outputs weird types - manually validated that they are correct
371
+     * @return StorageInfo
372
+     * @throws \OCP\Files\NotFoundException
373
+     */
374
+    public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true, $useCache = true) {
375
+        if (!self::$cacheFactory) {
376
+            self::$cacheFactory = \OC::$server->get(ICacheFactory::class);
377
+        }
378
+        $memcache = self::$cacheFactory->createLocal('storage_info');
379
+
380
+        // return storage info without adding mount points
381
+        if (self::$quotaIncludeExternalStorage === null) {
382
+            self::$quotaIncludeExternalStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
383
+        }
384
+
385
+        $view = Filesystem::getView();
386
+        if (!$view) {
387
+            throw new \OCP\Files\NotFoundException();
388
+        }
389
+        $fullPath = Filesystem::normalizePath($view->getAbsolutePath($path));
390
+
391
+        $cacheKey = $fullPath . '::' . ($includeMountPoints ? 'include' : 'exclude');
392
+        if ($useCache) {
393
+            $cached = $memcache->get($cacheKey);
394
+            if ($cached) {
395
+                return $cached;
396
+            }
397
+        }
398
+
399
+        if (!$rootInfo) {
400
+            $rootInfo = \OC\Files\Filesystem::getFileInfo($path, self::$quotaIncludeExternalStorage ? 'ext' : false);
401
+        }
402
+        if (!$rootInfo instanceof \OCP\Files\FileInfo) {
403
+            throw new \OCP\Files\NotFoundException('The root directory of the user\'s files is missing');
404
+        }
405
+        $used = $rootInfo->getSize($includeMountPoints);
406
+        if ($used < 0) {
407
+            $used = 0.0;
408
+        }
409
+        /** @var int|float $quota */
410
+        $quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
411
+        $mount = $rootInfo->getMountPoint();
412
+        $storage = $mount->getStorage();
413
+        $sourceStorage = $storage;
414
+        if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
415
+            self::$quotaIncludeExternalStorage = false;
416
+        }
417
+        if (self::$quotaIncludeExternalStorage) {
418
+            if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
419
+                || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
420
+            ) {
421
+                /** @var \OC\Files\Storage\Home $storage */
422
+                $user = $storage->getUser();
423
+            } else {
424
+                $user = \OC::$server->getUserSession()->getUser();
425
+            }
426
+            $quota = OC_Util::getUserQuota($user);
427
+            if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
428
+                // always get free space / total space from root + mount points
429
+                return self::getGlobalStorageInfo($quota, $user, $mount);
430
+            }
431
+        }
432
+
433
+        // TODO: need a better way to get total space from storage
434
+        if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
435
+            /** @var \OC\Files\Storage\Wrapper\Quota $storage */
436
+            $quota = $sourceStorage->getQuota();
437
+        }
438
+        try {
439
+            $free = $sourceStorage->free_space($rootInfo->getInternalPath());
440
+            if (is_bool($free)) {
441
+                $free = 0.0;
442
+            }
443
+        } catch (\Exception $e) {
444
+            if ($path === '') {
445
+                throw $e;
446
+            }
447
+            /** @var LoggerInterface $logger */
448
+            $logger = \OC::$server->get(LoggerInterface::class);
449
+            $logger->warning('Error while getting quota info, using root quota', ['exception' => $e]);
450
+            $rootInfo = self::getStorageInfo('');
451
+            $memcache->set($cacheKey, $rootInfo, 5 * 60);
452
+            return $rootInfo;
453
+        }
454
+        if ($free >= 0) {
455
+            $total = $free + $used;
456
+        } else {
457
+            $total = $free; //either unknown or unlimited
458
+        }
459
+        if ($total > 0) {
460
+            if ($quota > 0 && $total > $quota) {
461
+                $total = $quota;
462
+            }
463
+            // prevent division by zero or error codes (negative values)
464
+            $relative = round(($used / $total) * 10000) / 100;
465
+        } else {
466
+            $relative = 0;
467
+        }
468
+
469
+        /*
470 470
 		 * \OCA\Files_Sharing\External\Storage returns the cloud ID as the owner for the storage.
471 471
 		 * It is unnecessary to query the user manager for the display name, as it won't have this information.
472 472
 		 */
473
-		$isRemoteShare = $storage->instanceOfStorage(\OCA\Files_Sharing\External\Storage::class);
474
-
475
-		$ownerId = $storage->getOwner($path);
476
-		$ownerDisplayName = '';
477
-
478
-		if ($isRemoteShare === false && $ownerId !== false) {
479
-			$ownerDisplayName = \OC::$server->getUserManager()->getDisplayName($ownerId) ?? '';
480
-		}
481
-
482
-		if (substr_count($mount->getMountPoint(), '/') < 3) {
483
-			$mountPoint = '';
484
-		} else {
485
-			[,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
486
-		}
487
-
488
-		$info = [
489
-			'free' => $free,
490
-			'used' => $used,
491
-			'quota' => $quota,
492
-			'total' => $total,
493
-			'relative' => $relative,
494
-			'owner' => $ownerId,
495
-			'ownerDisplayName' => $ownerDisplayName,
496
-			'mountType' => $mount->getMountType(),
497
-			'mountPoint' => trim($mountPoint, '/'),
498
-		];
499
-
500
-		if ($isRemoteShare === false && $ownerId !== false && $path === '/') {
501
-			// If path is root, store this as last known quota usage for this user
502
-			\OCP\Server::get(\OCP\IConfig::class)->setUserValue($ownerId, 'files', 'lastSeenQuotaUsage', (string)$relative);
503
-		}
504
-
505
-		$memcache->set($cacheKey, $info, 5 * 60);
506
-
507
-		return $info;
508
-	}
509
-
510
-	/**
511
-	 * Get storage info including all mount points and quota
512
-	 *
513
-	 * @psalm-suppress LessSpecificReturnStatement Legacy code outputs weird types - manually validated that they are correct
514
-	 * @return StorageInfo
515
-	 */
516
-	private static function getGlobalStorageInfo(int|float $quota, IUser $user, IMountPoint $mount): array {
517
-		$rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
518
-		/** @var int|float $used */
519
-		$used = $rootInfo['size'];
520
-		if ($used < 0) {
521
-			$used = 0.0;
522
-		}
523
-
524
-		$total = $quota;
525
-		/** @var int|float $free */
526
-		$free = $quota - $used;
527
-
528
-		if ($total > 0) {
529
-			if ($quota > 0 && $total > $quota) {
530
-				$total = $quota;
531
-			}
532
-			// prevent division by zero or error codes (negative values)
533
-			$relative = round(($used / $total) * 10000) / 100;
534
-		} else {
535
-			$relative = 0.0;
536
-		}
537
-
538
-		if (substr_count($mount->getMountPoint(), '/') < 3) {
539
-			$mountPoint = '';
540
-		} else {
541
-			[,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
542
-		}
543
-
544
-		return [
545
-			'free' => $free,
546
-			'used' => $used,
547
-			'total' => $total,
548
-			'relative' => $relative,
549
-			'quota' => $quota,
550
-			'owner' => $user->getUID(),
551
-			'ownerDisplayName' => $user->getDisplayName(),
552
-			'mountType' => $mount->getMountType(),
553
-			'mountPoint' => trim($mountPoint, '/'),
554
-		];
555
-	}
556
-
557
-	public static function clearStorageInfo(string $absolutePath): void {
558
-		/** @var ICacheFactory $cacheFactory */
559
-		$cacheFactory = \OC::$server->get(ICacheFactory::class);
560
-		$memcache = $cacheFactory->createLocal('storage_info');
561
-		$cacheKeyPrefix = Filesystem::normalizePath($absolutePath) . '::';
562
-		$memcache->remove($cacheKeyPrefix . 'include');
563
-		$memcache->remove($cacheKeyPrefix . 'exclude');
564
-	}
565
-
566
-	/**
567
-	 * Returns whether the config file is set manually to read-only
568
-	 * @return bool
569
-	 */
570
-	public static function isReadOnlyConfigEnabled() {
571
-		return \OC::$server->getConfig()->getSystemValueBool('config_is_read_only', false);
572
-	}
473
+        $isRemoteShare = $storage->instanceOfStorage(\OCA\Files_Sharing\External\Storage::class);
474
+
475
+        $ownerId = $storage->getOwner($path);
476
+        $ownerDisplayName = '';
477
+
478
+        if ($isRemoteShare === false && $ownerId !== false) {
479
+            $ownerDisplayName = \OC::$server->getUserManager()->getDisplayName($ownerId) ?? '';
480
+        }
481
+
482
+        if (substr_count($mount->getMountPoint(), '/') < 3) {
483
+            $mountPoint = '';
484
+        } else {
485
+            [,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
486
+        }
487
+
488
+        $info = [
489
+            'free' => $free,
490
+            'used' => $used,
491
+            'quota' => $quota,
492
+            'total' => $total,
493
+            'relative' => $relative,
494
+            'owner' => $ownerId,
495
+            'ownerDisplayName' => $ownerDisplayName,
496
+            'mountType' => $mount->getMountType(),
497
+            'mountPoint' => trim($mountPoint, '/'),
498
+        ];
499
+
500
+        if ($isRemoteShare === false && $ownerId !== false && $path === '/') {
501
+            // If path is root, store this as last known quota usage for this user
502
+            \OCP\Server::get(\OCP\IConfig::class)->setUserValue($ownerId, 'files', 'lastSeenQuotaUsage', (string)$relative);
503
+        }
504
+
505
+        $memcache->set($cacheKey, $info, 5 * 60);
506
+
507
+        return $info;
508
+    }
509
+
510
+    /**
511
+     * Get storage info including all mount points and quota
512
+     *
513
+     * @psalm-suppress LessSpecificReturnStatement Legacy code outputs weird types - manually validated that they are correct
514
+     * @return StorageInfo
515
+     */
516
+    private static function getGlobalStorageInfo(int|float $quota, IUser $user, IMountPoint $mount): array {
517
+        $rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
518
+        /** @var int|float $used */
519
+        $used = $rootInfo['size'];
520
+        if ($used < 0) {
521
+            $used = 0.0;
522
+        }
523
+
524
+        $total = $quota;
525
+        /** @var int|float $free */
526
+        $free = $quota - $used;
527
+
528
+        if ($total > 0) {
529
+            if ($quota > 0 && $total > $quota) {
530
+                $total = $quota;
531
+            }
532
+            // prevent division by zero or error codes (negative values)
533
+            $relative = round(($used / $total) * 10000) / 100;
534
+        } else {
535
+            $relative = 0.0;
536
+        }
537
+
538
+        if (substr_count($mount->getMountPoint(), '/') < 3) {
539
+            $mountPoint = '';
540
+        } else {
541
+            [,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
542
+        }
543
+
544
+        return [
545
+            'free' => $free,
546
+            'used' => $used,
547
+            'total' => $total,
548
+            'relative' => $relative,
549
+            'quota' => $quota,
550
+            'owner' => $user->getUID(),
551
+            'ownerDisplayName' => $user->getDisplayName(),
552
+            'mountType' => $mount->getMountType(),
553
+            'mountPoint' => trim($mountPoint, '/'),
554
+        ];
555
+    }
556
+
557
+    public static function clearStorageInfo(string $absolutePath): void {
558
+        /** @var ICacheFactory $cacheFactory */
559
+        $cacheFactory = \OC::$server->get(ICacheFactory::class);
560
+        $memcache = $cacheFactory->createLocal('storage_info');
561
+        $cacheKeyPrefix = Filesystem::normalizePath($absolutePath) . '::';
562
+        $memcache->remove($cacheKeyPrefix . 'include');
563
+        $memcache->remove($cacheKeyPrefix . 'exclude');
564
+    }
565
+
566
+    /**
567
+     * Returns whether the config file is set manually to read-only
568
+     * @return bool
569
+     */
570
+    public static function isReadOnlyConfigEnabled() {
571
+        return \OC::$server->getConfig()->getSystemValueBool('config_is_read_only', false);
572
+    }
573 573
 }
Please login to merge, or discard this patch.
lib/private/Installer.php 2 patches
Indentation   +556 added lines, -556 removed lines patch added patch discarded remove patch
@@ -32,560 +32,560 @@
 block discarded – undo
32 32
  * This class provides the functionality needed to install, update and remove apps
33 33
  */
34 34
 class Installer {
35
-	private ?bool $isInstanceReadyForUpdates = null;
36
-	private ?array $apps = null;
37
-
38
-	public function __construct(
39
-		private AppFetcher $appFetcher,
40
-		private IClientService $clientService,
41
-		private ITempManager $tempManager,
42
-		private LoggerInterface $logger,
43
-		private IConfig $config,
44
-		private bool $isCLI,
45
-	) {
46
-	}
47
-
48
-	/**
49
-	 * Installs an app that is located in one of the app folders already
50
-	 *
51
-	 * @param string $appId App to install
52
-	 * @param bool $forceEnable
53
-	 * @throws \Exception
54
-	 * @return string app ID
55
-	 */
56
-	public function installApp(string $appId, bool $forceEnable = false): string {
57
-		$app = \OC_App::findAppInDirectories($appId);
58
-		if ($app === false) {
59
-			throw new \Exception('App not found in any app directory');
60
-		}
61
-
62
-		$basedir = $app['path'] . '/' . $appId;
63
-
64
-		if (is_file($basedir . '/appinfo/database.xml')) {
65
-			throw new \Exception('The appinfo/database.xml file is not longer supported. Used in ' . $appId);
66
-		}
67
-
68
-		$l = \OCP\Util::getL10N('core');
69
-		$info = \OCP\Server::get(IAppManager::class)->getAppInfoByPath($basedir . '/appinfo/info.xml', $l->getLanguageCode());
70
-
71
-		if (!is_array($info)) {
72
-			throw new \Exception(
73
-				$l->t('App "%s" cannot be installed because appinfo file cannot be read.',
74
-					[$appId]
75
-				)
76
-			);
77
-		}
78
-
79
-		$ignoreMaxApps = $this->config->getSystemValue('app_install_overwrite', []);
80
-		$ignoreMax = $forceEnable || in_array($appId, $ignoreMaxApps, true);
81
-
82
-		$version = implode('.', \OCP\Util::getVersion());
83
-		if (!\OC_App::isAppCompatible($version, $info, $ignoreMax)) {
84
-			throw new \Exception(
85
-				// TODO $l
86
-				$l->t('App "%s" cannot be installed because it is not compatible with this version of the server.',
87
-					[$info['name']]
88
-				)
89
-			);
90
-		}
91
-
92
-		// check for required dependencies
93
-		\OC_App::checkAppDependencies($this->config, $l, $info, $ignoreMax);
94
-		/** @var Coordinator $coordinator */
95
-		$coordinator = \OC::$server->get(Coordinator::class);
96
-		$coordinator->runLazyRegistration($appId);
97
-		\OC_App::registerAutoloading($appId, $basedir);
98
-
99
-		$previousVersion = $this->config->getAppValue($info['id'], 'installed_version', false);
100
-		if ($previousVersion) {
101
-			OC_App::executeRepairSteps($appId, $info['repair-steps']['pre-migration']);
102
-		}
103
-
104
-		//install the database
105
-		$ms = new MigrationService($info['id'], \OCP\Server::get(Connection::class));
106
-		$ms->migrate('latest', !$previousVersion);
107
-
108
-		if ($previousVersion) {
109
-			OC_App::executeRepairSteps($appId, $info['repair-steps']['post-migration']);
110
-		}
111
-
112
-		\OC_App::setupBackgroundJobs($info['background-jobs']);
113
-
114
-		//run appinfo/install.php
115
-		self::includeAppScript($basedir . '/appinfo/install.php');
116
-
117
-		OC_App::executeRepairSteps($appId, $info['repair-steps']['install']);
118
-
119
-		$config = \OCP\Server::get(IConfig::class);
120
-		//set the installed version
121
-		$config->setAppValue($info['id'], 'installed_version', \OCP\Server::get(IAppManager::class)->getAppVersion($info['id'], false));
122
-		$config->setAppValue($info['id'], 'enabled', 'no');
123
-
124
-		//set remote/public handlers
125
-		foreach ($info['remote'] as $name => $path) {
126
-			$config->setAppValue('core', 'remote_' . $name, $info['id'] . '/' . $path);
127
-		}
128
-		foreach ($info['public'] as $name => $path) {
129
-			$config->setAppValue('core', 'public_' . $name, $info['id'] . '/' . $path);
130
-		}
131
-
132
-		OC_App::setAppTypes($info['id']);
133
-
134
-		return $info['id'];
135
-	}
136
-
137
-	/**
138
-	 * Updates the specified app from the appstore
139
-	 *
140
-	 * @param bool $allowUnstable Allow unstable releases
141
-	 */
142
-	public function updateAppstoreApp(string $appId, bool $allowUnstable = false): bool {
143
-		if ($this->isUpdateAvailable($appId, $allowUnstable)) {
144
-			try {
145
-				$this->downloadApp($appId, $allowUnstable);
146
-			} catch (\Exception $e) {
147
-				$this->logger->error($e->getMessage(), [
148
-					'exception' => $e,
149
-				]);
150
-				return false;
151
-			}
152
-			return OC_App::updateApp($appId);
153
-		}
154
-
155
-		return false;
156
-	}
157
-
158
-	/**
159
-	 * Split the certificate file in individual certs
160
-	 *
161
-	 * @param string $cert
162
-	 * @return string[]
163
-	 */
164
-	private function splitCerts(string $cert): array {
165
-		preg_match_all('([\-]{3,}[\S\ ]+?[\-]{3,}[\S\s]+?[\-]{3,}[\S\ ]+?[\-]{3,})', $cert, $matches);
166
-
167
-		return $matches[0];
168
-	}
169
-
170
-	/**
171
-	 * Downloads an app and puts it into the app directory
172
-	 *
173
-	 * @param string $appId
174
-	 * @param bool [$allowUnstable]
175
-	 *
176
-	 * @throws \Exception If the installation was not successful
177
-	 */
178
-	public function downloadApp(string $appId, bool $allowUnstable = false): void {
179
-		$appId = strtolower($appId);
180
-
181
-		$apps = $this->appFetcher->get($allowUnstable);
182
-		foreach ($apps as $app) {
183
-			if ($app['id'] === $appId) {
184
-				// Load the certificate
185
-				$certificate = new X509();
186
-				$rootCrt = file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt');
187
-				$rootCrts = $this->splitCerts($rootCrt);
188
-				foreach ($rootCrts as $rootCrt) {
189
-					$certificate->loadCA($rootCrt);
190
-				}
191
-				$loadedCertificate = $certificate->loadX509($app['certificate']);
192
-
193
-				// Verify if the certificate has been revoked
194
-				$crl = new X509();
195
-				foreach ($rootCrts as $rootCrt) {
196
-					$crl->loadCA($rootCrt);
197
-				}
198
-				$crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl'));
199
-				if ($crl->validateSignature() !== true) {
200
-					throw new \Exception('Could not validate CRL signature');
201
-				}
202
-				$csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString();
203
-				$revoked = $crl->getRevoked($csn);
204
-				if ($revoked !== false) {
205
-					throw new \Exception(
206
-						sprintf(
207
-							'Certificate "%s" has been revoked',
208
-							$csn
209
-						)
210
-					);
211
-				}
212
-
213
-				// Verify if the certificate has been issued by the Nextcloud Code Authority CA
214
-				if ($certificate->validateSignature() !== true) {
215
-					throw new \Exception(
216
-						sprintf(
217
-							'App with id %s has a certificate not issued by a trusted Code Signing Authority',
218
-							$appId
219
-						)
220
-					);
221
-				}
222
-
223
-				// Verify if the certificate is issued for the requested app id
224
-				$certInfo = openssl_x509_parse($app['certificate']);
225
-				if (!isset($certInfo['subject']['CN'])) {
226
-					throw new \Exception(
227
-						sprintf(
228
-							'App with id %s has a cert with no CN',
229
-							$appId
230
-						)
231
-					);
232
-				}
233
-				if ($certInfo['subject']['CN'] !== $appId) {
234
-					throw new \Exception(
235
-						sprintf(
236
-							'App with id %s has a cert issued to %s',
237
-							$appId,
238
-							$certInfo['subject']['CN']
239
-						)
240
-					);
241
-				}
242
-
243
-				// Download the release
244
-				$tempFile = $this->tempManager->getTemporaryFile('.tar.gz');
245
-				$timeout = $this->isCLI ? 0 : 120;
246
-				$client = $this->clientService->newClient();
247
-				$client->get($app['releases'][0]['download'], ['sink' => $tempFile, 'timeout' => $timeout]);
248
-
249
-				// Check if the signature actually matches the downloaded content
250
-				$certificate = openssl_get_publickey($app['certificate']);
251
-				$verified = openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512) === 1;
252
-
253
-				if ($verified === true) {
254
-					// Seems to match, let's proceed
255
-					$extractDir = $this->tempManager->getTemporaryFolder();
256
-					$archive = new TAR($tempFile);
257
-
258
-					if (!$archive->extract($extractDir)) {
259
-						$errorMessage = 'Could not extract app ' . $appId;
260
-
261
-						$archiveError = $archive->getError();
262
-						if ($archiveError instanceof \PEAR_Error) {
263
-							$errorMessage .= ': ' . $archiveError->getMessage();
264
-						}
265
-
266
-						throw new \Exception($errorMessage);
267
-					}
268
-					$allFiles = scandir($extractDir);
269
-					$folders = array_diff($allFiles, ['.', '..']);
270
-					$folders = array_values($folders);
271
-
272
-					if (count($folders) < 1) {
273
-						throw new \Exception(
274
-							sprintf(
275
-								'Extracted app %s has no folders',
276
-								$appId
277
-							)
278
-						);
279
-					}
280
-
281
-					if (count($folders) > 1) {
282
-						throw new \Exception(
283
-							sprintf(
284
-								'Extracted app %s has more than 1 folder',
285
-								$appId
286
-							)
287
-						);
288
-					}
289
-
290
-					// Check if appinfo/info.xml has the same app ID as well
291
-					$xml = simplexml_load_string(file_get_contents($extractDir . '/' . $folders[0] . '/appinfo/info.xml'));
292
-
293
-					if ($xml === false) {
294
-						throw new \Exception(
295
-							sprintf(
296
-								'Failed to load info.xml for app id %s',
297
-								$appId,
298
-							)
299
-						);
300
-					}
301
-
302
-					if ((string)$xml->id !== $appId) {
303
-						throw new \Exception(
304
-							sprintf(
305
-								'App for id %s has a wrong app ID in info.xml: %s',
306
-								$appId,
307
-								(string)$xml->id
308
-							)
309
-						);
310
-					}
311
-
312
-					// Check if the version is lower than before
313
-					$currentVersion = \OCP\Server::get(IAppManager::class)->getAppVersion($appId, true);
314
-					$newVersion = (string)$xml->version;
315
-					if (version_compare($currentVersion, $newVersion) === 1) {
316
-						throw new \Exception(
317
-							sprintf(
318
-								'App for id %s has version %s and tried to update to lower version %s',
319
-								$appId,
320
-								$currentVersion,
321
-								$newVersion
322
-							)
323
-						);
324
-					}
325
-
326
-					$baseDir = OC_App::getInstallPath() . '/' . $appId;
327
-					// Remove old app with the ID if existent
328
-					Files::rmdirr($baseDir);
329
-					// Move to app folder
330
-					if (@mkdir($baseDir)) {
331
-						$extractDir .= '/' . $folders[0];
332
-						OC_Helper::copyr($extractDir, $baseDir);
333
-					}
334
-					OC_Helper::copyr($extractDir, $baseDir);
335
-					Files::rmdirr($extractDir);
336
-					return;
337
-				}
338
-				// Signature does not match
339
-				throw new \Exception(
340
-					sprintf(
341
-						'App with id %s has invalid signature',
342
-						$appId
343
-					)
344
-				);
345
-			}
346
-		}
347
-
348
-		throw new \Exception(
349
-			sprintf(
350
-				'Could not download app %s',
351
-				$appId
352
-			)
353
-		);
354
-	}
355
-
356
-	/**
357
-	 * Check if an update for the app is available
358
-	 *
359
-	 * @param string $appId
360
-	 * @param bool $allowUnstable
361
-	 * @return string|false false or the version number of the update
362
-	 */
363
-	public function isUpdateAvailable($appId, $allowUnstable = false): string|false {
364
-		if ($this->isInstanceReadyForUpdates === null) {
365
-			$installPath = OC_App::getInstallPath();
366
-			if ($installPath === null) {
367
-				$this->isInstanceReadyForUpdates = false;
368
-			} else {
369
-				$this->isInstanceReadyForUpdates = true;
370
-			}
371
-		}
372
-
373
-		if ($this->isInstanceReadyForUpdates === false) {
374
-			return false;
375
-		}
376
-
377
-		if ($this->isInstalledFromGit($appId) === true) {
378
-			return false;
379
-		}
380
-
381
-		if ($this->apps === null) {
382
-			$this->apps = $this->appFetcher->get($allowUnstable);
383
-		}
384
-
385
-		foreach ($this->apps as $app) {
386
-			if ($app['id'] === $appId) {
387
-				$currentVersion = \OCP\Server::get(IAppManager::class)->getAppVersion($appId, true);
388
-
389
-				if (!isset($app['releases'][0]['version'])) {
390
-					return false;
391
-				}
392
-				$newestVersion = $app['releases'][0]['version'];
393
-				if ($currentVersion !== '0' && version_compare($newestVersion, $currentVersion, '>')) {
394
-					return $newestVersion;
395
-				} else {
396
-					return false;
397
-				}
398
-			}
399
-		}
400
-
401
-		return false;
402
-	}
403
-
404
-	/**
405
-	 * Check if app has been installed from git
406
-	 *
407
-	 * The function will check if the path contains a .git folder
408
-	 */
409
-	private function isInstalledFromGit(string $appId): bool {
410
-		$app = \OC_App::findAppInDirectories($appId);
411
-		if ($app === false) {
412
-			return false;
413
-		}
414
-		$basedir = $app['path'] . '/' . $appId;
415
-		return file_exists($basedir . '/.git/');
416
-	}
417
-
418
-	/**
419
-	 * Check if app is already downloaded
420
-	 *
421
-	 * The function will check if the app is already downloaded in the apps repository
422
-	 */
423
-	public function isDownloaded(string $name): bool {
424
-		foreach (\OC::$APPSROOTS as $dir) {
425
-			$dirToTest = $dir['path'];
426
-			$dirToTest .= '/';
427
-			$dirToTest .= $name;
428
-			$dirToTest .= '/';
429
-
430
-			if (is_dir($dirToTest)) {
431
-				return true;
432
-			}
433
-		}
434
-
435
-		return false;
436
-	}
437
-
438
-	/**
439
-	 * Removes an app
440
-	 *
441
-	 * This function works as follows
442
-	 *   -# call uninstall repair steps
443
-	 *   -# removing the files
444
-	 *
445
-	 * The function will not delete preferences, tables and the configuration,
446
-	 * this has to be done by the function oc_app_uninstall().
447
-	 */
448
-	public function removeApp(string $appId): bool {
449
-		if ($this->isDownloaded($appId)) {
450
-			if (\OCP\Server::get(IAppManager::class)->isShipped($appId)) {
451
-				return false;
452
-			}
453
-			$appDir = OC_App::getInstallPath() . '/' . $appId;
454
-			Files::rmdirr($appDir);
455
-			return true;
456
-		} else {
457
-			$this->logger->error('can\'t remove app ' . $appId . '. It is not installed.');
458
-
459
-			return false;
460
-		}
461
-	}
462
-
463
-	/**
464
-	 * Installs the app within the bundle and marks the bundle as installed
465
-	 *
466
-	 * @throws \Exception If app could not get installed
467
-	 */
468
-	public function installAppBundle(Bundle $bundle): void {
469
-		$appIds = $bundle->getAppIdentifiers();
470
-		foreach ($appIds as $appId) {
471
-			if (!$this->isDownloaded($appId)) {
472
-				$this->downloadApp($appId);
473
-			}
474
-			$this->installApp($appId);
475
-			$app = new OC_App();
476
-			$app->enable($appId);
477
-		}
478
-		$bundles = json_decode($this->config->getAppValue('core', 'installed.bundles', json_encode([])), true);
479
-		$bundles[] = $bundle->getIdentifier();
480
-		$this->config->setAppValue('core', 'installed.bundles', json_encode($bundles));
481
-	}
482
-
483
-	/**
484
-	 * Installs shipped apps
485
-	 *
486
-	 * This function installs all apps found in the 'apps' directory that should be enabled by default;
487
-	 * @param bool $softErrors When updating we ignore errors and simply log them, better to have a
488
-	 *                         working ownCloud at the end instead of an aborted update.
489
-	 * @return array Array of error messages (appid => Exception)
490
-	 */
491
-	public static function installShippedApps(bool $softErrors = false, ?IOutput $output = null): array {
492
-		if ($output instanceof IOutput) {
493
-			$output->debug('Installing shipped apps');
494
-		}
495
-		$appManager = \OCP\Server::get(IAppManager::class);
496
-		$config = \OCP\Server::get(IConfig::class);
497
-		$errors = [];
498
-		foreach (\OC::$APPSROOTS as $app_dir) {
499
-			if ($dir = opendir($app_dir['path'])) {
500
-				while (false !== ($filename = readdir($dir))) {
501
-					if ($filename[0] !== '.' and is_dir($app_dir['path'] . "/$filename")) {
502
-						if (file_exists($app_dir['path'] . "/$filename/appinfo/info.xml")) {
503
-							if ($config->getAppValue($filename, 'installed_version', null) === null) {
504
-								$enabled = $appManager->isDefaultEnabled($filename);
505
-								if (($enabled || in_array($filename, $appManager->getAlwaysEnabledApps()))
506
-									  && $config->getAppValue($filename, 'enabled') !== 'no') {
507
-									if ($softErrors) {
508
-										try {
509
-											Installer::installShippedApp($filename, $output);
510
-										} catch (HintException $e) {
511
-											if ($e->getPrevious() instanceof TableExistsException) {
512
-												$errors[$filename] = $e;
513
-												continue;
514
-											}
515
-											throw $e;
516
-										}
517
-									} else {
518
-										Installer::installShippedApp($filename, $output);
519
-									}
520
-									$config->setAppValue($filename, 'enabled', 'yes');
521
-								}
522
-							}
523
-						}
524
-					}
525
-				}
526
-				closedir($dir);
527
-			}
528
-		}
529
-
530
-		return $errors;
531
-	}
532
-
533
-	/**
534
-	 * install an app already placed in the app folder
535
-	 */
536
-	public static function installShippedApp(string $app, ?IOutput $output = null): string|false {
537
-		if ($output instanceof IOutput) {
538
-			$output->debug('Installing ' . $app);
539
-		}
540
-
541
-		$appManager = \OCP\Server::get(IAppManager::class);
542
-		$config = \OCP\Server::get(IConfig::class);
543
-
544
-		$appPath = $appManager->getAppPath($app);
545
-		\OC_App::registerAutoloading($app, $appPath);
546
-
547
-		$ms = new MigrationService($app, \OCP\Server::get(Connection::class));
548
-		if ($output instanceof IOutput) {
549
-			$ms->setOutput($output);
550
-		}
551
-		$previousVersion = $config->getAppValue($app, 'installed_version', false);
552
-		$ms->migrate('latest', !$previousVersion);
553
-
554
-		//run appinfo/install.php
555
-		self::includeAppScript("$appPath/appinfo/install.php");
556
-
557
-		$info = \OCP\Server::get(IAppManager::class)->getAppInfo($app);
558
-		if (is_null($info)) {
559
-			return false;
560
-		}
561
-		if ($output instanceof IOutput) {
562
-			$output->debug('Registering tasks of ' . $app);
563
-		}
564
-		\OC_App::setupBackgroundJobs($info['background-jobs']);
565
-
566
-		OC_App::executeRepairSteps($app, $info['repair-steps']['install']);
567
-
568
-		$config->setAppValue($app, 'installed_version', \OCP\Server::get(IAppManager::class)->getAppVersion($app));
569
-		if (array_key_exists('ocsid', $info)) {
570
-			$config->setAppValue($app, 'ocsid', $info['ocsid']);
571
-		}
572
-
573
-		//set remote/public handlers
574
-		foreach ($info['remote'] as $name => $path) {
575
-			$config->setAppValue('core', 'remote_' . $name, $app . '/' . $path);
576
-		}
577
-		foreach ($info['public'] as $name => $path) {
578
-			$config->setAppValue('core', 'public_' . $name, $app . '/' . $path);
579
-		}
580
-
581
-		OC_App::setAppTypes($info['id']);
582
-
583
-		return $info['id'];
584
-	}
585
-
586
-	private static function includeAppScript(string $script): void {
587
-		if (file_exists($script)) {
588
-			include $script;
589
-		}
590
-	}
35
+    private ?bool $isInstanceReadyForUpdates = null;
36
+    private ?array $apps = null;
37
+
38
+    public function __construct(
39
+        private AppFetcher $appFetcher,
40
+        private IClientService $clientService,
41
+        private ITempManager $tempManager,
42
+        private LoggerInterface $logger,
43
+        private IConfig $config,
44
+        private bool $isCLI,
45
+    ) {
46
+    }
47
+
48
+    /**
49
+     * Installs an app that is located in one of the app folders already
50
+     *
51
+     * @param string $appId App to install
52
+     * @param bool $forceEnable
53
+     * @throws \Exception
54
+     * @return string app ID
55
+     */
56
+    public function installApp(string $appId, bool $forceEnable = false): string {
57
+        $app = \OC_App::findAppInDirectories($appId);
58
+        if ($app === false) {
59
+            throw new \Exception('App not found in any app directory');
60
+        }
61
+
62
+        $basedir = $app['path'] . '/' . $appId;
63
+
64
+        if (is_file($basedir . '/appinfo/database.xml')) {
65
+            throw new \Exception('The appinfo/database.xml file is not longer supported. Used in ' . $appId);
66
+        }
67
+
68
+        $l = \OCP\Util::getL10N('core');
69
+        $info = \OCP\Server::get(IAppManager::class)->getAppInfoByPath($basedir . '/appinfo/info.xml', $l->getLanguageCode());
70
+
71
+        if (!is_array($info)) {
72
+            throw new \Exception(
73
+                $l->t('App "%s" cannot be installed because appinfo file cannot be read.',
74
+                    [$appId]
75
+                )
76
+            );
77
+        }
78
+
79
+        $ignoreMaxApps = $this->config->getSystemValue('app_install_overwrite', []);
80
+        $ignoreMax = $forceEnable || in_array($appId, $ignoreMaxApps, true);
81
+
82
+        $version = implode('.', \OCP\Util::getVersion());
83
+        if (!\OC_App::isAppCompatible($version, $info, $ignoreMax)) {
84
+            throw new \Exception(
85
+                // TODO $l
86
+                $l->t('App "%s" cannot be installed because it is not compatible with this version of the server.',
87
+                    [$info['name']]
88
+                )
89
+            );
90
+        }
91
+
92
+        // check for required dependencies
93
+        \OC_App::checkAppDependencies($this->config, $l, $info, $ignoreMax);
94
+        /** @var Coordinator $coordinator */
95
+        $coordinator = \OC::$server->get(Coordinator::class);
96
+        $coordinator->runLazyRegistration($appId);
97
+        \OC_App::registerAutoloading($appId, $basedir);
98
+
99
+        $previousVersion = $this->config->getAppValue($info['id'], 'installed_version', false);
100
+        if ($previousVersion) {
101
+            OC_App::executeRepairSteps($appId, $info['repair-steps']['pre-migration']);
102
+        }
103
+
104
+        //install the database
105
+        $ms = new MigrationService($info['id'], \OCP\Server::get(Connection::class));
106
+        $ms->migrate('latest', !$previousVersion);
107
+
108
+        if ($previousVersion) {
109
+            OC_App::executeRepairSteps($appId, $info['repair-steps']['post-migration']);
110
+        }
111
+
112
+        \OC_App::setupBackgroundJobs($info['background-jobs']);
113
+
114
+        //run appinfo/install.php
115
+        self::includeAppScript($basedir . '/appinfo/install.php');
116
+
117
+        OC_App::executeRepairSteps($appId, $info['repair-steps']['install']);
118
+
119
+        $config = \OCP\Server::get(IConfig::class);
120
+        //set the installed version
121
+        $config->setAppValue($info['id'], 'installed_version', \OCP\Server::get(IAppManager::class)->getAppVersion($info['id'], false));
122
+        $config->setAppValue($info['id'], 'enabled', 'no');
123
+
124
+        //set remote/public handlers
125
+        foreach ($info['remote'] as $name => $path) {
126
+            $config->setAppValue('core', 'remote_' . $name, $info['id'] . '/' . $path);
127
+        }
128
+        foreach ($info['public'] as $name => $path) {
129
+            $config->setAppValue('core', 'public_' . $name, $info['id'] . '/' . $path);
130
+        }
131
+
132
+        OC_App::setAppTypes($info['id']);
133
+
134
+        return $info['id'];
135
+    }
136
+
137
+    /**
138
+     * Updates the specified app from the appstore
139
+     *
140
+     * @param bool $allowUnstable Allow unstable releases
141
+     */
142
+    public function updateAppstoreApp(string $appId, bool $allowUnstable = false): bool {
143
+        if ($this->isUpdateAvailable($appId, $allowUnstable)) {
144
+            try {
145
+                $this->downloadApp($appId, $allowUnstable);
146
+            } catch (\Exception $e) {
147
+                $this->logger->error($e->getMessage(), [
148
+                    'exception' => $e,
149
+                ]);
150
+                return false;
151
+            }
152
+            return OC_App::updateApp($appId);
153
+        }
154
+
155
+        return false;
156
+    }
157
+
158
+    /**
159
+     * Split the certificate file in individual certs
160
+     *
161
+     * @param string $cert
162
+     * @return string[]
163
+     */
164
+    private function splitCerts(string $cert): array {
165
+        preg_match_all('([\-]{3,}[\S\ ]+?[\-]{3,}[\S\s]+?[\-]{3,}[\S\ ]+?[\-]{3,})', $cert, $matches);
166
+
167
+        return $matches[0];
168
+    }
169
+
170
+    /**
171
+     * Downloads an app and puts it into the app directory
172
+     *
173
+     * @param string $appId
174
+     * @param bool [$allowUnstable]
175
+     *
176
+     * @throws \Exception If the installation was not successful
177
+     */
178
+    public function downloadApp(string $appId, bool $allowUnstable = false): void {
179
+        $appId = strtolower($appId);
180
+
181
+        $apps = $this->appFetcher->get($allowUnstable);
182
+        foreach ($apps as $app) {
183
+            if ($app['id'] === $appId) {
184
+                // Load the certificate
185
+                $certificate = new X509();
186
+                $rootCrt = file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt');
187
+                $rootCrts = $this->splitCerts($rootCrt);
188
+                foreach ($rootCrts as $rootCrt) {
189
+                    $certificate->loadCA($rootCrt);
190
+                }
191
+                $loadedCertificate = $certificate->loadX509($app['certificate']);
192
+
193
+                // Verify if the certificate has been revoked
194
+                $crl = new X509();
195
+                foreach ($rootCrts as $rootCrt) {
196
+                    $crl->loadCA($rootCrt);
197
+                }
198
+                $crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl'));
199
+                if ($crl->validateSignature() !== true) {
200
+                    throw new \Exception('Could not validate CRL signature');
201
+                }
202
+                $csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString();
203
+                $revoked = $crl->getRevoked($csn);
204
+                if ($revoked !== false) {
205
+                    throw new \Exception(
206
+                        sprintf(
207
+                            'Certificate "%s" has been revoked',
208
+                            $csn
209
+                        )
210
+                    );
211
+                }
212
+
213
+                // Verify if the certificate has been issued by the Nextcloud Code Authority CA
214
+                if ($certificate->validateSignature() !== true) {
215
+                    throw new \Exception(
216
+                        sprintf(
217
+                            'App with id %s has a certificate not issued by a trusted Code Signing Authority',
218
+                            $appId
219
+                        )
220
+                    );
221
+                }
222
+
223
+                // Verify if the certificate is issued for the requested app id
224
+                $certInfo = openssl_x509_parse($app['certificate']);
225
+                if (!isset($certInfo['subject']['CN'])) {
226
+                    throw new \Exception(
227
+                        sprintf(
228
+                            'App with id %s has a cert with no CN',
229
+                            $appId
230
+                        )
231
+                    );
232
+                }
233
+                if ($certInfo['subject']['CN'] !== $appId) {
234
+                    throw new \Exception(
235
+                        sprintf(
236
+                            'App with id %s has a cert issued to %s',
237
+                            $appId,
238
+                            $certInfo['subject']['CN']
239
+                        )
240
+                    );
241
+                }
242
+
243
+                // Download the release
244
+                $tempFile = $this->tempManager->getTemporaryFile('.tar.gz');
245
+                $timeout = $this->isCLI ? 0 : 120;
246
+                $client = $this->clientService->newClient();
247
+                $client->get($app['releases'][0]['download'], ['sink' => $tempFile, 'timeout' => $timeout]);
248
+
249
+                // Check if the signature actually matches the downloaded content
250
+                $certificate = openssl_get_publickey($app['certificate']);
251
+                $verified = openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512) === 1;
252
+
253
+                if ($verified === true) {
254
+                    // Seems to match, let's proceed
255
+                    $extractDir = $this->tempManager->getTemporaryFolder();
256
+                    $archive = new TAR($tempFile);
257
+
258
+                    if (!$archive->extract($extractDir)) {
259
+                        $errorMessage = 'Could not extract app ' . $appId;
260
+
261
+                        $archiveError = $archive->getError();
262
+                        if ($archiveError instanceof \PEAR_Error) {
263
+                            $errorMessage .= ': ' . $archiveError->getMessage();
264
+                        }
265
+
266
+                        throw new \Exception($errorMessage);
267
+                    }
268
+                    $allFiles = scandir($extractDir);
269
+                    $folders = array_diff($allFiles, ['.', '..']);
270
+                    $folders = array_values($folders);
271
+
272
+                    if (count($folders) < 1) {
273
+                        throw new \Exception(
274
+                            sprintf(
275
+                                'Extracted app %s has no folders',
276
+                                $appId
277
+                            )
278
+                        );
279
+                    }
280
+
281
+                    if (count($folders) > 1) {
282
+                        throw new \Exception(
283
+                            sprintf(
284
+                                'Extracted app %s has more than 1 folder',
285
+                                $appId
286
+                            )
287
+                        );
288
+                    }
289
+
290
+                    // Check if appinfo/info.xml has the same app ID as well
291
+                    $xml = simplexml_load_string(file_get_contents($extractDir . '/' . $folders[0] . '/appinfo/info.xml'));
292
+
293
+                    if ($xml === false) {
294
+                        throw new \Exception(
295
+                            sprintf(
296
+                                'Failed to load info.xml for app id %s',
297
+                                $appId,
298
+                            )
299
+                        );
300
+                    }
301
+
302
+                    if ((string)$xml->id !== $appId) {
303
+                        throw new \Exception(
304
+                            sprintf(
305
+                                'App for id %s has a wrong app ID in info.xml: %s',
306
+                                $appId,
307
+                                (string)$xml->id
308
+                            )
309
+                        );
310
+                    }
311
+
312
+                    // Check if the version is lower than before
313
+                    $currentVersion = \OCP\Server::get(IAppManager::class)->getAppVersion($appId, true);
314
+                    $newVersion = (string)$xml->version;
315
+                    if (version_compare($currentVersion, $newVersion) === 1) {
316
+                        throw new \Exception(
317
+                            sprintf(
318
+                                'App for id %s has version %s and tried to update to lower version %s',
319
+                                $appId,
320
+                                $currentVersion,
321
+                                $newVersion
322
+                            )
323
+                        );
324
+                    }
325
+
326
+                    $baseDir = OC_App::getInstallPath() . '/' . $appId;
327
+                    // Remove old app with the ID if existent
328
+                    Files::rmdirr($baseDir);
329
+                    // Move to app folder
330
+                    if (@mkdir($baseDir)) {
331
+                        $extractDir .= '/' . $folders[0];
332
+                        OC_Helper::copyr($extractDir, $baseDir);
333
+                    }
334
+                    OC_Helper::copyr($extractDir, $baseDir);
335
+                    Files::rmdirr($extractDir);
336
+                    return;
337
+                }
338
+                // Signature does not match
339
+                throw new \Exception(
340
+                    sprintf(
341
+                        'App with id %s has invalid signature',
342
+                        $appId
343
+                    )
344
+                );
345
+            }
346
+        }
347
+
348
+        throw new \Exception(
349
+            sprintf(
350
+                'Could not download app %s',
351
+                $appId
352
+            )
353
+        );
354
+    }
355
+
356
+    /**
357
+     * Check if an update for the app is available
358
+     *
359
+     * @param string $appId
360
+     * @param bool $allowUnstable
361
+     * @return string|false false or the version number of the update
362
+     */
363
+    public function isUpdateAvailable($appId, $allowUnstable = false): string|false {
364
+        if ($this->isInstanceReadyForUpdates === null) {
365
+            $installPath = OC_App::getInstallPath();
366
+            if ($installPath === null) {
367
+                $this->isInstanceReadyForUpdates = false;
368
+            } else {
369
+                $this->isInstanceReadyForUpdates = true;
370
+            }
371
+        }
372
+
373
+        if ($this->isInstanceReadyForUpdates === false) {
374
+            return false;
375
+        }
376
+
377
+        if ($this->isInstalledFromGit($appId) === true) {
378
+            return false;
379
+        }
380
+
381
+        if ($this->apps === null) {
382
+            $this->apps = $this->appFetcher->get($allowUnstable);
383
+        }
384
+
385
+        foreach ($this->apps as $app) {
386
+            if ($app['id'] === $appId) {
387
+                $currentVersion = \OCP\Server::get(IAppManager::class)->getAppVersion($appId, true);
388
+
389
+                if (!isset($app['releases'][0]['version'])) {
390
+                    return false;
391
+                }
392
+                $newestVersion = $app['releases'][0]['version'];
393
+                if ($currentVersion !== '0' && version_compare($newestVersion, $currentVersion, '>')) {
394
+                    return $newestVersion;
395
+                } else {
396
+                    return false;
397
+                }
398
+            }
399
+        }
400
+
401
+        return false;
402
+    }
403
+
404
+    /**
405
+     * Check if app has been installed from git
406
+     *
407
+     * The function will check if the path contains a .git folder
408
+     */
409
+    private function isInstalledFromGit(string $appId): bool {
410
+        $app = \OC_App::findAppInDirectories($appId);
411
+        if ($app === false) {
412
+            return false;
413
+        }
414
+        $basedir = $app['path'] . '/' . $appId;
415
+        return file_exists($basedir . '/.git/');
416
+    }
417
+
418
+    /**
419
+     * Check if app is already downloaded
420
+     *
421
+     * The function will check if the app is already downloaded in the apps repository
422
+     */
423
+    public function isDownloaded(string $name): bool {
424
+        foreach (\OC::$APPSROOTS as $dir) {
425
+            $dirToTest = $dir['path'];
426
+            $dirToTest .= '/';
427
+            $dirToTest .= $name;
428
+            $dirToTest .= '/';
429
+
430
+            if (is_dir($dirToTest)) {
431
+                return true;
432
+            }
433
+        }
434
+
435
+        return false;
436
+    }
437
+
438
+    /**
439
+     * Removes an app
440
+     *
441
+     * This function works as follows
442
+     *   -# call uninstall repair steps
443
+     *   -# removing the files
444
+     *
445
+     * The function will not delete preferences, tables and the configuration,
446
+     * this has to be done by the function oc_app_uninstall().
447
+     */
448
+    public function removeApp(string $appId): bool {
449
+        if ($this->isDownloaded($appId)) {
450
+            if (\OCP\Server::get(IAppManager::class)->isShipped($appId)) {
451
+                return false;
452
+            }
453
+            $appDir = OC_App::getInstallPath() . '/' . $appId;
454
+            Files::rmdirr($appDir);
455
+            return true;
456
+        } else {
457
+            $this->logger->error('can\'t remove app ' . $appId . '. It is not installed.');
458
+
459
+            return false;
460
+        }
461
+    }
462
+
463
+    /**
464
+     * Installs the app within the bundle and marks the bundle as installed
465
+     *
466
+     * @throws \Exception If app could not get installed
467
+     */
468
+    public function installAppBundle(Bundle $bundle): void {
469
+        $appIds = $bundle->getAppIdentifiers();
470
+        foreach ($appIds as $appId) {
471
+            if (!$this->isDownloaded($appId)) {
472
+                $this->downloadApp($appId);
473
+            }
474
+            $this->installApp($appId);
475
+            $app = new OC_App();
476
+            $app->enable($appId);
477
+        }
478
+        $bundles = json_decode($this->config->getAppValue('core', 'installed.bundles', json_encode([])), true);
479
+        $bundles[] = $bundle->getIdentifier();
480
+        $this->config->setAppValue('core', 'installed.bundles', json_encode($bundles));
481
+    }
482
+
483
+    /**
484
+     * Installs shipped apps
485
+     *
486
+     * This function installs all apps found in the 'apps' directory that should be enabled by default;
487
+     * @param bool $softErrors When updating we ignore errors and simply log them, better to have a
488
+     *                         working ownCloud at the end instead of an aborted update.
489
+     * @return array Array of error messages (appid => Exception)
490
+     */
491
+    public static function installShippedApps(bool $softErrors = false, ?IOutput $output = null): array {
492
+        if ($output instanceof IOutput) {
493
+            $output->debug('Installing shipped apps');
494
+        }
495
+        $appManager = \OCP\Server::get(IAppManager::class);
496
+        $config = \OCP\Server::get(IConfig::class);
497
+        $errors = [];
498
+        foreach (\OC::$APPSROOTS as $app_dir) {
499
+            if ($dir = opendir($app_dir['path'])) {
500
+                while (false !== ($filename = readdir($dir))) {
501
+                    if ($filename[0] !== '.' and is_dir($app_dir['path'] . "/$filename")) {
502
+                        if (file_exists($app_dir['path'] . "/$filename/appinfo/info.xml")) {
503
+                            if ($config->getAppValue($filename, 'installed_version', null) === null) {
504
+                                $enabled = $appManager->isDefaultEnabled($filename);
505
+                                if (($enabled || in_array($filename, $appManager->getAlwaysEnabledApps()))
506
+                                      && $config->getAppValue($filename, 'enabled') !== 'no') {
507
+                                    if ($softErrors) {
508
+                                        try {
509
+                                            Installer::installShippedApp($filename, $output);
510
+                                        } catch (HintException $e) {
511
+                                            if ($e->getPrevious() instanceof TableExistsException) {
512
+                                                $errors[$filename] = $e;
513
+                                                continue;
514
+                                            }
515
+                                            throw $e;
516
+                                        }
517
+                                    } else {
518
+                                        Installer::installShippedApp($filename, $output);
519
+                                    }
520
+                                    $config->setAppValue($filename, 'enabled', 'yes');
521
+                                }
522
+                            }
523
+                        }
524
+                    }
525
+                }
526
+                closedir($dir);
527
+            }
528
+        }
529
+
530
+        return $errors;
531
+    }
532
+
533
+    /**
534
+     * install an app already placed in the app folder
535
+     */
536
+    public static function installShippedApp(string $app, ?IOutput $output = null): string|false {
537
+        if ($output instanceof IOutput) {
538
+            $output->debug('Installing ' . $app);
539
+        }
540
+
541
+        $appManager = \OCP\Server::get(IAppManager::class);
542
+        $config = \OCP\Server::get(IConfig::class);
543
+
544
+        $appPath = $appManager->getAppPath($app);
545
+        \OC_App::registerAutoloading($app, $appPath);
546
+
547
+        $ms = new MigrationService($app, \OCP\Server::get(Connection::class));
548
+        if ($output instanceof IOutput) {
549
+            $ms->setOutput($output);
550
+        }
551
+        $previousVersion = $config->getAppValue($app, 'installed_version', false);
552
+        $ms->migrate('latest', !$previousVersion);
553
+
554
+        //run appinfo/install.php
555
+        self::includeAppScript("$appPath/appinfo/install.php");
556
+
557
+        $info = \OCP\Server::get(IAppManager::class)->getAppInfo($app);
558
+        if (is_null($info)) {
559
+            return false;
560
+        }
561
+        if ($output instanceof IOutput) {
562
+            $output->debug('Registering tasks of ' . $app);
563
+        }
564
+        \OC_App::setupBackgroundJobs($info['background-jobs']);
565
+
566
+        OC_App::executeRepairSteps($app, $info['repair-steps']['install']);
567
+
568
+        $config->setAppValue($app, 'installed_version', \OCP\Server::get(IAppManager::class)->getAppVersion($app));
569
+        if (array_key_exists('ocsid', $info)) {
570
+            $config->setAppValue($app, 'ocsid', $info['ocsid']);
571
+        }
572
+
573
+        //set remote/public handlers
574
+        foreach ($info['remote'] as $name => $path) {
575
+            $config->setAppValue('core', 'remote_' . $name, $app . '/' . $path);
576
+        }
577
+        foreach ($info['public'] as $name => $path) {
578
+            $config->setAppValue('core', 'public_' . $name, $app . '/' . $path);
579
+        }
580
+
581
+        OC_App::setAppTypes($info['id']);
582
+
583
+        return $info['id'];
584
+    }
585
+
586
+    private static function includeAppScript(string $script): void {
587
+        if (file_exists($script)) {
588
+            include $script;
589
+        }
590
+    }
591 591
 }
Please login to merge, or discard this patch.
Spacing   +29 added lines, -29 removed lines patch added patch discarded remove patch
@@ -59,14 +59,14 @@  discard block
 block discarded – undo
59 59
 			throw new \Exception('App not found in any app directory');
60 60
 		}
61 61
 
62
-		$basedir = $app['path'] . '/' . $appId;
62
+		$basedir = $app['path'].'/'.$appId;
63 63
 
64
-		if (is_file($basedir . '/appinfo/database.xml')) {
65
-			throw new \Exception('The appinfo/database.xml file is not longer supported. Used in ' . $appId);
64
+		if (is_file($basedir.'/appinfo/database.xml')) {
65
+			throw new \Exception('The appinfo/database.xml file is not longer supported. Used in '.$appId);
66 66
 		}
67 67
 
68 68
 		$l = \OCP\Util::getL10N('core');
69
-		$info = \OCP\Server::get(IAppManager::class)->getAppInfoByPath($basedir . '/appinfo/info.xml', $l->getLanguageCode());
69
+		$info = \OCP\Server::get(IAppManager::class)->getAppInfoByPath($basedir.'/appinfo/info.xml', $l->getLanguageCode());
70 70
 
71 71
 		if (!is_array($info)) {
72 72
 			throw new \Exception(
@@ -112,7 +112,7 @@  discard block
 block discarded – undo
112 112
 		\OC_App::setupBackgroundJobs($info['background-jobs']);
113 113
 
114 114
 		//run appinfo/install.php
115
-		self::includeAppScript($basedir . '/appinfo/install.php');
115
+		self::includeAppScript($basedir.'/appinfo/install.php');
116 116
 
117 117
 		OC_App::executeRepairSteps($appId, $info['repair-steps']['install']);
118 118
 
@@ -123,10 +123,10 @@  discard block
 block discarded – undo
123 123
 
124 124
 		//set remote/public handlers
125 125
 		foreach ($info['remote'] as $name => $path) {
126
-			$config->setAppValue('core', 'remote_' . $name, $info['id'] . '/' . $path);
126
+			$config->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path);
127 127
 		}
128 128
 		foreach ($info['public'] as $name => $path) {
129
-			$config->setAppValue('core', 'public_' . $name, $info['id'] . '/' . $path);
129
+			$config->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path);
130 130
 		}
131 131
 
132 132
 		OC_App::setAppTypes($info['id']);
@@ -183,7 +183,7 @@  discard block
 block discarded – undo
183 183
 			if ($app['id'] === $appId) {
184 184
 				// Load the certificate
185 185
 				$certificate = new X509();
186
-				$rootCrt = file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt');
186
+				$rootCrt = file_get_contents(__DIR__.'/../../resources/codesigning/root.crt');
187 187
 				$rootCrts = $this->splitCerts($rootCrt);
188 188
 				foreach ($rootCrts as $rootCrt) {
189 189
 					$certificate->loadCA($rootCrt);
@@ -195,7 +195,7 @@  discard block
 block discarded – undo
195 195
 				foreach ($rootCrts as $rootCrt) {
196 196
 					$crl->loadCA($rootCrt);
197 197
 				}
198
-				$crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl'));
198
+				$crl->loadCRL(file_get_contents(__DIR__.'/../../resources/codesigning/root.crl'));
199 199
 				if ($crl->validateSignature() !== true) {
200 200
 					throw new \Exception('Could not validate CRL signature');
201 201
 				}
@@ -256,11 +256,11 @@  discard block
 block discarded – undo
256 256
 					$archive = new TAR($tempFile);
257 257
 
258 258
 					if (!$archive->extract($extractDir)) {
259
-						$errorMessage = 'Could not extract app ' . $appId;
259
+						$errorMessage = 'Could not extract app '.$appId;
260 260
 
261 261
 						$archiveError = $archive->getError();
262 262
 						if ($archiveError instanceof \PEAR_Error) {
263
-							$errorMessage .= ': ' . $archiveError->getMessage();
263
+							$errorMessage .= ': '.$archiveError->getMessage();
264 264
 						}
265 265
 
266 266
 						throw new \Exception($errorMessage);
@@ -288,7 +288,7 @@  discard block
 block discarded – undo
288 288
 					}
289 289
 
290 290
 					// Check if appinfo/info.xml has the same app ID as well
291
-					$xml = simplexml_load_string(file_get_contents($extractDir . '/' . $folders[0] . '/appinfo/info.xml'));
291
+					$xml = simplexml_load_string(file_get_contents($extractDir.'/'.$folders[0].'/appinfo/info.xml'));
292 292
 
293 293
 					if ($xml === false) {
294 294
 						throw new \Exception(
@@ -299,19 +299,19 @@  discard block
 block discarded – undo
299 299
 						);
300 300
 					}
301 301
 
302
-					if ((string)$xml->id !== $appId) {
302
+					if ((string) $xml->id !== $appId) {
303 303
 						throw new \Exception(
304 304
 							sprintf(
305 305
 								'App for id %s has a wrong app ID in info.xml: %s',
306 306
 								$appId,
307
-								(string)$xml->id
307
+								(string) $xml->id
308 308
 							)
309 309
 						);
310 310
 					}
311 311
 
312 312
 					// Check if the version is lower than before
313 313
 					$currentVersion = \OCP\Server::get(IAppManager::class)->getAppVersion($appId, true);
314
-					$newVersion = (string)$xml->version;
314
+					$newVersion = (string) $xml->version;
315 315
 					if (version_compare($currentVersion, $newVersion) === 1) {
316 316
 						throw new \Exception(
317 317
 							sprintf(
@@ -323,12 +323,12 @@  discard block
 block discarded – undo
323 323
 						);
324 324
 					}
325 325
 
326
-					$baseDir = OC_App::getInstallPath() . '/' . $appId;
326
+					$baseDir = OC_App::getInstallPath().'/'.$appId;
327 327
 					// Remove old app with the ID if existent
328 328
 					Files::rmdirr($baseDir);
329 329
 					// Move to app folder
330 330
 					if (@mkdir($baseDir)) {
331
-						$extractDir .= '/' . $folders[0];
331
+						$extractDir .= '/'.$folders[0];
332 332
 						OC_Helper::copyr($extractDir, $baseDir);
333 333
 					}
334 334
 					OC_Helper::copyr($extractDir, $baseDir);
@@ -360,7 +360,7 @@  discard block
 block discarded – undo
360 360
 	 * @param bool $allowUnstable
361 361
 	 * @return string|false false or the version number of the update
362 362
 	 */
363
-	public function isUpdateAvailable($appId, $allowUnstable = false): string|false {
363
+	public function isUpdateAvailable($appId, $allowUnstable = false): string | false {
364 364
 		if ($this->isInstanceReadyForUpdates === null) {
365 365
 			$installPath = OC_App::getInstallPath();
366 366
 			if ($installPath === null) {
@@ -411,8 +411,8 @@  discard block
 block discarded – undo
411 411
 		if ($app === false) {
412 412
 			return false;
413 413
 		}
414
-		$basedir = $app['path'] . '/' . $appId;
415
-		return file_exists($basedir . '/.git/');
414
+		$basedir = $app['path'].'/'.$appId;
415
+		return file_exists($basedir.'/.git/');
416 416
 	}
417 417
 
418 418
 	/**
@@ -450,11 +450,11 @@  discard block
 block discarded – undo
450 450
 			if (\OCP\Server::get(IAppManager::class)->isShipped($appId)) {
451 451
 				return false;
452 452
 			}
453
-			$appDir = OC_App::getInstallPath() . '/' . $appId;
453
+			$appDir = OC_App::getInstallPath().'/'.$appId;
454 454
 			Files::rmdirr($appDir);
455 455
 			return true;
456 456
 		} else {
457
-			$this->logger->error('can\'t remove app ' . $appId . '. It is not installed.');
457
+			$this->logger->error('can\'t remove app '.$appId.'. It is not installed.');
458 458
 
459 459
 			return false;
460 460
 		}
@@ -498,8 +498,8 @@  discard block
 block discarded – undo
498 498
 		foreach (\OC::$APPSROOTS as $app_dir) {
499 499
 			if ($dir = opendir($app_dir['path'])) {
500 500
 				while (false !== ($filename = readdir($dir))) {
501
-					if ($filename[0] !== '.' and is_dir($app_dir['path'] . "/$filename")) {
502
-						if (file_exists($app_dir['path'] . "/$filename/appinfo/info.xml")) {
501
+					if ($filename[0] !== '.' and is_dir($app_dir['path']."/$filename")) {
502
+						if (file_exists($app_dir['path']."/$filename/appinfo/info.xml")) {
503 503
 							if ($config->getAppValue($filename, 'installed_version', null) === null) {
504 504
 								$enabled = $appManager->isDefaultEnabled($filename);
505 505
 								if (($enabled || in_array($filename, $appManager->getAlwaysEnabledApps()))
@@ -533,9 +533,9 @@  discard block
 block discarded – undo
533 533
 	/**
534 534
 	 * install an app already placed in the app folder
535 535
 	 */
536
-	public static function installShippedApp(string $app, ?IOutput $output = null): string|false {
536
+	public static function installShippedApp(string $app, ?IOutput $output = null): string | false {
537 537
 		if ($output instanceof IOutput) {
538
-			$output->debug('Installing ' . $app);
538
+			$output->debug('Installing '.$app);
539 539
 		}
540 540
 
541 541
 		$appManager = \OCP\Server::get(IAppManager::class);
@@ -559,7 +559,7 @@  discard block
 block discarded – undo
559 559
 			return false;
560 560
 		}
561 561
 		if ($output instanceof IOutput) {
562
-			$output->debug('Registering tasks of ' . $app);
562
+			$output->debug('Registering tasks of '.$app);
563 563
 		}
564 564
 		\OC_App::setupBackgroundJobs($info['background-jobs']);
565 565
 
@@ -572,10 +572,10 @@  discard block
 block discarded – undo
572 572
 
573 573
 		//set remote/public handlers
574 574
 		foreach ($info['remote'] as $name => $path) {
575
-			$config->setAppValue('core', 'remote_' . $name, $app . '/' . $path);
575
+			$config->setAppValue('core', 'remote_'.$name, $app.'/'.$path);
576 576
 		}
577 577
 		foreach ($info['public'] as $name => $path) {
578
-			$config->setAppValue('core', 'public_' . $name, $app . '/' . $path);
578
+			$config->setAppValue('core', 'public_'.$name, $app.'/'.$path);
579 579
 		}
580 580
 
581 581
 		OC_App::setAppTypes($info['id']);
Please login to merge, or discard this patch.
lib/private/Files/Storage/Temporary.php 1 patch
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -15,20 +15,20 @@
 block discarded – undo
15 15
  * local storage backend in temporary folder for testing purpose
16 16
  */
17 17
 class Temporary extends Local {
18
-	public function __construct(array $parameters = []) {
19
-		parent::__construct(['datadir' => Server::get(ITempManager::class)->getTemporaryFolder()]);
20
-	}
18
+    public function __construct(array $parameters = []) {
19
+        parent::__construct(['datadir' => Server::get(ITempManager::class)->getTemporaryFolder()]);
20
+    }
21 21
 
22
-	public function cleanUp(): void {
23
-		Files::rmdirr($this->datadir);
24
-	}
22
+    public function cleanUp(): void {
23
+        Files::rmdirr($this->datadir);
24
+    }
25 25
 
26
-	public function __destruct() {
27
-		parent::__destruct();
28
-		$this->cleanUp();
29
-	}
26
+    public function __destruct() {
27
+        parent::__destruct();
28
+        $this->cleanUp();
29
+    }
30 30
 
31
-	public function getDataDir(): array|string {
32
-		return $this->datadir;
33
-	}
31
+    public function getDataDir(): array|string {
32
+        return $this->datadir;
33
+    }
34 34
 }
Please login to merge, or discard this patch.
lib/private/TempManager.php 1 patch
Indentation   +203 added lines, -203 removed lines patch added patch discarded remove patch
@@ -15,207 +15,207 @@
 block discarded – undo
15 15
 use Psr\Log\LoggerInterface;
16 16
 
17 17
 class TempManager implements ITempManager {
18
-	/** @var string[] Current temporary files and folders, used for cleanup */
19
-	protected $current = [];
20
-	/** @var string i.e. /tmp on linux systems */
21
-	protected $tmpBaseDir;
22
-	/** @var LoggerInterface */
23
-	protected $log;
24
-	/** @var IConfig */
25
-	protected $config;
26
-	/** @var IniGetWrapper */
27
-	protected $iniGetWrapper;
28
-
29
-	/** Prefix */
30
-	public const TMP_PREFIX = 'oc_tmp_';
31
-
32
-	public function __construct(LoggerInterface $logger, IConfig $config, IniGetWrapper $iniGetWrapper) {
33
-		$this->log = $logger;
34
-		$this->config = $config;
35
-		$this->iniGetWrapper = $iniGetWrapper;
36
-		$this->tmpBaseDir = $this->getTempBaseDir();
37
-	}
38
-
39
-	private function generateTemporaryPath(string $postFix): string {
40
-		$secureRandom = \OCP\Server::get(ISecureRandom::class);
41
-		$absolutePath = $this->tmpBaseDir . '/' . self::TMP_PREFIX . $secureRandom->generate(32, ISecureRandom::CHAR_ALPHANUMERIC);
42
-
43
-		if ($postFix !== '') {
44
-			$postFix = '.' . ltrim($postFix, '.');
45
-			$postFix = str_replace(['\\', '/'], '', $postFix);
46
-		}
47
-
48
-		return $absolutePath . $postFix;
49
-	}
50
-
51
-	public function getTemporaryFile($postFix = ''): string|false {
52
-		$path = $this->generateTemporaryPath($postFix);
53
-
54
-		$old_umask = umask(0077);
55
-		$fp = fopen($path, 'x');
56
-		umask($old_umask);
57
-		if ($fp === false) {
58
-			$this->log->warning(
59
-				'Can not create a temporary file in directory {dir}. Check it exists and has correct permissions',
60
-				[
61
-					'dir' => $this->tmpBaseDir,
62
-				]
63
-			);
64
-			return false;
65
-		}
66
-
67
-		fclose($fp);
68
-		$this->current[] = $path;
69
-		return $path;
70
-	}
71
-
72
-	public function getTemporaryFolder($postFix = ''): string|false {
73
-		$path = $this->generateTemporaryPath($postFix) . '/';
74
-
75
-		if (mkdir($path, 0700) === false) {
76
-			$this->log->warning(
77
-				'Can not create a temporary folder in directory {dir}. Check it exists and has correct permissions',
78
-				[
79
-					'dir' => $this->tmpBaseDir,
80
-				]
81
-			);
82
-			return false;
83
-		}
84
-
85
-		$this->current[] = $path;
86
-		return $path;
87
-	}
88
-
89
-	/**
90
-	 * Remove the temporary files and folders generated during this request
91
-	 */
92
-	public function clean() {
93
-		$this->cleanFiles($this->current);
94
-	}
95
-
96
-	/**
97
-	 * @param string[] $files
98
-	 */
99
-	protected function cleanFiles($files) {
100
-		foreach ($files as $file) {
101
-			if (file_exists($file)) {
102
-				try {
103
-					Files::rmdirr($file);
104
-				} catch (\UnexpectedValueException $ex) {
105
-					$this->log->warning(
106
-						'Error deleting temporary file/folder: {file} - Reason: {error}',
107
-						[
108
-							'file' => $file,
109
-							'error' => $ex->getMessage(),
110
-						]
111
-					);
112
-				}
113
-			}
114
-		}
115
-	}
116
-
117
-	/**
118
-	 * Remove old temporary files and folders that were failed to be cleaned
119
-	 */
120
-	public function cleanOld() {
121
-		$this->cleanFiles($this->getOldFiles());
122
-	}
123
-
124
-	/**
125
-	 * Get all temporary files and folders generated by oc older than an hour
126
-	 *
127
-	 * @return string[]
128
-	 */
129
-	protected function getOldFiles() {
130
-		$cutOfTime = time() - 3600;
131
-		$files = [];
132
-		$dh = opendir($this->tmpBaseDir);
133
-		if ($dh) {
134
-			while (($file = readdir($dh)) !== false) {
135
-				if (substr($file, 0, 7) === self::TMP_PREFIX) {
136
-					$path = $this->tmpBaseDir . '/' . $file;
137
-					$mtime = filemtime($path);
138
-					if ($mtime < $cutOfTime) {
139
-						$files[] = $path;
140
-					}
141
-				}
142
-			}
143
-		}
144
-		return $files;
145
-	}
146
-
147
-	/**
148
-	 * Get the temporary base directory configured on the server
149
-	 *
150
-	 * @return string Path to the temporary directory or null
151
-	 * @throws \UnexpectedValueException
152
-	 */
153
-	public function getTempBaseDir() {
154
-		if ($this->tmpBaseDir) {
155
-			return $this->tmpBaseDir;
156
-		}
157
-
158
-		$directories = [];
159
-		if ($temp = $this->config->getSystemValue('tempdirectory', null)) {
160
-			$directories[] = $temp;
161
-		}
162
-		if ($temp = $this->iniGetWrapper->get('upload_tmp_dir')) {
163
-			$directories[] = $temp;
164
-		}
165
-		if ($temp = getenv('TMP')) {
166
-			$directories[] = $temp;
167
-		}
168
-		if ($temp = getenv('TEMP')) {
169
-			$directories[] = $temp;
170
-		}
171
-		if ($temp = getenv('TMPDIR')) {
172
-			$directories[] = $temp;
173
-		}
174
-		if ($temp = sys_get_temp_dir()) {
175
-			$directories[] = $temp;
176
-		}
177
-
178
-		foreach ($directories as $dir) {
179
-			if ($this->checkTemporaryDirectory($dir)) {
180
-				return $dir;
181
-			}
182
-		}
183
-
184
-		$temp = tempnam(__DIR__, '');
185
-		if (file_exists($temp)) {
186
-			unlink($temp);
187
-			return dirname($temp);
188
-		}
189
-		throw new \UnexpectedValueException('Unable to detect system temporary directory');
190
-	}
191
-
192
-	/**
193
-	 * Check if a temporary directory is ready for use
194
-	 *
195
-	 * @param mixed $directory
196
-	 * @return bool
197
-	 */
198
-	private function checkTemporaryDirectory($directory) {
199
-		// suppress any possible errors caused by is_writable
200
-		// checks missing or invalid path or characters, wrong permissions etc
201
-		try {
202
-			if (is_writable($directory)) {
203
-				return true;
204
-			}
205
-		} catch (\Exception $e) {
206
-		}
207
-		$this->log->warning('Temporary directory {dir} is not present or writable',
208
-			['dir' => $directory]
209
-		);
210
-		return false;
211
-	}
212
-
213
-	/**
214
-	 * Override the temporary base directory
215
-	 *
216
-	 * @param string $directory
217
-	 */
218
-	public function overrideTempBaseDir($directory) {
219
-		$this->tmpBaseDir = $directory;
220
-	}
18
+    /** @var string[] Current temporary files and folders, used for cleanup */
19
+    protected $current = [];
20
+    /** @var string i.e. /tmp on linux systems */
21
+    protected $tmpBaseDir;
22
+    /** @var LoggerInterface */
23
+    protected $log;
24
+    /** @var IConfig */
25
+    protected $config;
26
+    /** @var IniGetWrapper */
27
+    protected $iniGetWrapper;
28
+
29
+    /** Prefix */
30
+    public const TMP_PREFIX = 'oc_tmp_';
31
+
32
+    public function __construct(LoggerInterface $logger, IConfig $config, IniGetWrapper $iniGetWrapper) {
33
+        $this->log = $logger;
34
+        $this->config = $config;
35
+        $this->iniGetWrapper = $iniGetWrapper;
36
+        $this->tmpBaseDir = $this->getTempBaseDir();
37
+    }
38
+
39
+    private function generateTemporaryPath(string $postFix): string {
40
+        $secureRandom = \OCP\Server::get(ISecureRandom::class);
41
+        $absolutePath = $this->tmpBaseDir . '/' . self::TMP_PREFIX . $secureRandom->generate(32, ISecureRandom::CHAR_ALPHANUMERIC);
42
+
43
+        if ($postFix !== '') {
44
+            $postFix = '.' . ltrim($postFix, '.');
45
+            $postFix = str_replace(['\\', '/'], '', $postFix);
46
+        }
47
+
48
+        return $absolutePath . $postFix;
49
+    }
50
+
51
+    public function getTemporaryFile($postFix = ''): string|false {
52
+        $path = $this->generateTemporaryPath($postFix);
53
+
54
+        $old_umask = umask(0077);
55
+        $fp = fopen($path, 'x');
56
+        umask($old_umask);
57
+        if ($fp === false) {
58
+            $this->log->warning(
59
+                'Can not create a temporary file in directory {dir}. Check it exists and has correct permissions',
60
+                [
61
+                    'dir' => $this->tmpBaseDir,
62
+                ]
63
+            );
64
+            return false;
65
+        }
66
+
67
+        fclose($fp);
68
+        $this->current[] = $path;
69
+        return $path;
70
+    }
71
+
72
+    public function getTemporaryFolder($postFix = ''): string|false {
73
+        $path = $this->generateTemporaryPath($postFix) . '/';
74
+
75
+        if (mkdir($path, 0700) === false) {
76
+            $this->log->warning(
77
+                'Can not create a temporary folder in directory {dir}. Check it exists and has correct permissions',
78
+                [
79
+                    'dir' => $this->tmpBaseDir,
80
+                ]
81
+            );
82
+            return false;
83
+        }
84
+
85
+        $this->current[] = $path;
86
+        return $path;
87
+    }
88
+
89
+    /**
90
+     * Remove the temporary files and folders generated during this request
91
+     */
92
+    public function clean() {
93
+        $this->cleanFiles($this->current);
94
+    }
95
+
96
+    /**
97
+     * @param string[] $files
98
+     */
99
+    protected function cleanFiles($files) {
100
+        foreach ($files as $file) {
101
+            if (file_exists($file)) {
102
+                try {
103
+                    Files::rmdirr($file);
104
+                } catch (\UnexpectedValueException $ex) {
105
+                    $this->log->warning(
106
+                        'Error deleting temporary file/folder: {file} - Reason: {error}',
107
+                        [
108
+                            'file' => $file,
109
+                            'error' => $ex->getMessage(),
110
+                        ]
111
+                    );
112
+                }
113
+            }
114
+        }
115
+    }
116
+
117
+    /**
118
+     * Remove old temporary files and folders that were failed to be cleaned
119
+     */
120
+    public function cleanOld() {
121
+        $this->cleanFiles($this->getOldFiles());
122
+    }
123
+
124
+    /**
125
+     * Get all temporary files and folders generated by oc older than an hour
126
+     *
127
+     * @return string[]
128
+     */
129
+    protected function getOldFiles() {
130
+        $cutOfTime = time() - 3600;
131
+        $files = [];
132
+        $dh = opendir($this->tmpBaseDir);
133
+        if ($dh) {
134
+            while (($file = readdir($dh)) !== false) {
135
+                if (substr($file, 0, 7) === self::TMP_PREFIX) {
136
+                    $path = $this->tmpBaseDir . '/' . $file;
137
+                    $mtime = filemtime($path);
138
+                    if ($mtime < $cutOfTime) {
139
+                        $files[] = $path;
140
+                    }
141
+                }
142
+            }
143
+        }
144
+        return $files;
145
+    }
146
+
147
+    /**
148
+     * Get the temporary base directory configured on the server
149
+     *
150
+     * @return string Path to the temporary directory or null
151
+     * @throws \UnexpectedValueException
152
+     */
153
+    public function getTempBaseDir() {
154
+        if ($this->tmpBaseDir) {
155
+            return $this->tmpBaseDir;
156
+        }
157
+
158
+        $directories = [];
159
+        if ($temp = $this->config->getSystemValue('tempdirectory', null)) {
160
+            $directories[] = $temp;
161
+        }
162
+        if ($temp = $this->iniGetWrapper->get('upload_tmp_dir')) {
163
+            $directories[] = $temp;
164
+        }
165
+        if ($temp = getenv('TMP')) {
166
+            $directories[] = $temp;
167
+        }
168
+        if ($temp = getenv('TEMP')) {
169
+            $directories[] = $temp;
170
+        }
171
+        if ($temp = getenv('TMPDIR')) {
172
+            $directories[] = $temp;
173
+        }
174
+        if ($temp = sys_get_temp_dir()) {
175
+            $directories[] = $temp;
176
+        }
177
+
178
+        foreach ($directories as $dir) {
179
+            if ($this->checkTemporaryDirectory($dir)) {
180
+                return $dir;
181
+            }
182
+        }
183
+
184
+        $temp = tempnam(__DIR__, '');
185
+        if (file_exists($temp)) {
186
+            unlink($temp);
187
+            return dirname($temp);
188
+        }
189
+        throw new \UnexpectedValueException('Unable to detect system temporary directory');
190
+    }
191
+
192
+    /**
193
+     * Check if a temporary directory is ready for use
194
+     *
195
+     * @param mixed $directory
196
+     * @return bool
197
+     */
198
+    private function checkTemporaryDirectory($directory) {
199
+        // suppress any possible errors caused by is_writable
200
+        // checks missing or invalid path or characters, wrong permissions etc
201
+        try {
202
+            if (is_writable($directory)) {
203
+                return true;
204
+            }
205
+        } catch (\Exception $e) {
206
+        }
207
+        $this->log->warning('Temporary directory {dir} is not present or writable',
208
+            ['dir' => $directory]
209
+        );
210
+        return false;
211
+    }
212
+
213
+    /**
214
+     * Override the temporary base directory
215
+     *
216
+     * @param string $directory
217
+     */
218
+    public function overrideTempBaseDir($directory) {
219
+        $this->tmpBaseDir = $directory;
220
+    }
221 221
 }
Please login to merge, or discard this patch.
lib/private/Repair/MoveUpdaterStepFile.php 1 patch
Indentation   +47 added lines, -47 removed lines patch added patch discarded remove patch
@@ -10,51 +10,51 @@
 block discarded – undo
10 10
 use OCP\Migration\IRepairStep;
11 11
 
12 12
 class MoveUpdaterStepFile implements IRepairStep {
13
-	/** @var \OCP\IConfig */
14
-	protected $config;
15
-
16
-	/**
17
-	 * @param \OCP\IConfig $config
18
-	 */
19
-	public function __construct($config) {
20
-		$this->config = $config;
21
-	}
22
-
23
-	public function getName() {
24
-		return 'Move .step file of updater to backup location';
25
-	}
26
-
27
-	public function run(IOutput $output) {
28
-		$updateDir = $this->config->getSystemValue('updatedirectory', null) ?? $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data');
29
-		$instanceId = $this->config->getSystemValueString('instanceid');
30
-
31
-		if (empty($instanceId)) {
32
-			return;
33
-		}
34
-
35
-		$updaterFolderPath = $updateDir . '/updater-' . $instanceId;
36
-		$stepFile = $updaterFolderPath . '/.step';
37
-		if (file_exists($stepFile)) {
38
-			$output->info('.step file exists');
39
-
40
-			$previousStepFile = $updaterFolderPath . '/.step-previous-update';
41
-
42
-			// cleanup
43
-			if (file_exists($previousStepFile)) {
44
-				if (Files::rmdirr($previousStepFile)) {
45
-					$output->info('.step-previous-update removed');
46
-				} else {
47
-					$output->info('.step-previous-update can\'t be removed - abort move of .step file');
48
-					return;
49
-				}
50
-			}
51
-
52
-			// move step file
53
-			if (rename($stepFile, $previousStepFile)) {
54
-				$output->info('.step file moved to .step-previous-update');
55
-			} else {
56
-				$output->warning('.step file can\'t be moved');
57
-			}
58
-		}
59
-	}
13
+    /** @var \OCP\IConfig */
14
+    protected $config;
15
+
16
+    /**
17
+     * @param \OCP\IConfig $config
18
+     */
19
+    public function __construct($config) {
20
+        $this->config = $config;
21
+    }
22
+
23
+    public function getName() {
24
+        return 'Move .step file of updater to backup location';
25
+    }
26
+
27
+    public function run(IOutput $output) {
28
+        $updateDir = $this->config->getSystemValue('updatedirectory', null) ?? $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data');
29
+        $instanceId = $this->config->getSystemValueString('instanceid');
30
+
31
+        if (empty($instanceId)) {
32
+            return;
33
+        }
34
+
35
+        $updaterFolderPath = $updateDir . '/updater-' . $instanceId;
36
+        $stepFile = $updaterFolderPath . '/.step';
37
+        if (file_exists($stepFile)) {
38
+            $output->info('.step file exists');
39
+
40
+            $previousStepFile = $updaterFolderPath . '/.step-previous-update';
41
+
42
+            // cleanup
43
+            if (file_exists($previousStepFile)) {
44
+                if (Files::rmdirr($previousStepFile)) {
45
+                    $output->info('.step-previous-update removed');
46
+                } else {
47
+                    $output->info('.step-previous-update can\'t be removed - abort move of .step file');
48
+                    return;
49
+                }
50
+            }
51
+
52
+            // move step file
53
+            if (rename($stepFile, $previousStepFile)) {
54
+                $output->info('.step file moved to .step-previous-update');
55
+            } else {
56
+                $output->warning('.step file can\'t be moved');
57
+            }
58
+        }
59
+    }
60 60
 }
Please login to merge, or discard this patch.
core/BackgroundJobs/BackgroundCleanupUpdaterBackupsJob.php 1 patch
Indentation   +56 added lines, -56 removed lines patch added patch discarded remove patch
@@ -15,70 +15,70 @@
 block discarded – undo
15 15
 use Psr\Log\LoggerInterface;
16 16
 
17 17
 class BackgroundCleanupUpdaterBackupsJob extends QueuedJob {
18
-	public function __construct(
19
-		protected IConfig $config,
20
-		protected LoggerInterface $log,
21
-		ITimeFactory $time,
22
-	) {
23
-		parent::__construct($time);
24
-	}
18
+    public function __construct(
19
+        protected IConfig $config,
20
+        protected LoggerInterface $log,
21
+        ITimeFactory $time,
22
+    ) {
23
+        parent::__construct($time);
24
+    }
25 25
 
26
-	/**
27
-	 * This job cleans up all backups except the latest 3 from the updaters backup directory
28
-	 *
29
-	 * @param array $argument
30
-	 */
31
-	public function run($argument): void {
32
-		$this->log->info('Running background job to clean-up outdated updater backups');
26
+    /**
27
+     * This job cleans up all backups except the latest 3 from the updaters backup directory
28
+     *
29
+     * @param array $argument
30
+     */
31
+    public function run($argument): void {
32
+        $this->log->info('Running background job to clean-up outdated updater backups');
33 33
 
34
-		$updateDir = $this->config->getSystemValue('updatedirectory', null) ?? $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data');
35
-		$instanceId = $this->config->getSystemValue('instanceid', null);
34
+        $updateDir = $this->config->getSystemValue('updatedirectory', null) ?? $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data');
35
+        $instanceId = $this->config->getSystemValue('instanceid', null);
36 36
 
37
-		if (!is_string($instanceId) || empty($instanceId)) {
38
-			$this->log->error('Skipping updater backup clean-up - instanceId is missing!');
39
-			return;
40
-		}
37
+        if (!is_string($instanceId) || empty($instanceId)) {
38
+            $this->log->error('Skipping updater backup clean-up - instanceId is missing!');
39
+            return;
40
+        }
41 41
 
42
-		$updaterFolderPath = $updateDir . '/updater-' . $instanceId;
43
-		$backupFolderPath = $updaterFolderPath . '/backups';
44
-		if (file_exists($backupFolderPath)) {
45
-			$this->log->debug("Updater backup folder detected: $backupFolderPath");
42
+        $updaterFolderPath = $updateDir . '/updater-' . $instanceId;
43
+        $backupFolderPath = $updaterFolderPath . '/backups';
44
+        if (file_exists($backupFolderPath)) {
45
+            $this->log->debug("Updater backup folder detected: $backupFolderPath");
46 46
 
47
-			$dirList = [];
48
-			$dirs = new \DirectoryIterator($backupFolderPath);
49
-			foreach ($dirs as $dir) {
50
-				// skip files and dot dirs
51
-				if ($dir->isFile() || $dir->isDot()) {
52
-					continue;
53
-				}
47
+            $dirList = [];
48
+            $dirs = new \DirectoryIterator($backupFolderPath);
49
+            foreach ($dirs as $dir) {
50
+                // skip files and dot dirs
51
+                if ($dir->isFile() || $dir->isDot()) {
52
+                    continue;
53
+                }
54 54
 
55
-				$mtime = $dir->getMTime();
56
-				$realPath = $dir->getRealPath();
55
+                $mtime = $dir->getMTime();
56
+                $realPath = $dir->getRealPath();
57 57
 
58
-				if ($realPath === false) {
59
-					$pathName = $dir->getPathname();
60
-					$this->log->warning("Skipping updater backup folder: $pathName (not found)");
61
-					continue;
62
-				}
58
+                if ($realPath === false) {
59
+                    $pathName = $dir->getPathname();
60
+                    $this->log->warning("Skipping updater backup folder: $pathName (not found)");
61
+                    continue;
62
+                }
63 63
 
64
-				$dirList[$mtime] = $realPath;
65
-			}
64
+                $dirList[$mtime] = $realPath;
65
+            }
66 66
 
67
-			ksort($dirList);
68
-			// drop the newest 3 directories
69
-			$dirList = array_slice($dirList, 0, -3);
70
-			$this->log->debug('Updater backup folders that will be deleted: ' . json_encode($dirList));
67
+            ksort($dirList);
68
+            // drop the newest 3 directories
69
+            $dirList = array_slice($dirList, 0, -3);
70
+            $this->log->debug('Updater backup folders that will be deleted: ' . json_encode($dirList));
71 71
 
72
-			foreach ($dirList as $dir) {
73
-				$this->log->info("Removing $dir ...");
74
-				$result = Files::rmdirr($dir);
75
-				if (!$result) {
76
-					$this->log->error('Could not remove updater backup folder $dir');
77
-				}
78
-			}
79
-			$this->log->info('Background job to clean-up updater backups has finished');
80
-		} else {
81
-			$this->log->warning("Skipping updater backup clean-up - could not find updater backup folder $backupFolderPath");
82
-		}
83
-	}
72
+            foreach ($dirList as $dir) {
73
+                $this->log->info("Removing $dir ...");
74
+                $result = Files::rmdirr($dir);
75
+                if (!$result) {
76
+                    $this->log->error('Could not remove updater backup folder $dir');
77
+                }
78
+            }
79
+            $this->log->info('Background job to clean-up updater backups has finished');
80
+        } else {
81
+            $this->log->warning("Skipping updater backup clean-up - could not find updater backup folder $backupFolderPath");
82
+        }
83
+    }
84 84
 }
Please login to merge, or discard this patch.