Completed
Push — master ( 4c6c51...16708b )
by
unknown
27:36 queued 10s
created
lib/public/Files.php 1 patch
Indentation   +110 added lines, -110 removed lines patch added patch discarded remove patch
@@ -19,123 +19,123 @@
 block discarded – undo
19 19
  * @deprecated 14.0.0
20 20
  */
21 21
 class Files {
22
-	/**
23
-	 * Recursive deletion of folders
24
-	 *
25
-	 * @param string $dir path to the folder
26
-	 * @param bool $deleteSelf if set to false only the content of the folder will be deleted
27
-	 * @return bool
28
-	 * @since 5.0.0
29
-	 * @since 32.0.0 added the $deleteSelf parameter
30
-	 * @deprecated 14.0.0
31
-	 */
32
-	public static function rmdirr($dir, bool $deleteSelf = true) {
33
-		if (is_dir($dir)) {
34
-			$files = new \RecursiveIteratorIterator(
35
-				new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
36
-				\RecursiveIteratorIterator::CHILD_FIRST
37
-			);
22
+    /**
23
+     * Recursive deletion of folders
24
+     *
25
+     * @param string $dir path to the folder
26
+     * @param bool $deleteSelf if set to false only the content of the folder will be deleted
27
+     * @return bool
28
+     * @since 5.0.0
29
+     * @since 32.0.0 added the $deleteSelf parameter
30
+     * @deprecated 14.0.0
31
+     */
32
+    public static function rmdirr($dir, bool $deleteSelf = true) {
33
+        if (is_dir($dir)) {
34
+            $files = new \RecursiveIteratorIterator(
35
+                new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
36
+                \RecursiveIteratorIterator::CHILD_FIRST
37
+            );
38 38
 
39
-			foreach ($files as $fileInfo) {
40
-				/** @var \SplFileInfo $fileInfo */
41
-				if ($fileInfo->isLink()) {
42
-					unlink($fileInfo->getPathname());
43
-				} elseif ($fileInfo->isDir()) {
44
-					rmdir($fileInfo->getRealPath());
45
-				} else {
46
-					unlink($fileInfo->getRealPath());
47
-				}
48
-			}
49
-			if ($deleteSelf) {
50
-				rmdir($dir);
51
-			}
52
-		} elseif (file_exists($dir)) {
53
-			if ($deleteSelf) {
54
-				unlink($dir);
55
-			}
56
-		}
57
-		if (!$deleteSelf) {
58
-			return true;
59
-		}
39
+            foreach ($files as $fileInfo) {
40
+                /** @var \SplFileInfo $fileInfo */
41
+                if ($fileInfo->isLink()) {
42
+                    unlink($fileInfo->getPathname());
43
+                } elseif ($fileInfo->isDir()) {
44
+                    rmdir($fileInfo->getRealPath());
45
+                } else {
46
+                    unlink($fileInfo->getRealPath());
47
+                }
48
+            }
49
+            if ($deleteSelf) {
50
+                rmdir($dir);
51
+            }
52
+        } elseif (file_exists($dir)) {
53
+            if ($deleteSelf) {
54
+                unlink($dir);
55
+            }
56
+        }
57
+        if (!$deleteSelf) {
58
+            return true;
59
+        }
60 60
 
61
-		return !file_exists($dir);
62
-	}
61
+        return !file_exists($dir);
62
+    }
63 63
 
64
-	/**
65
-	 * Get the mimetype form a local file
66
-	 * @param string $path
67
-	 * @return string
68
-	 *                does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead
69
-	 * @since 5.0.0
70
-	 * @deprecated 14.0.0
71
-	 */
72
-	public static function getMimeType($path) {
73
-		return Server::get(IMimeTypeDetector::class)->detect($path);
74
-	}
64
+    /**
65
+     * Get the mimetype form a local file
66
+     * @param string $path
67
+     * @return string
68
+     *                does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead
69
+     * @since 5.0.0
70
+     * @deprecated 14.0.0
71
+     */
72
+    public static function getMimeType($path) {
73
+        return Server::get(IMimeTypeDetector::class)->detect($path);
74
+    }
75 75
 
76
-	/**
77
-	 * Search for files by mimetype
78
-	 * @param string $mimetype
79
-	 * @return array
80
-	 * @since 6.0.0
81
-	 * @deprecated 14.0.0
82
-	 */
83
-	public static function searchByMime($mimetype) {
84
-		return \OC\Files\Filesystem::searchByMime($mimetype);
85
-	}
76
+    /**
77
+     * Search for files by mimetype
78
+     * @param string $mimetype
79
+     * @return array
80
+     * @since 6.0.0
81
+     * @deprecated 14.0.0
82
+     */
83
+    public static function searchByMime($mimetype) {
84
+        return \OC\Files\Filesystem::searchByMime($mimetype);
85
+    }
86 86
 
87
-	/**
88
-	 * Copy the contents of one stream to another
89
-	 *
90
-	 * @template T of null|true
91
-	 * @param resource $source
92
-	 * @param resource $target
93
-	 * @param T $includeResult
94
-	 * @return int|array
95
-	 * @psalm-return (T is true ? array{0: int, 1: bool} : int)
96
-	 * @since 5.0.0
97
-	 * @since 32.0.0 added $includeResult parameter
98
-	 * @deprecated 14.0.0
99
-	 */
100
-	public static function streamCopy($source, $target, ?bool $includeResult = null) {
101
-		if (!$source || !$target) {
102
-			return $includeResult ? [0, false] : 0;
103
-		}
87
+    /**
88
+     * Copy the contents of one stream to another
89
+     *
90
+     * @template T of null|true
91
+     * @param resource $source
92
+     * @param resource $target
93
+     * @param T $includeResult
94
+     * @return int|array
95
+     * @psalm-return (T is true ? array{0: int, 1: bool} : int)
96
+     * @since 5.0.0
97
+     * @since 32.0.0 added $includeResult parameter
98
+     * @deprecated 14.0.0
99
+     */
100
+    public static function streamCopy($source, $target, ?bool $includeResult = null) {
101
+        if (!$source || !$target) {
102
+            return $includeResult ? [0, false] : 0;
103
+        }
104 104
 
105
-		$bufSize = 8192;
106
-		$count = 0;
107
-		$result = true;
108
-		while (!feof($source)) {
109
-			$buf = fread($source, $bufSize);
110
-			if ($buf === false) {
111
-				$result = false;
112
-				break;
113
-			}
105
+        $bufSize = 8192;
106
+        $count = 0;
107
+        $result = true;
108
+        while (!feof($source)) {
109
+            $buf = fread($source, $bufSize);
110
+            if ($buf === false) {
111
+                $result = false;
112
+                break;
113
+            }
114 114
 
115
-			$bytesWritten = fwrite($target, $buf);
116
-			if ($bytesWritten !== false) {
117
-				$count += $bytesWritten;
118
-			}
115
+            $bytesWritten = fwrite($target, $buf);
116
+            if ($bytesWritten !== false) {
117
+                $count += $bytesWritten;
118
+            }
119 119
 
120
-			if ($bytesWritten === false
121
-				|| ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
122
-			) {
123
-				$result = false;
124
-				break;
125
-			}
126
-		}
127
-		return $includeResult ? [$count, $result] : $count;
128
-	}
120
+            if ($bytesWritten === false
121
+                || ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
122
+            ) {
123
+                $result = false;
124
+                break;
125
+            }
126
+        }
127
+        return $includeResult ? [$count, $result] : $count;
128
+    }
129 129
 
130
-	/**
131
-	 * Adds a suffix to the name in case the file exists
132
-	 * @param string $path
133
-	 * @param string $filename
134
-	 * @return string
135
-	 * @since 5.0.0
136
-	 * @deprecated 14.0.0 use getNonExistingName of the OCP\Files\Folder object
137
-	 */
138
-	public static function buildNotExistingFileName($path, $filename) {
139
-		return \OC_Helper::buildNotExistingFileName($path, $filename);
140
-	}
130
+    /**
131
+     * Adds a suffix to the name in case the file exists
132
+     * @param string $path
133
+     * @param string $filename
134
+     * @return string
135
+     * @since 5.0.0
136
+     * @deprecated 14.0.0 use getNonExistingName of the OCP\Files\Folder object
137
+     */
138
+    public static function buildNotExistingFileName($path, $filename) {
139
+        return \OC_Helper::buildNotExistingFileName($path, $filename);
140
+    }
141 141
 }
Please login to merge, or discard this patch.
lib/public/Util.php 1 patch
Indentation   +637 added lines, -637 removed lines patch added patch discarded remove patch
@@ -25,641 +25,641 @@
 block discarded – undo
25 25
  * @since 4.0.0
26 26
  */
27 27
 class Util {
28
-	private static ?IManager $shareManager = null;
29
-
30
-	private static array $scriptsInit = [];
31
-	private static array $scripts = [];
32
-	private static array $scriptDeps = [];
33
-
34
-	/**
35
-	 * get the current installed version of Nextcloud
36
-	 * @return array
37
-	 * @since 4.0.0
38
-	 * @deprecated 31.0.0 Use \OCP\ServerVersion::getVersion
39
-	 */
40
-	public static function getVersion() {
41
-		return Server::get(ServerVersion::class)->getVersion();
42
-	}
43
-
44
-	/**
45
-	 * @since 17.0.0
46
-	 */
47
-	public static function hasExtendedSupport(): bool {
48
-		try {
49
-			/** @var \OCP\Support\Subscription\IRegistry */
50
-			$subscriptionRegistry = Server::get(\OCP\Support\Subscription\IRegistry::class);
51
-			return $subscriptionRegistry->delegateHasExtendedSupport();
52
-		} catch (ContainerExceptionInterface $e) {
53
-		}
54
-		return \OCP\Server::get(IConfig::class)->getSystemValueBool('extendedSupport', false);
55
-	}
56
-
57
-	/**
58
-	 * Set current update channel
59
-	 * @param string $channel
60
-	 * @since 8.1.0
61
-	 */
62
-	public static function setChannel($channel) {
63
-		\OCP\Server::get(IConfig::class)->setSystemValue('updater.release.channel', $channel);
64
-	}
65
-
66
-	/**
67
-	 * Get current update channel
68
-	 * @return string
69
-	 * @since 8.1.0
70
-	 * @deprecated 31.0.0 Use \OCP\ServerVersion::getChannel
71
-	 */
72
-	public static function getChannel() {
73
-		return \OCP\Server::get(ServerVersion::class)->getChannel();
74
-	}
75
-
76
-	/**
77
-	 * check if sharing is disabled for the current user
78
-	 *
79
-	 * @return boolean
80
-	 * @since 7.0.0
81
-	 * @deprecated 9.1.0 Use Server::get(\OCP\Share\IManager::class)->sharingDisabledForUser
82
-	 */
83
-	public static function isSharingDisabledForUser() {
84
-		if (self::$shareManager === null) {
85
-			self::$shareManager = Server::get(IManager::class);
86
-		}
87
-
88
-		$user = Server::get(\OCP\IUserSession::class)->getUser();
89
-
90
-		return self::$shareManager->sharingDisabledForUser($user?->getUID());
91
-	}
92
-
93
-	/**
94
-	 * get l10n object
95
-	 * @since 6.0.0 - parameter $language was added in 8.0.0
96
-	 */
97
-	public static function getL10N(string $application, ?string $language = null): IL10N {
98
-		return Server::get(\OCP\L10N\IFactory::class)->get($application, $language);
99
-	}
100
-
101
-	/**
102
-	 * Add a css file
103
-	 *
104
-	 * @param string $application application id
105
-	 * @param ?string $file filename
106
-	 * @param bool $prepend prepend the style to the beginning of the list
107
-	 * @since 4.0.0
108
-	 */
109
-	public static function addStyle(string $application, ?string $file = null, bool $prepend = false): void {
110
-		\OC_Util::addStyle($application, $file, $prepend);
111
-	}
112
-
113
-	/**
114
-	 * Add a standalone init js file that is loaded for initialization
115
-	 *
116
-	 * Be careful loading scripts using this method as they are loaded early
117
-	 * and block the initial page rendering. They should not have dependencies
118
-	 * on any other scripts than core-common and core-main.
119
-	 *
120
-	 * @since 28.0.0
121
-	 */
122
-	public static function addInitScript(string $application, string $file): void {
123
-		if (!empty($application)) {
124
-			$path = "$application/js/$file";
125
-		} else {
126
-			$path = "js/$file";
127
-		}
128
-
129
-		// We need to handle the translation BEFORE the init script
130
-		// is loaded, as the init script might use translations
131
-		if ($application !== 'core' && !str_contains($file, 'l10n')) {
132
-			self::addTranslations($application, null, true);
133
-		}
134
-
135
-		self::$scriptsInit[] = $path;
136
-	}
137
-
138
-	/**
139
-	 * add a javascript file
140
-	 *
141
-	 * @param string $application
142
-	 * @param string|null $file
143
-	 * @param string $afterAppId
144
-	 * @param bool $prepend
145
-	 * @since 4.0.0
146
-	 */
147
-	public static function addScript(string $application, ?string $file = null, string $afterAppId = 'core', bool $prepend = false): void {
148
-		if (!empty($application)) {
149
-			$path = "$application/js/$file";
150
-		} else {
151
-			$path = "js/$file";
152
-		}
153
-
154
-		// Inject js translations if we load a script for
155
-		// a specific app that is not core, as those js files
156
-		// need separate handling
157
-		if ($application !== 'core'
158
-			&& $file !== null
159
-			&& !str_contains($file, 'l10n')) {
160
-			self::addTranslations($application);
161
-		}
162
-
163
-		// store app in dependency list
164
-		if (!array_key_exists($application, self::$scriptDeps)) {
165
-			self::$scriptDeps[$application] = new AppScriptDependency($application, [$afterAppId]);
166
-		} else {
167
-			self::$scriptDeps[$application]->addDep($afterAppId);
168
-		}
169
-
170
-		if ($prepend) {
171
-			array_unshift(self::$scripts[$application], $path);
172
-		} else {
173
-			self::$scripts[$application][] = $path;
174
-		}
175
-	}
176
-
177
-	/**
178
-	 * Return the list of scripts injected to the page
179
-	 *
180
-	 * @return array
181
-	 * @since 24.0.0
182
-	 */
183
-	public static function getScripts(): array {
184
-		// Sort scriptDeps into sortedScriptDeps
185
-		$scriptSort = \OCP\Server::get(AppScriptSort::class);
186
-		$sortedScripts = $scriptSort->sort(self::$scripts, self::$scriptDeps);
187
-
188
-		// Flatten array and remove duplicates
189
-		$sortedScripts = array_merge([self::$scriptsInit], $sortedScripts);
190
-		$sortedScripts = array_merge(...array_values($sortedScripts));
191
-
192
-		// Override core-common and core-main order
193
-		if (in_array('core/js/main', $sortedScripts)) {
194
-			array_unshift($sortedScripts, 'core/js/main');
195
-		}
196
-		if (in_array('core/js/common', $sortedScripts)) {
197
-			array_unshift($sortedScripts, 'core/js/common');
198
-		}
199
-
200
-		return array_unique($sortedScripts);
201
-	}
202
-
203
-	/**
204
-	 * Add a translation JS file
205
-	 * @param string $application application id
206
-	 * @param string $languageCode language code, defaults to the current locale
207
-	 * @param bool $init whether the translations should be loaded early or not
208
-	 * @since 8.0.0
209
-	 */
210
-	public static function addTranslations($application, $languageCode = null, $init = false) {
211
-		if (is_null($languageCode)) {
212
-			$languageCode = \OCP\Server::get(IFactory::class)->findLanguage($application);
213
-		}
214
-		if (!empty($application)) {
215
-			$path = "$application/l10n/$languageCode";
216
-		} else {
217
-			$path = "l10n/$languageCode";
218
-		}
219
-
220
-		if ($init) {
221
-			self::$scriptsInit[] = $path;
222
-		} else {
223
-			self::$scripts[$application][] = $path;
224
-		}
225
-	}
226
-
227
-	/**
228
-	 * Add a custom element to the header
229
-	 * If $text is null then the element will be written as empty element.
230
-	 * So use "" to get a closing tag.
231
-	 * @param string $tag tag name of the element
232
-	 * @param array $attributes array of attributes for the element
233
-	 * @param string $text the text content for the element
234
-	 * @since 4.0.0
235
-	 */
236
-	public static function addHeader($tag, $attributes, $text = null) {
237
-		\OC_Util::addHeader($tag, $attributes, $text);
238
-	}
239
-
240
-	/**
241
-	 * Creates an absolute url to the given app and file.
242
-	 * @param string $app app
243
-	 * @param string $file file
244
-	 * @param array $args array with param=>value, will be appended to the returned url
245
-	 *                    The value of $args will be urlencoded
246
-	 * @return string the url
247
-	 * @since 4.0.0 - parameter $args was added in 4.5.0
248
-	 */
249
-	public static function linkToAbsolute($app, $file, $args = []) {
250
-		$urlGenerator = \OCP\Server::get(IURLGenerator::class);
251
-		return $urlGenerator->getAbsoluteURL(
252
-			$urlGenerator->linkTo($app, $file, $args)
253
-		);
254
-	}
255
-
256
-	/**
257
-	 * Creates an absolute url for remote use.
258
-	 * @param string $service id
259
-	 * @return string the url
260
-	 * @since 4.0.0
261
-	 */
262
-	public static function linkToRemote($service) {
263
-		$urlGenerator = \OCP\Server::get(IURLGenerator::class);
264
-		$remoteBase = $urlGenerator->linkTo('', 'remote.php') . '/' . $service;
265
-		return $urlGenerator->getAbsoluteURL(
266
-			$remoteBase . (($service[strlen($service) - 1] != '/') ? '/' : '')
267
-		);
268
-	}
269
-
270
-	/**
271
-	 * Returns the server host name without an eventual port number
272
-	 * @return string the server hostname
273
-	 * @since 5.0.0
274
-	 */
275
-	public static function getServerHostName() {
276
-		$host_name = \OCP\Server::get(IRequest::class)->getServerHost();
277
-		// strip away port number (if existing)
278
-		$colon_pos = strpos($host_name, ':');
279
-		if ($colon_pos != false) {
280
-			$host_name = substr($host_name, 0, $colon_pos);
281
-		}
282
-		return $host_name;
283
-	}
284
-
285
-	/**
286
-	 * Returns the default email address
287
-	 * @param string $user_part the user part of the address
288
-	 * @return string the default email address
289
-	 *
290
-	 * Assembles a default email address (using the server hostname
291
-	 * and the given user part, and returns it
292
-	 * Example: when given lostpassword-noreply as $user_part param,
293
-	 *     and is currently accessed via http(s)://example.com/,
294
-	 *     it would return '[email protected]'
295
-	 *
296
-	 * If the configuration value 'mail_from_address' is set in
297
-	 * config.php, this value will override the $user_part that
298
-	 * is passed to this function
299
-	 * @since 5.0.0
300
-	 */
301
-	public static function getDefaultEmailAddress(string $user_part): string {
302
-		$config = \OCP\Server::get(IConfig::class);
303
-		$user_part = $config->getSystemValueString('mail_from_address', $user_part);
304
-		$host_name = self::getServerHostName();
305
-		$host_name = $config->getSystemValueString('mail_domain', $host_name);
306
-		$defaultEmailAddress = $user_part . '@' . $host_name;
307
-
308
-		$mailer = \OCP\Server::get(IMailer::class);
309
-		if ($mailer->validateMailAddress($defaultEmailAddress)) {
310
-			return $defaultEmailAddress;
311
-		}
312
-
313
-		// in case we cannot build a valid email address from the hostname let's fallback to 'localhost.localdomain'
314
-		return $user_part . '@localhost.localdomain';
315
-	}
316
-
317
-	/**
318
-	 * Converts string to int of float depending on if it fits an int
319
-	 * @param numeric-string|float|int $number numeric string
320
-	 * @return int|float int if it fits, float if it is too big
321
-	 * @since 26.0.0
322
-	 */
323
-	public static function numericToNumber(string|float|int $number): int|float {
324
-		/* This is a hack to cast to (int|float) */
325
-		return 0 + (string)$number;
326
-	}
327
-
328
-	/**
329
-	 * Make a human file size (2048 to 2 kB)
330
-	 * @param int|float $bytes file size in bytes
331
-	 * @return string a human readable file size
332
-	 * @since 4.0.0
333
-	 */
334
-	public static function humanFileSize(int|float $bytes): string {
335
-		if ($bytes < 0) {
336
-			return '?';
337
-		}
338
-		if ($bytes < 1024) {
339
-			return "$bytes B";
340
-		}
341
-		$bytes = round($bytes / 1024, 0);
342
-		if ($bytes < 1024) {
343
-			return "$bytes KB";
344
-		}
345
-		$bytes = round($bytes / 1024, 1);
346
-		if ($bytes < 1024) {
347
-			return "$bytes MB";
348
-		}
349
-		$bytes = round($bytes / 1024, 1);
350
-		if ($bytes < 1024) {
351
-			return "$bytes GB";
352
-		}
353
-		$bytes = round($bytes / 1024, 1);
354
-		if ($bytes < 1024) {
355
-			return "$bytes TB";
356
-		}
357
-
358
-		$bytes = round($bytes / 1024, 1);
359
-		return "$bytes PB";
360
-	}
361
-
362
-	/**
363
-	 * Make a computer file size (2 kB to 2048)
364
-	 * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418
365
-	 *
366
-	 * @param string $str file size in a fancy format
367
-	 * @return false|int|float a file size in bytes
368
-	 * @since 4.0.0
369
-	 */
370
-	public static function computerFileSize(string $str): false|int|float {
371
-		$str = strtolower($str);
372
-		if (is_numeric($str)) {
373
-			return Util::numericToNumber($str);
374
-		}
375
-
376
-		$bytes_array = [
377
-			'b' => 1,
378
-			'k' => 1024,
379
-			'kb' => 1024,
380
-			'mb' => 1024 * 1024,
381
-			'm' => 1024 * 1024,
382
-			'gb' => 1024 * 1024 * 1024,
383
-			'g' => 1024 * 1024 * 1024,
384
-			'tb' => 1024 * 1024 * 1024 * 1024,
385
-			't' => 1024 * 1024 * 1024 * 1024,
386
-			'pb' => 1024 * 1024 * 1024 * 1024 * 1024,
387
-			'p' => 1024 * 1024 * 1024 * 1024 * 1024,
388
-		];
389
-
390
-		$bytes = (float)$str;
391
-
392
-		if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && isset($bytes_array[$matches[1]])) {
393
-			$bytes *= $bytes_array[$matches[1]];
394
-		} else {
395
-			return false;
396
-		}
397
-
398
-		return Util::numericToNumber(round($bytes));
399
-	}
400
-
401
-	/**
402
-	 * connects a function to a hook
403
-	 *
404
-	 * @param string $signalClass class name of emitter
405
-	 * @param string $signalName name of signal
406
-	 * @param string|object $slotClass class name of slot
407
-	 * @param string $slotName name of slot
408
-	 * @return bool
409
-	 *
410
-	 * This function makes it very easy to connect to use hooks.
411
-	 *
412
-	 * TODO: write example
413
-	 * @since 4.0.0
414
-	 * @deprecated 21.0.0 use \OCP\EventDispatcher\IEventDispatcher::addListener
415
-	 */
416
-	public static function connectHook($signalClass, $signalName, $slotClass, $slotName) {
417
-		return \OC_Hook::connect($signalClass, $signalName, $slotClass, $slotName);
418
-	}
419
-
420
-	/**
421
-	 * Emits a signal. To get data from the slot use references!
422
-	 * @param string $signalclass class name of emitter
423
-	 * @param string $signalname name of signal
424
-	 * @param array $params default: array() array with additional data
425
-	 * @return bool true if slots exists or false if not
426
-	 *
427
-	 * TODO: write example
428
-	 * @since 4.0.0
429
-	 * @deprecated 21.0.0 use \OCP\EventDispatcher\IEventDispatcher::dispatchTypedEvent
430
-	 */
431
-	public static function emitHook($signalclass, $signalname, $params = []) {
432
-		return \OC_Hook::emit($signalclass, $signalname, $params);
433
-	}
434
-
435
-	/**
436
-	 * Cached encrypted CSRF token. Some static unit-tests of ownCloud compare
437
-	 * multiple Template elements which invoke `callRegister`. If the value
438
-	 * would not be cached these unit-tests would fail.
439
-	 * @var string
440
-	 */
441
-	private static $token = '';
442
-
443
-	/**
444
-	 * Register an get/post call. This is important to prevent CSRF attacks
445
-	 * @since 4.5.0
446
-	 * @deprecated 32.0.0 directly use CsrfTokenManager instead
447
-	 */
448
-	public static function callRegister() {
449
-		if (self::$token === '') {
450
-			self::$token = \OCP\Server::get(CsrfTokenManager::class)->getToken()->getEncryptedValue();
451
-		}
452
-		return self::$token;
453
-	}
454
-
455
-	/**
456
-	 * Used to sanitize HTML
457
-	 *
458
-	 * This function is used to sanitize HTML and should be applied on any
459
-	 * string or array of strings before displaying it on a web page.
460
-	 *
461
-	 * @param string|string[] $value
462
-	 * @return ($value is array ? string[] : string) an array of sanitized strings or a single sanitized string, depends on the input parameter.
463
-	 * @since 4.5.0
464
-	 */
465
-	public static function sanitizeHTML($value) {
466
-		return \OC_Util::sanitizeHTML($value);
467
-	}
468
-
469
-	/**
470
-	 * Public function to encode url parameters
471
-	 *
472
-	 * This function is used to encode path to file before output.
473
-	 * Encoding is done according to RFC 3986 with one exception:
474
-	 * Character '/' is preserved as is.
475
-	 *
476
-	 * @param string $component part of URI to encode
477
-	 * @return string
478
-	 * @since 6.0.0
479
-	 */
480
-	public static function encodePath($component) {
481
-		return \OC_Util::encodePath($component);
482
-	}
483
-
484
-	/**
485
-	 * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
486
-	 *
487
-	 * @param array $input The array to work on
488
-	 * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
489
-	 * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
490
-	 * @return array
491
-	 * @since 4.5.0
492
-	 */
493
-	public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
494
-		$case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
495
-		$ret = [];
496
-		foreach ($input as $k => $v) {
497
-			$ret[mb_convert_case($k, $case, $encoding)] = $v;
498
-		}
499
-		return $ret;
500
-	}
501
-
502
-	/**
503
-	 * performs a search in a nested array
504
-	 *
505
-	 * @param array $haystack the array to be searched
506
-	 * @param string $needle the search string
507
-	 * @param mixed $index optional, only search this key name
508
-	 * @return mixed the key of the matching field, otherwise false
509
-	 * @since 4.5.0
510
-	 * @deprecated 15.0.0
511
-	 */
512
-	public static function recursiveArraySearch($haystack, $needle, $index = null) {
513
-		$aIt = new \RecursiveArrayIterator($haystack);
514
-		$it = new \RecursiveIteratorIterator($aIt);
515
-
516
-		while ($it->valid()) {
517
-			if (((isset($index) && ($it->key() == $index)) || !isset($index)) && ($it->current() == $needle)) {
518
-				return $aIt->key();
519
-			}
520
-
521
-			$it->next();
522
-		}
523
-
524
-		return false;
525
-	}
526
-
527
-	/**
528
-	 * calculates the maximum upload size respecting system settings, free space and user quota
529
-	 *
530
-	 * @param string $dir the current folder where the user currently operates
531
-	 * @param int|float|null $free the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
532
-	 * @return int|float number of bytes representing
533
-	 * @since 5.0.0
534
-	 */
535
-	public static function maxUploadFilesize(string $dir, int|float|null $free = null): int|float {
536
-		if (is_null($free) || $free < 0) {
537
-			$free = self::freeSpace($dir);
538
-		}
539
-		return min($free, self::uploadLimit());
540
-	}
541
-
542
-	/**
543
-	 * Calculate free space left within user quota
544
-	 * @param string $dir the current folder where the user currently operates
545
-	 * @return int|float number of bytes representing
546
-	 * @since 7.0.0
547
-	 */
548
-	public static function freeSpace(string $dir): int|float {
549
-		$freeSpace = \OC\Files\Filesystem::free_space($dir);
550
-		if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
551
-			$freeSpace = max($freeSpace, 0);
552
-			return $freeSpace;
553
-		} else {
554
-			return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
555
-		}
556
-	}
557
-
558
-	/**
559
-	 * Calculate PHP upload limit
560
-	 *
561
-	 * @return int|float number of bytes representing
562
-	 * @since 7.0.0
563
-	 */
564
-	public static function uploadLimit(): int|float {
565
-		$ini = Server::get(IniGetWrapper::class);
566
-		$upload_max_filesize = self::computerFileSize($ini->get('upload_max_filesize')) ?: 0;
567
-		$post_max_size = self::computerFileSize($ini->get('post_max_size')) ?: 0;
568
-		if ($upload_max_filesize === 0 && $post_max_size === 0) {
569
-			return INF;
570
-		} elseif ($upload_max_filesize === 0 || $post_max_size === 0) {
571
-			return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
572
-		} else {
573
-			return min($upload_max_filesize, $post_max_size);
574
-		}
575
-	}
576
-
577
-	/**
578
-	 * Compare two strings to provide a natural sort
579
-	 * @param string $a first string to compare
580
-	 * @param string $b second string to compare
581
-	 * @return int -1 if $b comes before $a, 1 if $a comes before $b
582
-	 *             or 0 if the strings are identical
583
-	 * @since 7.0.0
584
-	 */
585
-	public static function naturalSortCompare($a, $b) {
586
-		return \OC\NaturalSort::getInstance()->compare($a, $b);
587
-	}
588
-
589
-	/**
590
-	 * Check if a password is required for each public link
591
-	 *
592
-	 * @param bool $checkGroupMembership Check group membership exclusion
593
-	 * @return boolean
594
-	 * @since 7.0.0
595
-	 */
596
-	public static function isPublicLinkPasswordRequired(bool $checkGroupMembership = true) {
597
-		return \OC_Util::isPublicLinkPasswordRequired($checkGroupMembership);
598
-	}
599
-
600
-	/**
601
-	 * check if share API enforces a default expire date
602
-	 * @return boolean
603
-	 * @since 8.0.0
604
-	 */
605
-	public static function isDefaultExpireDateEnforced() {
606
-		return \OC_Util::isDefaultExpireDateEnforced();
607
-	}
608
-
609
-	protected static $needUpgradeCache = null;
610
-
611
-	/**
612
-	 * Checks whether the current version needs upgrade.
613
-	 *
614
-	 * @return bool true if upgrade is needed, false otherwise
615
-	 * @since 7.0.0
616
-	 */
617
-	public static function needUpgrade() {
618
-		if (!isset(self::$needUpgradeCache)) {
619
-			self::$needUpgradeCache = \OC_Util::needUpgrade(\OCP\Server::get(\OC\SystemConfig::class));
620
-		}
621
-		return self::$needUpgradeCache;
622
-	}
623
-
624
-	/**
625
-	 * Sometimes a string has to be shortened to fit within a certain maximum
626
-	 * data length in bytes. substr() you may break multibyte characters,
627
-	 * because it operates on single byte level. mb_substr() operates on
628
-	 * characters, so does not ensure that the shortened string satisfies the
629
-	 * max length in bytes.
630
-	 *
631
-	 * For example, json_encode is messing with multibyte characters a lot,
632
-	 * replacing them with something along "\u1234".
633
-	 *
634
-	 * This function shortens the string with by $accuracy (-5) from
635
-	 * $dataLength characters, until it fits within $dataLength bytes.
636
-	 *
637
-	 * @since 23.0.0
638
-	 */
639
-	public static function shortenMultibyteString(string $subject, int $dataLength, int $accuracy = 5): string {
640
-		$temp = mb_substr($subject, 0, $dataLength);
641
-		// json encodes encapsulates the string in double quotes, they need to be substracted
642
-		while ((strlen(json_encode($temp)) - 2) > $dataLength) {
643
-			$temp = mb_substr($temp, 0, -$accuracy);
644
-		}
645
-		return $temp;
646
-	}
647
-
648
-	/**
649
-	 * Check if a function is enabled in the php configuration
650
-	 *
651
-	 * @since 25.0.0
652
-	 */
653
-	public static function isFunctionEnabled(string $functionName): bool {
654
-		if (!function_exists($functionName)) {
655
-			return false;
656
-		}
657
-		$ini = Server::get(IniGetWrapper::class);
658
-		$disabled = explode(',', $ini->get('disable_functions') ?: '');
659
-		$disabled = array_map('trim', $disabled);
660
-		if (in_array($functionName, $disabled)) {
661
-			return false;
662
-		}
663
-		return true;
664
-	}
28
+    private static ?IManager $shareManager = null;
29
+
30
+    private static array $scriptsInit = [];
31
+    private static array $scripts = [];
32
+    private static array $scriptDeps = [];
33
+
34
+    /**
35
+     * get the current installed version of Nextcloud
36
+     * @return array
37
+     * @since 4.0.0
38
+     * @deprecated 31.0.0 Use \OCP\ServerVersion::getVersion
39
+     */
40
+    public static function getVersion() {
41
+        return Server::get(ServerVersion::class)->getVersion();
42
+    }
43
+
44
+    /**
45
+     * @since 17.0.0
46
+     */
47
+    public static function hasExtendedSupport(): bool {
48
+        try {
49
+            /** @var \OCP\Support\Subscription\IRegistry */
50
+            $subscriptionRegistry = Server::get(\OCP\Support\Subscription\IRegistry::class);
51
+            return $subscriptionRegistry->delegateHasExtendedSupport();
52
+        } catch (ContainerExceptionInterface $e) {
53
+        }
54
+        return \OCP\Server::get(IConfig::class)->getSystemValueBool('extendedSupport', false);
55
+    }
56
+
57
+    /**
58
+     * Set current update channel
59
+     * @param string $channel
60
+     * @since 8.1.0
61
+     */
62
+    public static function setChannel($channel) {
63
+        \OCP\Server::get(IConfig::class)->setSystemValue('updater.release.channel', $channel);
64
+    }
65
+
66
+    /**
67
+     * Get current update channel
68
+     * @return string
69
+     * @since 8.1.0
70
+     * @deprecated 31.0.0 Use \OCP\ServerVersion::getChannel
71
+     */
72
+    public static function getChannel() {
73
+        return \OCP\Server::get(ServerVersion::class)->getChannel();
74
+    }
75
+
76
+    /**
77
+     * check if sharing is disabled for the current user
78
+     *
79
+     * @return boolean
80
+     * @since 7.0.0
81
+     * @deprecated 9.1.0 Use Server::get(\OCP\Share\IManager::class)->sharingDisabledForUser
82
+     */
83
+    public static function isSharingDisabledForUser() {
84
+        if (self::$shareManager === null) {
85
+            self::$shareManager = Server::get(IManager::class);
86
+        }
87
+
88
+        $user = Server::get(\OCP\IUserSession::class)->getUser();
89
+
90
+        return self::$shareManager->sharingDisabledForUser($user?->getUID());
91
+    }
92
+
93
+    /**
94
+     * get l10n object
95
+     * @since 6.0.0 - parameter $language was added in 8.0.0
96
+     */
97
+    public static function getL10N(string $application, ?string $language = null): IL10N {
98
+        return Server::get(\OCP\L10N\IFactory::class)->get($application, $language);
99
+    }
100
+
101
+    /**
102
+     * Add a css file
103
+     *
104
+     * @param string $application application id
105
+     * @param ?string $file filename
106
+     * @param bool $prepend prepend the style to the beginning of the list
107
+     * @since 4.0.0
108
+     */
109
+    public static function addStyle(string $application, ?string $file = null, bool $prepend = false): void {
110
+        \OC_Util::addStyle($application, $file, $prepend);
111
+    }
112
+
113
+    /**
114
+     * Add a standalone init js file that is loaded for initialization
115
+     *
116
+     * Be careful loading scripts using this method as they are loaded early
117
+     * and block the initial page rendering. They should not have dependencies
118
+     * on any other scripts than core-common and core-main.
119
+     *
120
+     * @since 28.0.0
121
+     */
122
+    public static function addInitScript(string $application, string $file): void {
123
+        if (!empty($application)) {
124
+            $path = "$application/js/$file";
125
+        } else {
126
+            $path = "js/$file";
127
+        }
128
+
129
+        // We need to handle the translation BEFORE the init script
130
+        // is loaded, as the init script might use translations
131
+        if ($application !== 'core' && !str_contains($file, 'l10n')) {
132
+            self::addTranslations($application, null, true);
133
+        }
134
+
135
+        self::$scriptsInit[] = $path;
136
+    }
137
+
138
+    /**
139
+     * add a javascript file
140
+     *
141
+     * @param string $application
142
+     * @param string|null $file
143
+     * @param string $afterAppId
144
+     * @param bool $prepend
145
+     * @since 4.0.0
146
+     */
147
+    public static function addScript(string $application, ?string $file = null, string $afterAppId = 'core', bool $prepend = false): void {
148
+        if (!empty($application)) {
149
+            $path = "$application/js/$file";
150
+        } else {
151
+            $path = "js/$file";
152
+        }
153
+
154
+        // Inject js translations if we load a script for
155
+        // a specific app that is not core, as those js files
156
+        // need separate handling
157
+        if ($application !== 'core'
158
+            && $file !== null
159
+            && !str_contains($file, 'l10n')) {
160
+            self::addTranslations($application);
161
+        }
162
+
163
+        // store app in dependency list
164
+        if (!array_key_exists($application, self::$scriptDeps)) {
165
+            self::$scriptDeps[$application] = new AppScriptDependency($application, [$afterAppId]);
166
+        } else {
167
+            self::$scriptDeps[$application]->addDep($afterAppId);
168
+        }
169
+
170
+        if ($prepend) {
171
+            array_unshift(self::$scripts[$application], $path);
172
+        } else {
173
+            self::$scripts[$application][] = $path;
174
+        }
175
+    }
176
+
177
+    /**
178
+     * Return the list of scripts injected to the page
179
+     *
180
+     * @return array
181
+     * @since 24.0.0
182
+     */
183
+    public static function getScripts(): array {
184
+        // Sort scriptDeps into sortedScriptDeps
185
+        $scriptSort = \OCP\Server::get(AppScriptSort::class);
186
+        $sortedScripts = $scriptSort->sort(self::$scripts, self::$scriptDeps);
187
+
188
+        // Flatten array and remove duplicates
189
+        $sortedScripts = array_merge([self::$scriptsInit], $sortedScripts);
190
+        $sortedScripts = array_merge(...array_values($sortedScripts));
191
+
192
+        // Override core-common and core-main order
193
+        if (in_array('core/js/main', $sortedScripts)) {
194
+            array_unshift($sortedScripts, 'core/js/main');
195
+        }
196
+        if (in_array('core/js/common', $sortedScripts)) {
197
+            array_unshift($sortedScripts, 'core/js/common');
198
+        }
199
+
200
+        return array_unique($sortedScripts);
201
+    }
202
+
203
+    /**
204
+     * Add a translation JS file
205
+     * @param string $application application id
206
+     * @param string $languageCode language code, defaults to the current locale
207
+     * @param bool $init whether the translations should be loaded early or not
208
+     * @since 8.0.0
209
+     */
210
+    public static function addTranslations($application, $languageCode = null, $init = false) {
211
+        if (is_null($languageCode)) {
212
+            $languageCode = \OCP\Server::get(IFactory::class)->findLanguage($application);
213
+        }
214
+        if (!empty($application)) {
215
+            $path = "$application/l10n/$languageCode";
216
+        } else {
217
+            $path = "l10n/$languageCode";
218
+        }
219
+
220
+        if ($init) {
221
+            self::$scriptsInit[] = $path;
222
+        } else {
223
+            self::$scripts[$application][] = $path;
224
+        }
225
+    }
226
+
227
+    /**
228
+     * Add a custom element to the header
229
+     * If $text is null then the element will be written as empty element.
230
+     * So use "" to get a closing tag.
231
+     * @param string $tag tag name of the element
232
+     * @param array $attributes array of attributes for the element
233
+     * @param string $text the text content for the element
234
+     * @since 4.0.0
235
+     */
236
+    public static function addHeader($tag, $attributes, $text = null) {
237
+        \OC_Util::addHeader($tag, $attributes, $text);
238
+    }
239
+
240
+    /**
241
+     * Creates an absolute url to the given app and file.
242
+     * @param string $app app
243
+     * @param string $file file
244
+     * @param array $args array with param=>value, will be appended to the returned url
245
+     *                    The value of $args will be urlencoded
246
+     * @return string the url
247
+     * @since 4.0.0 - parameter $args was added in 4.5.0
248
+     */
249
+    public static function linkToAbsolute($app, $file, $args = []) {
250
+        $urlGenerator = \OCP\Server::get(IURLGenerator::class);
251
+        return $urlGenerator->getAbsoluteURL(
252
+            $urlGenerator->linkTo($app, $file, $args)
253
+        );
254
+    }
255
+
256
+    /**
257
+     * Creates an absolute url for remote use.
258
+     * @param string $service id
259
+     * @return string the url
260
+     * @since 4.0.0
261
+     */
262
+    public static function linkToRemote($service) {
263
+        $urlGenerator = \OCP\Server::get(IURLGenerator::class);
264
+        $remoteBase = $urlGenerator->linkTo('', 'remote.php') . '/' . $service;
265
+        return $urlGenerator->getAbsoluteURL(
266
+            $remoteBase . (($service[strlen($service) - 1] != '/') ? '/' : '')
267
+        );
268
+    }
269
+
270
+    /**
271
+     * Returns the server host name without an eventual port number
272
+     * @return string the server hostname
273
+     * @since 5.0.0
274
+     */
275
+    public static function getServerHostName() {
276
+        $host_name = \OCP\Server::get(IRequest::class)->getServerHost();
277
+        // strip away port number (if existing)
278
+        $colon_pos = strpos($host_name, ':');
279
+        if ($colon_pos != false) {
280
+            $host_name = substr($host_name, 0, $colon_pos);
281
+        }
282
+        return $host_name;
283
+    }
284
+
285
+    /**
286
+     * Returns the default email address
287
+     * @param string $user_part the user part of the address
288
+     * @return string the default email address
289
+     *
290
+     * Assembles a default email address (using the server hostname
291
+     * and the given user part, and returns it
292
+     * Example: when given lostpassword-noreply as $user_part param,
293
+     *     and is currently accessed via http(s)://example.com/,
294
+     *     it would return '[email protected]'
295
+     *
296
+     * If the configuration value 'mail_from_address' is set in
297
+     * config.php, this value will override the $user_part that
298
+     * is passed to this function
299
+     * @since 5.0.0
300
+     */
301
+    public static function getDefaultEmailAddress(string $user_part): string {
302
+        $config = \OCP\Server::get(IConfig::class);
303
+        $user_part = $config->getSystemValueString('mail_from_address', $user_part);
304
+        $host_name = self::getServerHostName();
305
+        $host_name = $config->getSystemValueString('mail_domain', $host_name);
306
+        $defaultEmailAddress = $user_part . '@' . $host_name;
307
+
308
+        $mailer = \OCP\Server::get(IMailer::class);
309
+        if ($mailer->validateMailAddress($defaultEmailAddress)) {
310
+            return $defaultEmailAddress;
311
+        }
312
+
313
+        // in case we cannot build a valid email address from the hostname let's fallback to 'localhost.localdomain'
314
+        return $user_part . '@localhost.localdomain';
315
+    }
316
+
317
+    /**
318
+     * Converts string to int of float depending on if it fits an int
319
+     * @param numeric-string|float|int $number numeric string
320
+     * @return int|float int if it fits, float if it is too big
321
+     * @since 26.0.0
322
+     */
323
+    public static function numericToNumber(string|float|int $number): int|float {
324
+        /* This is a hack to cast to (int|float) */
325
+        return 0 + (string)$number;
326
+    }
327
+
328
+    /**
329
+     * Make a human file size (2048 to 2 kB)
330
+     * @param int|float $bytes file size in bytes
331
+     * @return string a human readable file size
332
+     * @since 4.0.0
333
+     */
334
+    public static function humanFileSize(int|float $bytes): string {
335
+        if ($bytes < 0) {
336
+            return '?';
337
+        }
338
+        if ($bytes < 1024) {
339
+            return "$bytes B";
340
+        }
341
+        $bytes = round($bytes / 1024, 0);
342
+        if ($bytes < 1024) {
343
+            return "$bytes KB";
344
+        }
345
+        $bytes = round($bytes / 1024, 1);
346
+        if ($bytes < 1024) {
347
+            return "$bytes MB";
348
+        }
349
+        $bytes = round($bytes / 1024, 1);
350
+        if ($bytes < 1024) {
351
+            return "$bytes GB";
352
+        }
353
+        $bytes = round($bytes / 1024, 1);
354
+        if ($bytes < 1024) {
355
+            return "$bytes TB";
356
+        }
357
+
358
+        $bytes = round($bytes / 1024, 1);
359
+        return "$bytes PB";
360
+    }
361
+
362
+    /**
363
+     * Make a computer file size (2 kB to 2048)
364
+     * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418
365
+     *
366
+     * @param string $str file size in a fancy format
367
+     * @return false|int|float a file size in bytes
368
+     * @since 4.0.0
369
+     */
370
+    public static function computerFileSize(string $str): false|int|float {
371
+        $str = strtolower($str);
372
+        if (is_numeric($str)) {
373
+            return Util::numericToNumber($str);
374
+        }
375
+
376
+        $bytes_array = [
377
+            'b' => 1,
378
+            'k' => 1024,
379
+            'kb' => 1024,
380
+            'mb' => 1024 * 1024,
381
+            'm' => 1024 * 1024,
382
+            'gb' => 1024 * 1024 * 1024,
383
+            'g' => 1024 * 1024 * 1024,
384
+            'tb' => 1024 * 1024 * 1024 * 1024,
385
+            't' => 1024 * 1024 * 1024 * 1024,
386
+            'pb' => 1024 * 1024 * 1024 * 1024 * 1024,
387
+            'p' => 1024 * 1024 * 1024 * 1024 * 1024,
388
+        ];
389
+
390
+        $bytes = (float)$str;
391
+
392
+        if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && isset($bytes_array[$matches[1]])) {
393
+            $bytes *= $bytes_array[$matches[1]];
394
+        } else {
395
+            return false;
396
+        }
397
+
398
+        return Util::numericToNumber(round($bytes));
399
+    }
400
+
401
+    /**
402
+     * connects a function to a hook
403
+     *
404
+     * @param string $signalClass class name of emitter
405
+     * @param string $signalName name of signal
406
+     * @param string|object $slotClass class name of slot
407
+     * @param string $slotName name of slot
408
+     * @return bool
409
+     *
410
+     * This function makes it very easy to connect to use hooks.
411
+     *
412
+     * TODO: write example
413
+     * @since 4.0.0
414
+     * @deprecated 21.0.0 use \OCP\EventDispatcher\IEventDispatcher::addListener
415
+     */
416
+    public static function connectHook($signalClass, $signalName, $slotClass, $slotName) {
417
+        return \OC_Hook::connect($signalClass, $signalName, $slotClass, $slotName);
418
+    }
419
+
420
+    /**
421
+     * Emits a signal. To get data from the slot use references!
422
+     * @param string $signalclass class name of emitter
423
+     * @param string $signalname name of signal
424
+     * @param array $params default: array() array with additional data
425
+     * @return bool true if slots exists or false if not
426
+     *
427
+     * TODO: write example
428
+     * @since 4.0.0
429
+     * @deprecated 21.0.0 use \OCP\EventDispatcher\IEventDispatcher::dispatchTypedEvent
430
+     */
431
+    public static function emitHook($signalclass, $signalname, $params = []) {
432
+        return \OC_Hook::emit($signalclass, $signalname, $params);
433
+    }
434
+
435
+    /**
436
+     * Cached encrypted CSRF token. Some static unit-tests of ownCloud compare
437
+     * multiple Template elements which invoke `callRegister`. If the value
438
+     * would not be cached these unit-tests would fail.
439
+     * @var string
440
+     */
441
+    private static $token = '';
442
+
443
+    /**
444
+     * Register an get/post call. This is important to prevent CSRF attacks
445
+     * @since 4.5.0
446
+     * @deprecated 32.0.0 directly use CsrfTokenManager instead
447
+     */
448
+    public static function callRegister() {
449
+        if (self::$token === '') {
450
+            self::$token = \OCP\Server::get(CsrfTokenManager::class)->getToken()->getEncryptedValue();
451
+        }
452
+        return self::$token;
453
+    }
454
+
455
+    /**
456
+     * Used to sanitize HTML
457
+     *
458
+     * This function is used to sanitize HTML and should be applied on any
459
+     * string or array of strings before displaying it on a web page.
460
+     *
461
+     * @param string|string[] $value
462
+     * @return ($value is array ? string[] : string) an array of sanitized strings or a single sanitized string, depends on the input parameter.
463
+     * @since 4.5.0
464
+     */
465
+    public static function sanitizeHTML($value) {
466
+        return \OC_Util::sanitizeHTML($value);
467
+    }
468
+
469
+    /**
470
+     * Public function to encode url parameters
471
+     *
472
+     * This function is used to encode path to file before output.
473
+     * Encoding is done according to RFC 3986 with one exception:
474
+     * Character '/' is preserved as is.
475
+     *
476
+     * @param string $component part of URI to encode
477
+     * @return string
478
+     * @since 6.0.0
479
+     */
480
+    public static function encodePath($component) {
481
+        return \OC_Util::encodePath($component);
482
+    }
483
+
484
+    /**
485
+     * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
486
+     *
487
+     * @param array $input The array to work on
488
+     * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
489
+     * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
490
+     * @return array
491
+     * @since 4.5.0
492
+     */
493
+    public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
494
+        $case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
495
+        $ret = [];
496
+        foreach ($input as $k => $v) {
497
+            $ret[mb_convert_case($k, $case, $encoding)] = $v;
498
+        }
499
+        return $ret;
500
+    }
501
+
502
+    /**
503
+     * performs a search in a nested array
504
+     *
505
+     * @param array $haystack the array to be searched
506
+     * @param string $needle the search string
507
+     * @param mixed $index optional, only search this key name
508
+     * @return mixed the key of the matching field, otherwise false
509
+     * @since 4.5.0
510
+     * @deprecated 15.0.0
511
+     */
512
+    public static function recursiveArraySearch($haystack, $needle, $index = null) {
513
+        $aIt = new \RecursiveArrayIterator($haystack);
514
+        $it = new \RecursiveIteratorIterator($aIt);
515
+
516
+        while ($it->valid()) {
517
+            if (((isset($index) && ($it->key() == $index)) || !isset($index)) && ($it->current() == $needle)) {
518
+                return $aIt->key();
519
+            }
520
+
521
+            $it->next();
522
+        }
523
+
524
+        return false;
525
+    }
526
+
527
+    /**
528
+     * calculates the maximum upload size respecting system settings, free space and user quota
529
+     *
530
+     * @param string $dir the current folder where the user currently operates
531
+     * @param int|float|null $free the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
532
+     * @return int|float number of bytes representing
533
+     * @since 5.0.0
534
+     */
535
+    public static function maxUploadFilesize(string $dir, int|float|null $free = null): int|float {
536
+        if (is_null($free) || $free < 0) {
537
+            $free = self::freeSpace($dir);
538
+        }
539
+        return min($free, self::uploadLimit());
540
+    }
541
+
542
+    /**
543
+     * Calculate free space left within user quota
544
+     * @param string $dir the current folder where the user currently operates
545
+     * @return int|float number of bytes representing
546
+     * @since 7.0.0
547
+     */
548
+    public static function freeSpace(string $dir): int|float {
549
+        $freeSpace = \OC\Files\Filesystem::free_space($dir);
550
+        if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
551
+            $freeSpace = max($freeSpace, 0);
552
+            return $freeSpace;
553
+        } else {
554
+            return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
555
+        }
556
+    }
557
+
558
+    /**
559
+     * Calculate PHP upload limit
560
+     *
561
+     * @return int|float number of bytes representing
562
+     * @since 7.0.0
563
+     */
564
+    public static function uploadLimit(): int|float {
565
+        $ini = Server::get(IniGetWrapper::class);
566
+        $upload_max_filesize = self::computerFileSize($ini->get('upload_max_filesize')) ?: 0;
567
+        $post_max_size = self::computerFileSize($ini->get('post_max_size')) ?: 0;
568
+        if ($upload_max_filesize === 0 && $post_max_size === 0) {
569
+            return INF;
570
+        } elseif ($upload_max_filesize === 0 || $post_max_size === 0) {
571
+            return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
572
+        } else {
573
+            return min($upload_max_filesize, $post_max_size);
574
+        }
575
+    }
576
+
577
+    /**
578
+     * Compare two strings to provide a natural sort
579
+     * @param string $a first string to compare
580
+     * @param string $b second string to compare
581
+     * @return int -1 if $b comes before $a, 1 if $a comes before $b
582
+     *             or 0 if the strings are identical
583
+     * @since 7.0.0
584
+     */
585
+    public static function naturalSortCompare($a, $b) {
586
+        return \OC\NaturalSort::getInstance()->compare($a, $b);
587
+    }
588
+
589
+    /**
590
+     * Check if a password is required for each public link
591
+     *
592
+     * @param bool $checkGroupMembership Check group membership exclusion
593
+     * @return boolean
594
+     * @since 7.0.0
595
+     */
596
+    public static function isPublicLinkPasswordRequired(bool $checkGroupMembership = true) {
597
+        return \OC_Util::isPublicLinkPasswordRequired($checkGroupMembership);
598
+    }
599
+
600
+    /**
601
+     * check if share API enforces a default expire date
602
+     * @return boolean
603
+     * @since 8.0.0
604
+     */
605
+    public static function isDefaultExpireDateEnforced() {
606
+        return \OC_Util::isDefaultExpireDateEnforced();
607
+    }
608
+
609
+    protected static $needUpgradeCache = null;
610
+
611
+    /**
612
+     * Checks whether the current version needs upgrade.
613
+     *
614
+     * @return bool true if upgrade is needed, false otherwise
615
+     * @since 7.0.0
616
+     */
617
+    public static function needUpgrade() {
618
+        if (!isset(self::$needUpgradeCache)) {
619
+            self::$needUpgradeCache = \OC_Util::needUpgrade(\OCP\Server::get(\OC\SystemConfig::class));
620
+        }
621
+        return self::$needUpgradeCache;
622
+    }
623
+
624
+    /**
625
+     * Sometimes a string has to be shortened to fit within a certain maximum
626
+     * data length in bytes. substr() you may break multibyte characters,
627
+     * because it operates on single byte level. mb_substr() operates on
628
+     * characters, so does not ensure that the shortened string satisfies the
629
+     * max length in bytes.
630
+     *
631
+     * For example, json_encode is messing with multibyte characters a lot,
632
+     * replacing them with something along "\u1234".
633
+     *
634
+     * This function shortens the string with by $accuracy (-5) from
635
+     * $dataLength characters, until it fits within $dataLength bytes.
636
+     *
637
+     * @since 23.0.0
638
+     */
639
+    public static function shortenMultibyteString(string $subject, int $dataLength, int $accuracy = 5): string {
640
+        $temp = mb_substr($subject, 0, $dataLength);
641
+        // json encodes encapsulates the string in double quotes, they need to be substracted
642
+        while ((strlen(json_encode($temp)) - 2) > $dataLength) {
643
+            $temp = mb_substr($temp, 0, -$accuracy);
644
+        }
645
+        return $temp;
646
+    }
647
+
648
+    /**
649
+     * Check if a function is enabled in the php configuration
650
+     *
651
+     * @since 25.0.0
652
+     */
653
+    public static function isFunctionEnabled(string $functionName): bool {
654
+        if (!function_exists($functionName)) {
655
+            return false;
656
+        }
657
+        $ini = Server::get(IniGetWrapper::class);
658
+        $disabled = explode(',', $ini->get('disable_functions') ?: '');
659
+        $disabled = array_map('trim', $disabled);
660
+        if (in_array($functionName, $disabled)) {
661
+            return false;
662
+        }
663
+        return true;
664
+    }
665 665
 }
Please login to merge, or discard this patch.
lib/private/PreviewManager.php 1 patch
Indentation   +431 added lines, -431 removed lines patch added patch discarded remove patch
@@ -28,435 +28,435 @@
 block discarded – undo
28 28
 use function array_key_exists;
29 29
 
30 30
 class PreviewManager implements IPreview {
31
-	protected IConfig $config;
32
-	protected IRootFolder $rootFolder;
33
-	protected IAppData $appData;
34
-	protected IEventDispatcher $eventDispatcher;
35
-	private ?Generator $generator = null;
36
-	private GeneratorHelper $helper;
37
-	protected bool $providerListDirty = false;
38
-	protected bool $registeredCoreProviders = false;
39
-	protected array $providers = [];
40
-
41
-	/** @var array mime type => support status */
42
-	protected array $mimeTypeSupportMap = [];
43
-	protected ?array $defaultProviders = null;
44
-	protected ?string $userId;
45
-	private Coordinator $bootstrapCoordinator;
46
-
47
-	/**
48
-	 * Hash map (without value) of loaded bootstrap providers
49
-	 * @psalm-var array<string, null>
50
-	 */
51
-	private array $loadedBootstrapProviders = [];
52
-	private ContainerInterface $container;
53
-	private IBinaryFinder $binaryFinder;
54
-	private IMagickSupport $imagickSupport;
55
-	private bool $enablePreviews;
56
-
57
-	public function __construct(
58
-		IConfig $config,
59
-		IRootFolder $rootFolder,
60
-		IAppData $appData,
61
-		IEventDispatcher $eventDispatcher,
62
-		GeneratorHelper $helper,
63
-		?string $userId,
64
-		Coordinator $bootstrapCoordinator,
65
-		ContainerInterface $container,
66
-		IBinaryFinder $binaryFinder,
67
-		IMagickSupport $imagickSupport,
68
-	) {
69
-		$this->config = $config;
70
-		$this->rootFolder = $rootFolder;
71
-		$this->appData = $appData;
72
-		$this->eventDispatcher = $eventDispatcher;
73
-		$this->helper = $helper;
74
-		$this->userId = $userId;
75
-		$this->bootstrapCoordinator = $bootstrapCoordinator;
76
-		$this->container = $container;
77
-		$this->binaryFinder = $binaryFinder;
78
-		$this->imagickSupport = $imagickSupport;
79
-		$this->enablePreviews = $config->getSystemValueBool('enable_previews', true);
80
-	}
81
-
82
-	/**
83
-	 * In order to improve lazy loading a closure can be registered which will be
84
-	 * called in case preview providers are actually requested
85
-	 *
86
-	 * $callable has to return an instance of \OCP\Preview\IProvider or \OCP\Preview\IProviderV2
87
-	 *
88
-	 * @param string $mimeTypeRegex Regex with the mime types that are supported by this provider
89
-	 * @param \Closure $callable
90
-	 * @return void
91
-	 */
92
-	public function registerProvider($mimeTypeRegex, \Closure $callable): void {
93
-		if (!$this->enablePreviews) {
94
-			return;
95
-		}
96
-
97
-		if (!isset($this->providers[$mimeTypeRegex])) {
98
-			$this->providers[$mimeTypeRegex] = [];
99
-		}
100
-		$this->providers[$mimeTypeRegex][] = $callable;
101
-		$this->providerListDirty = true;
102
-	}
103
-
104
-	/**
105
-	 * Get all providers
106
-	 */
107
-	public function getProviders(): array {
108
-		if (!$this->enablePreviews) {
109
-			return [];
110
-		}
111
-
112
-		$this->registerCoreProviders();
113
-		$this->registerBootstrapProviders();
114
-		if ($this->providerListDirty) {
115
-			$keys = array_map('strlen', array_keys($this->providers));
116
-			array_multisort($keys, SORT_DESC, $this->providers);
117
-			$this->providerListDirty = false;
118
-		}
119
-
120
-		return $this->providers;
121
-	}
122
-
123
-	/**
124
-	 * Does the manager have any providers
125
-	 */
126
-	public function hasProviders(): bool {
127
-		$this->registerCoreProviders();
128
-		return !empty($this->providers);
129
-	}
130
-
131
-	private function getGenerator(): Generator {
132
-		if ($this->generator === null) {
133
-			$this->generator = new Generator(
134
-				$this->config,
135
-				$this,
136
-				$this->appData,
137
-				new GeneratorHelper(
138
-					$this->rootFolder,
139
-					$this->config
140
-				),
141
-				$this->eventDispatcher,
142
-				$this->container->get(LoggerInterface::class),
143
-			);
144
-		}
145
-		return $this->generator;
146
-	}
147
-
148
-	public function getPreview(
149
-		File $file,
150
-		$width = -1,
151
-		$height = -1,
152
-		$crop = false,
153
-		$mode = IPreview::MODE_FILL,
154
-		$mimeType = null,
155
-		bool $cacheResult = true,
156
-	): ISimpleFile {
157
-		$this->throwIfPreviewsDisabled($file, $mimeType);
158
-		$previewConcurrency = $this->getGenerator()->getNumConcurrentPreviews('preview_concurrency_all');
159
-		$sem = Generator::guardWithSemaphore(Generator::SEMAPHORE_ID_ALL, $previewConcurrency);
160
-		try {
161
-			$preview = $this->getGenerator()->getPreview($file, $width, $height, $crop, $mode, $mimeType, $cacheResult);
162
-		} finally {
163
-			Generator::unguardWithSemaphore($sem);
164
-		}
165
-
166
-		return $preview;
167
-	}
168
-
169
-	/**
170
-	 * Generates previews of a file
171
-	 *
172
-	 * @param File $file
173
-	 * @param array $specifications
174
-	 * @param string $mimeType
175
-	 * @return ISimpleFile the last preview that was generated
176
-	 * @throws NotFoundException
177
-	 * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
178
-	 * @since 19.0.0
179
-	 */
180
-	public function generatePreviews(File $file, array $specifications, $mimeType = null) {
181
-		$this->throwIfPreviewsDisabled($file, $mimeType);
182
-		return $this->getGenerator()->generatePreviews($file, $specifications, $mimeType);
183
-	}
184
-
185
-	/**
186
-	 * returns true if the passed mime type is supported
187
-	 *
188
-	 * @param string $mimeType
189
-	 * @return boolean
190
-	 */
191
-	public function isMimeSupported($mimeType = '*') {
192
-		if (!$this->enablePreviews) {
193
-			return false;
194
-		}
195
-
196
-		if (isset($this->mimeTypeSupportMap[$mimeType])) {
197
-			return $this->mimeTypeSupportMap[$mimeType];
198
-		}
199
-
200
-		$this->registerCoreProviders();
201
-		$this->registerBootstrapProviders();
202
-		$providerMimeTypes = array_keys($this->providers);
203
-		foreach ($providerMimeTypes as $supportedMimeType) {
204
-			if (preg_match($supportedMimeType, $mimeType)) {
205
-				$this->mimeTypeSupportMap[$mimeType] = true;
206
-				return true;
207
-			}
208
-		}
209
-		$this->mimeTypeSupportMap[$mimeType] = false;
210
-		return false;
211
-	}
212
-
213
-	/**
214
-	 * Check if a preview can be generated for a file
215
-	 */
216
-	public function isAvailable(\OCP\Files\FileInfo $file, ?string $mimeType = null): bool {
217
-		if (!$this->enablePreviews) {
218
-			return false;
219
-		}
220
-
221
-		$fileMimeType = $mimeType ?? $file->getMimeType();
222
-
223
-		$this->registerCoreProviders();
224
-		if (!$this->isMimeSupported($fileMimeType)) {
225
-			return false;
226
-		}
227
-
228
-		$mount = $file->getMountPoint();
229
-		if ($mount && !$mount->getOption('previews', true)) {
230
-			return false;
231
-		}
232
-
233
-		foreach ($this->providers as $supportedMimeType => $providers) {
234
-			if (preg_match($supportedMimeType, $fileMimeType)) {
235
-				foreach ($providers as $providerClosure) {
236
-					$provider = $this->helper->getProvider($providerClosure);
237
-					if (!($provider instanceof IProviderV2)) {
238
-						continue;
239
-					}
240
-
241
-					if ($provider->isAvailable($file)) {
242
-						return true;
243
-					}
244
-				}
245
-			}
246
-		}
247
-		return false;
248
-	}
249
-
250
-	/**
251
-	 * List of enabled default providers
252
-	 *
253
-	 * The following providers are enabled by default:
254
-	 *  - OC\Preview\PNG
255
-	 *  - OC\Preview\JPEG
256
-	 *  - OC\Preview\GIF
257
-	 *  - OC\Preview\BMP
258
-	 *  - OC\Preview\XBitmap
259
-	 *  - OC\Preview\MarkDown
260
-	 *  - OC\Preview\MP3
261
-	 *  - OC\Preview\TXT
262
-	 *
263
-	 * The following providers are disabled by default due to performance or privacy concerns:
264
-	 *  - OC\Preview\Font
265
-	 *  - OC\Preview\HEIC
266
-	 *  - OC\Preview\Illustrator
267
-	 *  - OC\Preview\Movie
268
-	 *  - OC\Preview\MSOfficeDoc
269
-	 *  - OC\Preview\MSOffice2003
270
-	 *  - OC\Preview\MSOffice2007
271
-	 *  - OC\Preview\OpenDocument
272
-	 *  - OC\Preview\PDF
273
-	 *  - OC\Preview\Photoshop
274
-	 *  - OC\Preview\Postscript
275
-	 *  - OC\Preview\StarOffice
276
-	 *  - OC\Preview\SVG
277
-	 *  - OC\Preview\TIFF
278
-	 *
279
-	 * @return array
280
-	 */
281
-	protected function getEnabledDefaultProvider() {
282
-		if ($this->defaultProviders !== null) {
283
-			return $this->defaultProviders;
284
-		}
285
-
286
-		$imageProviders = [
287
-			Preview\PNG::class,
288
-			Preview\JPEG::class,
289
-			Preview\GIF::class,
290
-			Preview\BMP::class,
291
-			Preview\XBitmap::class,
292
-			Preview\Krita::class,
293
-			Preview\WebP::class,
294
-		];
295
-
296
-		$this->defaultProviders = $this->config->getSystemValue('enabledPreviewProviders', array_merge([
297
-			Preview\MarkDown::class,
298
-			Preview\MP3::class,
299
-			Preview\TXT::class,
300
-			Preview\OpenDocument::class,
301
-		], $imageProviders));
302
-
303
-		if (in_array(Preview\Image::class, $this->defaultProviders)) {
304
-			$this->defaultProviders = array_merge($this->defaultProviders, $imageProviders);
305
-		}
306
-		$this->defaultProviders = array_unique($this->defaultProviders);
307
-		return $this->defaultProviders;
308
-	}
309
-
310
-	/**
311
-	 * Register the default providers (if enabled)
312
-	 *
313
-	 * @param string $class
314
-	 * @param string $mimeType
315
-	 */
316
-	protected function registerCoreProvider($class, $mimeType, $options = []) {
317
-		if (in_array(trim($class, '\\'), $this->getEnabledDefaultProvider())) {
318
-			$this->registerProvider($mimeType, function () use ($class, $options) {
319
-				return new $class($options);
320
-			});
321
-		}
322
-	}
323
-
324
-	/**
325
-	 * Register the default providers (if enabled)
326
-	 */
327
-	protected function registerCoreProviders() {
328
-		if ($this->registeredCoreProviders) {
329
-			return;
330
-		}
331
-		$this->registeredCoreProviders = true;
332
-
333
-		$this->registerCoreProvider(Preview\TXT::class, '/text\/plain/');
334
-		$this->registerCoreProvider(Preview\MarkDown::class, '/text\/(x-)?markdown/');
335
-		$this->registerCoreProvider(Preview\PNG::class, '/image\/png/');
336
-		$this->registerCoreProvider(Preview\JPEG::class, '/image\/jpeg/');
337
-		$this->registerCoreProvider(Preview\GIF::class, '/image\/gif/');
338
-		$this->registerCoreProvider(Preview\BMP::class, '/image\/bmp/');
339
-		$this->registerCoreProvider(Preview\XBitmap::class, '/image\/x-xbitmap/');
340
-		$this->registerCoreProvider(Preview\WebP::class, '/image\/webp/');
341
-		$this->registerCoreProvider(Preview\Krita::class, '/application\/x-krita/');
342
-		$this->registerCoreProvider(Preview\MP3::class, '/audio\/mpeg$/');
343
-		$this->registerCoreProvider(Preview\OpenDocument::class, '/application\/vnd.oasis.opendocument.*/');
344
-		$this->registerCoreProvider(Preview\Imaginary::class, Preview\Imaginary::supportedMimeTypes());
345
-		$this->registerCoreProvider(Preview\ImaginaryPDF::class, Preview\ImaginaryPDF::supportedMimeTypes());
346
-
347
-		// SVG and Bitmap require imagick
348
-		if ($this->imagickSupport->hasExtension()) {
349
-			$imagickProviders = [
350
-				'SVG' => ['mimetype' => '/image\/svg\+xml/', 'class' => Preview\SVG::class],
351
-				'TIFF' => ['mimetype' => '/image\/tiff/', 'class' => Preview\TIFF::class],
352
-				'PDF' => ['mimetype' => '/application\/pdf/', 'class' => Preview\PDF::class],
353
-				'AI' => ['mimetype' => '/application\/illustrator/', 'class' => Preview\Illustrator::class],
354
-				'PSD' => ['mimetype' => '/application\/x-photoshop/', 'class' => Preview\Photoshop::class],
355
-				'EPS' => ['mimetype' => '/application\/postscript/', 'class' => Preview\Postscript::class],
356
-				'TTF' => ['mimetype' => '/application\/(?:font-sfnt|x-font$)/', 'class' => Preview\Font::class],
357
-				'HEIC' => ['mimetype' => '/image\/(x-)?hei(f|c)/', 'class' => Preview\HEIC::class],
358
-				'TGA' => ['mimetype' => '/image\/(x-)?t(ar)?ga/', 'class' => Preview\TGA::class],
359
-				'SGI' => ['mimetype' => '/image\/(x-)?sgi/', 'class' => Preview\SGI::class],
360
-			];
361
-
362
-			foreach ($imagickProviders as $queryFormat => $provider) {
363
-				$class = $provider['class'];
364
-				if (!in_array(trim($class, '\\'), $this->getEnabledDefaultProvider())) {
365
-					continue;
366
-				}
367
-
368
-				if ($this->imagickSupport->supportsFormat($queryFormat)) {
369
-					$this->registerCoreProvider($class, $provider['mimetype']);
370
-				}
371
-			}
372
-		}
373
-
374
-		$this->registerCoreProvidersOffice();
375
-
376
-		// Video requires ffmpeg
377
-		if (in_array(Preview\Movie::class, $this->getEnabledDefaultProvider())) {
378
-			$movieBinary = $this->config->getSystemValue('preview_ffmpeg_path', null);
379
-			if (!is_string($movieBinary)) {
380
-				$movieBinary = $this->binaryFinder->findBinaryPath('ffmpeg');
381
-			}
382
-
383
-
384
-			if (is_string($movieBinary)) {
385
-				$this->registerCoreProvider(Preview\Movie::class, '/video\/.*/', ['movieBinary' => $movieBinary]);
386
-			}
387
-		}
388
-	}
389
-
390
-	private function registerCoreProvidersOffice(): void {
391
-		$officeProviders = [
392
-			['mimetype' => '/application\/msword/', 'class' => Preview\MSOfficeDoc::class],
393
-			['mimetype' => '/application\/vnd.ms-.*/', 'class' => Preview\MSOffice2003::class],
394
-			['mimetype' => '/application\/vnd.openxmlformats-officedocument.*/', 'class' => Preview\MSOffice2007::class],
395
-			['mimetype' => '/application\/vnd.oasis.opendocument.*/', 'class' => Preview\OpenDocument::class],
396
-			['mimetype' => '/application\/vnd.sun.xml.*/', 'class' => Preview\StarOffice::class],
397
-			['mimetype' => '/image\/emf/', 'class' => Preview\EMF::class],
398
-		];
399
-
400
-		$findBinary = true;
401
-		$officeBinary = false;
402
-
403
-		foreach ($officeProviders as $provider) {
404
-			$class = $provider['class'];
405
-			if (!in_array(trim($class, '\\'), $this->getEnabledDefaultProvider())) {
406
-				continue;
407
-			}
408
-
409
-			if ($findBinary) {
410
-				// Office requires openoffice or libreoffice
411
-				$officeBinary = $this->config->getSystemValue('preview_libreoffice_path', false);
412
-				if ($officeBinary === false) {
413
-					$officeBinary = $this->binaryFinder->findBinaryPath('libreoffice');
414
-				}
415
-				if ($officeBinary === false) {
416
-					$officeBinary = $this->binaryFinder->findBinaryPath('openoffice');
417
-				}
418
-				$findBinary = false;
419
-			}
420
-
421
-			if ($officeBinary) {
422
-				$this->registerCoreProvider($class, $provider['mimetype'], ['officeBinary' => $officeBinary]);
423
-			}
424
-		}
425
-	}
426
-
427
-	private function registerBootstrapProviders(): void {
428
-		$context = $this->bootstrapCoordinator->getRegistrationContext();
429
-
430
-		if ($context === null) {
431
-			// Just ignore for now
432
-			return;
433
-		}
434
-
435
-		$providers = $context->getPreviewProviders();
436
-		foreach ($providers as $provider) {
437
-			$key = $provider->getMimeTypeRegex() . '-' . $provider->getService();
438
-			if (array_key_exists($key, $this->loadedBootstrapProviders)) {
439
-				// Do not load the provider more than once
440
-				continue;
441
-			}
442
-			$this->loadedBootstrapProviders[$key] = null;
443
-
444
-			$this->registerProvider($provider->getMimeTypeRegex(), function () use ($provider) {
445
-				try {
446
-					return $this->container->get($provider->getService());
447
-				} catch (QueryException $e) {
448
-					return null;
449
-				}
450
-			});
451
-		}
452
-	}
453
-
454
-	/**
455
-	 * @throws NotFoundException if preview generation is disabled
456
-	 */
457
-	private function throwIfPreviewsDisabled(File $file, ?string $mimeType = null): void {
458
-		if (!$this->isAvailable($file, $mimeType)) {
459
-			throw new NotFoundException('Previews disabled');
460
-		}
461
-	}
31
+    protected IConfig $config;
32
+    protected IRootFolder $rootFolder;
33
+    protected IAppData $appData;
34
+    protected IEventDispatcher $eventDispatcher;
35
+    private ?Generator $generator = null;
36
+    private GeneratorHelper $helper;
37
+    protected bool $providerListDirty = false;
38
+    protected bool $registeredCoreProviders = false;
39
+    protected array $providers = [];
40
+
41
+    /** @var array mime type => support status */
42
+    protected array $mimeTypeSupportMap = [];
43
+    protected ?array $defaultProviders = null;
44
+    protected ?string $userId;
45
+    private Coordinator $bootstrapCoordinator;
46
+
47
+    /**
48
+     * Hash map (without value) of loaded bootstrap providers
49
+     * @psalm-var array<string, null>
50
+     */
51
+    private array $loadedBootstrapProviders = [];
52
+    private ContainerInterface $container;
53
+    private IBinaryFinder $binaryFinder;
54
+    private IMagickSupport $imagickSupport;
55
+    private bool $enablePreviews;
56
+
57
+    public function __construct(
58
+        IConfig $config,
59
+        IRootFolder $rootFolder,
60
+        IAppData $appData,
61
+        IEventDispatcher $eventDispatcher,
62
+        GeneratorHelper $helper,
63
+        ?string $userId,
64
+        Coordinator $bootstrapCoordinator,
65
+        ContainerInterface $container,
66
+        IBinaryFinder $binaryFinder,
67
+        IMagickSupport $imagickSupport,
68
+    ) {
69
+        $this->config = $config;
70
+        $this->rootFolder = $rootFolder;
71
+        $this->appData = $appData;
72
+        $this->eventDispatcher = $eventDispatcher;
73
+        $this->helper = $helper;
74
+        $this->userId = $userId;
75
+        $this->bootstrapCoordinator = $bootstrapCoordinator;
76
+        $this->container = $container;
77
+        $this->binaryFinder = $binaryFinder;
78
+        $this->imagickSupport = $imagickSupport;
79
+        $this->enablePreviews = $config->getSystemValueBool('enable_previews', true);
80
+    }
81
+
82
+    /**
83
+     * In order to improve lazy loading a closure can be registered which will be
84
+     * called in case preview providers are actually requested
85
+     *
86
+     * $callable has to return an instance of \OCP\Preview\IProvider or \OCP\Preview\IProviderV2
87
+     *
88
+     * @param string $mimeTypeRegex Regex with the mime types that are supported by this provider
89
+     * @param \Closure $callable
90
+     * @return void
91
+     */
92
+    public function registerProvider($mimeTypeRegex, \Closure $callable): void {
93
+        if (!$this->enablePreviews) {
94
+            return;
95
+        }
96
+
97
+        if (!isset($this->providers[$mimeTypeRegex])) {
98
+            $this->providers[$mimeTypeRegex] = [];
99
+        }
100
+        $this->providers[$mimeTypeRegex][] = $callable;
101
+        $this->providerListDirty = true;
102
+    }
103
+
104
+    /**
105
+     * Get all providers
106
+     */
107
+    public function getProviders(): array {
108
+        if (!$this->enablePreviews) {
109
+            return [];
110
+        }
111
+
112
+        $this->registerCoreProviders();
113
+        $this->registerBootstrapProviders();
114
+        if ($this->providerListDirty) {
115
+            $keys = array_map('strlen', array_keys($this->providers));
116
+            array_multisort($keys, SORT_DESC, $this->providers);
117
+            $this->providerListDirty = false;
118
+        }
119
+
120
+        return $this->providers;
121
+    }
122
+
123
+    /**
124
+     * Does the manager have any providers
125
+     */
126
+    public function hasProviders(): bool {
127
+        $this->registerCoreProviders();
128
+        return !empty($this->providers);
129
+    }
130
+
131
+    private function getGenerator(): Generator {
132
+        if ($this->generator === null) {
133
+            $this->generator = new Generator(
134
+                $this->config,
135
+                $this,
136
+                $this->appData,
137
+                new GeneratorHelper(
138
+                    $this->rootFolder,
139
+                    $this->config
140
+                ),
141
+                $this->eventDispatcher,
142
+                $this->container->get(LoggerInterface::class),
143
+            );
144
+        }
145
+        return $this->generator;
146
+    }
147
+
148
+    public function getPreview(
149
+        File $file,
150
+        $width = -1,
151
+        $height = -1,
152
+        $crop = false,
153
+        $mode = IPreview::MODE_FILL,
154
+        $mimeType = null,
155
+        bool $cacheResult = true,
156
+    ): ISimpleFile {
157
+        $this->throwIfPreviewsDisabled($file, $mimeType);
158
+        $previewConcurrency = $this->getGenerator()->getNumConcurrentPreviews('preview_concurrency_all');
159
+        $sem = Generator::guardWithSemaphore(Generator::SEMAPHORE_ID_ALL, $previewConcurrency);
160
+        try {
161
+            $preview = $this->getGenerator()->getPreview($file, $width, $height, $crop, $mode, $mimeType, $cacheResult);
162
+        } finally {
163
+            Generator::unguardWithSemaphore($sem);
164
+        }
165
+
166
+        return $preview;
167
+    }
168
+
169
+    /**
170
+     * Generates previews of a file
171
+     *
172
+     * @param File $file
173
+     * @param array $specifications
174
+     * @param string $mimeType
175
+     * @return ISimpleFile the last preview that was generated
176
+     * @throws NotFoundException
177
+     * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
178
+     * @since 19.0.0
179
+     */
180
+    public function generatePreviews(File $file, array $specifications, $mimeType = null) {
181
+        $this->throwIfPreviewsDisabled($file, $mimeType);
182
+        return $this->getGenerator()->generatePreviews($file, $specifications, $mimeType);
183
+    }
184
+
185
+    /**
186
+     * returns true if the passed mime type is supported
187
+     *
188
+     * @param string $mimeType
189
+     * @return boolean
190
+     */
191
+    public function isMimeSupported($mimeType = '*') {
192
+        if (!$this->enablePreviews) {
193
+            return false;
194
+        }
195
+
196
+        if (isset($this->mimeTypeSupportMap[$mimeType])) {
197
+            return $this->mimeTypeSupportMap[$mimeType];
198
+        }
199
+
200
+        $this->registerCoreProviders();
201
+        $this->registerBootstrapProviders();
202
+        $providerMimeTypes = array_keys($this->providers);
203
+        foreach ($providerMimeTypes as $supportedMimeType) {
204
+            if (preg_match($supportedMimeType, $mimeType)) {
205
+                $this->mimeTypeSupportMap[$mimeType] = true;
206
+                return true;
207
+            }
208
+        }
209
+        $this->mimeTypeSupportMap[$mimeType] = false;
210
+        return false;
211
+    }
212
+
213
+    /**
214
+     * Check if a preview can be generated for a file
215
+     */
216
+    public function isAvailable(\OCP\Files\FileInfo $file, ?string $mimeType = null): bool {
217
+        if (!$this->enablePreviews) {
218
+            return false;
219
+        }
220
+
221
+        $fileMimeType = $mimeType ?? $file->getMimeType();
222
+
223
+        $this->registerCoreProviders();
224
+        if (!$this->isMimeSupported($fileMimeType)) {
225
+            return false;
226
+        }
227
+
228
+        $mount = $file->getMountPoint();
229
+        if ($mount && !$mount->getOption('previews', true)) {
230
+            return false;
231
+        }
232
+
233
+        foreach ($this->providers as $supportedMimeType => $providers) {
234
+            if (preg_match($supportedMimeType, $fileMimeType)) {
235
+                foreach ($providers as $providerClosure) {
236
+                    $provider = $this->helper->getProvider($providerClosure);
237
+                    if (!($provider instanceof IProviderV2)) {
238
+                        continue;
239
+                    }
240
+
241
+                    if ($provider->isAvailable($file)) {
242
+                        return true;
243
+                    }
244
+                }
245
+            }
246
+        }
247
+        return false;
248
+    }
249
+
250
+    /**
251
+     * List of enabled default providers
252
+     *
253
+     * The following providers are enabled by default:
254
+     *  - OC\Preview\PNG
255
+     *  - OC\Preview\JPEG
256
+     *  - OC\Preview\GIF
257
+     *  - OC\Preview\BMP
258
+     *  - OC\Preview\XBitmap
259
+     *  - OC\Preview\MarkDown
260
+     *  - OC\Preview\MP3
261
+     *  - OC\Preview\TXT
262
+     *
263
+     * The following providers are disabled by default due to performance or privacy concerns:
264
+     *  - OC\Preview\Font
265
+     *  - OC\Preview\HEIC
266
+     *  - OC\Preview\Illustrator
267
+     *  - OC\Preview\Movie
268
+     *  - OC\Preview\MSOfficeDoc
269
+     *  - OC\Preview\MSOffice2003
270
+     *  - OC\Preview\MSOffice2007
271
+     *  - OC\Preview\OpenDocument
272
+     *  - OC\Preview\PDF
273
+     *  - OC\Preview\Photoshop
274
+     *  - OC\Preview\Postscript
275
+     *  - OC\Preview\StarOffice
276
+     *  - OC\Preview\SVG
277
+     *  - OC\Preview\TIFF
278
+     *
279
+     * @return array
280
+     */
281
+    protected function getEnabledDefaultProvider() {
282
+        if ($this->defaultProviders !== null) {
283
+            return $this->defaultProviders;
284
+        }
285
+
286
+        $imageProviders = [
287
+            Preview\PNG::class,
288
+            Preview\JPEG::class,
289
+            Preview\GIF::class,
290
+            Preview\BMP::class,
291
+            Preview\XBitmap::class,
292
+            Preview\Krita::class,
293
+            Preview\WebP::class,
294
+        ];
295
+
296
+        $this->defaultProviders = $this->config->getSystemValue('enabledPreviewProviders', array_merge([
297
+            Preview\MarkDown::class,
298
+            Preview\MP3::class,
299
+            Preview\TXT::class,
300
+            Preview\OpenDocument::class,
301
+        ], $imageProviders));
302
+
303
+        if (in_array(Preview\Image::class, $this->defaultProviders)) {
304
+            $this->defaultProviders = array_merge($this->defaultProviders, $imageProviders);
305
+        }
306
+        $this->defaultProviders = array_unique($this->defaultProviders);
307
+        return $this->defaultProviders;
308
+    }
309
+
310
+    /**
311
+     * Register the default providers (if enabled)
312
+     *
313
+     * @param string $class
314
+     * @param string $mimeType
315
+     */
316
+    protected function registerCoreProvider($class, $mimeType, $options = []) {
317
+        if (in_array(trim($class, '\\'), $this->getEnabledDefaultProvider())) {
318
+            $this->registerProvider($mimeType, function () use ($class, $options) {
319
+                return new $class($options);
320
+            });
321
+        }
322
+    }
323
+
324
+    /**
325
+     * Register the default providers (if enabled)
326
+     */
327
+    protected function registerCoreProviders() {
328
+        if ($this->registeredCoreProviders) {
329
+            return;
330
+        }
331
+        $this->registeredCoreProviders = true;
332
+
333
+        $this->registerCoreProvider(Preview\TXT::class, '/text\/plain/');
334
+        $this->registerCoreProvider(Preview\MarkDown::class, '/text\/(x-)?markdown/');
335
+        $this->registerCoreProvider(Preview\PNG::class, '/image\/png/');
336
+        $this->registerCoreProvider(Preview\JPEG::class, '/image\/jpeg/');
337
+        $this->registerCoreProvider(Preview\GIF::class, '/image\/gif/');
338
+        $this->registerCoreProvider(Preview\BMP::class, '/image\/bmp/');
339
+        $this->registerCoreProvider(Preview\XBitmap::class, '/image\/x-xbitmap/');
340
+        $this->registerCoreProvider(Preview\WebP::class, '/image\/webp/');
341
+        $this->registerCoreProvider(Preview\Krita::class, '/application\/x-krita/');
342
+        $this->registerCoreProvider(Preview\MP3::class, '/audio\/mpeg$/');
343
+        $this->registerCoreProvider(Preview\OpenDocument::class, '/application\/vnd.oasis.opendocument.*/');
344
+        $this->registerCoreProvider(Preview\Imaginary::class, Preview\Imaginary::supportedMimeTypes());
345
+        $this->registerCoreProvider(Preview\ImaginaryPDF::class, Preview\ImaginaryPDF::supportedMimeTypes());
346
+
347
+        // SVG and Bitmap require imagick
348
+        if ($this->imagickSupport->hasExtension()) {
349
+            $imagickProviders = [
350
+                'SVG' => ['mimetype' => '/image\/svg\+xml/', 'class' => Preview\SVG::class],
351
+                'TIFF' => ['mimetype' => '/image\/tiff/', 'class' => Preview\TIFF::class],
352
+                'PDF' => ['mimetype' => '/application\/pdf/', 'class' => Preview\PDF::class],
353
+                'AI' => ['mimetype' => '/application\/illustrator/', 'class' => Preview\Illustrator::class],
354
+                'PSD' => ['mimetype' => '/application\/x-photoshop/', 'class' => Preview\Photoshop::class],
355
+                'EPS' => ['mimetype' => '/application\/postscript/', 'class' => Preview\Postscript::class],
356
+                'TTF' => ['mimetype' => '/application\/(?:font-sfnt|x-font$)/', 'class' => Preview\Font::class],
357
+                'HEIC' => ['mimetype' => '/image\/(x-)?hei(f|c)/', 'class' => Preview\HEIC::class],
358
+                'TGA' => ['mimetype' => '/image\/(x-)?t(ar)?ga/', 'class' => Preview\TGA::class],
359
+                'SGI' => ['mimetype' => '/image\/(x-)?sgi/', 'class' => Preview\SGI::class],
360
+            ];
361
+
362
+            foreach ($imagickProviders as $queryFormat => $provider) {
363
+                $class = $provider['class'];
364
+                if (!in_array(trim($class, '\\'), $this->getEnabledDefaultProvider())) {
365
+                    continue;
366
+                }
367
+
368
+                if ($this->imagickSupport->supportsFormat($queryFormat)) {
369
+                    $this->registerCoreProvider($class, $provider['mimetype']);
370
+                }
371
+            }
372
+        }
373
+
374
+        $this->registerCoreProvidersOffice();
375
+
376
+        // Video requires ffmpeg
377
+        if (in_array(Preview\Movie::class, $this->getEnabledDefaultProvider())) {
378
+            $movieBinary = $this->config->getSystemValue('preview_ffmpeg_path', null);
379
+            if (!is_string($movieBinary)) {
380
+                $movieBinary = $this->binaryFinder->findBinaryPath('ffmpeg');
381
+            }
382
+
383
+
384
+            if (is_string($movieBinary)) {
385
+                $this->registerCoreProvider(Preview\Movie::class, '/video\/.*/', ['movieBinary' => $movieBinary]);
386
+            }
387
+        }
388
+    }
389
+
390
+    private function registerCoreProvidersOffice(): void {
391
+        $officeProviders = [
392
+            ['mimetype' => '/application\/msword/', 'class' => Preview\MSOfficeDoc::class],
393
+            ['mimetype' => '/application\/vnd.ms-.*/', 'class' => Preview\MSOffice2003::class],
394
+            ['mimetype' => '/application\/vnd.openxmlformats-officedocument.*/', 'class' => Preview\MSOffice2007::class],
395
+            ['mimetype' => '/application\/vnd.oasis.opendocument.*/', 'class' => Preview\OpenDocument::class],
396
+            ['mimetype' => '/application\/vnd.sun.xml.*/', 'class' => Preview\StarOffice::class],
397
+            ['mimetype' => '/image\/emf/', 'class' => Preview\EMF::class],
398
+        ];
399
+
400
+        $findBinary = true;
401
+        $officeBinary = false;
402
+
403
+        foreach ($officeProviders as $provider) {
404
+            $class = $provider['class'];
405
+            if (!in_array(trim($class, '\\'), $this->getEnabledDefaultProvider())) {
406
+                continue;
407
+            }
408
+
409
+            if ($findBinary) {
410
+                // Office requires openoffice or libreoffice
411
+                $officeBinary = $this->config->getSystemValue('preview_libreoffice_path', false);
412
+                if ($officeBinary === false) {
413
+                    $officeBinary = $this->binaryFinder->findBinaryPath('libreoffice');
414
+                }
415
+                if ($officeBinary === false) {
416
+                    $officeBinary = $this->binaryFinder->findBinaryPath('openoffice');
417
+                }
418
+                $findBinary = false;
419
+            }
420
+
421
+            if ($officeBinary) {
422
+                $this->registerCoreProvider($class, $provider['mimetype'], ['officeBinary' => $officeBinary]);
423
+            }
424
+        }
425
+    }
426
+
427
+    private function registerBootstrapProviders(): void {
428
+        $context = $this->bootstrapCoordinator->getRegistrationContext();
429
+
430
+        if ($context === null) {
431
+            // Just ignore for now
432
+            return;
433
+        }
434
+
435
+        $providers = $context->getPreviewProviders();
436
+        foreach ($providers as $provider) {
437
+            $key = $provider->getMimeTypeRegex() . '-' . $provider->getService();
438
+            if (array_key_exists($key, $this->loadedBootstrapProviders)) {
439
+                // Do not load the provider more than once
440
+                continue;
441
+            }
442
+            $this->loadedBootstrapProviders[$key] = null;
443
+
444
+            $this->registerProvider($provider->getMimeTypeRegex(), function () use ($provider) {
445
+                try {
446
+                    return $this->container->get($provider->getService());
447
+                } catch (QueryException $e) {
448
+                    return null;
449
+                }
450
+            });
451
+        }
452
+    }
453
+
454
+    /**
455
+     * @throws NotFoundException if preview generation is disabled
456
+     */
457
+    private function throwIfPreviewsDisabled(File $file, ?string $mimeType = null): void {
458
+        if (!$this->isAvailable($file, $mimeType)) {
459
+            throw new NotFoundException('Previews disabled');
460
+        }
461
+    }
462 462
 }
Please login to merge, or discard this patch.
lib/private/IntegrityCheck/Checker.php 1 patch
Indentation   +518 added lines, -518 removed lines patch added patch discarded remove patch
@@ -35,522 +35,522 @@
 block discarded – undo
35 35
  * @package OC\IntegrityCheck
36 36
  */
37 37
 class Checker {
38
-	public const CACHE_KEY = 'oc.integritycheck.checker';
39
-
40
-	private ICache $cache;
41
-
42
-	public function __construct(
43
-		private ServerVersion $serverVersion,
44
-		private EnvironmentHelper $environmentHelper,
45
-		private FileAccessHelper $fileAccessHelper,
46
-		private ?IConfig $config,
47
-		private ?IAppConfig $appConfig,
48
-		ICacheFactory $cacheFactory,
49
-		private IAppManager $appManager,
50
-		private IMimeTypeDetector $mimeTypeDetector,
51
-	) {
52
-		$this->cache = $cacheFactory->createDistributed(self::CACHE_KEY);
53
-	}
54
-
55
-	/**
56
-	 * Whether code signing is enforced or not.
57
-	 *
58
-	 * @return bool
59
-	 */
60
-	public function isCodeCheckEnforced(): bool {
61
-		$notSignedChannels = [ '', 'git'];
62
-		if (\in_array($this->serverVersion->getChannel(), $notSignedChannels, true)) {
63
-			return false;
64
-		}
65
-
66
-		/**
67
-		 * This config option is undocumented and supposed to be so, it's only
68
-		 * applicable for very specific scenarios and we should not advertise it
69
-		 * too prominent. So please do not add it to config.sample.php.
70
-		 */
71
-		return !($this->config?->getSystemValueBool('integrity.check.disabled', false) ?? false);
72
-	}
73
-
74
-	/**
75
-	 * Enumerates all files belonging to the folder. Sensible defaults are excluded.
76
-	 *
77
-	 * @param string $folderToIterate
78
-	 * @param string $root
79
-	 * @return \RecursiveIteratorIterator
80
-	 * @throws \Exception
81
-	 */
82
-	private function getFolderIterator(string $folderToIterate, string $root = ''): \RecursiveIteratorIterator {
83
-		$dirItr = new \RecursiveDirectoryIterator(
84
-			$folderToIterate,
85
-			\RecursiveDirectoryIterator::SKIP_DOTS
86
-		);
87
-		if ($root === '') {
88
-			$root = \OC::$SERVERROOT;
89
-		}
90
-		$root = rtrim($root, '/');
91
-
92
-		$excludeGenericFilesIterator = new ExcludeFileByNameFilterIterator($dirItr);
93
-		$excludeFoldersIterator = new ExcludeFoldersByPathFilterIterator($excludeGenericFilesIterator, $root);
94
-
95
-		return new \RecursiveIteratorIterator(
96
-			$excludeFoldersIterator,
97
-			\RecursiveIteratorIterator::SELF_FIRST
98
-		);
99
-	}
100
-
101
-	/**
102
-	 * Returns an array of ['filename' => 'SHA512-hash-of-file'] for all files found
103
-	 * in the iterator.
104
-	 *
105
-	 * @param \RecursiveIteratorIterator $iterator
106
-	 * @param string $path
107
-	 * @return array Array of hashes.
108
-	 */
109
-	private function generateHashes(\RecursiveIteratorIterator $iterator,
110
-		string $path): array {
111
-		$hashes = [];
112
-
113
-		$baseDirectoryLength = \strlen($path);
114
-		foreach ($iterator as $filename => $data) {
115
-			/** @var \DirectoryIterator $data */
116
-			if ($data->isDir()) {
117
-				continue;
118
-			}
119
-
120
-			$relativeFileName = substr($filename, $baseDirectoryLength);
121
-			$relativeFileName = ltrim($relativeFileName, '/');
122
-
123
-			// Exclude signature.json files in the appinfo and root folder
124
-			if ($relativeFileName === 'appinfo/signature.json') {
125
-				continue;
126
-			}
127
-			// Exclude signature.json files in the appinfo and core folder
128
-			if ($relativeFileName === 'core/signature.json') {
129
-				continue;
130
-			}
131
-
132
-			// The .htaccess file in the root folder of ownCloud can contain
133
-			// custom content after the installation due to the fact that dynamic
134
-			// content is written into it at installation time as well. This
135
-			// includes for example the 404 and 403 instructions.
136
-			// Thus we ignore everything below the first occurrence of
137
-			// "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####" and have the
138
-			// hash generated based on this.
139
-			if ($filename === $this->environmentHelper->getServerRoot() . '/.htaccess') {
140
-				$fileContent = file_get_contents($filename);
141
-				$explodedArray = explode('#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####', $fileContent);
142
-				if (\count($explodedArray) === 2) {
143
-					$hashes[$relativeFileName] = hash('sha512', $explodedArray[0]);
144
-					continue;
145
-				}
146
-			}
147
-			if ($filename === $this->environmentHelper->getServerRoot() . '/core/js/mimetypelist.js') {
148
-				$oldMimetypeList = new GenerateMimetypeFileBuilder();
149
-				$newFile = $oldMimetypeList->generateFile($this->mimeTypeDetector->getAllAliases(), $this->mimeTypeDetector->getAllNamings());
150
-				$oldFile = $this->fileAccessHelper->file_get_contents($filename);
151
-				if ($newFile === $oldFile) {
152
-					$hashes[$relativeFileName] = hash('sha512', $oldMimetypeList->generateFile($this->mimeTypeDetector->getOnlyDefaultAliases(), $this->mimeTypeDetector->getAllNamings()));
153
-					continue;
154
-				}
155
-			}
156
-
157
-			$hashes[$relativeFileName] = hash_file('sha512', $filename);
158
-		}
159
-
160
-		return $hashes;
161
-	}
162
-
163
-	/**
164
-	 * Creates the signature data
165
-	 *
166
-	 * @param array $hashes
167
-	 * @param X509 $certificate
168
-	 * @param RSA $privateKey
169
-	 * @return array
170
-	 */
171
-	private function createSignatureData(array $hashes,
172
-		X509 $certificate,
173
-		RSA $privateKey): array {
174
-		ksort($hashes);
175
-
176
-		$privateKey->setSignatureMode(RSA::SIGNATURE_PSS);
177
-		$privateKey->setMGFHash('sha512');
178
-		// See https://tools.ietf.org/html/rfc3447#page-38
179
-		$privateKey->setSaltLength(0);
180
-		$signature = $privateKey->sign(json_encode($hashes));
181
-
182
-		return [
183
-			'hashes' => $hashes,
184
-			'signature' => base64_encode($signature),
185
-			'certificate' => $certificate->saveX509($certificate->currentCert),
186
-		];
187
-	}
188
-
189
-	/**
190
-	 * Write the signature of the app in the specified folder
191
-	 *
192
-	 * @param string $path
193
-	 * @param X509 $certificate
194
-	 * @param RSA $privateKey
195
-	 * @throws \Exception
196
-	 */
197
-	public function writeAppSignature($path,
198
-		X509 $certificate,
199
-		RSA $privateKey) {
200
-		$appInfoDir = $path . '/appinfo';
201
-		try {
202
-			$this->fileAccessHelper->assertDirectoryExists($appInfoDir);
203
-
204
-			$iterator = $this->getFolderIterator($path);
205
-			$hashes = $this->generateHashes($iterator, $path);
206
-			$signature = $this->createSignatureData($hashes, $certificate, $privateKey);
207
-			$this->fileAccessHelper->file_put_contents(
208
-				$appInfoDir . '/signature.json',
209
-				json_encode($signature, JSON_PRETTY_PRINT)
210
-			);
211
-		} catch (\Exception $e) {
212
-			if (!$this->fileAccessHelper->is_writable($appInfoDir)) {
213
-				throw new \Exception($appInfoDir . ' is not writable');
214
-			}
215
-			throw $e;
216
-		}
217
-	}
218
-
219
-	/**
220
-	 * Write the signature of core
221
-	 *
222
-	 * @param X509 $certificate
223
-	 * @param RSA $rsa
224
-	 * @param string $path
225
-	 * @throws \Exception
226
-	 */
227
-	public function writeCoreSignature(X509 $certificate,
228
-		RSA $rsa,
229
-		$path) {
230
-		$coreDir = $path . '/core';
231
-		try {
232
-			$this->fileAccessHelper->assertDirectoryExists($coreDir);
233
-			$iterator = $this->getFolderIterator($path, $path);
234
-			$hashes = $this->generateHashes($iterator, $path);
235
-			$signatureData = $this->createSignatureData($hashes, $certificate, $rsa);
236
-			$this->fileAccessHelper->file_put_contents(
237
-				$coreDir . '/signature.json',
238
-				json_encode($signatureData, JSON_PRETTY_PRINT)
239
-			);
240
-		} catch (\Exception $e) {
241
-			if (!$this->fileAccessHelper->is_writable($coreDir)) {
242
-				throw new \Exception($coreDir . ' is not writable');
243
-			}
244
-			throw $e;
245
-		}
246
-	}
247
-
248
-	/**
249
-	 * Split the certificate file in individual certs
250
-	 *
251
-	 * @param string $cert
252
-	 * @return string[]
253
-	 */
254
-	private function splitCerts(string $cert): array {
255
-		preg_match_all('([\-]{3,}[\S\ ]+?[\-]{3,}[\S\s]+?[\-]{3,}[\S\ ]+?[\-]{3,})', $cert, $matches);
256
-
257
-		return $matches[0];
258
-	}
259
-
260
-	/**
261
-	 * Verifies the signature for the specified path.
262
-	 *
263
-	 * @param string $signaturePath
264
-	 * @param string $basePath
265
-	 * @param string $certificateCN
266
-	 * @param bool $forceVerify
267
-	 * @return array
268
-	 * @throws InvalidSignatureException
269
-	 * @throws \Exception
270
-	 */
271
-	private function verify(string $signaturePath, string $basePath, string $certificateCN, bool $forceVerify = false): array {
272
-		if (!$forceVerify && !$this->isCodeCheckEnforced()) {
273
-			return [];
274
-		}
275
-
276
-		$content = $this->fileAccessHelper->file_get_contents($signaturePath);
277
-		$signatureData = null;
278
-
279
-		if (\is_string($content)) {
280
-			$signatureData = json_decode($content, true);
281
-		}
282
-		if (!\is_array($signatureData)) {
283
-			throw new InvalidSignatureException('Signature data not found.');
284
-		}
285
-
286
-		$expectedHashes = $signatureData['hashes'];
287
-		ksort($expectedHashes);
288
-		$signature = base64_decode($signatureData['signature']);
289
-		$certificate = $signatureData['certificate'];
290
-
291
-		// Check if certificate is signed by Nextcloud Root Authority
292
-		$x509 = new \phpseclib\File\X509();
293
-		$rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot() . '/resources/codesigning/root.crt');
294
-
295
-		$rootCerts = $this->splitCerts($rootCertificatePublicKey);
296
-		foreach ($rootCerts as $rootCert) {
297
-			$x509->loadCA($rootCert);
298
-		}
299
-		$x509->loadX509($certificate);
300
-		if (!$x509->validateSignature()) {
301
-			throw new InvalidSignatureException('Certificate is not valid.');
302
-		}
303
-		// Verify if certificate has proper CN. "core" CN is always trusted.
304
-		if ($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') {
305
-			throw new InvalidSignatureException(
306
-				sprintf('Certificate is not valid for required scope. (Requested: %s, current: CN=%s)', $certificateCN, $x509->getDN(true)['CN'])
307
-			);
308
-		}
309
-
310
-		// Check if the signature of the files is valid
311
-		$rsa = new \phpseclib\Crypt\RSA();
312
-		$rsa->loadKey($x509->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']);
313
-		$rsa->setSignatureMode(RSA::SIGNATURE_PSS);
314
-		$rsa->setMGFHash('sha512');
315
-		// See https://tools.ietf.org/html/rfc3447#page-38
316
-		$rsa->setSaltLength(0);
317
-		if (!$rsa->verify(json_encode($expectedHashes), $signature)) {
318
-			throw new InvalidSignatureException('Signature could not get verified.');
319
-		}
320
-
321
-		// Fixes for the updater as shipped with ownCloud 9.0.x: The updater is
322
-		// replaced after the code integrity check is performed.
323
-		//
324
-		// Due to this reason we exclude the whole updater/ folder from the code
325
-		// integrity check.
326
-		if ($basePath === $this->environmentHelper->getServerRoot()) {
327
-			foreach ($expectedHashes as $fileName => $hash) {
328
-				if (str_starts_with($fileName, 'updater/')) {
329
-					unset($expectedHashes[$fileName]);
330
-				}
331
-			}
332
-		}
333
-
334
-		// Compare the list of files which are not identical
335
-		$currentInstanceHashes = $this->generateHashes($this->getFolderIterator($basePath), $basePath);
336
-		$differencesA = array_diff_assoc($expectedHashes, $currentInstanceHashes);
337
-		$differencesB = array_diff_assoc($currentInstanceHashes, $expectedHashes);
338
-		$differences = array_unique(array_merge($differencesA, $differencesB));
339
-		$differenceArray = [];
340
-		foreach ($differences as $filename => $hash) {
341
-			// Check if file should not exist in the new signature table
342
-			if (!array_key_exists($filename, $expectedHashes)) {
343
-				$differenceArray['EXTRA_FILE'][$filename]['expected'] = '';
344
-				$differenceArray['EXTRA_FILE'][$filename]['current'] = $hash;
345
-				continue;
346
-			}
347
-
348
-			// Check if file is missing
349
-			if (!array_key_exists($filename, $currentInstanceHashes)) {
350
-				$differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename];
351
-				$differenceArray['FILE_MISSING'][$filename]['current'] = '';
352
-				continue;
353
-			}
354
-
355
-			// Check if hash does mismatch
356
-			if ($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) {
357
-				$differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename];
358
-				$differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename];
359
-				continue;
360
-			}
361
-
362
-			// Should never happen.
363
-			throw new \Exception('Invalid behaviour in file hash comparison experienced. Please report this error to the developers.');
364
-		}
365
-
366
-		return $differenceArray;
367
-	}
368
-
369
-	/**
370
-	 * Whether the code integrity check has passed successful or not
371
-	 *
372
-	 * @return bool
373
-	 */
374
-	public function hasPassedCheck(): bool {
375
-		$results = $this->getResults();
376
-		if ($results !== null && empty($results)) {
377
-			return true;
378
-		}
379
-
380
-		return false;
381
-	}
382
-
383
-	/**
384
-	 * @return array|null Either the results or null if no results available
385
-	 */
386
-	public function getResults(): ?array {
387
-		$cachedResults = $this->cache->get(self::CACHE_KEY);
388
-		if (!\is_null($cachedResults) && $cachedResults !== false) {
389
-			return json_decode($cachedResults, true);
390
-		}
391
-
392
-		if ($this->appConfig?->hasKey('core', self::CACHE_KEY, lazy: true)) {
393
-			return $this->appConfig->getValueArray('core', self::CACHE_KEY, lazy: true);
394
-		}
395
-
396
-		// No results available
397
-		return null;
398
-	}
399
-
400
-	/**
401
-	 * Stores the results in the app config as well as cache
402
-	 *
403
-	 * @param string $scope
404
-	 * @param array $result
405
-	 */
406
-	private function storeResults(string $scope, array $result) {
407
-		$resultArray = $this->getResults() ?? [];
408
-		unset($resultArray[$scope]);
409
-		if (!empty($result)) {
410
-			$resultArray[$scope] = $result;
411
-		}
412
-		$this->appConfig?->setValueArray('core', self::CACHE_KEY, $resultArray, lazy: true);
413
-		$this->cache->set(self::CACHE_KEY, json_encode($resultArray));
414
-	}
415
-
416
-	/**
417
-	 *
418
-	 * Clean previous results for a proper rescanning. Otherwise
419
-	 */
420
-	private function cleanResults() {
421
-		$this->appConfig->deleteKey('core', self::CACHE_KEY);
422
-		$this->cache->remove(self::CACHE_KEY);
423
-	}
424
-
425
-	/**
426
-	 * Verify the signature of $appId. Returns an array with the following content:
427
-	 * [
428
-	 * 	'FILE_MISSING' =>
429
-	 * 	[
430
-	 * 		'filename' => [
431
-	 * 			'expected' => 'expectedSHA512',
432
-	 * 			'current' => 'currentSHA512',
433
-	 * 		],
434
-	 * 	],
435
-	 * 	'EXTRA_FILE' =>
436
-	 * 	[
437
-	 * 		'filename' => [
438
-	 * 			'expected' => 'expectedSHA512',
439
-	 * 			'current' => 'currentSHA512',
440
-	 * 		],
441
-	 * 	],
442
-	 * 	'INVALID_HASH' =>
443
-	 * 	[
444
-	 * 		'filename' => [
445
-	 * 			'expected' => 'expectedSHA512',
446
-	 * 			'current' => 'currentSHA512',
447
-	 * 		],
448
-	 * 	],
449
-	 * ]
450
-	 *
451
-	 * Array may be empty in case no problems have been found.
452
-	 *
453
-	 * @param string $appId
454
-	 * @param string $path Optional path. If none is given it will be guessed.
455
-	 * @param bool $forceVerify
456
-	 * @return array
457
-	 */
458
-	public function verifyAppSignature(string $appId, string $path = '', bool $forceVerify = false): array {
459
-		try {
460
-			if ($path === '') {
461
-				$path = $this->appManager->getAppPath($appId);
462
-			}
463
-			$result = $this->verify(
464
-				$path . '/appinfo/signature.json',
465
-				$path,
466
-				$appId,
467
-				$forceVerify
468
-			);
469
-		} catch (\Exception $e) {
470
-			$result = [
471
-				'EXCEPTION' => [
472
-					'class' => \get_class($e),
473
-					'message' => $e->getMessage(),
474
-				],
475
-			];
476
-		}
477
-		$this->storeResults($appId, $result);
478
-
479
-		return $result;
480
-	}
481
-
482
-	/**
483
-	 * Verify the signature of core. Returns an array with the following content:
484
-	 * [
485
-	 * 	'FILE_MISSING' =>
486
-	 * 	[
487
-	 * 		'filename' => [
488
-	 * 			'expected' => 'expectedSHA512',
489
-	 * 			'current' => 'currentSHA512',
490
-	 * 		],
491
-	 * 	],
492
-	 * 	'EXTRA_FILE' =>
493
-	 * 	[
494
-	 * 		'filename' => [
495
-	 * 			'expected' => 'expectedSHA512',
496
-	 * 			'current' => 'currentSHA512',
497
-	 * 		],
498
-	 * 	],
499
-	 * 	'INVALID_HASH' =>
500
-	 * 	[
501
-	 * 		'filename' => [
502
-	 * 			'expected' => 'expectedSHA512',
503
-	 * 			'current' => 'currentSHA512',
504
-	 * 		],
505
-	 * 	],
506
-	 * ]
507
-	 *
508
-	 * Array may be empty in case no problems have been found.
509
-	 *
510
-	 * @return array
511
-	 */
512
-	public function verifyCoreSignature(): array {
513
-		try {
514
-			$result = $this->verify(
515
-				$this->environmentHelper->getServerRoot() . '/core/signature.json',
516
-				$this->environmentHelper->getServerRoot(),
517
-				'core'
518
-			);
519
-		} catch (\Exception $e) {
520
-			$result = [
521
-				'EXCEPTION' => [
522
-					'class' => \get_class($e),
523
-					'message' => $e->getMessage(),
524
-				],
525
-			];
526
-		}
527
-		$this->storeResults('core', $result);
528
-
529
-		return $result;
530
-	}
531
-
532
-	/**
533
-	 * Verify the core code of the instance as well as all applicable applications
534
-	 * and store the results.
535
-	 */
536
-	public function runInstanceVerification() {
537
-		$this->cleanResults();
538
-		$this->verifyCoreSignature();
539
-		$appIds = $this->appManager->getAllAppsInAppsFolders();
540
-		foreach ($appIds as $appId) {
541
-			// If an application is shipped a valid signature is required
542
-			$isShipped = $this->appManager->isShipped($appId);
543
-			$appNeedsToBeChecked = false;
544
-			if ($isShipped) {
545
-				$appNeedsToBeChecked = true;
546
-			} elseif ($this->fileAccessHelper->file_exists($this->appManager->getAppPath($appId) . '/appinfo/signature.json')) {
547
-				// Otherwise only if the application explicitly ships a signature.json file
548
-				$appNeedsToBeChecked = true;
549
-			}
550
-
551
-			if ($appNeedsToBeChecked) {
552
-				$this->verifyAppSignature($appId);
553
-			}
554
-		}
555
-	}
38
+    public const CACHE_KEY = 'oc.integritycheck.checker';
39
+
40
+    private ICache $cache;
41
+
42
+    public function __construct(
43
+        private ServerVersion $serverVersion,
44
+        private EnvironmentHelper $environmentHelper,
45
+        private FileAccessHelper $fileAccessHelper,
46
+        private ?IConfig $config,
47
+        private ?IAppConfig $appConfig,
48
+        ICacheFactory $cacheFactory,
49
+        private IAppManager $appManager,
50
+        private IMimeTypeDetector $mimeTypeDetector,
51
+    ) {
52
+        $this->cache = $cacheFactory->createDistributed(self::CACHE_KEY);
53
+    }
54
+
55
+    /**
56
+     * Whether code signing is enforced or not.
57
+     *
58
+     * @return bool
59
+     */
60
+    public function isCodeCheckEnforced(): bool {
61
+        $notSignedChannels = [ '', 'git'];
62
+        if (\in_array($this->serverVersion->getChannel(), $notSignedChannels, true)) {
63
+            return false;
64
+        }
65
+
66
+        /**
67
+         * This config option is undocumented and supposed to be so, it's only
68
+         * applicable for very specific scenarios and we should not advertise it
69
+         * too prominent. So please do not add it to config.sample.php.
70
+         */
71
+        return !($this->config?->getSystemValueBool('integrity.check.disabled', false) ?? false);
72
+    }
73
+
74
+    /**
75
+     * Enumerates all files belonging to the folder. Sensible defaults are excluded.
76
+     *
77
+     * @param string $folderToIterate
78
+     * @param string $root
79
+     * @return \RecursiveIteratorIterator
80
+     * @throws \Exception
81
+     */
82
+    private function getFolderIterator(string $folderToIterate, string $root = ''): \RecursiveIteratorIterator {
83
+        $dirItr = new \RecursiveDirectoryIterator(
84
+            $folderToIterate,
85
+            \RecursiveDirectoryIterator::SKIP_DOTS
86
+        );
87
+        if ($root === '') {
88
+            $root = \OC::$SERVERROOT;
89
+        }
90
+        $root = rtrim($root, '/');
91
+
92
+        $excludeGenericFilesIterator = new ExcludeFileByNameFilterIterator($dirItr);
93
+        $excludeFoldersIterator = new ExcludeFoldersByPathFilterIterator($excludeGenericFilesIterator, $root);
94
+
95
+        return new \RecursiveIteratorIterator(
96
+            $excludeFoldersIterator,
97
+            \RecursiveIteratorIterator::SELF_FIRST
98
+        );
99
+    }
100
+
101
+    /**
102
+     * Returns an array of ['filename' => 'SHA512-hash-of-file'] for all files found
103
+     * in the iterator.
104
+     *
105
+     * @param \RecursiveIteratorIterator $iterator
106
+     * @param string $path
107
+     * @return array Array of hashes.
108
+     */
109
+    private function generateHashes(\RecursiveIteratorIterator $iterator,
110
+        string $path): array {
111
+        $hashes = [];
112
+
113
+        $baseDirectoryLength = \strlen($path);
114
+        foreach ($iterator as $filename => $data) {
115
+            /** @var \DirectoryIterator $data */
116
+            if ($data->isDir()) {
117
+                continue;
118
+            }
119
+
120
+            $relativeFileName = substr($filename, $baseDirectoryLength);
121
+            $relativeFileName = ltrim($relativeFileName, '/');
122
+
123
+            // Exclude signature.json files in the appinfo and root folder
124
+            if ($relativeFileName === 'appinfo/signature.json') {
125
+                continue;
126
+            }
127
+            // Exclude signature.json files in the appinfo and core folder
128
+            if ($relativeFileName === 'core/signature.json') {
129
+                continue;
130
+            }
131
+
132
+            // The .htaccess file in the root folder of ownCloud can contain
133
+            // custom content after the installation due to the fact that dynamic
134
+            // content is written into it at installation time as well. This
135
+            // includes for example the 404 and 403 instructions.
136
+            // Thus we ignore everything below the first occurrence of
137
+            // "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####" and have the
138
+            // hash generated based on this.
139
+            if ($filename === $this->environmentHelper->getServerRoot() . '/.htaccess') {
140
+                $fileContent = file_get_contents($filename);
141
+                $explodedArray = explode('#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####', $fileContent);
142
+                if (\count($explodedArray) === 2) {
143
+                    $hashes[$relativeFileName] = hash('sha512', $explodedArray[0]);
144
+                    continue;
145
+                }
146
+            }
147
+            if ($filename === $this->environmentHelper->getServerRoot() . '/core/js/mimetypelist.js') {
148
+                $oldMimetypeList = new GenerateMimetypeFileBuilder();
149
+                $newFile = $oldMimetypeList->generateFile($this->mimeTypeDetector->getAllAliases(), $this->mimeTypeDetector->getAllNamings());
150
+                $oldFile = $this->fileAccessHelper->file_get_contents($filename);
151
+                if ($newFile === $oldFile) {
152
+                    $hashes[$relativeFileName] = hash('sha512', $oldMimetypeList->generateFile($this->mimeTypeDetector->getOnlyDefaultAliases(), $this->mimeTypeDetector->getAllNamings()));
153
+                    continue;
154
+                }
155
+            }
156
+
157
+            $hashes[$relativeFileName] = hash_file('sha512', $filename);
158
+        }
159
+
160
+        return $hashes;
161
+    }
162
+
163
+    /**
164
+     * Creates the signature data
165
+     *
166
+     * @param array $hashes
167
+     * @param X509 $certificate
168
+     * @param RSA $privateKey
169
+     * @return array
170
+     */
171
+    private function createSignatureData(array $hashes,
172
+        X509 $certificate,
173
+        RSA $privateKey): array {
174
+        ksort($hashes);
175
+
176
+        $privateKey->setSignatureMode(RSA::SIGNATURE_PSS);
177
+        $privateKey->setMGFHash('sha512');
178
+        // See https://tools.ietf.org/html/rfc3447#page-38
179
+        $privateKey->setSaltLength(0);
180
+        $signature = $privateKey->sign(json_encode($hashes));
181
+
182
+        return [
183
+            'hashes' => $hashes,
184
+            'signature' => base64_encode($signature),
185
+            'certificate' => $certificate->saveX509($certificate->currentCert),
186
+        ];
187
+    }
188
+
189
+    /**
190
+     * Write the signature of the app in the specified folder
191
+     *
192
+     * @param string $path
193
+     * @param X509 $certificate
194
+     * @param RSA $privateKey
195
+     * @throws \Exception
196
+     */
197
+    public function writeAppSignature($path,
198
+        X509 $certificate,
199
+        RSA $privateKey) {
200
+        $appInfoDir = $path . '/appinfo';
201
+        try {
202
+            $this->fileAccessHelper->assertDirectoryExists($appInfoDir);
203
+
204
+            $iterator = $this->getFolderIterator($path);
205
+            $hashes = $this->generateHashes($iterator, $path);
206
+            $signature = $this->createSignatureData($hashes, $certificate, $privateKey);
207
+            $this->fileAccessHelper->file_put_contents(
208
+                $appInfoDir . '/signature.json',
209
+                json_encode($signature, JSON_PRETTY_PRINT)
210
+            );
211
+        } catch (\Exception $e) {
212
+            if (!$this->fileAccessHelper->is_writable($appInfoDir)) {
213
+                throw new \Exception($appInfoDir . ' is not writable');
214
+            }
215
+            throw $e;
216
+        }
217
+    }
218
+
219
+    /**
220
+     * Write the signature of core
221
+     *
222
+     * @param X509 $certificate
223
+     * @param RSA $rsa
224
+     * @param string $path
225
+     * @throws \Exception
226
+     */
227
+    public function writeCoreSignature(X509 $certificate,
228
+        RSA $rsa,
229
+        $path) {
230
+        $coreDir = $path . '/core';
231
+        try {
232
+            $this->fileAccessHelper->assertDirectoryExists($coreDir);
233
+            $iterator = $this->getFolderIterator($path, $path);
234
+            $hashes = $this->generateHashes($iterator, $path);
235
+            $signatureData = $this->createSignatureData($hashes, $certificate, $rsa);
236
+            $this->fileAccessHelper->file_put_contents(
237
+                $coreDir . '/signature.json',
238
+                json_encode($signatureData, JSON_PRETTY_PRINT)
239
+            );
240
+        } catch (\Exception $e) {
241
+            if (!$this->fileAccessHelper->is_writable($coreDir)) {
242
+                throw new \Exception($coreDir . ' is not writable');
243
+            }
244
+            throw $e;
245
+        }
246
+    }
247
+
248
+    /**
249
+     * Split the certificate file in individual certs
250
+     *
251
+     * @param string $cert
252
+     * @return string[]
253
+     */
254
+    private function splitCerts(string $cert): array {
255
+        preg_match_all('([\-]{3,}[\S\ ]+?[\-]{3,}[\S\s]+?[\-]{3,}[\S\ ]+?[\-]{3,})', $cert, $matches);
256
+
257
+        return $matches[0];
258
+    }
259
+
260
+    /**
261
+     * Verifies the signature for the specified path.
262
+     *
263
+     * @param string $signaturePath
264
+     * @param string $basePath
265
+     * @param string $certificateCN
266
+     * @param bool $forceVerify
267
+     * @return array
268
+     * @throws InvalidSignatureException
269
+     * @throws \Exception
270
+     */
271
+    private function verify(string $signaturePath, string $basePath, string $certificateCN, bool $forceVerify = false): array {
272
+        if (!$forceVerify && !$this->isCodeCheckEnforced()) {
273
+            return [];
274
+        }
275
+
276
+        $content = $this->fileAccessHelper->file_get_contents($signaturePath);
277
+        $signatureData = null;
278
+
279
+        if (\is_string($content)) {
280
+            $signatureData = json_decode($content, true);
281
+        }
282
+        if (!\is_array($signatureData)) {
283
+            throw new InvalidSignatureException('Signature data not found.');
284
+        }
285
+
286
+        $expectedHashes = $signatureData['hashes'];
287
+        ksort($expectedHashes);
288
+        $signature = base64_decode($signatureData['signature']);
289
+        $certificate = $signatureData['certificate'];
290
+
291
+        // Check if certificate is signed by Nextcloud Root Authority
292
+        $x509 = new \phpseclib\File\X509();
293
+        $rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot() . '/resources/codesigning/root.crt');
294
+
295
+        $rootCerts = $this->splitCerts($rootCertificatePublicKey);
296
+        foreach ($rootCerts as $rootCert) {
297
+            $x509->loadCA($rootCert);
298
+        }
299
+        $x509->loadX509($certificate);
300
+        if (!$x509->validateSignature()) {
301
+            throw new InvalidSignatureException('Certificate is not valid.');
302
+        }
303
+        // Verify if certificate has proper CN. "core" CN is always trusted.
304
+        if ($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') {
305
+            throw new InvalidSignatureException(
306
+                sprintf('Certificate is not valid for required scope. (Requested: %s, current: CN=%s)', $certificateCN, $x509->getDN(true)['CN'])
307
+            );
308
+        }
309
+
310
+        // Check if the signature of the files is valid
311
+        $rsa = new \phpseclib\Crypt\RSA();
312
+        $rsa->loadKey($x509->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']);
313
+        $rsa->setSignatureMode(RSA::SIGNATURE_PSS);
314
+        $rsa->setMGFHash('sha512');
315
+        // See https://tools.ietf.org/html/rfc3447#page-38
316
+        $rsa->setSaltLength(0);
317
+        if (!$rsa->verify(json_encode($expectedHashes), $signature)) {
318
+            throw new InvalidSignatureException('Signature could not get verified.');
319
+        }
320
+
321
+        // Fixes for the updater as shipped with ownCloud 9.0.x: The updater is
322
+        // replaced after the code integrity check is performed.
323
+        //
324
+        // Due to this reason we exclude the whole updater/ folder from the code
325
+        // integrity check.
326
+        if ($basePath === $this->environmentHelper->getServerRoot()) {
327
+            foreach ($expectedHashes as $fileName => $hash) {
328
+                if (str_starts_with($fileName, 'updater/')) {
329
+                    unset($expectedHashes[$fileName]);
330
+                }
331
+            }
332
+        }
333
+
334
+        // Compare the list of files which are not identical
335
+        $currentInstanceHashes = $this->generateHashes($this->getFolderIterator($basePath), $basePath);
336
+        $differencesA = array_diff_assoc($expectedHashes, $currentInstanceHashes);
337
+        $differencesB = array_diff_assoc($currentInstanceHashes, $expectedHashes);
338
+        $differences = array_unique(array_merge($differencesA, $differencesB));
339
+        $differenceArray = [];
340
+        foreach ($differences as $filename => $hash) {
341
+            // Check if file should not exist in the new signature table
342
+            if (!array_key_exists($filename, $expectedHashes)) {
343
+                $differenceArray['EXTRA_FILE'][$filename]['expected'] = '';
344
+                $differenceArray['EXTRA_FILE'][$filename]['current'] = $hash;
345
+                continue;
346
+            }
347
+
348
+            // Check if file is missing
349
+            if (!array_key_exists($filename, $currentInstanceHashes)) {
350
+                $differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename];
351
+                $differenceArray['FILE_MISSING'][$filename]['current'] = '';
352
+                continue;
353
+            }
354
+
355
+            // Check if hash does mismatch
356
+            if ($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) {
357
+                $differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename];
358
+                $differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename];
359
+                continue;
360
+            }
361
+
362
+            // Should never happen.
363
+            throw new \Exception('Invalid behaviour in file hash comparison experienced. Please report this error to the developers.');
364
+        }
365
+
366
+        return $differenceArray;
367
+    }
368
+
369
+    /**
370
+     * Whether the code integrity check has passed successful or not
371
+     *
372
+     * @return bool
373
+     */
374
+    public function hasPassedCheck(): bool {
375
+        $results = $this->getResults();
376
+        if ($results !== null && empty($results)) {
377
+            return true;
378
+        }
379
+
380
+        return false;
381
+    }
382
+
383
+    /**
384
+     * @return array|null Either the results or null if no results available
385
+     */
386
+    public function getResults(): ?array {
387
+        $cachedResults = $this->cache->get(self::CACHE_KEY);
388
+        if (!\is_null($cachedResults) && $cachedResults !== false) {
389
+            return json_decode($cachedResults, true);
390
+        }
391
+
392
+        if ($this->appConfig?->hasKey('core', self::CACHE_KEY, lazy: true)) {
393
+            return $this->appConfig->getValueArray('core', self::CACHE_KEY, lazy: true);
394
+        }
395
+
396
+        // No results available
397
+        return null;
398
+    }
399
+
400
+    /**
401
+     * Stores the results in the app config as well as cache
402
+     *
403
+     * @param string $scope
404
+     * @param array $result
405
+     */
406
+    private function storeResults(string $scope, array $result) {
407
+        $resultArray = $this->getResults() ?? [];
408
+        unset($resultArray[$scope]);
409
+        if (!empty($result)) {
410
+            $resultArray[$scope] = $result;
411
+        }
412
+        $this->appConfig?->setValueArray('core', self::CACHE_KEY, $resultArray, lazy: true);
413
+        $this->cache->set(self::CACHE_KEY, json_encode($resultArray));
414
+    }
415
+
416
+    /**
417
+     *
418
+     * Clean previous results for a proper rescanning. Otherwise
419
+     */
420
+    private function cleanResults() {
421
+        $this->appConfig->deleteKey('core', self::CACHE_KEY);
422
+        $this->cache->remove(self::CACHE_KEY);
423
+    }
424
+
425
+    /**
426
+     * Verify the signature of $appId. Returns an array with the following content:
427
+     * [
428
+     * 	'FILE_MISSING' =>
429
+     * 	[
430
+     * 		'filename' => [
431
+     * 			'expected' => 'expectedSHA512',
432
+     * 			'current' => 'currentSHA512',
433
+     * 		],
434
+     * 	],
435
+     * 	'EXTRA_FILE' =>
436
+     * 	[
437
+     * 		'filename' => [
438
+     * 			'expected' => 'expectedSHA512',
439
+     * 			'current' => 'currentSHA512',
440
+     * 		],
441
+     * 	],
442
+     * 	'INVALID_HASH' =>
443
+     * 	[
444
+     * 		'filename' => [
445
+     * 			'expected' => 'expectedSHA512',
446
+     * 			'current' => 'currentSHA512',
447
+     * 		],
448
+     * 	],
449
+     * ]
450
+     *
451
+     * Array may be empty in case no problems have been found.
452
+     *
453
+     * @param string $appId
454
+     * @param string $path Optional path. If none is given it will be guessed.
455
+     * @param bool $forceVerify
456
+     * @return array
457
+     */
458
+    public function verifyAppSignature(string $appId, string $path = '', bool $forceVerify = false): array {
459
+        try {
460
+            if ($path === '') {
461
+                $path = $this->appManager->getAppPath($appId);
462
+            }
463
+            $result = $this->verify(
464
+                $path . '/appinfo/signature.json',
465
+                $path,
466
+                $appId,
467
+                $forceVerify
468
+            );
469
+        } catch (\Exception $e) {
470
+            $result = [
471
+                'EXCEPTION' => [
472
+                    'class' => \get_class($e),
473
+                    'message' => $e->getMessage(),
474
+                ],
475
+            ];
476
+        }
477
+        $this->storeResults($appId, $result);
478
+
479
+        return $result;
480
+    }
481
+
482
+    /**
483
+     * Verify the signature of core. Returns an array with the following content:
484
+     * [
485
+     * 	'FILE_MISSING' =>
486
+     * 	[
487
+     * 		'filename' => [
488
+     * 			'expected' => 'expectedSHA512',
489
+     * 			'current' => 'currentSHA512',
490
+     * 		],
491
+     * 	],
492
+     * 	'EXTRA_FILE' =>
493
+     * 	[
494
+     * 		'filename' => [
495
+     * 			'expected' => 'expectedSHA512',
496
+     * 			'current' => 'currentSHA512',
497
+     * 		],
498
+     * 	],
499
+     * 	'INVALID_HASH' =>
500
+     * 	[
501
+     * 		'filename' => [
502
+     * 			'expected' => 'expectedSHA512',
503
+     * 			'current' => 'currentSHA512',
504
+     * 		],
505
+     * 	],
506
+     * ]
507
+     *
508
+     * Array may be empty in case no problems have been found.
509
+     *
510
+     * @return array
511
+     */
512
+    public function verifyCoreSignature(): array {
513
+        try {
514
+            $result = $this->verify(
515
+                $this->environmentHelper->getServerRoot() . '/core/signature.json',
516
+                $this->environmentHelper->getServerRoot(),
517
+                'core'
518
+            );
519
+        } catch (\Exception $e) {
520
+            $result = [
521
+                'EXCEPTION' => [
522
+                    'class' => \get_class($e),
523
+                    'message' => $e->getMessage(),
524
+                ],
525
+            ];
526
+        }
527
+        $this->storeResults('core', $result);
528
+
529
+        return $result;
530
+    }
531
+
532
+    /**
533
+     * Verify the core code of the instance as well as all applicable applications
534
+     * and store the results.
535
+     */
536
+    public function runInstanceVerification() {
537
+        $this->cleanResults();
538
+        $this->verifyCoreSignature();
539
+        $appIds = $this->appManager->getAllAppsInAppsFolders();
540
+        foreach ($appIds as $appId) {
541
+            // If an application is shipped a valid signature is required
542
+            $isShipped = $this->appManager->isShipped($appId);
543
+            $appNeedsToBeChecked = false;
544
+            if ($isShipped) {
545
+                $appNeedsToBeChecked = true;
546
+            } elseif ($this->fileAccessHelper->file_exists($this->appManager->getAppPath($appId) . '/appinfo/signature.json')) {
547
+                // Otherwise only if the application explicitly ships a signature.json file
548
+                $appNeedsToBeChecked = true;
549
+            }
550
+
551
+            if ($appNeedsToBeChecked) {
552
+                $this->verifyAppSignature($appId);
553
+            }
554
+        }
555
+    }
556 556
 }
Please login to merge, or discard this patch.
lib/private/Hooks/EmitterTrait.php 2 patches
Indentation   +75 added lines, -75 removed lines patch added patch discarded remove patch
@@ -11,83 +11,83 @@
 block discarded – undo
11 11
  * @deprecated 18.0.0 use events and the \OCP\EventDispatcher\IEventDispatcher service
12 12
  */
13 13
 trait EmitterTrait {
14
-	/**
15
-	 * @var callable[][] $listeners
16
-	 */
17
-	protected $listeners = [];
14
+    /**
15
+     * @var callable[][] $listeners
16
+     */
17
+    protected $listeners = [];
18 18
 
19
-	/**
20
-	 * @param string $scope
21
-	 * @param string $method
22
-	 * @param callable $callback
23
-	 * @deprecated 18.0.0 use \OCP\EventDispatcher\IEventDispatcher::addListener
24
-	 */
25
-	public function listen($scope, $method, callable $callback) {
26
-		$eventName = $scope . '::' . $method;
27
-		if (!isset($this->listeners[$eventName])) {
28
-			$this->listeners[$eventName] = [];
29
-		}
30
-		if (!in_array($callback, $this->listeners[$eventName], true)) {
31
-			$this->listeners[$eventName][] = $callback;
32
-		}
33
-	}
19
+    /**
20
+     * @param string $scope
21
+     * @param string $method
22
+     * @param callable $callback
23
+     * @deprecated 18.0.0 use \OCP\EventDispatcher\IEventDispatcher::addListener
24
+     */
25
+    public function listen($scope, $method, callable $callback) {
26
+        $eventName = $scope . '::' . $method;
27
+        if (!isset($this->listeners[$eventName])) {
28
+            $this->listeners[$eventName] = [];
29
+        }
30
+        if (!in_array($callback, $this->listeners[$eventName], true)) {
31
+            $this->listeners[$eventName][] = $callback;
32
+        }
33
+    }
34 34
 
35
-	/**
36
-	 * @param string $scope optional
37
-	 * @param string $method optional
38
-	 * @param callable $callback optional
39
-	 * @deprecated 18.0.0 use \OCP\EventDispatcher\IEventDispatcher::removeListener
40
-	 */
41
-	public function removeListener($scope = null, $method = null, ?callable $callback = null) {
42
-		$names = [];
43
-		$allNames = array_keys($this->listeners);
44
-		if ($scope && $method) {
45
-			$name = $scope . '::' . $method;
46
-			if (isset($this->listeners[$name])) {
47
-				$names[] = $name;
48
-			}
49
-		} elseif ($scope) {
50
-			foreach ($allNames as $name) {
51
-				$parts = explode('::', $name, 2);
52
-				if ($parts[0] == $scope) {
53
-					$names[] = $name;
54
-				}
55
-			}
56
-		} elseif ($method) {
57
-			foreach ($allNames as $name) {
58
-				$parts = explode('::', $name, 2);
59
-				if ($parts[1] == $method) {
60
-					$names[] = $name;
61
-				}
62
-			}
63
-		} else {
64
-			$names = $allNames;
65
-		}
35
+    /**
36
+     * @param string $scope optional
37
+     * @param string $method optional
38
+     * @param callable $callback optional
39
+     * @deprecated 18.0.0 use \OCP\EventDispatcher\IEventDispatcher::removeListener
40
+     */
41
+    public function removeListener($scope = null, $method = null, ?callable $callback = null) {
42
+        $names = [];
43
+        $allNames = array_keys($this->listeners);
44
+        if ($scope && $method) {
45
+            $name = $scope . '::' . $method;
46
+            if (isset($this->listeners[$name])) {
47
+                $names[] = $name;
48
+            }
49
+        } elseif ($scope) {
50
+            foreach ($allNames as $name) {
51
+                $parts = explode('::', $name, 2);
52
+                if ($parts[0] == $scope) {
53
+                    $names[] = $name;
54
+                }
55
+            }
56
+        } elseif ($method) {
57
+            foreach ($allNames as $name) {
58
+                $parts = explode('::', $name, 2);
59
+                if ($parts[1] == $method) {
60
+                    $names[] = $name;
61
+                }
62
+            }
63
+        } else {
64
+            $names = $allNames;
65
+        }
66 66
 
67
-		foreach ($names as $name) {
68
-			if ($callback) {
69
-				$index = array_search($callback, $this->listeners[$name], true);
70
-				if ($index !== false) {
71
-					unset($this->listeners[$name][$index]);
72
-				}
73
-			} else {
74
-				$this->listeners[$name] = [];
75
-			}
76
-		}
77
-	}
67
+        foreach ($names as $name) {
68
+            if ($callback) {
69
+                $index = array_search($callback, $this->listeners[$name], true);
70
+                if ($index !== false) {
71
+                    unset($this->listeners[$name][$index]);
72
+                }
73
+            } else {
74
+                $this->listeners[$name] = [];
75
+            }
76
+        }
77
+    }
78 78
 
79
-	/**
80
-	 * @param string $scope
81
-	 * @param string $method
82
-	 * @param array $arguments optional
83
-	 * @deprecated 18.0.0 use \OCP\EventDispatcher\IEventDispatcher::dispatchTyped
84
-	 */
85
-	protected function emit($scope, $method, array $arguments = []) {
86
-		$eventName = $scope . '::' . $method;
87
-		if (isset($this->listeners[$eventName])) {
88
-			foreach ($this->listeners[$eventName] as $callback) {
89
-				call_user_func_array($callback, $arguments);
90
-			}
91
-		}
92
-	}
79
+    /**
80
+     * @param string $scope
81
+     * @param string $method
82
+     * @param array $arguments optional
83
+     * @deprecated 18.0.0 use \OCP\EventDispatcher\IEventDispatcher::dispatchTyped
84
+     */
85
+    protected function emit($scope, $method, array $arguments = []) {
86
+        $eventName = $scope . '::' . $method;
87
+        if (isset($this->listeners[$eventName])) {
88
+            foreach ($this->listeners[$eventName] as $callback) {
89
+                call_user_func_array($callback, $arguments);
90
+            }
91
+        }
92
+    }
93 93
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -23,7 +23,7 @@  discard block
 block discarded – undo
23 23
 	 * @deprecated 18.0.0 use \OCP\EventDispatcher\IEventDispatcher::addListener
24 24
 	 */
25 25
 	public function listen($scope, $method, callable $callback) {
26
-		$eventName = $scope . '::' . $method;
26
+		$eventName = $scope.'::'.$method;
27 27
 		if (!isset($this->listeners[$eventName])) {
28 28
 			$this->listeners[$eventName] = [];
29 29
 		}
@@ -42,7 +42,7 @@  discard block
 block discarded – undo
42 42
 		$names = [];
43 43
 		$allNames = array_keys($this->listeners);
44 44
 		if ($scope && $method) {
45
-			$name = $scope . '::' . $method;
45
+			$name = $scope.'::'.$method;
46 46
 			if (isset($this->listeners[$name])) {
47 47
 				$names[] = $name;
48 48
 			}
@@ -83,7 +83,7 @@  discard block
 block discarded – undo
83 83
 	 * @deprecated 18.0.0 use \OCP\EventDispatcher\IEventDispatcher::dispatchTyped
84 84
 	 */
85 85
 	protected function emit($scope, $method, array $arguments = []) {
86
-		$eventName = $scope . '::' . $method;
86
+		$eventName = $scope.'::'.$method;
87 87
 		if (isset($this->listeners[$eventName])) {
88 88
 			foreach ($this->listeners[$eventName] as $callback) {
89 89
 				call_user_func_array($callback, $arguments);
Please login to merge, or discard this patch.
lib/private/Memcache/APCu.php 2 patches
Indentation   +99 added lines, -99 removed lines patch added patch discarded remove patch
@@ -11,114 +11,114 @@
 block discarded – undo
11 11
 use OCP\IMemcache;
12 12
 
13 13
 class APCu extends Cache implements IMemcache {
14
-	use CASTrait {
15
-		cas as casEmulated;
16
-	}
14
+    use CASTrait {
15
+        cas as casEmulated;
16
+    }
17 17
 
18
-	use CADTrait;
18
+    use CADTrait;
19 19
 
20
-	public function get($key) {
21
-		$result = apcu_fetch($this->getPrefix() . $key, $success);
22
-		if (!$success) {
23
-			return null;
24
-		}
25
-		return $result;
26
-	}
20
+    public function get($key) {
21
+        $result = apcu_fetch($this->getPrefix() . $key, $success);
22
+        if (!$success) {
23
+            return null;
24
+        }
25
+        return $result;
26
+    }
27 27
 
28
-	public function set($key, $value, $ttl = 0) {
29
-		if ($ttl === 0) {
30
-			$ttl = self::DEFAULT_TTL;
31
-		}
32
-		return apcu_store($this->getPrefix() . $key, $value, $ttl);
33
-	}
28
+    public function set($key, $value, $ttl = 0) {
29
+        if ($ttl === 0) {
30
+            $ttl = self::DEFAULT_TTL;
31
+        }
32
+        return apcu_store($this->getPrefix() . $key, $value, $ttl);
33
+    }
34 34
 
35
-	public function hasKey($key) {
36
-		return apcu_exists($this->getPrefix() . $key);
37
-	}
35
+    public function hasKey($key) {
36
+        return apcu_exists($this->getPrefix() . $key);
37
+    }
38 38
 
39
-	public function remove($key) {
40
-		return apcu_delete($this->getPrefix() . $key);
41
-	}
39
+    public function remove($key) {
40
+        return apcu_delete($this->getPrefix() . $key);
41
+    }
42 42
 
43
-	public function clear($prefix = '') {
44
-		$ns = $this->getPrefix() . $prefix;
45
-		$ns = preg_quote($ns, '/');
46
-		if (class_exists('\APCIterator')) {
47
-			$iter = new \APCIterator('user', '/^' . $ns . '/', APC_ITER_KEY);
48
-		} else {
49
-			$iter = new \APCUIterator('/^' . $ns . '/', APC_ITER_KEY);
50
-		}
51
-		return apcu_delete($iter);
52
-	}
43
+    public function clear($prefix = '') {
44
+        $ns = $this->getPrefix() . $prefix;
45
+        $ns = preg_quote($ns, '/');
46
+        if (class_exists('\APCIterator')) {
47
+            $iter = new \APCIterator('user', '/^' . $ns . '/', APC_ITER_KEY);
48
+        } else {
49
+            $iter = new \APCUIterator('/^' . $ns . '/', APC_ITER_KEY);
50
+        }
51
+        return apcu_delete($iter);
52
+    }
53 53
 
54
-	/**
55
-	 * Set a value in the cache if it's not already stored
56
-	 *
57
-	 * @param string $key
58
-	 * @param mixed $value
59
-	 * @param int $ttl Time To Live in seconds. Defaults to 60*60*24
60
-	 * @return bool
61
-	 */
62
-	public function add($key, $value, $ttl = 0) {
63
-		if ($ttl === 0) {
64
-			$ttl = self::DEFAULT_TTL;
65
-		}
66
-		return apcu_add($this->getPrefix() . $key, $value, $ttl);
67
-	}
54
+    /**
55
+     * Set a value in the cache if it's not already stored
56
+     *
57
+     * @param string $key
58
+     * @param mixed $value
59
+     * @param int $ttl Time To Live in seconds. Defaults to 60*60*24
60
+     * @return bool
61
+     */
62
+    public function add($key, $value, $ttl = 0) {
63
+        if ($ttl === 0) {
64
+            $ttl = self::DEFAULT_TTL;
65
+        }
66
+        return apcu_add($this->getPrefix() . $key, $value, $ttl);
67
+    }
68 68
 
69
-	/**
70
-	 * Increase a stored number
71
-	 *
72
-	 * @param string $key
73
-	 * @param int $step
74
-	 * @return int | bool
75
-	 */
76
-	public function inc($key, $step = 1) {
77
-		$success = null;
78
-		return apcu_inc($this->getPrefix() . $key, $step, $success, self::DEFAULT_TTL);
79
-	}
69
+    /**
70
+     * Increase a stored number
71
+     *
72
+     * @param string $key
73
+     * @param int $step
74
+     * @return int | bool
75
+     */
76
+    public function inc($key, $step = 1) {
77
+        $success = null;
78
+        return apcu_inc($this->getPrefix() . $key, $step, $success, self::DEFAULT_TTL);
79
+    }
80 80
 
81
-	/**
82
-	 * Decrease a stored number
83
-	 *
84
-	 * @param string $key
85
-	 * @param int $step
86
-	 * @return int | bool
87
-	 */
88
-	public function dec($key, $step = 1) {
89
-		return apcu_exists($this->getPrefix() . $key)
90
-			? apcu_dec($this->getPrefix() . $key, $step)
91
-			: false;
92
-	}
81
+    /**
82
+     * Decrease a stored number
83
+     *
84
+     * @param string $key
85
+     * @param int $step
86
+     * @return int | bool
87
+     */
88
+    public function dec($key, $step = 1) {
89
+        return apcu_exists($this->getPrefix() . $key)
90
+            ? apcu_dec($this->getPrefix() . $key, $step)
91
+            : false;
92
+    }
93 93
 
94
-	/**
95
-	 * Compare and set
96
-	 *
97
-	 * @param string $key
98
-	 * @param mixed $old
99
-	 * @param mixed $new
100
-	 * @return bool
101
-	 */
102
-	public function cas($key, $old, $new) {
103
-		// apc only does cas for ints
104
-		if (is_int($old) && is_int($new)) {
105
-			return apcu_cas($this->getPrefix() . $key, $old, $new);
106
-		} else {
107
-			return $this->casEmulated($key, $old, $new);
108
-		}
109
-	}
94
+    /**
95
+     * Compare and set
96
+     *
97
+     * @param string $key
98
+     * @param mixed $old
99
+     * @param mixed $new
100
+     * @return bool
101
+     */
102
+    public function cas($key, $old, $new) {
103
+        // apc only does cas for ints
104
+        if (is_int($old) && is_int($new)) {
105
+            return apcu_cas($this->getPrefix() . $key, $old, $new);
106
+        } else {
107
+            return $this->casEmulated($key, $old, $new);
108
+        }
109
+    }
110 110
 
111
-	public static function isAvailable(): bool {
112
-		if (!extension_loaded('apcu')) {
113
-			return false;
114
-		} elseif (!\OC::$server->get(IniGetWrapper::class)->getBool('apc.enabled')) {
115
-			return false;
116
-		} elseif (!\OC::$server->get(IniGetWrapper::class)->getBool('apc.enable_cli') && \OC::$CLI) {
117
-			return false;
118
-		} elseif (version_compare(phpversion('apcu') ?: '0.0.0', '5.1.0') === -1) {
119
-			return false;
120
-		} else {
121
-			return true;
122
-		}
123
-	}
111
+    public static function isAvailable(): bool {
112
+        if (!extension_loaded('apcu')) {
113
+            return false;
114
+        } elseif (!\OC::$server->get(IniGetWrapper::class)->getBool('apc.enabled')) {
115
+            return false;
116
+        } elseif (!\OC::$server->get(IniGetWrapper::class)->getBool('apc.enable_cli') && \OC::$CLI) {
117
+            return false;
118
+        } elseif (version_compare(phpversion('apcu') ?: '0.0.0', '5.1.0') === -1) {
119
+            return false;
120
+        } else {
121
+            return true;
122
+        }
123
+    }
124 124
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -18,7 +18,7 @@  discard block
 block discarded – undo
18 18
 	use CADTrait;
19 19
 
20 20
 	public function get($key) {
21
-		$result = apcu_fetch($this->getPrefix() . $key, $success);
21
+		$result = apcu_fetch($this->getPrefix().$key, $success);
22 22
 		if (!$success) {
23 23
 			return null;
24 24
 		}
@@ -29,24 +29,24 @@  discard block
 block discarded – undo
29 29
 		if ($ttl === 0) {
30 30
 			$ttl = self::DEFAULT_TTL;
31 31
 		}
32
-		return apcu_store($this->getPrefix() . $key, $value, $ttl);
32
+		return apcu_store($this->getPrefix().$key, $value, $ttl);
33 33
 	}
34 34
 
35 35
 	public function hasKey($key) {
36
-		return apcu_exists($this->getPrefix() . $key);
36
+		return apcu_exists($this->getPrefix().$key);
37 37
 	}
38 38
 
39 39
 	public function remove($key) {
40
-		return apcu_delete($this->getPrefix() . $key);
40
+		return apcu_delete($this->getPrefix().$key);
41 41
 	}
42 42
 
43 43
 	public function clear($prefix = '') {
44
-		$ns = $this->getPrefix() . $prefix;
44
+		$ns = $this->getPrefix().$prefix;
45 45
 		$ns = preg_quote($ns, '/');
46 46
 		if (class_exists('\APCIterator')) {
47
-			$iter = new \APCIterator('user', '/^' . $ns . '/', APC_ITER_KEY);
47
+			$iter = new \APCIterator('user', '/^'.$ns.'/', APC_ITER_KEY);
48 48
 		} else {
49
-			$iter = new \APCUIterator('/^' . $ns . '/', APC_ITER_KEY);
49
+			$iter = new \APCUIterator('/^'.$ns.'/', APC_ITER_KEY);
50 50
 		}
51 51
 		return apcu_delete($iter);
52 52
 	}
@@ -63,7 +63,7 @@  discard block
 block discarded – undo
63 63
 		if ($ttl === 0) {
64 64
 			$ttl = self::DEFAULT_TTL;
65 65
 		}
66
-		return apcu_add($this->getPrefix() . $key, $value, $ttl);
66
+		return apcu_add($this->getPrefix().$key, $value, $ttl);
67 67
 	}
68 68
 
69 69
 	/**
@@ -75,7 +75,7 @@  discard block
 block discarded – undo
75 75
 	 */
76 76
 	public function inc($key, $step = 1) {
77 77
 		$success = null;
78
-		return apcu_inc($this->getPrefix() . $key, $step, $success, self::DEFAULT_TTL);
78
+		return apcu_inc($this->getPrefix().$key, $step, $success, self::DEFAULT_TTL);
79 79
 	}
80 80
 
81 81
 	/**
@@ -86,8 +86,8 @@  discard block
 block discarded – undo
86 86
 	 * @return int | bool
87 87
 	 */
88 88
 	public function dec($key, $step = 1) {
89
-		return apcu_exists($this->getPrefix() . $key)
90
-			? apcu_dec($this->getPrefix() . $key, $step)
89
+		return apcu_exists($this->getPrefix().$key)
90
+			? apcu_dec($this->getPrefix().$key, $step)
91 91
 			: false;
92 92
 	}
93 93
 
@@ -102,7 +102,7 @@  discard block
 block discarded – undo
102 102
 	public function cas($key, $old, $new) {
103 103
 		// apc only does cas for ints
104 104
 		if (is_int($old) && is_int($new)) {
105
-			return apcu_cas($this->getPrefix() . $key, $old, $new);
105
+			return apcu_cas($this->getPrefix().$key, $old, $new);
106 106
 		} else {
107 107
 			return $this->casEmulated($key, $old, $new);
108 108
 		}
Please login to merge, or discard this patch.
lib/private/Memcache/Memcached.php 2 patches
Indentation   +158 added lines, -158 removed lines patch added patch discarded remove patch
@@ -11,162 +11,162 @@
 block discarded – undo
11 11
 use OCP\IMemcache;
12 12
 
13 13
 class Memcached extends Cache implements IMemcache {
14
-	use CASTrait;
15
-
16
-	/**
17
-	 * @var \Memcached $cache
18
-	 */
19
-	private static $cache = null;
20
-
21
-	use CADTrait;
22
-
23
-	public function __construct($prefix = '') {
24
-		parent::__construct($prefix);
25
-		if (is_null(self::$cache)) {
26
-			self::$cache = new \Memcached();
27
-
28
-			$defaultOptions = [
29
-				\Memcached::OPT_CONNECT_TIMEOUT => 50,
30
-				\Memcached::OPT_RETRY_TIMEOUT => 50,
31
-				\Memcached::OPT_SEND_TIMEOUT => 50,
32
-				\Memcached::OPT_RECV_TIMEOUT => 50,
33
-				\Memcached::OPT_POLL_TIMEOUT => 50,
34
-
35
-				// Enable compression
36
-				\Memcached::OPT_COMPRESSION => true,
37
-
38
-				// Turn on consistent hashing
39
-				\Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
40
-
41
-				// Enable Binary Protocol
42
-				\Memcached::OPT_BINARY_PROTOCOL => true,
43
-			];
44
-			/**
45
-			 * By default enable igbinary serializer if available
46
-			 *
47
-			 * Psalm checks depend on if igbinary is installed or not with memcached
48
-			 * @psalm-suppress RedundantCondition
49
-			 * @psalm-suppress TypeDoesNotContainType
50
-			 */
51
-			if (\Memcached::HAVE_IGBINARY) {
52
-				$defaultOptions[\Memcached::OPT_SERIALIZER]
53
-					= \Memcached::SERIALIZER_IGBINARY;
54
-			}
55
-			$options = \OC::$server->getConfig()->getSystemValue('memcached_options', []);
56
-			if (is_array($options)) {
57
-				$options = $options + $defaultOptions;
58
-				self::$cache->setOptions($options);
59
-			} else {
60
-				throw new HintException("Expected 'memcached_options' config to be an array, got $options");
61
-			}
62
-
63
-			$servers = \OC::$server->getSystemConfig()->getValue('memcached_servers');
64
-			if (!$servers) {
65
-				$server = \OC::$server->getSystemConfig()->getValue('memcached_server');
66
-				if ($server) {
67
-					$servers = [$server];
68
-				} else {
69
-					$servers = [['localhost', 11211]];
70
-				}
71
-			}
72
-			self::$cache->addServers($servers);
73
-		}
74
-	}
75
-
76
-	/**
77
-	 * entries in XCache gets namespaced to prevent collisions between owncloud instances and users
78
-	 */
79
-	protected function getNameSpace() {
80
-		return $this->prefix;
81
-	}
82
-
83
-	public function get($key) {
84
-		$result = self::$cache->get($this->getNameSpace() . $key);
85
-		if ($result === false && self::$cache->getResultCode() === \Memcached::RES_NOTFOUND) {
86
-			return null;
87
-		} else {
88
-			return $result;
89
-		}
90
-	}
91
-
92
-	public function set($key, $value, $ttl = 0) {
93
-		if ($ttl > 0) {
94
-			$result = self::$cache->set($this->getNameSpace() . $key, $value, $ttl);
95
-		} else {
96
-			$result = self::$cache->set($this->getNameSpace() . $key, $value);
97
-		}
98
-		return $result || $this->isSuccess();
99
-	}
100
-
101
-	public function hasKey($key) {
102
-		self::$cache->get($this->getNameSpace() . $key);
103
-		return self::$cache->getResultCode() === \Memcached::RES_SUCCESS;
104
-	}
105
-
106
-	public function remove($key) {
107
-		$result = self::$cache->delete($this->getNameSpace() . $key);
108
-		return $result || $this->isSuccess() || self::$cache->getResultCode() === \Memcached::RES_NOTFOUND;
109
-	}
110
-
111
-	public function clear($prefix = '') {
112
-		// Newer Memcached doesn't like getAllKeys(), flush everything
113
-		self::$cache->flush();
114
-		return true;
115
-	}
116
-
117
-	/**
118
-	 * Set a value in the cache if it's not already stored
119
-	 *
120
-	 * @param string $key
121
-	 * @param mixed $value
122
-	 * @param int $ttl Time To Live in seconds. Defaults to 60*60*24
123
-	 * @return bool
124
-	 */
125
-	public function add($key, $value, $ttl = 0) {
126
-		$result = self::$cache->add($this->getPrefix() . $key, $value, $ttl);
127
-		return $result || $this->isSuccess();
128
-	}
129
-
130
-	/**
131
-	 * Increase a stored number
132
-	 *
133
-	 * @param string $key
134
-	 * @param int $step
135
-	 * @return int | bool
136
-	 */
137
-	public function inc($key, $step = 1) {
138
-		$this->add($key, 0);
139
-		$result = self::$cache->increment($this->getPrefix() . $key, $step);
140
-
141
-		if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) {
142
-			return false;
143
-		}
144
-
145
-		return $result;
146
-	}
147
-
148
-	/**
149
-	 * Decrease a stored number
150
-	 *
151
-	 * @param string $key
152
-	 * @param int $step
153
-	 * @return int | bool
154
-	 */
155
-	public function dec($key, $step = 1) {
156
-		$result = self::$cache->decrement($this->getPrefix() . $key, $step);
157
-
158
-		if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) {
159
-			return false;
160
-		}
161
-
162
-		return $result;
163
-	}
164
-
165
-	public static function isAvailable(): bool {
166
-		return extension_loaded('memcached');
167
-	}
168
-
169
-	private function isSuccess(): bool {
170
-		return self::$cache->getResultCode() === \Memcached::RES_SUCCESS;
171
-	}
14
+    use CASTrait;
15
+
16
+    /**
17
+     * @var \Memcached $cache
18
+     */
19
+    private static $cache = null;
20
+
21
+    use CADTrait;
22
+
23
+    public function __construct($prefix = '') {
24
+        parent::__construct($prefix);
25
+        if (is_null(self::$cache)) {
26
+            self::$cache = new \Memcached();
27
+
28
+            $defaultOptions = [
29
+                \Memcached::OPT_CONNECT_TIMEOUT => 50,
30
+                \Memcached::OPT_RETRY_TIMEOUT => 50,
31
+                \Memcached::OPT_SEND_TIMEOUT => 50,
32
+                \Memcached::OPT_RECV_TIMEOUT => 50,
33
+                \Memcached::OPT_POLL_TIMEOUT => 50,
34
+
35
+                // Enable compression
36
+                \Memcached::OPT_COMPRESSION => true,
37
+
38
+                // Turn on consistent hashing
39
+                \Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
40
+
41
+                // Enable Binary Protocol
42
+                \Memcached::OPT_BINARY_PROTOCOL => true,
43
+            ];
44
+            /**
45
+             * By default enable igbinary serializer if available
46
+             *
47
+             * Psalm checks depend on if igbinary is installed or not with memcached
48
+             * @psalm-suppress RedundantCondition
49
+             * @psalm-suppress TypeDoesNotContainType
50
+             */
51
+            if (\Memcached::HAVE_IGBINARY) {
52
+                $defaultOptions[\Memcached::OPT_SERIALIZER]
53
+                    = \Memcached::SERIALIZER_IGBINARY;
54
+            }
55
+            $options = \OC::$server->getConfig()->getSystemValue('memcached_options', []);
56
+            if (is_array($options)) {
57
+                $options = $options + $defaultOptions;
58
+                self::$cache->setOptions($options);
59
+            } else {
60
+                throw new HintException("Expected 'memcached_options' config to be an array, got $options");
61
+            }
62
+
63
+            $servers = \OC::$server->getSystemConfig()->getValue('memcached_servers');
64
+            if (!$servers) {
65
+                $server = \OC::$server->getSystemConfig()->getValue('memcached_server');
66
+                if ($server) {
67
+                    $servers = [$server];
68
+                } else {
69
+                    $servers = [['localhost', 11211]];
70
+                }
71
+            }
72
+            self::$cache->addServers($servers);
73
+        }
74
+    }
75
+
76
+    /**
77
+     * entries in XCache gets namespaced to prevent collisions between owncloud instances and users
78
+     */
79
+    protected function getNameSpace() {
80
+        return $this->prefix;
81
+    }
82
+
83
+    public function get($key) {
84
+        $result = self::$cache->get($this->getNameSpace() . $key);
85
+        if ($result === false && self::$cache->getResultCode() === \Memcached::RES_NOTFOUND) {
86
+            return null;
87
+        } else {
88
+            return $result;
89
+        }
90
+    }
91
+
92
+    public function set($key, $value, $ttl = 0) {
93
+        if ($ttl > 0) {
94
+            $result = self::$cache->set($this->getNameSpace() . $key, $value, $ttl);
95
+        } else {
96
+            $result = self::$cache->set($this->getNameSpace() . $key, $value);
97
+        }
98
+        return $result || $this->isSuccess();
99
+    }
100
+
101
+    public function hasKey($key) {
102
+        self::$cache->get($this->getNameSpace() . $key);
103
+        return self::$cache->getResultCode() === \Memcached::RES_SUCCESS;
104
+    }
105
+
106
+    public function remove($key) {
107
+        $result = self::$cache->delete($this->getNameSpace() . $key);
108
+        return $result || $this->isSuccess() || self::$cache->getResultCode() === \Memcached::RES_NOTFOUND;
109
+    }
110
+
111
+    public function clear($prefix = '') {
112
+        // Newer Memcached doesn't like getAllKeys(), flush everything
113
+        self::$cache->flush();
114
+        return true;
115
+    }
116
+
117
+    /**
118
+     * Set a value in the cache if it's not already stored
119
+     *
120
+     * @param string $key
121
+     * @param mixed $value
122
+     * @param int $ttl Time To Live in seconds. Defaults to 60*60*24
123
+     * @return bool
124
+     */
125
+    public function add($key, $value, $ttl = 0) {
126
+        $result = self::$cache->add($this->getPrefix() . $key, $value, $ttl);
127
+        return $result || $this->isSuccess();
128
+    }
129
+
130
+    /**
131
+     * Increase a stored number
132
+     *
133
+     * @param string $key
134
+     * @param int $step
135
+     * @return int | bool
136
+     */
137
+    public function inc($key, $step = 1) {
138
+        $this->add($key, 0);
139
+        $result = self::$cache->increment($this->getPrefix() . $key, $step);
140
+
141
+        if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) {
142
+            return false;
143
+        }
144
+
145
+        return $result;
146
+    }
147
+
148
+    /**
149
+     * Decrease a stored number
150
+     *
151
+     * @param string $key
152
+     * @param int $step
153
+     * @return int | bool
154
+     */
155
+    public function dec($key, $step = 1) {
156
+        $result = self::$cache->decrement($this->getPrefix() . $key, $step);
157
+
158
+        if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) {
159
+            return false;
160
+        }
161
+
162
+        return $result;
163
+    }
164
+
165
+    public static function isAvailable(): bool {
166
+        return extension_loaded('memcached');
167
+    }
168
+
169
+    private function isSuccess(): bool {
170
+        return self::$cache->getResultCode() === \Memcached::RES_SUCCESS;
171
+    }
172 172
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -81,7 +81,7 @@  discard block
 block discarded – undo
81 81
 	}
82 82
 
83 83
 	public function get($key) {
84
-		$result = self::$cache->get($this->getNameSpace() . $key);
84
+		$result = self::$cache->get($this->getNameSpace().$key);
85 85
 		if ($result === false && self::$cache->getResultCode() === \Memcached::RES_NOTFOUND) {
86 86
 			return null;
87 87
 		} else {
@@ -91,20 +91,20 @@  discard block
 block discarded – undo
91 91
 
92 92
 	public function set($key, $value, $ttl = 0) {
93 93
 		if ($ttl > 0) {
94
-			$result = self::$cache->set($this->getNameSpace() . $key, $value, $ttl);
94
+			$result = self::$cache->set($this->getNameSpace().$key, $value, $ttl);
95 95
 		} else {
96
-			$result = self::$cache->set($this->getNameSpace() . $key, $value);
96
+			$result = self::$cache->set($this->getNameSpace().$key, $value);
97 97
 		}
98 98
 		return $result || $this->isSuccess();
99 99
 	}
100 100
 
101 101
 	public function hasKey($key) {
102
-		self::$cache->get($this->getNameSpace() . $key);
102
+		self::$cache->get($this->getNameSpace().$key);
103 103
 		return self::$cache->getResultCode() === \Memcached::RES_SUCCESS;
104 104
 	}
105 105
 
106 106
 	public function remove($key) {
107
-		$result = self::$cache->delete($this->getNameSpace() . $key);
107
+		$result = self::$cache->delete($this->getNameSpace().$key);
108 108
 		return $result || $this->isSuccess() || self::$cache->getResultCode() === \Memcached::RES_NOTFOUND;
109 109
 	}
110 110
 
@@ -123,7 +123,7 @@  discard block
 block discarded – undo
123 123
 	 * @return bool
124 124
 	 */
125 125
 	public function add($key, $value, $ttl = 0) {
126
-		$result = self::$cache->add($this->getPrefix() . $key, $value, $ttl);
126
+		$result = self::$cache->add($this->getPrefix().$key, $value, $ttl);
127 127
 		return $result || $this->isSuccess();
128 128
 	}
129 129
 
@@ -136,7 +136,7 @@  discard block
 block discarded – undo
136 136
 	 */
137 137
 	public function inc($key, $step = 1) {
138 138
 		$this->add($key, 0);
139
-		$result = self::$cache->increment($this->getPrefix() . $key, $step);
139
+		$result = self::$cache->increment($this->getPrefix().$key, $step);
140 140
 
141 141
 		if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) {
142 142
 			return false;
@@ -153,7 +153,7 @@  discard block
 block discarded – undo
153 153
 	 * @return int | bool
154 154
 	 */
155 155
 	public function dec($key, $step = 1) {
156
-		$result = self::$cache->decrement($this->getPrefix() . $key, $step);
156
+		$result = self::$cache->decrement($this->getPrefix().$key, $step);
157 157
 
158 158
 		if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) {
159 159
 			return false;
Please login to merge, or discard this patch.
lib/private/legacy/OC_Helper.php 1 patch
Indentation   +385 added lines, -385 removed lines patch added patch discarded remove patch
@@ -32,392 +32,392 @@
 block discarded – undo
32 32
  * }
33 33
  */
34 34
 class OC_Helper {
35
-	private static $templateManager;
36
-	private static ?ICacheFactory $cacheFactory = null;
37
-	private static ?bool $quotaIncludeExternalStorage = null;
38
-
39
-	/**
40
-	 * Recursive copying of folders
41
-	 * @param string $src source folder
42
-	 * @param string $dest target folder
43
-	 * @return void
44
-	 * @deprecated 32.0.0 - use \OCP\Files\Folder::copy
45
-	 */
46
-	public static function copyr($src, $dest) {
47
-		if (!file_exists($src)) {
48
-			return;
49
-		}
50
-
51
-		if (is_dir($src)) {
52
-			if (!is_dir($dest)) {
53
-				mkdir($dest);
54
-			}
55
-			$files = scandir($src);
56
-			foreach ($files as $file) {
57
-				if ($file != '.' && $file != '..') {
58
-					self::copyr("$src/$file", "$dest/$file");
59
-				}
60
-			}
61
-		} else {
62
-			$validator = \OCP\Server::get(FilenameValidator::class);
63
-			if (!$validator->isForbidden($src)) {
64
-				copy($src, $dest);
65
-			}
66
-		}
67
-	}
68
-
69
-	/**
70
-	 * @deprecated 18.0.0
71
-	 * @return \OC\Files\Type\TemplateManager
72
-	 */
73
-	public static function getFileTemplateManager() {
74
-		if (!self::$templateManager) {
75
-			self::$templateManager = new \OC\Files\Type\TemplateManager();
76
-		}
77
-		return self::$templateManager;
78
-	}
79
-
80
-	/**
81
-	 * detect if a given program is found in the search PATH
82
-	 *
83
-	 * @param string $name
84
-	 * @param bool $path
85
-	 * @internal param string $program name
86
-	 * @internal param string $optional search path, defaults to $PATH
87
-	 * @return bool true if executable program found in path
88
-	 * @deprecated 32.0.0 use the \OCP\IBinaryFinder
89
-	 */
90
-	public static function canExecute($name, $path = false) {
91
-		// path defaults to PATH from environment if not set
92
-		if ($path === false) {
93
-			$path = getenv('PATH');
94
-		}
95
-		// we look for an executable file of that name
96
-		$exts = [''];
97
-		$check_fn = 'is_executable';
98
-		// Default check will be done with $path directories :
99
-		$dirs = explode(PATH_SEPARATOR, (string)$path);
100
-		// WARNING : We have to check if open_basedir is enabled :
101
-		$obd = OC::$server->get(IniGetWrapper::class)->getString('open_basedir');
102
-		if ($obd != 'none') {
103
-			$obd_values = explode(PATH_SEPARATOR, $obd);
104
-			if (count($obd_values) > 0 && $obd_values[0]) {
105
-				// open_basedir is in effect !
106
-				// We need to check if the program is in one of these dirs :
107
-				$dirs = $obd_values;
108
-			}
109
-		}
110
-		foreach ($dirs as $dir) {
111
-			foreach ($exts as $ext) {
112
-				if ($check_fn("$dir/$name" . $ext)) {
113
-					return true;
114
-				}
115
-			}
116
-		}
117
-		return false;
118
-	}
119
-
120
-	/**
121
-	 * copy the contents of one stream to another
122
-	 *
123
-	 * @param resource $source
124
-	 * @param resource $target
125
-	 * @return array the number of bytes copied and result
126
-	 * @deprecated 5.0.0 - Use \OCP\Files::streamCopy
127
-	 */
128
-	public static function streamCopy($source, $target) {
129
-		return \OCP\Files::streamCopy($source, $target, true);
130
-	}
131
-
132
-	/**
133
-	 * Adds a suffix to the name in case the file exists
134
-	 *
135
-	 * @param string $path
136
-	 * @param string $filename
137
-	 * @return string
138
-	 */
139
-	public static function buildNotExistingFileName($path, $filename) {
140
-		$view = \OC\Files\Filesystem::getView();
141
-		return self::buildNotExistingFileNameForView($path, $filename, $view);
142
-	}
143
-
144
-	/**
145
-	 * Adds a suffix to the name in case the file exists
146
-	 *
147
-	 * @param string $path
148
-	 * @param string $filename
149
-	 * @return string
150
-	 */
151
-	public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
152
-		if ($path === '/') {
153
-			$path = '';
154
-		}
155
-		if ($pos = strrpos($filename, '.')) {
156
-			$name = substr($filename, 0, $pos);
157
-			$ext = substr($filename, $pos);
158
-		} else {
159
-			$name = $filename;
160
-			$ext = '';
161
-		}
162
-
163
-		$newpath = $path . '/' . $filename;
164
-		if ($view->file_exists($newpath)) {
165
-			if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
166
-				//Replace the last "(number)" with "(number+1)"
167
-				$last_match = count($matches[0]) - 1;
168
-				$counter = $matches[1][$last_match][0] + 1;
169
-				$offset = $matches[0][$last_match][1];
170
-				$match_length = strlen($matches[0][$last_match][0]);
171
-			} else {
172
-				$counter = 2;
173
-				$match_length = 0;
174
-				$offset = false;
175
-			}
176
-			do {
177
-				if ($offset) {
178
-					//Replace the last "(number)" with "(number+1)"
179
-					$newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
180
-				} else {
181
-					$newname = $name . ' (' . $counter . ')';
182
-				}
183
-				$newpath = $path . '/' . $newname . $ext;
184
-				$counter++;
185
-			} while ($view->file_exists($newpath));
186
-		}
187
-
188
-		return $newpath;
189
-	}
190
-
191
-	/**
192
-	 * Checks if a function is available
193
-	 *
194
-	 * @deprecated 25.0.0 use \OCP\Util::isFunctionEnabled instead
195
-	 */
196
-	public static function is_function_enabled(string $function_name): bool {
197
-		return Util::isFunctionEnabled($function_name);
198
-	}
199
-
200
-	/**
201
-	 * Try to find a program
202
-	 * @deprecated 25.0.0 Use \OCP\IBinaryFinder directly
203
-	 */
204
-	public static function findBinaryPath(string $program): ?string {
205
-		$result = Server::get(IBinaryFinder::class)->findBinaryPath($program);
206
-		return $result !== false ? $result : null;
207
-	}
208
-
209
-	/**
210
-	 * Calculate the disc space for the given path
211
-	 *
212
-	 * BEWARE: this requires that Util::setupFS() was called
213
-	 * already !
214
-	 *
215
-	 * @param string $path
216
-	 * @param \OCP\Files\FileInfo $rootInfo (optional)
217
-	 * @param bool $includeMountPoints whether to include mount points in the size calculation
218
-	 * @param bool $useCache whether to use the cached quota values
219
-	 * @psalm-suppress LessSpecificReturnStatement Legacy code outputs weird types - manually validated that they are correct
220
-	 * @return StorageInfo
221
-	 * @throws \OCP\Files\NotFoundException
222
-	 */
223
-	public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true, $useCache = true) {
224
-		if (!self::$cacheFactory) {
225
-			self::$cacheFactory = Server::get(ICacheFactory::class);
226
-		}
227
-		$memcache = self::$cacheFactory->createLocal('storage_info');
228
-
229
-		// return storage info without adding mount points
230
-		if (self::$quotaIncludeExternalStorage === null) {
231
-			self::$quotaIncludeExternalStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
232
-		}
233
-
234
-		$view = Filesystem::getView();
235
-		if (!$view) {
236
-			throw new \OCP\Files\NotFoundException();
237
-		}
238
-		$fullPath = Filesystem::normalizePath($view->getAbsolutePath($path));
239
-
240
-		$cacheKey = $fullPath . '::' . ($includeMountPoints ? 'include' : 'exclude');
241
-		if ($useCache) {
242
-			$cached = $memcache->get($cacheKey);
243
-			if ($cached) {
244
-				return $cached;
245
-			}
246
-		}
247
-
248
-		if (!$rootInfo) {
249
-			$rootInfo = \OC\Files\Filesystem::getFileInfo($path, self::$quotaIncludeExternalStorage ? 'ext' : false);
250
-		}
251
-		if (!$rootInfo instanceof \OCP\Files\FileInfo) {
252
-			throw new \OCP\Files\NotFoundException('The root directory of the user\'s files is missing');
253
-		}
254
-		$used = $rootInfo->getSize($includeMountPoints);
255
-		if ($used < 0) {
256
-			$used = 0.0;
257
-		}
258
-		/** @var int|float $quota */
259
-		$quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
260
-		$mount = $rootInfo->getMountPoint();
261
-		$storage = $mount->getStorage();
262
-		$sourceStorage = $storage;
263
-		if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
264
-			self::$quotaIncludeExternalStorage = false;
265
-		}
266
-		if (self::$quotaIncludeExternalStorage) {
267
-			if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
268
-				|| $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
269
-			) {
270
-				/** @var \OC\Files\Storage\Home $storage */
271
-				$user = $storage->getUser();
272
-			} else {
273
-				$user = \OC::$server->getUserSession()->getUser();
274
-			}
275
-			$quota = $user?->getQuotaBytes() ?? \OCP\Files\FileInfo::SPACE_UNKNOWN;
276
-			if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
277
-				// always get free space / total space from root + mount points
278
-				return self::getGlobalStorageInfo($quota, $user, $mount);
279
-			}
280
-		}
281
-
282
-		// TODO: need a better way to get total space from storage
283
-		if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
284
-			/** @var \OC\Files\Storage\Wrapper\Quota $storage */
285
-			$quota = $sourceStorage->getQuota();
286
-		}
287
-		try {
288
-			$free = $sourceStorage->free_space($rootInfo->getInternalPath());
289
-			if (is_bool($free)) {
290
-				$free = 0.0;
291
-			}
292
-		} catch (\Exception $e) {
293
-			if ($path === '') {
294
-				throw $e;
295
-			}
296
-			/** @var LoggerInterface $logger */
297
-			$logger = \OC::$server->get(LoggerInterface::class);
298
-			$logger->warning('Error while getting quota info, using root quota', ['exception' => $e]);
299
-			$rootInfo = self::getStorageInfo('');
300
-			$memcache->set($cacheKey, $rootInfo, 5 * 60);
301
-			return $rootInfo;
302
-		}
303
-		if ($free >= 0) {
304
-			$total = $free + $used;
305
-		} else {
306
-			$total = $free; //either unknown or unlimited
307
-		}
308
-		if ($total > 0) {
309
-			if ($quota > 0 && $total > $quota) {
310
-				$total = $quota;
311
-			}
312
-			// prevent division by zero or error codes (negative values)
313
-			$relative = round(($used / $total) * 10000) / 100;
314
-		} else {
315
-			$relative = 0;
316
-		}
317
-
318
-		/*
35
+    private static $templateManager;
36
+    private static ?ICacheFactory $cacheFactory = null;
37
+    private static ?bool $quotaIncludeExternalStorage = null;
38
+
39
+    /**
40
+     * Recursive copying of folders
41
+     * @param string $src source folder
42
+     * @param string $dest target folder
43
+     * @return void
44
+     * @deprecated 32.0.0 - use \OCP\Files\Folder::copy
45
+     */
46
+    public static function copyr($src, $dest) {
47
+        if (!file_exists($src)) {
48
+            return;
49
+        }
50
+
51
+        if (is_dir($src)) {
52
+            if (!is_dir($dest)) {
53
+                mkdir($dest);
54
+            }
55
+            $files = scandir($src);
56
+            foreach ($files as $file) {
57
+                if ($file != '.' && $file != '..') {
58
+                    self::copyr("$src/$file", "$dest/$file");
59
+                }
60
+            }
61
+        } else {
62
+            $validator = \OCP\Server::get(FilenameValidator::class);
63
+            if (!$validator->isForbidden($src)) {
64
+                copy($src, $dest);
65
+            }
66
+        }
67
+    }
68
+
69
+    /**
70
+     * @deprecated 18.0.0
71
+     * @return \OC\Files\Type\TemplateManager
72
+     */
73
+    public static function getFileTemplateManager() {
74
+        if (!self::$templateManager) {
75
+            self::$templateManager = new \OC\Files\Type\TemplateManager();
76
+        }
77
+        return self::$templateManager;
78
+    }
79
+
80
+    /**
81
+     * detect if a given program is found in the search PATH
82
+     *
83
+     * @param string $name
84
+     * @param bool $path
85
+     * @internal param string $program name
86
+     * @internal param string $optional search path, defaults to $PATH
87
+     * @return bool true if executable program found in path
88
+     * @deprecated 32.0.0 use the \OCP\IBinaryFinder
89
+     */
90
+    public static function canExecute($name, $path = false) {
91
+        // path defaults to PATH from environment if not set
92
+        if ($path === false) {
93
+            $path = getenv('PATH');
94
+        }
95
+        // we look for an executable file of that name
96
+        $exts = [''];
97
+        $check_fn = 'is_executable';
98
+        // Default check will be done with $path directories :
99
+        $dirs = explode(PATH_SEPARATOR, (string)$path);
100
+        // WARNING : We have to check if open_basedir is enabled :
101
+        $obd = OC::$server->get(IniGetWrapper::class)->getString('open_basedir');
102
+        if ($obd != 'none') {
103
+            $obd_values = explode(PATH_SEPARATOR, $obd);
104
+            if (count($obd_values) > 0 && $obd_values[0]) {
105
+                // open_basedir is in effect !
106
+                // We need to check if the program is in one of these dirs :
107
+                $dirs = $obd_values;
108
+            }
109
+        }
110
+        foreach ($dirs as $dir) {
111
+            foreach ($exts as $ext) {
112
+                if ($check_fn("$dir/$name" . $ext)) {
113
+                    return true;
114
+                }
115
+            }
116
+        }
117
+        return false;
118
+    }
119
+
120
+    /**
121
+     * copy the contents of one stream to another
122
+     *
123
+     * @param resource $source
124
+     * @param resource $target
125
+     * @return array the number of bytes copied and result
126
+     * @deprecated 5.0.0 - Use \OCP\Files::streamCopy
127
+     */
128
+    public static function streamCopy($source, $target) {
129
+        return \OCP\Files::streamCopy($source, $target, true);
130
+    }
131
+
132
+    /**
133
+     * Adds a suffix to the name in case the file exists
134
+     *
135
+     * @param string $path
136
+     * @param string $filename
137
+     * @return string
138
+     */
139
+    public static function buildNotExistingFileName($path, $filename) {
140
+        $view = \OC\Files\Filesystem::getView();
141
+        return self::buildNotExistingFileNameForView($path, $filename, $view);
142
+    }
143
+
144
+    /**
145
+     * Adds a suffix to the name in case the file exists
146
+     *
147
+     * @param string $path
148
+     * @param string $filename
149
+     * @return string
150
+     */
151
+    public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
152
+        if ($path === '/') {
153
+            $path = '';
154
+        }
155
+        if ($pos = strrpos($filename, '.')) {
156
+            $name = substr($filename, 0, $pos);
157
+            $ext = substr($filename, $pos);
158
+        } else {
159
+            $name = $filename;
160
+            $ext = '';
161
+        }
162
+
163
+        $newpath = $path . '/' . $filename;
164
+        if ($view->file_exists($newpath)) {
165
+            if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
166
+                //Replace the last "(number)" with "(number+1)"
167
+                $last_match = count($matches[0]) - 1;
168
+                $counter = $matches[1][$last_match][0] + 1;
169
+                $offset = $matches[0][$last_match][1];
170
+                $match_length = strlen($matches[0][$last_match][0]);
171
+            } else {
172
+                $counter = 2;
173
+                $match_length = 0;
174
+                $offset = false;
175
+            }
176
+            do {
177
+                if ($offset) {
178
+                    //Replace the last "(number)" with "(number+1)"
179
+                    $newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
180
+                } else {
181
+                    $newname = $name . ' (' . $counter . ')';
182
+                }
183
+                $newpath = $path . '/' . $newname . $ext;
184
+                $counter++;
185
+            } while ($view->file_exists($newpath));
186
+        }
187
+
188
+        return $newpath;
189
+    }
190
+
191
+    /**
192
+     * Checks if a function is available
193
+     *
194
+     * @deprecated 25.0.0 use \OCP\Util::isFunctionEnabled instead
195
+     */
196
+    public static function is_function_enabled(string $function_name): bool {
197
+        return Util::isFunctionEnabled($function_name);
198
+    }
199
+
200
+    /**
201
+     * Try to find a program
202
+     * @deprecated 25.0.0 Use \OCP\IBinaryFinder directly
203
+     */
204
+    public static function findBinaryPath(string $program): ?string {
205
+        $result = Server::get(IBinaryFinder::class)->findBinaryPath($program);
206
+        return $result !== false ? $result : null;
207
+    }
208
+
209
+    /**
210
+     * Calculate the disc space for the given path
211
+     *
212
+     * BEWARE: this requires that Util::setupFS() was called
213
+     * already !
214
+     *
215
+     * @param string $path
216
+     * @param \OCP\Files\FileInfo $rootInfo (optional)
217
+     * @param bool $includeMountPoints whether to include mount points in the size calculation
218
+     * @param bool $useCache whether to use the cached quota values
219
+     * @psalm-suppress LessSpecificReturnStatement Legacy code outputs weird types - manually validated that they are correct
220
+     * @return StorageInfo
221
+     * @throws \OCP\Files\NotFoundException
222
+     */
223
+    public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true, $useCache = true) {
224
+        if (!self::$cacheFactory) {
225
+            self::$cacheFactory = Server::get(ICacheFactory::class);
226
+        }
227
+        $memcache = self::$cacheFactory->createLocal('storage_info');
228
+
229
+        // return storage info without adding mount points
230
+        if (self::$quotaIncludeExternalStorage === null) {
231
+            self::$quotaIncludeExternalStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
232
+        }
233
+
234
+        $view = Filesystem::getView();
235
+        if (!$view) {
236
+            throw new \OCP\Files\NotFoundException();
237
+        }
238
+        $fullPath = Filesystem::normalizePath($view->getAbsolutePath($path));
239
+
240
+        $cacheKey = $fullPath . '::' . ($includeMountPoints ? 'include' : 'exclude');
241
+        if ($useCache) {
242
+            $cached = $memcache->get($cacheKey);
243
+            if ($cached) {
244
+                return $cached;
245
+            }
246
+        }
247
+
248
+        if (!$rootInfo) {
249
+            $rootInfo = \OC\Files\Filesystem::getFileInfo($path, self::$quotaIncludeExternalStorage ? 'ext' : false);
250
+        }
251
+        if (!$rootInfo instanceof \OCP\Files\FileInfo) {
252
+            throw new \OCP\Files\NotFoundException('The root directory of the user\'s files is missing');
253
+        }
254
+        $used = $rootInfo->getSize($includeMountPoints);
255
+        if ($used < 0) {
256
+            $used = 0.0;
257
+        }
258
+        /** @var int|float $quota */
259
+        $quota = \OCP\Files\FileInfo::SPACE_UNLIMITED;
260
+        $mount = $rootInfo->getMountPoint();
261
+        $storage = $mount->getStorage();
262
+        $sourceStorage = $storage;
263
+        if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
264
+            self::$quotaIncludeExternalStorage = false;
265
+        }
266
+        if (self::$quotaIncludeExternalStorage) {
267
+            if ($storage->instanceOfStorage('\OC\Files\Storage\Home')
268
+                || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')
269
+            ) {
270
+                /** @var \OC\Files\Storage\Home $storage */
271
+                $user = $storage->getUser();
272
+            } else {
273
+                $user = \OC::$server->getUserSession()->getUser();
274
+            }
275
+            $quota = $user?->getQuotaBytes() ?? \OCP\Files\FileInfo::SPACE_UNKNOWN;
276
+            if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
277
+                // always get free space / total space from root + mount points
278
+                return self::getGlobalStorageInfo($quota, $user, $mount);
279
+            }
280
+        }
281
+
282
+        // TODO: need a better way to get total space from storage
283
+        if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
284
+            /** @var \OC\Files\Storage\Wrapper\Quota $storage */
285
+            $quota = $sourceStorage->getQuota();
286
+        }
287
+        try {
288
+            $free = $sourceStorage->free_space($rootInfo->getInternalPath());
289
+            if (is_bool($free)) {
290
+                $free = 0.0;
291
+            }
292
+        } catch (\Exception $e) {
293
+            if ($path === '') {
294
+                throw $e;
295
+            }
296
+            /** @var LoggerInterface $logger */
297
+            $logger = \OC::$server->get(LoggerInterface::class);
298
+            $logger->warning('Error while getting quota info, using root quota', ['exception' => $e]);
299
+            $rootInfo = self::getStorageInfo('');
300
+            $memcache->set($cacheKey, $rootInfo, 5 * 60);
301
+            return $rootInfo;
302
+        }
303
+        if ($free >= 0) {
304
+            $total = $free + $used;
305
+        } else {
306
+            $total = $free; //either unknown or unlimited
307
+        }
308
+        if ($total > 0) {
309
+            if ($quota > 0 && $total > $quota) {
310
+                $total = $quota;
311
+            }
312
+            // prevent division by zero or error codes (negative values)
313
+            $relative = round(($used / $total) * 10000) / 100;
314
+        } else {
315
+            $relative = 0;
316
+        }
317
+
318
+        /*
319 319
 		 * \OCA\Files_Sharing\External\Storage returns the cloud ID as the owner for the storage.
320 320
 		 * It is unnecessary to query the user manager for the display name, as it won't have this information.
321 321
 		 */
322
-		$isRemoteShare = $storage->instanceOfStorage(\OCA\Files_Sharing\External\Storage::class);
323
-
324
-		$ownerId = $storage->getOwner($path);
325
-		$ownerDisplayName = '';
326
-
327
-		if ($isRemoteShare === false && $ownerId !== false) {
328
-			$ownerDisplayName = \OC::$server->getUserManager()->getDisplayName($ownerId) ?? '';
329
-		}
330
-
331
-		if (substr_count($mount->getMountPoint(), '/') < 3) {
332
-			$mountPoint = '';
333
-		} else {
334
-			[,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
335
-		}
336
-
337
-		$info = [
338
-			'free' => $free,
339
-			'used' => $used,
340
-			'quota' => $quota,
341
-			'total' => $total,
342
-			'relative' => $relative,
343
-			'owner' => $ownerId,
344
-			'ownerDisplayName' => $ownerDisplayName,
345
-			'mountType' => $mount->getMountType(),
346
-			'mountPoint' => trim($mountPoint, '/'),
347
-		];
348
-
349
-		if ($isRemoteShare === false && $ownerId !== false && $path === '/') {
350
-			// If path is root, store this as last known quota usage for this user
351
-			\OCP\Server::get(\OCP\IConfig::class)->setUserValue($ownerId, 'files', 'lastSeenQuotaUsage', (string)$relative);
352
-		}
353
-
354
-		$memcache->set($cacheKey, $info, 5 * 60);
355
-
356
-		return $info;
357
-	}
358
-
359
-	/**
360
-	 * Get storage info including all mount points and quota
361
-	 *
362
-	 * @psalm-suppress LessSpecificReturnStatement Legacy code outputs weird types - manually validated that they are correct
363
-	 * @return StorageInfo
364
-	 */
365
-	private static function getGlobalStorageInfo(int|float $quota, IUser $user, IMountPoint $mount): array {
366
-		$rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
367
-		/** @var int|float $used */
368
-		$used = $rootInfo['size'];
369
-		if ($used < 0) {
370
-			$used = 0.0;
371
-		}
372
-
373
-		$total = $quota;
374
-		/** @var int|float $free */
375
-		$free = $quota - $used;
376
-
377
-		if ($total > 0) {
378
-			if ($quota > 0 && $total > $quota) {
379
-				$total = $quota;
380
-			}
381
-			// prevent division by zero or error codes (negative values)
382
-			$relative = round(($used / $total) * 10000) / 100;
383
-		} else {
384
-			$relative = 0.0;
385
-		}
386
-
387
-		if (substr_count($mount->getMountPoint(), '/') < 3) {
388
-			$mountPoint = '';
389
-		} else {
390
-			[,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
391
-		}
392
-
393
-		return [
394
-			'free' => $free,
395
-			'used' => $used,
396
-			'total' => $total,
397
-			'relative' => $relative,
398
-			'quota' => $quota,
399
-			'owner' => $user->getUID(),
400
-			'ownerDisplayName' => $user->getDisplayName(),
401
-			'mountType' => $mount->getMountType(),
402
-			'mountPoint' => trim($mountPoint, '/'),
403
-		];
404
-	}
405
-
406
-	public static function clearStorageInfo(string $absolutePath): void {
407
-		/** @var ICacheFactory $cacheFactory */
408
-		$cacheFactory = \OC::$server->get(ICacheFactory::class);
409
-		$memcache = $cacheFactory->createLocal('storage_info');
410
-		$cacheKeyPrefix = Filesystem::normalizePath($absolutePath) . '::';
411
-		$memcache->remove($cacheKeyPrefix . 'include');
412
-		$memcache->remove($cacheKeyPrefix . 'exclude');
413
-	}
414
-
415
-	/**
416
-	 * Returns whether the config file is set manually to read-only
417
-	 * @return bool
418
-	 * @deprecated 32.0.0 use the `config_is_read_only` system config directly
419
-	 */
420
-	public static function isReadOnlyConfigEnabled() {
421
-		return \OC::$server->getConfig()->getSystemValueBool('config_is_read_only', false);
422
-	}
322
+        $isRemoteShare = $storage->instanceOfStorage(\OCA\Files_Sharing\External\Storage::class);
323
+
324
+        $ownerId = $storage->getOwner($path);
325
+        $ownerDisplayName = '';
326
+
327
+        if ($isRemoteShare === false && $ownerId !== false) {
328
+            $ownerDisplayName = \OC::$server->getUserManager()->getDisplayName($ownerId) ?? '';
329
+        }
330
+
331
+        if (substr_count($mount->getMountPoint(), '/') < 3) {
332
+            $mountPoint = '';
333
+        } else {
334
+            [,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
335
+        }
336
+
337
+        $info = [
338
+            'free' => $free,
339
+            'used' => $used,
340
+            'quota' => $quota,
341
+            'total' => $total,
342
+            'relative' => $relative,
343
+            'owner' => $ownerId,
344
+            'ownerDisplayName' => $ownerDisplayName,
345
+            'mountType' => $mount->getMountType(),
346
+            'mountPoint' => trim($mountPoint, '/'),
347
+        ];
348
+
349
+        if ($isRemoteShare === false && $ownerId !== false && $path === '/') {
350
+            // If path is root, store this as last known quota usage for this user
351
+            \OCP\Server::get(\OCP\IConfig::class)->setUserValue($ownerId, 'files', 'lastSeenQuotaUsage', (string)$relative);
352
+        }
353
+
354
+        $memcache->set($cacheKey, $info, 5 * 60);
355
+
356
+        return $info;
357
+    }
358
+
359
+    /**
360
+     * Get storage info including all mount points and quota
361
+     *
362
+     * @psalm-suppress LessSpecificReturnStatement Legacy code outputs weird types - manually validated that they are correct
363
+     * @return StorageInfo
364
+     */
365
+    private static function getGlobalStorageInfo(int|float $quota, IUser $user, IMountPoint $mount): array {
366
+        $rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
367
+        /** @var int|float $used */
368
+        $used = $rootInfo['size'];
369
+        if ($used < 0) {
370
+            $used = 0.0;
371
+        }
372
+
373
+        $total = $quota;
374
+        /** @var int|float $free */
375
+        $free = $quota - $used;
376
+
377
+        if ($total > 0) {
378
+            if ($quota > 0 && $total > $quota) {
379
+                $total = $quota;
380
+            }
381
+            // prevent division by zero or error codes (negative values)
382
+            $relative = round(($used / $total) * 10000) / 100;
383
+        } else {
384
+            $relative = 0.0;
385
+        }
386
+
387
+        if (substr_count($mount->getMountPoint(), '/') < 3) {
388
+            $mountPoint = '';
389
+        } else {
390
+            [,,,$mountPoint] = explode('/', $mount->getMountPoint(), 4);
391
+        }
392
+
393
+        return [
394
+            'free' => $free,
395
+            'used' => $used,
396
+            'total' => $total,
397
+            'relative' => $relative,
398
+            'quota' => $quota,
399
+            'owner' => $user->getUID(),
400
+            'ownerDisplayName' => $user->getDisplayName(),
401
+            'mountType' => $mount->getMountType(),
402
+            'mountPoint' => trim($mountPoint, '/'),
403
+        ];
404
+    }
405
+
406
+    public static function clearStorageInfo(string $absolutePath): void {
407
+        /** @var ICacheFactory $cacheFactory */
408
+        $cacheFactory = \OC::$server->get(ICacheFactory::class);
409
+        $memcache = $cacheFactory->createLocal('storage_info');
410
+        $cacheKeyPrefix = Filesystem::normalizePath($absolutePath) . '::';
411
+        $memcache->remove($cacheKeyPrefix . 'include');
412
+        $memcache->remove($cacheKeyPrefix . 'exclude');
413
+    }
414
+
415
+    /**
416
+     * Returns whether the config file is set manually to read-only
417
+     * @return bool
418
+     * @deprecated 32.0.0 use the `config_is_read_only` system config directly
419
+     */
420
+    public static function isReadOnlyConfigEnabled() {
421
+        return \OC::$server->getConfig()->getSystemValueBool('config_is_read_only', false);
422
+    }
423 423
 }
Please login to merge, or discard this patch.
lib/private/legacy/OC_User.php 1 patch
Indentation   +362 added lines, -362 removed lines patch added patch discarded remove patch
@@ -40,366 +40,366 @@
 block discarded – undo
40 40
  *   logout()
41 41
  */
42 42
 class OC_User {
43
-	private static $_setupedBackends = [];
44
-
45
-	// bool, stores if a user want to access a resource anonymously, e.g if they open a public link
46
-	private static $incognitoMode = false;
47
-
48
-	/**
49
-	 * Adds the backend to the list of used backends
50
-	 *
51
-	 * @param string|\OCP\UserInterface $backend default: database The backend to use for user management
52
-	 * @return bool
53
-	 * @deprecated 32.0.0 Use IUserManager::registerBackend instead
54
-	 *
55
-	 * Set the User Authentication Module
56
-	 */
57
-	public static function useBackend($backend = 'database') {
58
-		if ($backend instanceof \OCP\UserInterface) {
59
-			Server::get(IUserManager::class)->registerBackend($backend);
60
-		} else {
61
-			// You'll never know what happens
62
-			if ($backend === null || !is_string($backend)) {
63
-				$backend = 'database';
64
-			}
65
-
66
-			// Load backend
67
-			switch ($backend) {
68
-				case 'database':
69
-				case 'mysql':
70
-				case 'sqlite':
71
-					Server::get(LoggerInterface::class)->debug('Adding user backend ' . $backend . '.', ['app' => 'core']);
72
-					Server::get(IUserManager::class)->registerBackend(new \OC\User\Database());
73
-					break;
74
-				case 'dummy':
75
-					Server::get(IUserManager::class)->registerBackend(new \Test\Util\User\Dummy());
76
-					break;
77
-				default:
78
-					Server::get(LoggerInterface::class)->debug('Adding default user backend ' . $backend . '.', ['app' => 'core']);
79
-					$className = 'OC_USER_' . strtoupper($backend);
80
-					Server::get(IUserManager::class)->registerBackend(new $className());
81
-					break;
82
-			}
83
-		}
84
-		return true;
85
-	}
86
-
87
-	/**
88
-	 * remove all used backends
89
-	 * @deprecated 32.0.0 Use IUserManager::clearBackends instead
90
-	 */
91
-	public static function clearBackends() {
92
-		Server::get(IUserManager::class)->clearBackends();
93
-	}
94
-
95
-	/**
96
-	 * setup the configured backends in config.php
97
-	 * @suppress PhanDeprecatedFunction
98
-	 */
99
-	public static function setupBackends() {
100
-		OC_App::loadApps(['prelogin']);
101
-		$backends = \OC::$server->getSystemConfig()->getValue('user_backends', []);
102
-		if (isset($backends['default']) && !$backends['default']) {
103
-			// clear default backends
104
-			self::clearBackends();
105
-		}
106
-		foreach ($backends as $i => $config) {
107
-			if (!is_array($config)) {
108
-				continue;
109
-			}
110
-			$class = $config['class'];
111
-			$arguments = $config['arguments'];
112
-			if (class_exists($class)) {
113
-				if (!in_array($i, self::$_setupedBackends)) {
114
-					// make a reflection object
115
-					$reflectionObj = new ReflectionClass($class);
116
-
117
-					// use Reflection to create a new instance, using the $args
118
-					$backend = $reflectionObj->newInstanceArgs($arguments);
119
-					self::useBackend($backend);
120
-					self::$_setupedBackends[] = $i;
121
-				} else {
122
-					Server::get(LoggerInterface::class)->debug('User backend ' . $class . ' already initialized.', ['app' => 'core']);
123
-				}
124
-			} else {
125
-				Server::get(LoggerInterface::class)->error('User backend ' . $class . ' not found.', ['app' => 'core']);
126
-			}
127
-		}
128
-	}
129
-
130
-	/**
131
-	 * Try to login a user, assuming authentication
132
-	 * has already happened (e.g. via Single Sign On).
133
-	 *
134
-	 * Log in a user and regenerate a new session.
135
-	 *
136
-	 * @param \OCP\Authentication\IApacheBackend $backend
137
-	 * @return bool
138
-	 */
139
-	public static function loginWithApache(\OCP\Authentication\IApacheBackend $backend) {
140
-		$uid = $backend->getCurrentUserId();
141
-		$run = true;
142
-		OC_Hook::emit('OC_User', 'pre_login', ['run' => &$run, 'uid' => $uid, 'backend' => $backend]);
143
-
144
-		if ($uid) {
145
-			if (self::getUser() !== $uid) {
146
-				self::setUserId($uid);
147
-				$userSession = \OC::$server->getUserSession();
148
-
149
-				/** @var IEventDispatcher $dispatcher */
150
-				$dispatcher = \OC::$server->get(IEventDispatcher::class);
151
-
152
-				if ($userSession->getUser() && !$userSession->getUser()->isEnabled()) {
153
-					$message = \OC::$server->getL10N('lib')->t('Account disabled');
154
-					throw new DisabledUserException($message);
155
-				}
156
-				$userSession->setLoginName($uid);
157
-				$request = OC::$server->getRequest();
158
-				$password = null;
159
-				if ($backend instanceof \OCP\Authentication\IProvideUserSecretBackend) {
160
-					$password = $backend->getCurrentUserSecret();
161
-				}
162
-
163
-				/** @var IEventDispatcher $dispatcher */
164
-				$dispatcher->dispatchTyped(new BeforeUserLoggedInEvent($uid, $password, $backend));
165
-
166
-				$userSession->createSessionToken($request, $uid, $uid, $password);
167
-				$userSession->createRememberMeToken($userSession->getUser());
168
-
169
-				if (empty($password)) {
170
-					$tokenProvider = \OC::$server->get(IProvider::class);
171
-					try {
172
-						$token = $tokenProvider->getToken($userSession->getSession()->getId());
173
-						$token->setScope([
174
-							IToken::SCOPE_SKIP_PASSWORD_VALIDATION => true,
175
-							IToken::SCOPE_FILESYSTEM => true,
176
-						]);
177
-						$tokenProvider->updateToken($token);
178
-					} catch (InvalidTokenException|WipeTokenException|SessionNotAvailableException) {
179
-						// swallow the exceptions as we do not deal with them here
180
-						// simply skip updating the token when is it missing
181
-					}
182
-				}
183
-
184
-				// setup the filesystem
185
-				OC_Util::setupFS($uid);
186
-				// first call the post_login hooks, the login-process needs to be
187
-				// completed before we can safely create the users folder.
188
-				// For example encryption needs to initialize the users keys first
189
-				// before we can create the user folder with the skeleton files
190
-				OC_Hook::emit(
191
-					'OC_User',
192
-					'post_login',
193
-					[
194
-						'uid' => $uid,
195
-						'password' => $password,
196
-						'isTokenLogin' => false,
197
-					]
198
-				);
199
-				$dispatcher->dispatchTyped(new UserLoggedInEvent(
200
-					\OC::$server->get(IUserManager::class)->get($uid),
201
-					$uid,
202
-					null,
203
-					false)
204
-				);
205
-
206
-				//trigger creation of user home and /files folder
207
-				\OC::$server->getUserFolder($uid);
208
-			}
209
-			return true;
210
-		}
211
-		return false;
212
-	}
213
-
214
-	/**
215
-	 * Verify with Apache whether user is authenticated.
216
-	 *
217
-	 * @return boolean|null
218
-	 *                      true: authenticated
219
-	 *                      false: not authenticated
220
-	 *                      null: not handled / no backend available
221
-	 */
222
-	public static function handleApacheAuth() {
223
-		$backend = self::findFirstActiveUsedBackend();
224
-		if ($backend) {
225
-			OC_App::loadApps();
226
-
227
-			//setup extra user backends
228
-			self::setupBackends();
229
-			\OC::$server->getUserSession()->unsetMagicInCookie();
230
-
231
-			return self::loginWithApache($backend);
232
-		}
233
-
234
-		return null;
235
-	}
236
-
237
-
238
-	/**
239
-	 * Sets user id for session and triggers emit
240
-	 *
241
-	 * @param string $uid
242
-	 */
243
-	public static function setUserId($uid) {
244
-		$userSession = \OC::$server->getUserSession();
245
-		$userManager = Server::get(IUserManager::class);
246
-		if ($user = $userManager->get($uid)) {
247
-			$userSession->setUser($user);
248
-		} else {
249
-			\OC::$server->getSession()->set('user_id', $uid);
250
-		}
251
-	}
252
-
253
-	/**
254
-	 * Check if the user is logged in, considers also the HTTP basic credentials
255
-	 *
256
-	 * @deprecated 12.0.0 use \OC::$server->getUserSession()->isLoggedIn()
257
-	 * @return bool
258
-	 */
259
-	public static function isLoggedIn() {
260
-		return \OC::$server->getUserSession()->isLoggedIn();
261
-	}
262
-
263
-	/**
264
-	 * set incognito mode, e.g. if a user wants to open a public link
265
-	 *
266
-	 * @param bool $status
267
-	 */
268
-	public static function setIncognitoMode($status) {
269
-		self::$incognitoMode = $status;
270
-	}
271
-
272
-	/**
273
-	 * get incognito mode status
274
-	 *
275
-	 * @return bool
276
-	 */
277
-	public static function isIncognitoMode() {
278
-		return self::$incognitoMode;
279
-	}
280
-
281
-	/**
282
-	 * Returns the current logout URL valid for the currently logged-in user
283
-	 *
284
-	 * @param \OCP\IURLGenerator $urlGenerator
285
-	 * @return string
286
-	 */
287
-	public static function getLogoutUrl(\OCP\IURLGenerator $urlGenerator) {
288
-		$backend = self::findFirstActiveUsedBackend();
289
-		if ($backend) {
290
-			return $backend->getLogoutUrl();
291
-		}
292
-
293
-		$user = \OC::$server->getUserSession()->getUser();
294
-		if ($user instanceof IUser) {
295
-			$backend = $user->getBackend();
296
-			if ($backend instanceof \OCP\User\Backend\ICustomLogout) {
297
-				return $backend->getLogoutUrl();
298
-			}
299
-		}
300
-
301
-		$logoutUrl = $urlGenerator->linkToRoute('core.login.logout');
302
-		$logoutUrl .= '?requesttoken=' . urlencode(\OCP\Util::callRegister());
303
-
304
-		return $logoutUrl;
305
-	}
306
-
307
-	/**
308
-	 * Check if the user is an admin user
309
-	 *
310
-	 * @param string $uid uid of the admin
311
-	 * @return bool
312
-	 */
313
-	public static function isAdminUser($uid) {
314
-		$user = Server::get(IUserManager::class)->get($uid);
315
-		$isAdmin = $user && Server::get(IGroupManager::class)->isAdmin($user->getUID());
316
-		return $isAdmin && self::$incognitoMode === false;
317
-	}
318
-
319
-
320
-	/**
321
-	 * get the user id of the user currently logged in.
322
-	 *
323
-	 * @return string|false uid or false
324
-	 */
325
-	public static function getUser() {
326
-		$uid = Server::get(ISession::class)?->get('user_id');
327
-		if (!is_null($uid) && self::$incognitoMode === false) {
328
-			return $uid;
329
-		} else {
330
-			return false;
331
-		}
332
-	}
333
-
334
-	/**
335
-	 * Set password
336
-	 *
337
-	 * @param string $uid The username
338
-	 * @param string $password The new password
339
-	 * @param string $recoveryPassword for the encryption app to reset encryption keys
340
-	 * @return bool
341
-	 *
342
-	 * Change the password of a user
343
-	 */
344
-	public static function setPassword($uid, $password, $recoveryPassword = null) {
345
-		$user = Server::get(IUserManager::class)->get($uid);
346
-		if ($user) {
347
-			return $user->setPassword($password, $recoveryPassword);
348
-		} else {
349
-			return false;
350
-		}
351
-	}
352
-
353
-	/**
354
-	 * @param string $uid The username
355
-	 * @return string
356
-	 *
357
-	 * returns the path to the users home directory
358
-	 * @deprecated 12.0.0 Use \OC::$server->getUserManager->getHome()
359
-	 */
360
-	public static function getHome($uid) {
361
-		$user = Server::get(IUserManager::class)->get($uid);
362
-		if ($user) {
363
-			return $user->getHome();
364
-		} else {
365
-			return \OC::$server->getSystemConfig()->getValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $uid;
366
-		}
367
-	}
368
-
369
-	/**
370
-	 * Get a list of all users display name
371
-	 *
372
-	 * @param string $search
373
-	 * @param int $limit
374
-	 * @param int $offset
375
-	 * @return array associative array with all display names (value) and corresponding uids (key)
376
-	 *
377
-	 * Get a list of all display names and user ids.
378
-	 * @deprecated 12.0.0 Use \OC::$server->getUserManager->searchDisplayName($search, $limit, $offset) instead.
379
-	 */
380
-	public static function getDisplayNames($search = '', $limit = null, $offset = null) {
381
-		$displayNames = [];
382
-		$users = Server::get(IUserManager::class)->searchDisplayName($search, $limit, $offset);
383
-		foreach ($users as $user) {
384
-			$displayNames[$user->getUID()] = $user->getDisplayName();
385
-		}
386
-		return $displayNames;
387
-	}
388
-
389
-	/**
390
-	 * Returns the first active backend from self::$_usedBackends.
391
-	 *
392
-	 * @return OCP\Authentication\IApacheBackend|null if no backend active, otherwise OCP\Authentication\IApacheBackend
393
-	 */
394
-	private static function findFirstActiveUsedBackend() {
395
-		foreach (Server::get(IUserManager::class)->getBackends() as $backend) {
396
-			if ($backend instanceof OCP\Authentication\IApacheBackend) {
397
-				if ($backend->isSessionActive()) {
398
-					return $backend;
399
-				}
400
-			}
401
-		}
402
-
403
-		return null;
404
-	}
43
+    private static $_setupedBackends = [];
44
+
45
+    // bool, stores if a user want to access a resource anonymously, e.g if they open a public link
46
+    private static $incognitoMode = false;
47
+
48
+    /**
49
+     * Adds the backend to the list of used backends
50
+     *
51
+     * @param string|\OCP\UserInterface $backend default: database The backend to use for user management
52
+     * @return bool
53
+     * @deprecated 32.0.0 Use IUserManager::registerBackend instead
54
+     *
55
+     * Set the User Authentication Module
56
+     */
57
+    public static function useBackend($backend = 'database') {
58
+        if ($backend instanceof \OCP\UserInterface) {
59
+            Server::get(IUserManager::class)->registerBackend($backend);
60
+        } else {
61
+            // You'll never know what happens
62
+            if ($backend === null || !is_string($backend)) {
63
+                $backend = 'database';
64
+            }
65
+
66
+            // Load backend
67
+            switch ($backend) {
68
+                case 'database':
69
+                case 'mysql':
70
+                case 'sqlite':
71
+                    Server::get(LoggerInterface::class)->debug('Adding user backend ' . $backend . '.', ['app' => 'core']);
72
+                    Server::get(IUserManager::class)->registerBackend(new \OC\User\Database());
73
+                    break;
74
+                case 'dummy':
75
+                    Server::get(IUserManager::class)->registerBackend(new \Test\Util\User\Dummy());
76
+                    break;
77
+                default:
78
+                    Server::get(LoggerInterface::class)->debug('Adding default user backend ' . $backend . '.', ['app' => 'core']);
79
+                    $className = 'OC_USER_' . strtoupper($backend);
80
+                    Server::get(IUserManager::class)->registerBackend(new $className());
81
+                    break;
82
+            }
83
+        }
84
+        return true;
85
+    }
86
+
87
+    /**
88
+     * remove all used backends
89
+     * @deprecated 32.0.0 Use IUserManager::clearBackends instead
90
+     */
91
+    public static function clearBackends() {
92
+        Server::get(IUserManager::class)->clearBackends();
93
+    }
94
+
95
+    /**
96
+     * setup the configured backends in config.php
97
+     * @suppress PhanDeprecatedFunction
98
+     */
99
+    public static function setupBackends() {
100
+        OC_App::loadApps(['prelogin']);
101
+        $backends = \OC::$server->getSystemConfig()->getValue('user_backends', []);
102
+        if (isset($backends['default']) && !$backends['default']) {
103
+            // clear default backends
104
+            self::clearBackends();
105
+        }
106
+        foreach ($backends as $i => $config) {
107
+            if (!is_array($config)) {
108
+                continue;
109
+            }
110
+            $class = $config['class'];
111
+            $arguments = $config['arguments'];
112
+            if (class_exists($class)) {
113
+                if (!in_array($i, self::$_setupedBackends)) {
114
+                    // make a reflection object
115
+                    $reflectionObj = new ReflectionClass($class);
116
+
117
+                    // use Reflection to create a new instance, using the $args
118
+                    $backend = $reflectionObj->newInstanceArgs($arguments);
119
+                    self::useBackend($backend);
120
+                    self::$_setupedBackends[] = $i;
121
+                } else {
122
+                    Server::get(LoggerInterface::class)->debug('User backend ' . $class . ' already initialized.', ['app' => 'core']);
123
+                }
124
+            } else {
125
+                Server::get(LoggerInterface::class)->error('User backend ' . $class . ' not found.', ['app' => 'core']);
126
+            }
127
+        }
128
+    }
129
+
130
+    /**
131
+     * Try to login a user, assuming authentication
132
+     * has already happened (e.g. via Single Sign On).
133
+     *
134
+     * Log in a user and regenerate a new session.
135
+     *
136
+     * @param \OCP\Authentication\IApacheBackend $backend
137
+     * @return bool
138
+     */
139
+    public static function loginWithApache(\OCP\Authentication\IApacheBackend $backend) {
140
+        $uid = $backend->getCurrentUserId();
141
+        $run = true;
142
+        OC_Hook::emit('OC_User', 'pre_login', ['run' => &$run, 'uid' => $uid, 'backend' => $backend]);
143
+
144
+        if ($uid) {
145
+            if (self::getUser() !== $uid) {
146
+                self::setUserId($uid);
147
+                $userSession = \OC::$server->getUserSession();
148
+
149
+                /** @var IEventDispatcher $dispatcher */
150
+                $dispatcher = \OC::$server->get(IEventDispatcher::class);
151
+
152
+                if ($userSession->getUser() && !$userSession->getUser()->isEnabled()) {
153
+                    $message = \OC::$server->getL10N('lib')->t('Account disabled');
154
+                    throw new DisabledUserException($message);
155
+                }
156
+                $userSession->setLoginName($uid);
157
+                $request = OC::$server->getRequest();
158
+                $password = null;
159
+                if ($backend instanceof \OCP\Authentication\IProvideUserSecretBackend) {
160
+                    $password = $backend->getCurrentUserSecret();
161
+                }
162
+
163
+                /** @var IEventDispatcher $dispatcher */
164
+                $dispatcher->dispatchTyped(new BeforeUserLoggedInEvent($uid, $password, $backend));
165
+
166
+                $userSession->createSessionToken($request, $uid, $uid, $password);
167
+                $userSession->createRememberMeToken($userSession->getUser());
168
+
169
+                if (empty($password)) {
170
+                    $tokenProvider = \OC::$server->get(IProvider::class);
171
+                    try {
172
+                        $token = $tokenProvider->getToken($userSession->getSession()->getId());
173
+                        $token->setScope([
174
+                            IToken::SCOPE_SKIP_PASSWORD_VALIDATION => true,
175
+                            IToken::SCOPE_FILESYSTEM => true,
176
+                        ]);
177
+                        $tokenProvider->updateToken($token);
178
+                    } catch (InvalidTokenException|WipeTokenException|SessionNotAvailableException) {
179
+                        // swallow the exceptions as we do not deal with them here
180
+                        // simply skip updating the token when is it missing
181
+                    }
182
+                }
183
+
184
+                // setup the filesystem
185
+                OC_Util::setupFS($uid);
186
+                // first call the post_login hooks, the login-process needs to be
187
+                // completed before we can safely create the users folder.
188
+                // For example encryption needs to initialize the users keys first
189
+                // before we can create the user folder with the skeleton files
190
+                OC_Hook::emit(
191
+                    'OC_User',
192
+                    'post_login',
193
+                    [
194
+                        'uid' => $uid,
195
+                        'password' => $password,
196
+                        'isTokenLogin' => false,
197
+                    ]
198
+                );
199
+                $dispatcher->dispatchTyped(new UserLoggedInEvent(
200
+                    \OC::$server->get(IUserManager::class)->get($uid),
201
+                    $uid,
202
+                    null,
203
+                    false)
204
+                );
205
+
206
+                //trigger creation of user home and /files folder
207
+                \OC::$server->getUserFolder($uid);
208
+            }
209
+            return true;
210
+        }
211
+        return false;
212
+    }
213
+
214
+    /**
215
+     * Verify with Apache whether user is authenticated.
216
+     *
217
+     * @return boolean|null
218
+     *                      true: authenticated
219
+     *                      false: not authenticated
220
+     *                      null: not handled / no backend available
221
+     */
222
+    public static function handleApacheAuth() {
223
+        $backend = self::findFirstActiveUsedBackend();
224
+        if ($backend) {
225
+            OC_App::loadApps();
226
+
227
+            //setup extra user backends
228
+            self::setupBackends();
229
+            \OC::$server->getUserSession()->unsetMagicInCookie();
230
+
231
+            return self::loginWithApache($backend);
232
+        }
233
+
234
+        return null;
235
+    }
236
+
237
+
238
+    /**
239
+     * Sets user id for session and triggers emit
240
+     *
241
+     * @param string $uid
242
+     */
243
+    public static function setUserId($uid) {
244
+        $userSession = \OC::$server->getUserSession();
245
+        $userManager = Server::get(IUserManager::class);
246
+        if ($user = $userManager->get($uid)) {
247
+            $userSession->setUser($user);
248
+        } else {
249
+            \OC::$server->getSession()->set('user_id', $uid);
250
+        }
251
+    }
252
+
253
+    /**
254
+     * Check if the user is logged in, considers also the HTTP basic credentials
255
+     *
256
+     * @deprecated 12.0.0 use \OC::$server->getUserSession()->isLoggedIn()
257
+     * @return bool
258
+     */
259
+    public static function isLoggedIn() {
260
+        return \OC::$server->getUserSession()->isLoggedIn();
261
+    }
262
+
263
+    /**
264
+     * set incognito mode, e.g. if a user wants to open a public link
265
+     *
266
+     * @param bool $status
267
+     */
268
+    public static function setIncognitoMode($status) {
269
+        self::$incognitoMode = $status;
270
+    }
271
+
272
+    /**
273
+     * get incognito mode status
274
+     *
275
+     * @return bool
276
+     */
277
+    public static function isIncognitoMode() {
278
+        return self::$incognitoMode;
279
+    }
280
+
281
+    /**
282
+     * Returns the current logout URL valid for the currently logged-in user
283
+     *
284
+     * @param \OCP\IURLGenerator $urlGenerator
285
+     * @return string
286
+     */
287
+    public static function getLogoutUrl(\OCP\IURLGenerator $urlGenerator) {
288
+        $backend = self::findFirstActiveUsedBackend();
289
+        if ($backend) {
290
+            return $backend->getLogoutUrl();
291
+        }
292
+
293
+        $user = \OC::$server->getUserSession()->getUser();
294
+        if ($user instanceof IUser) {
295
+            $backend = $user->getBackend();
296
+            if ($backend instanceof \OCP\User\Backend\ICustomLogout) {
297
+                return $backend->getLogoutUrl();
298
+            }
299
+        }
300
+
301
+        $logoutUrl = $urlGenerator->linkToRoute('core.login.logout');
302
+        $logoutUrl .= '?requesttoken=' . urlencode(\OCP\Util::callRegister());
303
+
304
+        return $logoutUrl;
305
+    }
306
+
307
+    /**
308
+     * Check if the user is an admin user
309
+     *
310
+     * @param string $uid uid of the admin
311
+     * @return bool
312
+     */
313
+    public static function isAdminUser($uid) {
314
+        $user = Server::get(IUserManager::class)->get($uid);
315
+        $isAdmin = $user && Server::get(IGroupManager::class)->isAdmin($user->getUID());
316
+        return $isAdmin && self::$incognitoMode === false;
317
+    }
318
+
319
+
320
+    /**
321
+     * get the user id of the user currently logged in.
322
+     *
323
+     * @return string|false uid or false
324
+     */
325
+    public static function getUser() {
326
+        $uid = Server::get(ISession::class)?->get('user_id');
327
+        if (!is_null($uid) && self::$incognitoMode === false) {
328
+            return $uid;
329
+        } else {
330
+            return false;
331
+        }
332
+    }
333
+
334
+    /**
335
+     * Set password
336
+     *
337
+     * @param string $uid The username
338
+     * @param string $password The new password
339
+     * @param string $recoveryPassword for the encryption app to reset encryption keys
340
+     * @return bool
341
+     *
342
+     * Change the password of a user
343
+     */
344
+    public static function setPassword($uid, $password, $recoveryPassword = null) {
345
+        $user = Server::get(IUserManager::class)->get($uid);
346
+        if ($user) {
347
+            return $user->setPassword($password, $recoveryPassword);
348
+        } else {
349
+            return false;
350
+        }
351
+    }
352
+
353
+    /**
354
+     * @param string $uid The username
355
+     * @return string
356
+     *
357
+     * returns the path to the users home directory
358
+     * @deprecated 12.0.0 Use \OC::$server->getUserManager->getHome()
359
+     */
360
+    public static function getHome($uid) {
361
+        $user = Server::get(IUserManager::class)->get($uid);
362
+        if ($user) {
363
+            return $user->getHome();
364
+        } else {
365
+            return \OC::$server->getSystemConfig()->getValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $uid;
366
+        }
367
+    }
368
+
369
+    /**
370
+     * Get a list of all users display name
371
+     *
372
+     * @param string $search
373
+     * @param int $limit
374
+     * @param int $offset
375
+     * @return array associative array with all display names (value) and corresponding uids (key)
376
+     *
377
+     * Get a list of all display names and user ids.
378
+     * @deprecated 12.0.0 Use \OC::$server->getUserManager->searchDisplayName($search, $limit, $offset) instead.
379
+     */
380
+    public static function getDisplayNames($search = '', $limit = null, $offset = null) {
381
+        $displayNames = [];
382
+        $users = Server::get(IUserManager::class)->searchDisplayName($search, $limit, $offset);
383
+        foreach ($users as $user) {
384
+            $displayNames[$user->getUID()] = $user->getDisplayName();
385
+        }
386
+        return $displayNames;
387
+    }
388
+
389
+    /**
390
+     * Returns the first active backend from self::$_usedBackends.
391
+     *
392
+     * @return OCP\Authentication\IApacheBackend|null if no backend active, otherwise OCP\Authentication\IApacheBackend
393
+     */
394
+    private static function findFirstActiveUsedBackend() {
395
+        foreach (Server::get(IUserManager::class)->getBackends() as $backend) {
396
+            if ($backend instanceof OCP\Authentication\IApacheBackend) {
397
+                if ($backend->isSessionActive()) {
398
+                    return $backend;
399
+                }
400
+            }
401
+        }
402
+
403
+        return null;
404
+    }
405 405
 }
Please login to merge, or discard this patch.