Completed
Push — master ( 86560b...c68386 )
by
unknown
30:21
created
lib/public/ICertificateManager.php 1 patch
Indentation   +41 added lines, -41 removed lines patch added patch discarded remove patch
@@ -13,50 +13,50 @@
 block discarded – undo
13 13
  * @since 8.0.0
14 14
  */
15 15
 interface ICertificateManager {
16
-	/**
17
-	 * Returns all certificates trusted by the system
18
-	 *
19
-	 * @return \OCP\ICertificate[]
20
-	 * @since 8.0.0
21
-	 */
22
-	public function listCertificates(): array;
16
+    /**
17
+     * Returns all certificates trusted by the system
18
+     *
19
+     * @return \OCP\ICertificate[]
20
+     * @since 8.0.0
21
+     */
22
+    public function listCertificates(): array;
23 23
 
24
-	/**
25
-	 * @param string $certificate the certificate data
26
-	 * @param string $name the filename for the certificate
27
-	 * @return \OCP\ICertificate
28
-	 * @throws \Exception If the certificate could not get added
29
-	 * @since 8.0.0 - since 8.1.0 throws exception instead of returning false
30
-	 */
31
-	public function addCertificate(string $certificate, string $name): \OCP\ICertificate;
24
+    /**
25
+     * @param string $certificate the certificate data
26
+     * @param string $name the filename for the certificate
27
+     * @return \OCP\ICertificate
28
+     * @throws \Exception If the certificate could not get added
29
+     * @since 8.0.0 - since 8.1.0 throws exception instead of returning false
30
+     */
31
+    public function addCertificate(string $certificate, string $name): \OCP\ICertificate;
32 32
 
33
-	/**
34
-	 * @param string $name
35
-	 * @return bool
36
-	 * @since 8.0.0
37
-	 */
38
-	public function removeCertificate(string $name): bool;
33
+    /**
34
+     * @param string $name
35
+     * @return bool
36
+     * @since 8.0.0
37
+     */
38
+    public function removeCertificate(string $name): bool;
39 39
 
40
-	/**
41
-	 * Get the path to the certificate bundle
42
-	 *
43
-	 * @return string
44
-	 * @since 8.0.0
45
-	 */
46
-	public function getCertificateBundle(): string;
40
+    /**
41
+     * Get the path to the certificate bundle
42
+     *
43
+     * @return string
44
+     * @since 8.0.0
45
+     */
46
+    public function getCertificateBundle(): string;
47 47
 
48
-	/**
49
-	 * Get the full local path to the certificate bundle
50
-	 *
51
-	 * @return string
52
-	 * @since 9.0.0
53
-	 */
54
-	public function getAbsoluteBundlePath(): string;
48
+    /**
49
+     * Get the full local path to the certificate bundle
50
+     *
51
+     * @return string
52
+     * @since 9.0.0
53
+     */
54
+    public function getAbsoluteBundlePath(): string;
55 55
 
56
-	/**
57
-	 * Get the path of the default certificates bundle.
58
-	 *
59
-	 * @since 33.0.0
60
-	 */
61
-	public function getDefaultCertificatesBundlePath(): string;
56
+    /**
57
+     * Get the path of the default certificates bundle.
58
+     *
59
+     * @since 33.0.0
60
+     */
61
+    public function getDefaultCertificatesBundlePath(): string;
62 62
 }
Please login to merge, or discard this patch.
lib/private/Http/Client/Client.php 2 patches
Indentation   +657 added lines, -657 removed lines patch added patch discarded remove patch
@@ -28,661 +28,661 @@
 block discarded – undo
28 28
  * @package OC\Http
29 29
  */
30 30
 class Client implements IClient {
31
-	/** @var GuzzleClient */
32
-	private $client;
33
-	/** @var IConfig */
34
-	private $config;
35
-	/** @var ICertificateManager */
36
-	private $certificateManager;
37
-	private IRemoteHostValidator $remoteHostValidator;
38
-
39
-	public function __construct(
40
-		IConfig $config,
41
-		ICertificateManager $certificateManager,
42
-		GuzzleClient $client,
43
-		IRemoteHostValidator $remoteHostValidator,
44
-		protected LoggerInterface $logger,
45
-		protected ServerVersion $serverVersion,
46
-	) {
47
-		$this->config = $config;
48
-		$this->client = $client;
49
-		$this->certificateManager = $certificateManager;
50
-		$this->remoteHostValidator = $remoteHostValidator;
51
-	}
52
-
53
-	private function buildRequestOptions(array $options): array {
54
-		$proxy = $this->getProxyUri();
55
-
56
-		$defaults = [
57
-			RequestOptions::VERIFY => $this->getCertBundle(),
58
-			RequestOptions::TIMEOUT => IClient::DEFAULT_REQUEST_TIMEOUT,
59
-		];
60
-
61
-		$options['nextcloud']['allow_local_address'] = $this->isLocalAddressAllowed($options);
62
-		if ($options['nextcloud']['allow_local_address'] === false) {
63
-			$onRedirectFunction = function (
64
-				\Psr\Http\Message\RequestInterface $request,
65
-				\Psr\Http\Message\ResponseInterface $response,
66
-				\Psr\Http\Message\UriInterface $uri,
67
-			) use ($options) {
68
-				$this->preventLocalAddress($uri->__toString(), $options);
69
-			};
70
-
71
-			$defaults[RequestOptions::ALLOW_REDIRECTS] = [
72
-				'on_redirect' => $onRedirectFunction
73
-			];
74
-		}
75
-
76
-		// Only add RequestOptions::PROXY if Nextcloud is explicitly
77
-		// configured to use a proxy. This is needed in order not to override
78
-		// Guzzle default values.
79
-		if ($proxy !== null) {
80
-			$defaults[RequestOptions::PROXY] = $proxy;
81
-		}
82
-
83
-		$options = array_merge($defaults, $options);
84
-
85
-		if (!isset($options[RequestOptions::HEADERS]['User-Agent'])) {
86
-			$userAgent = 'Nextcloud-Server-Crawler/' . $this->serverVersion->getVersionString();
87
-			$options[RequestOptions::HEADERS]['User-Agent'] = $userAgent;
88
-		}
89
-
90
-		if (!isset($options[RequestOptions::HEADERS]['Accept-Encoding'])) {
91
-			$options[RequestOptions::HEADERS]['Accept-Encoding'] = 'gzip';
92
-		}
93
-
94
-		// Fallback for save_to
95
-		if (isset($options['save_to'])) {
96
-			$options['sink'] = $options['save_to'];
97
-			unset($options['save_to']);
98
-		}
99
-
100
-		return $options;
101
-	}
102
-
103
-	private function getCertBundle(): string {
104
-		// If the instance is not yet setup we need to use the static path as
105
-		// $this->certificateManager->getAbsoluteBundlePath() tries to instantiate
106
-		// a view
107
-		if (!$this->config->getSystemValueBool('installed', false)) {
108
-			return $this->certificateManager->getDefaultCertificatesBundlePath();
109
-		}
110
-
111
-		return $this->certificateManager->getAbsoluteBundlePath();
112
-	}
113
-
114
-	/**
115
-	 * Returns a null or an associative array specifying the proxy URI for
116
-	 * 'http' and 'https' schemes, in addition to a 'no' key value pair
117
-	 * providing a list of host names that should not be proxied to.
118
-	 *
119
-	 * @return array|null
120
-	 *
121
-	 * The return array looks like:
122
-	 * [
123
-	 *   'http' => 'username:[email protected]',
124
-	 *   'https' => 'username:[email protected]',
125
-	 *   'no' => ['foo.com', 'bar.com']
126
-	 * ]
127
-	 *
128
-	 */
129
-	private function getProxyUri(): ?array {
130
-		$proxyHost = $this->config->getSystemValueString('proxy', '');
131
-
132
-		if ($proxyHost === '') {
133
-			return null;
134
-		}
135
-
136
-		$proxyUserPwd = $this->config->getSystemValueString('proxyuserpwd', '');
137
-		if ($proxyUserPwd !== '') {
138
-			$proxyHost = $proxyUserPwd . '@' . $proxyHost;
139
-		}
140
-
141
-		$proxy = [
142
-			'http' => $proxyHost,
143
-			'https' => $proxyHost,
144
-		];
145
-
146
-		$proxyExclude = $this->config->getSystemValue('proxyexclude', []);
147
-		if ($proxyExclude !== [] && $proxyExclude !== null) {
148
-			$proxy['no'] = $proxyExclude;
149
-		}
150
-
151
-		return $proxy;
152
-	}
153
-
154
-	private function isLocalAddressAllowed(array $options) : bool {
155
-		if (($options['nextcloud']['allow_local_address'] ?? false)
156
-			|| $this->config->getSystemValueBool('allow_local_remote_servers', false)) {
157
-			return true;
158
-		}
159
-
160
-		return false;
161
-	}
162
-
163
-	protected function preventLocalAddress(string $uri, array $options): void {
164
-		$host = parse_url($uri, PHP_URL_HOST);
165
-		if ($host === false || $host === null) {
166
-			throw new LocalServerException('Could not detect any host');
167
-		}
168
-
169
-		if ($this->isLocalAddressAllowed($options)) {
170
-			return;
171
-		}
172
-
173
-		if (!$this->remoteHostValidator->isValid($host)) {
174
-			throw new LocalServerException('Host "' . $host . '" violates local access rules');
175
-		}
176
-	}
177
-
178
-	/**
179
-	 * Sends a GET request
180
-	 *
181
-	 * @param string $uri
182
-	 * @param array $options Array such as
183
-	 *                       'query' => [
184
-	 *                       'field' => 'abc',
185
-	 *                       'other_field' => '123',
186
-	 *                       'file_name' => fopen('/path/to/file', 'r'),
187
-	 *                       ],
188
-	 *                       'headers' => [
189
-	 *                       'foo' => 'bar',
190
-	 *                       ],
191
-	 *                       'cookies' => [
192
-	 *                       'foo' => 'bar',
193
-	 *                       ],
194
-	 *                       'allow_redirects' => [
195
-	 *                       'max'       => 10,  // allow at most 10 redirects.
196
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
197
-	 *                       'referer'   => true,     // add a Referer header
198
-	 *                       'protocols' => ['https'] // only allow https URLs
199
-	 *                       ],
200
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
201
-	 *                       'verify' => true, // bool or string to CA file
202
-	 *                       'debug' => true,
203
-	 *                       'timeout' => 5,
204
-	 * @return IResponse
205
-	 * @throws \Exception If the request could not get completed
206
-	 */
207
-	public function get(string $uri, array $options = []): IResponse {
208
-		$this->preventLocalAddress($uri, $options);
209
-		$response = $this->client->request('get', $uri, $this->buildRequestOptions($options));
210
-		$isStream = isset($options['stream']) && $options['stream'];
211
-		return new Response($response, $isStream);
212
-	}
213
-
214
-	/**
215
-	 * Sends a HEAD request
216
-	 *
217
-	 * @param string $uri
218
-	 * @param array $options Array such as
219
-	 *                       'headers' => [
220
-	 *                       'foo' => 'bar',
221
-	 *                       ],
222
-	 *                       'cookies' => [
223
-	 *                       'foo' => 'bar',
224
-	 *                       ],
225
-	 *                       'allow_redirects' => [
226
-	 *                       'max'       => 10,  // allow at most 10 redirects.
227
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
228
-	 *                       'referer'   => true,     // add a Referer header
229
-	 *                       'protocols' => ['https'] // only allow https URLs
230
-	 *                       ],
231
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
232
-	 *                       'verify' => true, // bool or string to CA file
233
-	 *                       'debug' => true,
234
-	 *                       'timeout' => 5,
235
-	 * @return IResponse
236
-	 * @throws \Exception If the request could not get completed
237
-	 */
238
-	public function head(string $uri, array $options = []): IResponse {
239
-		$this->preventLocalAddress($uri, $options);
240
-		$response = $this->client->request('head', $uri, $this->buildRequestOptions($options));
241
-		return new Response($response);
242
-	}
243
-
244
-	/**
245
-	 * Sends a POST request
246
-	 *
247
-	 * @param string $uri
248
-	 * @param array $options Array such as
249
-	 *                       'body' => [
250
-	 *                       'field' => 'abc',
251
-	 *                       'other_field' => '123',
252
-	 *                       'file_name' => fopen('/path/to/file', 'r'),
253
-	 *                       ],
254
-	 *                       'headers' => [
255
-	 *                       'foo' => 'bar',
256
-	 *                       ],
257
-	 *                       'cookies' => [
258
-	 *                       'foo' => 'bar',
259
-	 *                       ],
260
-	 *                       'allow_redirects' => [
261
-	 *                       'max'       => 10,  // allow at most 10 redirects.
262
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
263
-	 *                       'referer'   => true,     // add a Referer header
264
-	 *                       'protocols' => ['https'] // only allow https URLs
265
-	 *                       ],
266
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
267
-	 *                       'verify' => true, // bool or string to CA file
268
-	 *                       'debug' => true,
269
-	 *                       'timeout' => 5,
270
-	 * @return IResponse
271
-	 * @throws \Exception If the request could not get completed
272
-	 */
273
-	public function post(string $uri, array $options = []): IResponse {
274
-		$this->preventLocalAddress($uri, $options);
275
-
276
-		if (isset($options['body']) && is_array($options['body'])) {
277
-			$options['form_params'] = $options['body'];
278
-			unset($options['body']);
279
-		}
280
-		$response = $this->client->request('post', $uri, $this->buildRequestOptions($options));
281
-		$isStream = isset($options['stream']) && $options['stream'];
282
-		return new Response($response, $isStream);
283
-	}
284
-
285
-	/**
286
-	 * Sends a PUT request
287
-	 *
288
-	 * @param string $uri
289
-	 * @param array $options Array such as
290
-	 *                       'body' => [
291
-	 *                       'field' => 'abc',
292
-	 *                       'other_field' => '123',
293
-	 *                       'file_name' => fopen('/path/to/file', 'r'),
294
-	 *                       ],
295
-	 *                       'headers' => [
296
-	 *                       'foo' => 'bar',
297
-	 *                       ],
298
-	 *                       'cookies' => [
299
-	 *                       'foo' => 'bar',
300
-	 *                       ],
301
-	 *                       'allow_redirects' => [
302
-	 *                       'max'       => 10,  // allow at most 10 redirects.
303
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
304
-	 *                       'referer'   => true,     // add a Referer header
305
-	 *                       'protocols' => ['https'] // only allow https URLs
306
-	 *                       ],
307
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
308
-	 *                       'verify' => true, // bool or string to CA file
309
-	 *                       'debug' => true,
310
-	 *                       'timeout' => 5,
311
-	 * @return IResponse
312
-	 * @throws \Exception If the request could not get completed
313
-	 */
314
-	public function put(string $uri, array $options = []): IResponse {
315
-		$this->preventLocalAddress($uri, $options);
316
-		$response = $this->client->request('put', $uri, $this->buildRequestOptions($options));
317
-		return new Response($response);
318
-	}
319
-
320
-	/**
321
-	 * Sends a PATCH request
322
-	 *
323
-	 * @param string $uri
324
-	 * @param array $options Array such as
325
-	 *                       'body' => [
326
-	 *                       'field' => 'abc',
327
-	 *                       'other_field' => '123',
328
-	 *                       'file_name' => fopen('/path/to/file', 'r'),
329
-	 *                       ],
330
-	 *                       'headers' => [
331
-	 *                       'foo' => 'bar',
332
-	 *                       ],
333
-	 *                       'cookies' => [
334
-	 *                       'foo' => 'bar',
335
-	 *                       ],
336
-	 *                       'allow_redirects' => [
337
-	 *                       'max'       => 10,  // allow at most 10 redirects.
338
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
339
-	 *                       'referer'   => true,     // add a Referer header
340
-	 *                       'protocols' => ['https'] // only allow https URLs
341
-	 *                       ],
342
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
343
-	 *                       'verify' => true, // bool or string to CA file
344
-	 *                       'debug' => true,
345
-	 *                       'timeout' => 5,
346
-	 * @return IResponse
347
-	 * @throws \Exception If the request could not get completed
348
-	 */
349
-	public function patch(string $uri, array $options = []): IResponse {
350
-		$this->preventLocalAddress($uri, $options);
351
-		$response = $this->client->request('patch', $uri, $this->buildRequestOptions($options));
352
-		return new Response($response);
353
-	}
354
-
355
-	/**
356
-	 * Sends a DELETE request
357
-	 *
358
-	 * @param string $uri
359
-	 * @param array $options Array such as
360
-	 *                       'body' => [
361
-	 *                       'field' => 'abc',
362
-	 *                       'other_field' => '123',
363
-	 *                       'file_name' => fopen('/path/to/file', 'r'),
364
-	 *                       ],
365
-	 *                       'headers' => [
366
-	 *                       'foo' => 'bar',
367
-	 *                       ],
368
-	 *                       'cookies' => [
369
-	 *                       'foo' => 'bar',
370
-	 *                       ],
371
-	 *                       'allow_redirects' => [
372
-	 *                       'max'       => 10,  // allow at most 10 redirects.
373
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
374
-	 *                       'referer'   => true,     // add a Referer header
375
-	 *                       'protocols' => ['https'] // only allow https URLs
376
-	 *                       ],
377
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
378
-	 *                       'verify' => true, // bool or string to CA file
379
-	 *                       'debug' => true,
380
-	 *                       'timeout' => 5,
381
-	 * @return IResponse
382
-	 * @throws \Exception If the request could not get completed
383
-	 */
384
-	public function delete(string $uri, array $options = []): IResponse {
385
-		$this->preventLocalAddress($uri, $options);
386
-		$response = $this->client->request('delete', $uri, $this->buildRequestOptions($options));
387
-		return new Response($response);
388
-	}
389
-
390
-	/**
391
-	 * Sends an OPTIONS request
392
-	 *
393
-	 * @param string $uri
394
-	 * @param array $options Array such as
395
-	 *                       'body' => [
396
-	 *                       'field' => 'abc',
397
-	 *                       'other_field' => '123',
398
-	 *                       'file_name' => fopen('/path/to/file', 'r'),
399
-	 *                       ],
400
-	 *                       'headers' => [
401
-	 *                       'foo' => 'bar',
402
-	 *                       ],
403
-	 *                       'cookies' => [
404
-	 *                       'foo' => 'bar',
405
-	 *                       ],
406
-	 *                       'allow_redirects' => [
407
-	 *                       'max'       => 10,  // allow at most 10 redirects.
408
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
409
-	 *                       'referer'   => true,     // add a Referer header
410
-	 *                       'protocols' => ['https'] // only allow https URLs
411
-	 *                       ],
412
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
413
-	 *                       'verify' => true, // bool or string to CA file
414
-	 *                       'debug' => true,
415
-	 *                       'timeout' => 5,
416
-	 * @return IResponse
417
-	 * @throws \Exception If the request could not get completed
418
-	 */
419
-	public function options(string $uri, array $options = []): IResponse {
420
-		$this->preventLocalAddress($uri, $options);
421
-		$response = $this->client->request('options', $uri, $this->buildRequestOptions($options));
422
-		return new Response($response);
423
-	}
424
-
425
-	/**
426
-	 * Get the response of a Throwable thrown by the request methods when possible
427
-	 *
428
-	 * @param \Throwable $e
429
-	 * @return IResponse
430
-	 * @throws \Throwable When $e did not have a response
431
-	 * @since 29.0.0
432
-	 */
433
-	public function getResponseFromThrowable(\Throwable $e): IResponse {
434
-		if (method_exists($e, 'hasResponse') && method_exists($e, 'getResponse') && $e->hasResponse()) {
435
-			return new Response($e->getResponse());
436
-		}
437
-
438
-		throw $e;
439
-	}
440
-
441
-	/**
442
-	 * Sends a HTTP request
443
-	 *
444
-	 * @param string $method The HTTP method to use
445
-	 * @param string $uri
446
-	 * @param array $options Array such as
447
-	 *                       'query' => [
448
-	 *                       'field' => 'abc',
449
-	 *                       'other_field' => '123',
450
-	 *                       'file_name' => fopen('/path/to/file', 'r'),
451
-	 *                       ],
452
-	 *                       'headers' => [
453
-	 *                       'foo' => 'bar',
454
-	 *                       ],
455
-	 *                       'cookies' => [
456
-	 *                       'foo' => 'bar',
457
-	 *                       ],
458
-	 *                       'allow_redirects' => [
459
-	 *                       'max'       => 10,  // allow at most 10 redirects.
460
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
461
-	 *                       'referer'   => true,     // add a Referer header
462
-	 *                       'protocols' => ['https'] // only allow https URLs
463
-	 *                       ],
464
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
465
-	 *                       'verify' => true, // bool or string to CA file
466
-	 *                       'debug' => true,
467
-	 *                       'timeout' => 5,
468
-	 * @return IResponse
469
-	 * @throws \Exception If the request could not get completed
470
-	 */
471
-	public function request(string $method, string $uri, array $options = []): IResponse {
472
-		$this->preventLocalAddress($uri, $options);
473
-		$response = $this->client->request($method, $uri, $this->buildRequestOptions($options));
474
-		$isStream = isset($options['stream']) && $options['stream'];
475
-		return new Response($response, $isStream);
476
-	}
477
-
478
-	protected function wrapGuzzlePromise(PromiseInterface $promise): IPromise {
479
-		return new GuzzlePromiseAdapter(
480
-			$promise,
481
-			$this->logger
482
-		);
483
-	}
484
-
485
-	/**
486
-	 * Sends an asynchronous GET request
487
-	 *
488
-	 * @param string $uri
489
-	 * @param array $options Array such as
490
-	 *                       'query' => [
491
-	 *                       'field' => 'abc',
492
-	 *                       'other_field' => '123',
493
-	 *                       'file_name' => fopen('/path/to/file', 'r'),
494
-	 *                       ],
495
-	 *                       'headers' => [
496
-	 *                       'foo' => 'bar',
497
-	 *                       ],
498
-	 *                       'cookies' => [
499
-	 *                       'foo' => 'bar',
500
-	 *                       ],
501
-	 *                       'allow_redirects' => [
502
-	 *                       'max'       => 10,  // allow at most 10 redirects.
503
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
504
-	 *                       'referer'   => true,     // add a Referer header
505
-	 *                       'protocols' => ['https'] // only allow https URLs
506
-	 *                       ],
507
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
508
-	 *                       'verify' => true, // bool or string to CA file
509
-	 *                       'debug' => true,
510
-	 *                       'timeout' => 5,
511
-	 * @return IPromise
512
-	 */
513
-	public function getAsync(string $uri, array $options = []): IPromise {
514
-		$this->preventLocalAddress($uri, $options);
515
-		$response = $this->client->requestAsync('get', $uri, $this->buildRequestOptions($options));
516
-		return $this->wrapGuzzlePromise($response);
517
-	}
518
-
519
-	/**
520
-	 * Sends an asynchronous HEAD request
521
-	 *
522
-	 * @param string $uri
523
-	 * @param array $options Array such as
524
-	 *                       'headers' => [
525
-	 *                       'foo' => 'bar',
526
-	 *                       ],
527
-	 *                       'cookies' => [
528
-	 *                       'foo' => 'bar',
529
-	 *                       ],
530
-	 *                       'allow_redirects' => [
531
-	 *                       'max'       => 10,  // allow at most 10 redirects.
532
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
533
-	 *                       'referer'   => true,     // add a Referer header
534
-	 *                       'protocols' => ['https'] // only allow https URLs
535
-	 *                       ],
536
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
537
-	 *                       'verify' => true, // bool or string to CA file
538
-	 *                       'debug' => true,
539
-	 *                       'timeout' => 5,
540
-	 * @return IPromise
541
-	 */
542
-	public function headAsync(string $uri, array $options = []): IPromise {
543
-		$this->preventLocalAddress($uri, $options);
544
-		$response = $this->client->requestAsync('head', $uri, $this->buildRequestOptions($options));
545
-		return $this->wrapGuzzlePromise($response);
546
-	}
547
-
548
-	/**
549
-	 * Sends an asynchronous POST request
550
-	 *
551
-	 * @param string $uri
552
-	 * @param array $options Array such as
553
-	 *                       'body' => [
554
-	 *                       'field' => 'abc',
555
-	 *                       'other_field' => '123',
556
-	 *                       'file_name' => fopen('/path/to/file', 'r'),
557
-	 *                       ],
558
-	 *                       'headers' => [
559
-	 *                       'foo' => 'bar',
560
-	 *                       ],
561
-	 *                       'cookies' => [
562
-	 *                       'foo' => 'bar',
563
-	 *                       ],
564
-	 *                       'allow_redirects' => [
565
-	 *                       'max'       => 10,  // allow at most 10 redirects.
566
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
567
-	 *                       'referer'   => true,     // add a Referer header
568
-	 *                       'protocols' => ['https'] // only allow https URLs
569
-	 *                       ],
570
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
571
-	 *                       'verify' => true, // bool or string to CA file
572
-	 *                       'debug' => true,
573
-	 *                       'timeout' => 5,
574
-	 * @return IPromise
575
-	 */
576
-	public function postAsync(string $uri, array $options = []): IPromise {
577
-		$this->preventLocalAddress($uri, $options);
578
-
579
-		if (isset($options['body']) && is_array($options['body'])) {
580
-			$options['form_params'] = $options['body'];
581
-			unset($options['body']);
582
-		}
583
-
584
-		return $this->wrapGuzzlePromise($this->client->requestAsync('post', $uri, $this->buildRequestOptions($options)));
585
-	}
586
-
587
-	/**
588
-	 * Sends an asynchronous PUT request
589
-	 *
590
-	 * @param string $uri
591
-	 * @param array $options Array such as
592
-	 *                       'body' => [
593
-	 *                       'field' => 'abc',
594
-	 *                       'other_field' => '123',
595
-	 *                       'file_name' => fopen('/path/to/file', 'r'),
596
-	 *                       ],
597
-	 *                       'headers' => [
598
-	 *                       'foo' => 'bar',
599
-	 *                       ],
600
-	 *                       'cookies' => [
601
-	 *                       'foo' => 'bar',
602
-	 *                       ],
603
-	 *                       'allow_redirects' => [
604
-	 *                       'max'       => 10,  // allow at most 10 redirects.
605
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
606
-	 *                       'referer'   => true,     // add a Referer header
607
-	 *                       'protocols' => ['https'] // only allow https URLs
608
-	 *                       ],
609
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
610
-	 *                       'verify' => true, // bool or string to CA file
611
-	 *                       'debug' => true,
612
-	 *                       'timeout' => 5,
613
-	 * @return IPromise
614
-	 */
615
-	public function putAsync(string $uri, array $options = []): IPromise {
616
-		$this->preventLocalAddress($uri, $options);
617
-		$response = $this->client->requestAsync('put', $uri, $this->buildRequestOptions($options));
618
-		return $this->wrapGuzzlePromise($response);
619
-	}
620
-
621
-	/**
622
-	 * Sends an asynchronous DELETE request
623
-	 *
624
-	 * @param string $uri
625
-	 * @param array $options Array such as
626
-	 *                       'body' => [
627
-	 *                       'field' => 'abc',
628
-	 *                       'other_field' => '123',
629
-	 *                       'file_name' => fopen('/path/to/file', 'r'),
630
-	 *                       ],
631
-	 *                       'headers' => [
632
-	 *                       'foo' => 'bar',
633
-	 *                       ],
634
-	 *                       'cookies' => [
635
-	 *                       'foo' => 'bar',
636
-	 *                       ],
637
-	 *                       'allow_redirects' => [
638
-	 *                       'max'       => 10,  // allow at most 10 redirects.
639
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
640
-	 *                       'referer'   => true,     // add a Referer header
641
-	 *                       'protocols' => ['https'] // only allow https URLs
642
-	 *                       ],
643
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
644
-	 *                       'verify' => true, // bool or string to CA file
645
-	 *                       'debug' => true,
646
-	 *                       'timeout' => 5,
647
-	 * @return IPromise
648
-	 */
649
-	public function deleteAsync(string $uri, array $options = []): IPromise {
650
-		$this->preventLocalAddress($uri, $options);
651
-		$response = $this->client->requestAsync('delete', $uri, $this->buildRequestOptions($options));
652
-		return $this->wrapGuzzlePromise($response);
653
-	}
654
-
655
-	/**
656
-	 * Sends an asynchronous OPTIONS request
657
-	 *
658
-	 * @param string $uri
659
-	 * @param array $options Array such as
660
-	 *                       'body' => [
661
-	 *                       'field' => 'abc',
662
-	 *                       'other_field' => '123',
663
-	 *                       'file_name' => fopen('/path/to/file', 'r'),
664
-	 *                       ],
665
-	 *                       'headers' => [
666
-	 *                       'foo' => 'bar',
667
-	 *                       ],
668
-	 *                       'cookies' => [
669
-	 *                       'foo' => 'bar',
670
-	 *                       ],
671
-	 *                       'allow_redirects' => [
672
-	 *                       'max'       => 10,  // allow at most 10 redirects.
673
-	 *                       'strict'    => true,     // use "strict" RFC compliant redirects.
674
-	 *                       'referer'   => true,     // add a Referer header
675
-	 *                       'protocols' => ['https'] // only allow https URLs
676
-	 *                       ],
677
-	 *                       'sink' => '/path/to/file', // save to a file or a stream
678
-	 *                       'verify' => true, // bool or string to CA file
679
-	 *                       'debug' => true,
680
-	 *                       'timeout' => 5,
681
-	 * @return IPromise
682
-	 */
683
-	public function optionsAsync(string $uri, array $options = []): IPromise {
684
-		$this->preventLocalAddress($uri, $options);
685
-		$response = $this->client->requestAsync('options', $uri, $this->buildRequestOptions($options));
686
-		return $this->wrapGuzzlePromise($response);
687
-	}
31
+    /** @var GuzzleClient */
32
+    private $client;
33
+    /** @var IConfig */
34
+    private $config;
35
+    /** @var ICertificateManager */
36
+    private $certificateManager;
37
+    private IRemoteHostValidator $remoteHostValidator;
38
+
39
+    public function __construct(
40
+        IConfig $config,
41
+        ICertificateManager $certificateManager,
42
+        GuzzleClient $client,
43
+        IRemoteHostValidator $remoteHostValidator,
44
+        protected LoggerInterface $logger,
45
+        protected ServerVersion $serverVersion,
46
+    ) {
47
+        $this->config = $config;
48
+        $this->client = $client;
49
+        $this->certificateManager = $certificateManager;
50
+        $this->remoteHostValidator = $remoteHostValidator;
51
+    }
52
+
53
+    private function buildRequestOptions(array $options): array {
54
+        $proxy = $this->getProxyUri();
55
+
56
+        $defaults = [
57
+            RequestOptions::VERIFY => $this->getCertBundle(),
58
+            RequestOptions::TIMEOUT => IClient::DEFAULT_REQUEST_TIMEOUT,
59
+        ];
60
+
61
+        $options['nextcloud']['allow_local_address'] = $this->isLocalAddressAllowed($options);
62
+        if ($options['nextcloud']['allow_local_address'] === false) {
63
+            $onRedirectFunction = function (
64
+                \Psr\Http\Message\RequestInterface $request,
65
+                \Psr\Http\Message\ResponseInterface $response,
66
+                \Psr\Http\Message\UriInterface $uri,
67
+            ) use ($options) {
68
+                $this->preventLocalAddress($uri->__toString(), $options);
69
+            };
70
+
71
+            $defaults[RequestOptions::ALLOW_REDIRECTS] = [
72
+                'on_redirect' => $onRedirectFunction
73
+            ];
74
+        }
75
+
76
+        // Only add RequestOptions::PROXY if Nextcloud is explicitly
77
+        // configured to use a proxy. This is needed in order not to override
78
+        // Guzzle default values.
79
+        if ($proxy !== null) {
80
+            $defaults[RequestOptions::PROXY] = $proxy;
81
+        }
82
+
83
+        $options = array_merge($defaults, $options);
84
+
85
+        if (!isset($options[RequestOptions::HEADERS]['User-Agent'])) {
86
+            $userAgent = 'Nextcloud-Server-Crawler/' . $this->serverVersion->getVersionString();
87
+            $options[RequestOptions::HEADERS]['User-Agent'] = $userAgent;
88
+        }
89
+
90
+        if (!isset($options[RequestOptions::HEADERS]['Accept-Encoding'])) {
91
+            $options[RequestOptions::HEADERS]['Accept-Encoding'] = 'gzip';
92
+        }
93
+
94
+        // Fallback for save_to
95
+        if (isset($options['save_to'])) {
96
+            $options['sink'] = $options['save_to'];
97
+            unset($options['save_to']);
98
+        }
99
+
100
+        return $options;
101
+    }
102
+
103
+    private function getCertBundle(): string {
104
+        // If the instance is not yet setup we need to use the static path as
105
+        // $this->certificateManager->getAbsoluteBundlePath() tries to instantiate
106
+        // a view
107
+        if (!$this->config->getSystemValueBool('installed', false)) {
108
+            return $this->certificateManager->getDefaultCertificatesBundlePath();
109
+        }
110
+
111
+        return $this->certificateManager->getAbsoluteBundlePath();
112
+    }
113
+
114
+    /**
115
+     * Returns a null or an associative array specifying the proxy URI for
116
+     * 'http' and 'https' schemes, in addition to a 'no' key value pair
117
+     * providing a list of host names that should not be proxied to.
118
+     *
119
+     * @return array|null
120
+     *
121
+     * The return array looks like:
122
+     * [
123
+     *   'http' => 'username:[email protected]',
124
+     *   'https' => 'username:[email protected]',
125
+     *   'no' => ['foo.com', 'bar.com']
126
+     * ]
127
+     *
128
+     */
129
+    private function getProxyUri(): ?array {
130
+        $proxyHost = $this->config->getSystemValueString('proxy', '');
131
+
132
+        if ($proxyHost === '') {
133
+            return null;
134
+        }
135
+
136
+        $proxyUserPwd = $this->config->getSystemValueString('proxyuserpwd', '');
137
+        if ($proxyUserPwd !== '') {
138
+            $proxyHost = $proxyUserPwd . '@' . $proxyHost;
139
+        }
140
+
141
+        $proxy = [
142
+            'http' => $proxyHost,
143
+            'https' => $proxyHost,
144
+        ];
145
+
146
+        $proxyExclude = $this->config->getSystemValue('proxyexclude', []);
147
+        if ($proxyExclude !== [] && $proxyExclude !== null) {
148
+            $proxy['no'] = $proxyExclude;
149
+        }
150
+
151
+        return $proxy;
152
+    }
153
+
154
+    private function isLocalAddressAllowed(array $options) : bool {
155
+        if (($options['nextcloud']['allow_local_address'] ?? false)
156
+            || $this->config->getSystemValueBool('allow_local_remote_servers', false)) {
157
+            return true;
158
+        }
159
+
160
+        return false;
161
+    }
162
+
163
+    protected function preventLocalAddress(string $uri, array $options): void {
164
+        $host = parse_url($uri, PHP_URL_HOST);
165
+        if ($host === false || $host === null) {
166
+            throw new LocalServerException('Could not detect any host');
167
+        }
168
+
169
+        if ($this->isLocalAddressAllowed($options)) {
170
+            return;
171
+        }
172
+
173
+        if (!$this->remoteHostValidator->isValid($host)) {
174
+            throw new LocalServerException('Host "' . $host . '" violates local access rules');
175
+        }
176
+    }
177
+
178
+    /**
179
+     * Sends a GET request
180
+     *
181
+     * @param string $uri
182
+     * @param array $options Array such as
183
+     *                       'query' => [
184
+     *                       'field' => 'abc',
185
+     *                       'other_field' => '123',
186
+     *                       'file_name' => fopen('/path/to/file', 'r'),
187
+     *                       ],
188
+     *                       'headers' => [
189
+     *                       'foo' => 'bar',
190
+     *                       ],
191
+     *                       'cookies' => [
192
+     *                       'foo' => 'bar',
193
+     *                       ],
194
+     *                       'allow_redirects' => [
195
+     *                       'max'       => 10,  // allow at most 10 redirects.
196
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
197
+     *                       'referer'   => true,     // add a Referer header
198
+     *                       'protocols' => ['https'] // only allow https URLs
199
+     *                       ],
200
+     *                       'sink' => '/path/to/file', // save to a file or a stream
201
+     *                       'verify' => true, // bool or string to CA file
202
+     *                       'debug' => true,
203
+     *                       'timeout' => 5,
204
+     * @return IResponse
205
+     * @throws \Exception If the request could not get completed
206
+     */
207
+    public function get(string $uri, array $options = []): IResponse {
208
+        $this->preventLocalAddress($uri, $options);
209
+        $response = $this->client->request('get', $uri, $this->buildRequestOptions($options));
210
+        $isStream = isset($options['stream']) && $options['stream'];
211
+        return new Response($response, $isStream);
212
+    }
213
+
214
+    /**
215
+     * Sends a HEAD request
216
+     *
217
+     * @param string $uri
218
+     * @param array $options Array such as
219
+     *                       'headers' => [
220
+     *                       'foo' => 'bar',
221
+     *                       ],
222
+     *                       'cookies' => [
223
+     *                       'foo' => 'bar',
224
+     *                       ],
225
+     *                       'allow_redirects' => [
226
+     *                       'max'       => 10,  // allow at most 10 redirects.
227
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
228
+     *                       'referer'   => true,     // add a Referer header
229
+     *                       'protocols' => ['https'] // only allow https URLs
230
+     *                       ],
231
+     *                       'sink' => '/path/to/file', // save to a file or a stream
232
+     *                       'verify' => true, // bool or string to CA file
233
+     *                       'debug' => true,
234
+     *                       'timeout' => 5,
235
+     * @return IResponse
236
+     * @throws \Exception If the request could not get completed
237
+     */
238
+    public function head(string $uri, array $options = []): IResponse {
239
+        $this->preventLocalAddress($uri, $options);
240
+        $response = $this->client->request('head', $uri, $this->buildRequestOptions($options));
241
+        return new Response($response);
242
+    }
243
+
244
+    /**
245
+     * Sends a POST request
246
+     *
247
+     * @param string $uri
248
+     * @param array $options Array such as
249
+     *                       'body' => [
250
+     *                       'field' => 'abc',
251
+     *                       'other_field' => '123',
252
+     *                       'file_name' => fopen('/path/to/file', 'r'),
253
+     *                       ],
254
+     *                       'headers' => [
255
+     *                       'foo' => 'bar',
256
+     *                       ],
257
+     *                       'cookies' => [
258
+     *                       'foo' => 'bar',
259
+     *                       ],
260
+     *                       'allow_redirects' => [
261
+     *                       'max'       => 10,  // allow at most 10 redirects.
262
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
263
+     *                       'referer'   => true,     // add a Referer header
264
+     *                       'protocols' => ['https'] // only allow https URLs
265
+     *                       ],
266
+     *                       'sink' => '/path/to/file', // save to a file or a stream
267
+     *                       'verify' => true, // bool or string to CA file
268
+     *                       'debug' => true,
269
+     *                       'timeout' => 5,
270
+     * @return IResponse
271
+     * @throws \Exception If the request could not get completed
272
+     */
273
+    public function post(string $uri, array $options = []): IResponse {
274
+        $this->preventLocalAddress($uri, $options);
275
+
276
+        if (isset($options['body']) && is_array($options['body'])) {
277
+            $options['form_params'] = $options['body'];
278
+            unset($options['body']);
279
+        }
280
+        $response = $this->client->request('post', $uri, $this->buildRequestOptions($options));
281
+        $isStream = isset($options['stream']) && $options['stream'];
282
+        return new Response($response, $isStream);
283
+    }
284
+
285
+    /**
286
+     * Sends a PUT request
287
+     *
288
+     * @param string $uri
289
+     * @param array $options Array such as
290
+     *                       'body' => [
291
+     *                       'field' => 'abc',
292
+     *                       'other_field' => '123',
293
+     *                       'file_name' => fopen('/path/to/file', 'r'),
294
+     *                       ],
295
+     *                       'headers' => [
296
+     *                       'foo' => 'bar',
297
+     *                       ],
298
+     *                       'cookies' => [
299
+     *                       'foo' => 'bar',
300
+     *                       ],
301
+     *                       'allow_redirects' => [
302
+     *                       'max'       => 10,  // allow at most 10 redirects.
303
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
304
+     *                       'referer'   => true,     // add a Referer header
305
+     *                       'protocols' => ['https'] // only allow https URLs
306
+     *                       ],
307
+     *                       'sink' => '/path/to/file', // save to a file or a stream
308
+     *                       'verify' => true, // bool or string to CA file
309
+     *                       'debug' => true,
310
+     *                       'timeout' => 5,
311
+     * @return IResponse
312
+     * @throws \Exception If the request could not get completed
313
+     */
314
+    public function put(string $uri, array $options = []): IResponse {
315
+        $this->preventLocalAddress($uri, $options);
316
+        $response = $this->client->request('put', $uri, $this->buildRequestOptions($options));
317
+        return new Response($response);
318
+    }
319
+
320
+    /**
321
+     * Sends a PATCH request
322
+     *
323
+     * @param string $uri
324
+     * @param array $options Array such as
325
+     *                       'body' => [
326
+     *                       'field' => 'abc',
327
+     *                       'other_field' => '123',
328
+     *                       'file_name' => fopen('/path/to/file', 'r'),
329
+     *                       ],
330
+     *                       'headers' => [
331
+     *                       'foo' => 'bar',
332
+     *                       ],
333
+     *                       'cookies' => [
334
+     *                       'foo' => 'bar',
335
+     *                       ],
336
+     *                       'allow_redirects' => [
337
+     *                       'max'       => 10,  // allow at most 10 redirects.
338
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
339
+     *                       'referer'   => true,     // add a Referer header
340
+     *                       'protocols' => ['https'] // only allow https URLs
341
+     *                       ],
342
+     *                       'sink' => '/path/to/file', // save to a file or a stream
343
+     *                       'verify' => true, // bool or string to CA file
344
+     *                       'debug' => true,
345
+     *                       'timeout' => 5,
346
+     * @return IResponse
347
+     * @throws \Exception If the request could not get completed
348
+     */
349
+    public function patch(string $uri, array $options = []): IResponse {
350
+        $this->preventLocalAddress($uri, $options);
351
+        $response = $this->client->request('patch', $uri, $this->buildRequestOptions($options));
352
+        return new Response($response);
353
+    }
354
+
355
+    /**
356
+     * Sends a DELETE request
357
+     *
358
+     * @param string $uri
359
+     * @param array $options Array such as
360
+     *                       'body' => [
361
+     *                       'field' => 'abc',
362
+     *                       'other_field' => '123',
363
+     *                       'file_name' => fopen('/path/to/file', 'r'),
364
+     *                       ],
365
+     *                       'headers' => [
366
+     *                       'foo' => 'bar',
367
+     *                       ],
368
+     *                       'cookies' => [
369
+     *                       'foo' => 'bar',
370
+     *                       ],
371
+     *                       'allow_redirects' => [
372
+     *                       'max'       => 10,  // allow at most 10 redirects.
373
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
374
+     *                       'referer'   => true,     // add a Referer header
375
+     *                       'protocols' => ['https'] // only allow https URLs
376
+     *                       ],
377
+     *                       'sink' => '/path/to/file', // save to a file or a stream
378
+     *                       'verify' => true, // bool or string to CA file
379
+     *                       'debug' => true,
380
+     *                       'timeout' => 5,
381
+     * @return IResponse
382
+     * @throws \Exception If the request could not get completed
383
+     */
384
+    public function delete(string $uri, array $options = []): IResponse {
385
+        $this->preventLocalAddress($uri, $options);
386
+        $response = $this->client->request('delete', $uri, $this->buildRequestOptions($options));
387
+        return new Response($response);
388
+    }
389
+
390
+    /**
391
+     * Sends an OPTIONS request
392
+     *
393
+     * @param string $uri
394
+     * @param array $options Array such as
395
+     *                       'body' => [
396
+     *                       'field' => 'abc',
397
+     *                       'other_field' => '123',
398
+     *                       'file_name' => fopen('/path/to/file', 'r'),
399
+     *                       ],
400
+     *                       'headers' => [
401
+     *                       'foo' => 'bar',
402
+     *                       ],
403
+     *                       'cookies' => [
404
+     *                       'foo' => 'bar',
405
+     *                       ],
406
+     *                       'allow_redirects' => [
407
+     *                       'max'       => 10,  // allow at most 10 redirects.
408
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
409
+     *                       'referer'   => true,     // add a Referer header
410
+     *                       'protocols' => ['https'] // only allow https URLs
411
+     *                       ],
412
+     *                       'sink' => '/path/to/file', // save to a file or a stream
413
+     *                       'verify' => true, // bool or string to CA file
414
+     *                       'debug' => true,
415
+     *                       'timeout' => 5,
416
+     * @return IResponse
417
+     * @throws \Exception If the request could not get completed
418
+     */
419
+    public function options(string $uri, array $options = []): IResponse {
420
+        $this->preventLocalAddress($uri, $options);
421
+        $response = $this->client->request('options', $uri, $this->buildRequestOptions($options));
422
+        return new Response($response);
423
+    }
424
+
425
+    /**
426
+     * Get the response of a Throwable thrown by the request methods when possible
427
+     *
428
+     * @param \Throwable $e
429
+     * @return IResponse
430
+     * @throws \Throwable When $e did not have a response
431
+     * @since 29.0.0
432
+     */
433
+    public function getResponseFromThrowable(\Throwable $e): IResponse {
434
+        if (method_exists($e, 'hasResponse') && method_exists($e, 'getResponse') && $e->hasResponse()) {
435
+            return new Response($e->getResponse());
436
+        }
437
+
438
+        throw $e;
439
+    }
440
+
441
+    /**
442
+     * Sends a HTTP request
443
+     *
444
+     * @param string $method The HTTP method to use
445
+     * @param string $uri
446
+     * @param array $options Array such as
447
+     *                       'query' => [
448
+     *                       'field' => 'abc',
449
+     *                       'other_field' => '123',
450
+     *                       'file_name' => fopen('/path/to/file', 'r'),
451
+     *                       ],
452
+     *                       'headers' => [
453
+     *                       'foo' => 'bar',
454
+     *                       ],
455
+     *                       'cookies' => [
456
+     *                       'foo' => 'bar',
457
+     *                       ],
458
+     *                       'allow_redirects' => [
459
+     *                       'max'       => 10,  // allow at most 10 redirects.
460
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
461
+     *                       'referer'   => true,     // add a Referer header
462
+     *                       'protocols' => ['https'] // only allow https URLs
463
+     *                       ],
464
+     *                       'sink' => '/path/to/file', // save to a file or a stream
465
+     *                       'verify' => true, // bool or string to CA file
466
+     *                       'debug' => true,
467
+     *                       'timeout' => 5,
468
+     * @return IResponse
469
+     * @throws \Exception If the request could not get completed
470
+     */
471
+    public function request(string $method, string $uri, array $options = []): IResponse {
472
+        $this->preventLocalAddress($uri, $options);
473
+        $response = $this->client->request($method, $uri, $this->buildRequestOptions($options));
474
+        $isStream = isset($options['stream']) && $options['stream'];
475
+        return new Response($response, $isStream);
476
+    }
477
+
478
+    protected function wrapGuzzlePromise(PromiseInterface $promise): IPromise {
479
+        return new GuzzlePromiseAdapter(
480
+            $promise,
481
+            $this->logger
482
+        );
483
+    }
484
+
485
+    /**
486
+     * Sends an asynchronous GET request
487
+     *
488
+     * @param string $uri
489
+     * @param array $options Array such as
490
+     *                       'query' => [
491
+     *                       'field' => 'abc',
492
+     *                       'other_field' => '123',
493
+     *                       'file_name' => fopen('/path/to/file', 'r'),
494
+     *                       ],
495
+     *                       'headers' => [
496
+     *                       'foo' => 'bar',
497
+     *                       ],
498
+     *                       'cookies' => [
499
+     *                       'foo' => 'bar',
500
+     *                       ],
501
+     *                       'allow_redirects' => [
502
+     *                       'max'       => 10,  // allow at most 10 redirects.
503
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
504
+     *                       'referer'   => true,     // add a Referer header
505
+     *                       'protocols' => ['https'] // only allow https URLs
506
+     *                       ],
507
+     *                       'sink' => '/path/to/file', // save to a file or a stream
508
+     *                       'verify' => true, // bool or string to CA file
509
+     *                       'debug' => true,
510
+     *                       'timeout' => 5,
511
+     * @return IPromise
512
+     */
513
+    public function getAsync(string $uri, array $options = []): IPromise {
514
+        $this->preventLocalAddress($uri, $options);
515
+        $response = $this->client->requestAsync('get', $uri, $this->buildRequestOptions($options));
516
+        return $this->wrapGuzzlePromise($response);
517
+    }
518
+
519
+    /**
520
+     * Sends an asynchronous HEAD request
521
+     *
522
+     * @param string $uri
523
+     * @param array $options Array such as
524
+     *                       'headers' => [
525
+     *                       'foo' => 'bar',
526
+     *                       ],
527
+     *                       'cookies' => [
528
+     *                       'foo' => 'bar',
529
+     *                       ],
530
+     *                       'allow_redirects' => [
531
+     *                       'max'       => 10,  // allow at most 10 redirects.
532
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
533
+     *                       'referer'   => true,     // add a Referer header
534
+     *                       'protocols' => ['https'] // only allow https URLs
535
+     *                       ],
536
+     *                       'sink' => '/path/to/file', // save to a file or a stream
537
+     *                       'verify' => true, // bool or string to CA file
538
+     *                       'debug' => true,
539
+     *                       'timeout' => 5,
540
+     * @return IPromise
541
+     */
542
+    public function headAsync(string $uri, array $options = []): IPromise {
543
+        $this->preventLocalAddress($uri, $options);
544
+        $response = $this->client->requestAsync('head', $uri, $this->buildRequestOptions($options));
545
+        return $this->wrapGuzzlePromise($response);
546
+    }
547
+
548
+    /**
549
+     * Sends an asynchronous POST request
550
+     *
551
+     * @param string $uri
552
+     * @param array $options Array such as
553
+     *                       'body' => [
554
+     *                       'field' => 'abc',
555
+     *                       'other_field' => '123',
556
+     *                       'file_name' => fopen('/path/to/file', 'r'),
557
+     *                       ],
558
+     *                       'headers' => [
559
+     *                       'foo' => 'bar',
560
+     *                       ],
561
+     *                       'cookies' => [
562
+     *                       'foo' => 'bar',
563
+     *                       ],
564
+     *                       'allow_redirects' => [
565
+     *                       'max'       => 10,  // allow at most 10 redirects.
566
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
567
+     *                       'referer'   => true,     // add a Referer header
568
+     *                       'protocols' => ['https'] // only allow https URLs
569
+     *                       ],
570
+     *                       'sink' => '/path/to/file', // save to a file or a stream
571
+     *                       'verify' => true, // bool or string to CA file
572
+     *                       'debug' => true,
573
+     *                       'timeout' => 5,
574
+     * @return IPromise
575
+     */
576
+    public function postAsync(string $uri, array $options = []): IPromise {
577
+        $this->preventLocalAddress($uri, $options);
578
+
579
+        if (isset($options['body']) && is_array($options['body'])) {
580
+            $options['form_params'] = $options['body'];
581
+            unset($options['body']);
582
+        }
583
+
584
+        return $this->wrapGuzzlePromise($this->client->requestAsync('post', $uri, $this->buildRequestOptions($options)));
585
+    }
586
+
587
+    /**
588
+     * Sends an asynchronous PUT request
589
+     *
590
+     * @param string $uri
591
+     * @param array $options Array such as
592
+     *                       'body' => [
593
+     *                       'field' => 'abc',
594
+     *                       'other_field' => '123',
595
+     *                       'file_name' => fopen('/path/to/file', 'r'),
596
+     *                       ],
597
+     *                       'headers' => [
598
+     *                       'foo' => 'bar',
599
+     *                       ],
600
+     *                       'cookies' => [
601
+     *                       'foo' => 'bar',
602
+     *                       ],
603
+     *                       'allow_redirects' => [
604
+     *                       'max'       => 10,  // allow at most 10 redirects.
605
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
606
+     *                       'referer'   => true,     // add a Referer header
607
+     *                       'protocols' => ['https'] // only allow https URLs
608
+     *                       ],
609
+     *                       'sink' => '/path/to/file', // save to a file or a stream
610
+     *                       'verify' => true, // bool or string to CA file
611
+     *                       'debug' => true,
612
+     *                       'timeout' => 5,
613
+     * @return IPromise
614
+     */
615
+    public function putAsync(string $uri, array $options = []): IPromise {
616
+        $this->preventLocalAddress($uri, $options);
617
+        $response = $this->client->requestAsync('put', $uri, $this->buildRequestOptions($options));
618
+        return $this->wrapGuzzlePromise($response);
619
+    }
620
+
621
+    /**
622
+     * Sends an asynchronous DELETE request
623
+     *
624
+     * @param string $uri
625
+     * @param array $options Array such as
626
+     *                       'body' => [
627
+     *                       'field' => 'abc',
628
+     *                       'other_field' => '123',
629
+     *                       'file_name' => fopen('/path/to/file', 'r'),
630
+     *                       ],
631
+     *                       'headers' => [
632
+     *                       'foo' => 'bar',
633
+     *                       ],
634
+     *                       'cookies' => [
635
+     *                       'foo' => 'bar',
636
+     *                       ],
637
+     *                       'allow_redirects' => [
638
+     *                       'max'       => 10,  // allow at most 10 redirects.
639
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
640
+     *                       'referer'   => true,     // add a Referer header
641
+     *                       'protocols' => ['https'] // only allow https URLs
642
+     *                       ],
643
+     *                       'sink' => '/path/to/file', // save to a file or a stream
644
+     *                       'verify' => true, // bool or string to CA file
645
+     *                       'debug' => true,
646
+     *                       'timeout' => 5,
647
+     * @return IPromise
648
+     */
649
+    public function deleteAsync(string $uri, array $options = []): IPromise {
650
+        $this->preventLocalAddress($uri, $options);
651
+        $response = $this->client->requestAsync('delete', $uri, $this->buildRequestOptions($options));
652
+        return $this->wrapGuzzlePromise($response);
653
+    }
654
+
655
+    /**
656
+     * Sends an asynchronous OPTIONS request
657
+     *
658
+     * @param string $uri
659
+     * @param array $options Array such as
660
+     *                       'body' => [
661
+     *                       'field' => 'abc',
662
+     *                       'other_field' => '123',
663
+     *                       'file_name' => fopen('/path/to/file', 'r'),
664
+     *                       ],
665
+     *                       'headers' => [
666
+     *                       'foo' => 'bar',
667
+     *                       ],
668
+     *                       'cookies' => [
669
+     *                       'foo' => 'bar',
670
+     *                       ],
671
+     *                       'allow_redirects' => [
672
+     *                       'max'       => 10,  // allow at most 10 redirects.
673
+     *                       'strict'    => true,     // use "strict" RFC compliant redirects.
674
+     *                       'referer'   => true,     // add a Referer header
675
+     *                       'protocols' => ['https'] // only allow https URLs
676
+     *                       ],
677
+     *                       'sink' => '/path/to/file', // save to a file or a stream
678
+     *                       'verify' => true, // bool or string to CA file
679
+     *                       'debug' => true,
680
+     *                       'timeout' => 5,
681
+     * @return IPromise
682
+     */
683
+    public function optionsAsync(string $uri, array $options = []): IPromise {
684
+        $this->preventLocalAddress($uri, $options);
685
+        $response = $this->client->requestAsync('options', $uri, $this->buildRequestOptions($options));
686
+        return $this->wrapGuzzlePromise($response);
687
+    }
688 688
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -60,7 +60,7 @@  discard block
 block discarded – undo
60 60
 
61 61
 		$options['nextcloud']['allow_local_address'] = $this->isLocalAddressAllowed($options);
62 62
 		if ($options['nextcloud']['allow_local_address'] === false) {
63
-			$onRedirectFunction = function (
63
+			$onRedirectFunction = function(
64 64
 				\Psr\Http\Message\RequestInterface $request,
65 65
 				\Psr\Http\Message\ResponseInterface $response,
66 66
 				\Psr\Http\Message\UriInterface $uri,
@@ -83,7 +83,7 @@  discard block
 block discarded – undo
83 83
 		$options = array_merge($defaults, $options);
84 84
 
85 85
 		if (!isset($options[RequestOptions::HEADERS]['User-Agent'])) {
86
-			$userAgent = 'Nextcloud-Server-Crawler/' . $this->serverVersion->getVersionString();
86
+			$userAgent = 'Nextcloud-Server-Crawler/'.$this->serverVersion->getVersionString();
87 87
 			$options[RequestOptions::HEADERS]['User-Agent'] = $userAgent;
88 88
 		}
89 89
 
@@ -135,7 +135,7 @@  discard block
 block discarded – undo
135 135
 
136 136
 		$proxyUserPwd = $this->config->getSystemValueString('proxyuserpwd', '');
137 137
 		if ($proxyUserPwd !== '') {
138
-			$proxyHost = $proxyUserPwd . '@' . $proxyHost;
138
+			$proxyHost = $proxyUserPwd.'@'.$proxyHost;
139 139
 		}
140 140
 
141 141
 		$proxy = [
@@ -171,7 +171,7 @@  discard block
 block discarded – undo
171 171
 		}
172 172
 
173 173
 		if (!$this->remoteHostValidator->isValid($host)) {
174
-			throw new LocalServerException('Host "' . $host . '" violates local access rules');
174
+			throw new LocalServerException('Host "'.$host.'" violates local access rules');
175 175
 		}
176 176
 	}
177 177
 
Please login to merge, or discard this patch.
lib/private/Files/ObjectStore/S3ConnectionTrait.php 2 patches
Indentation   +259 added lines, -259 removed lines patch added patch discarded remove patch
@@ -24,263 +24,263 @@
 block discarded – undo
24 24
 use Psr\Log\LoggerInterface;
25 25
 
26 26
 trait S3ConnectionTrait {
27
-	use S3ConfigTrait;
28
-
29
-	protected string $id;
30
-
31
-	protected bool $test;
32
-
33
-	protected ?S3Client $connection = null;
34
-
35
-	private ?ICache $existingBucketsCache = null;
36
-
37
-	protected function parseParams($params) {
38
-		if (empty($params['bucket'])) {
39
-			throw new \Exception('Bucket has to be configured.');
40
-		}
41
-
42
-		$this->id = 'amazon::' . $params['bucket'];
43
-
44
-		$this->test = isset($params['test']);
45
-		$this->bucket = $params['bucket'];
46
-		// Default to 5 like the S3 SDK does
47
-		$this->concurrency = $params['concurrency'] ?? 5;
48
-		$this->proxy = $params['proxy'] ?? false;
49
-		$this->connectTimeout = $params['connect_timeout'] ?? 5;
50
-		$this->timeout = $params['timeout'] ?? 15;
51
-		$this->storageClass = !empty($params['storageClass']) ? $params['storageClass'] : 'STANDARD';
52
-		$this->uploadPartSize = $params['uploadPartSize'] ?? 524288000;
53
-		$this->putSizeLimit = $params['putSizeLimit'] ?? 104857600;
54
-		$this->copySizeLimit = $params['copySizeLimit'] ?? 5242880000;
55
-		$this->useMultipartCopy = (bool)($params['useMultipartCopy'] ?? true);
56
-		$this->retriesMaxAttempts = $params['retriesMaxAttempts'] ?? 5;
57
-		$params['region'] = empty($params['region']) ? 'eu-west-1' : $params['region'];
58
-		$params['hostname'] = empty($params['hostname']) ? 's3.' . $params['region'] . '.amazonaws.com' : $params['hostname'];
59
-		$params['s3-accelerate'] = $params['hostname'] === 's3-accelerate.amazonaws.com' || $params['hostname'] === 's3-accelerate.dualstack.amazonaws.com';
60
-		if (!isset($params['port']) || $params['port'] === '') {
61
-			$params['port'] = (isset($params['use_ssl']) && $params['use_ssl'] === false) ? 80 : 443;
62
-		}
63
-		$params['verify_bucket_exists'] = $params['verify_bucket_exists'] ?? true;
64
-
65
-		if ($params['s3-accelerate']) {
66
-			$params['verify_bucket_exists'] = false;
67
-		}
68
-
69
-		$this->params = $params;
70
-	}
71
-
72
-	public function getBucket() {
73
-		return $this->bucket;
74
-	}
75
-
76
-	public function getProxy() {
77
-		return $this->proxy;
78
-	}
79
-
80
-	/**
81
-	 * Returns the connection
82
-	 *
83
-	 * @return S3Client connected client
84
-	 * @throws \Exception if connection could not be made
85
-	 */
86
-	public function getConnection() {
87
-		if ($this->connection !== null) {
88
-			return $this->connection;
89
-		}
90
-
91
-		if ($this->existingBucketsCache === null) {
92
-			$this->existingBucketsCache = Server::get(ICacheFactory::class)
93
-				->createLocal('s3-bucket-exists-cache');
94
-		}
95
-
96
-		$scheme = (isset($this->params['use_ssl']) && $this->params['use_ssl'] === false) ? 'http' : 'https';
97
-		$base_url = $scheme . '://' . $this->params['hostname'] . ':' . $this->params['port'] . '/';
98
-
99
-		// Adding explicit credential provider to the beginning chain.
100
-		// Including default credential provider (skipping AWS shared config files).
101
-		$provider = CredentialProvider::memoize(
102
-			CredentialProvider::chain(
103
-				$this->paramCredentialProvider(),
104
-				CredentialProvider::defaultProvider(['use_aws_shared_config_files' => false])
105
-			)
106
-		);
107
-
108
-		$options = [
109
-			'version' => $this->params['version'] ?? 'latest',
110
-			'credentials' => $provider,
111
-			'endpoint' => $base_url,
112
-			'region' => $this->params['region'],
113
-			'use_path_style_endpoint' => isset($this->params['use_path_style']) ? $this->params['use_path_style'] : false,
114
-			'signature_provider' => \Aws\or_chain([self::class, 'legacySignatureProvider'], ClientResolver::_default_signature_provider()),
115
-			'csm' => false,
116
-			'use_arn_region' => false,
117
-			'http' => [
118
-				'verify' => $this->getCertificateBundlePath(),
119
-				'connect_timeout' => $this->connectTimeout,
120
-			],
121
-			'use_aws_shared_config_files' => false,
122
-			'retries' => [
123
-				'mode' => 'standard',
124
-				'max_attempts' => $this->retriesMaxAttempts,
125
-			],
126
-		];
127
-
128
-		if ($this->params['s3-accelerate']) {
129
-			$options['use_accelerate_endpoint'] = true;
130
-		} else {
131
-			$options['endpoint'] = $base_url;
132
-		}
133
-
134
-		if (isset($this->params['request_checksum_calculation'])) {
135
-			$options['request_checksum_calculation'] = $this->params['request_checksum_calculation'];
136
-		}
137
-
138
-		if (isset($this->params['response_checksum_validation'])) {
139
-			$options['response_checksum_validation'] = $this->params['response_checksum_validation'];
140
-		}
141
-
142
-		if ($this->getProxy()) {
143
-			$options['http']['proxy'] = $this->getProxy();
144
-		}
145
-		if (isset($this->params['legacy_auth']) && $this->params['legacy_auth']) {
146
-			$options['signature_version'] = 'v2';
147
-		}
148
-		$this->connection = new S3Client($options);
149
-
150
-		try {
151
-			$logger = Server::get(LoggerInterface::class);
152
-			if (!$this->connection::isBucketDnsCompatible($this->bucket)) {
153
-				$logger->debug('Bucket "' . $this->bucket . '" This bucket name is not dns compatible, it may contain invalid characters.',
154
-					['app' => 'objectstore']);
155
-			}
156
-
157
-			if ($this->params['verify_bucket_exists']) {
158
-				$cacheKey = $this->params['hostname'] . $this->bucket;
159
-				$exist = $this->existingBucketsCache->get($cacheKey) === 1;
160
-
161
-				if (!$exist) {
162
-					if (!$this->connection->doesBucketExist($this->bucket)) {
163
-						try {
164
-							$logger->info('Bucket "' . $this->bucket . '" does not exist - creating it.', ['app' => 'objectstore']);
165
-							if (!$this->connection::isBucketDnsCompatible($this->bucket)) {
166
-								throw new StorageNotAvailableException('The bucket will not be created because the name is not dns compatible, please correct it: ' . $this->bucket);
167
-							}
168
-							$this->connection->createBucket(['Bucket' => $this->bucket]);
169
-							Server::get(IEventDispatcher::class)
170
-								->dispatchTyped(new BucketCreatedEvent(
171
-									$this->bucket,
172
-									$options['endpoint'],
173
-									$options['region'],
174
-									$options['version']
175
-								));
176
-							$this->testTimeout();
177
-						} catch (S3Exception $e) {
178
-							$logger->debug('Invalid remote storage.', [
179
-								'exception' => $e,
180
-								'app' => 'objectstore',
181
-							]);
182
-							if ($e->getAwsErrorCode() !== 'BucketAlreadyOwnedByYou') {
183
-								throw new StorageNotAvailableException('Creation of bucket "' . $this->bucket . '" failed. ' . $e->getMessage());
184
-							}
185
-						}
186
-					}
187
-					$this->existingBucketsCache->set($cacheKey, 1);
188
-				}
189
-			}
190
-
191
-			// google cloud's s3 compatibility doesn't like the EncodingType parameter
192
-			if (strpos($base_url, 'storage.googleapis.com')) {
193
-				$this->connection->getHandlerList()->remove('s3.auto_encode');
194
-			}
195
-		} catch (S3Exception $e) {
196
-			throw new StorageNotAvailableException('S3 service is unable to handle request: ' . $e->getMessage());
197
-		}
198
-
199
-		return $this->connection;
200
-	}
201
-
202
-	/**
203
-	 * when running the tests wait to let the buckets catch up
204
-	 */
205
-	private function testTimeout() {
206
-		if ($this->test) {
207
-			sleep($this->timeout);
208
-		}
209
-	}
210
-
211
-	public static function legacySignatureProvider($version, $service, $region) {
212
-		switch ($version) {
213
-			case 'v2':
214
-			case 's3':
215
-				return new S3Signature();
216
-			default:
217
-				return null;
218
-		}
219
-	}
220
-
221
-	/**
222
-	 * This function creates a credential provider based on user parameter file
223
-	 */
224
-	protected function paramCredentialProvider(): callable {
225
-		return function () {
226
-			$key = empty($this->params['key']) ? null : $this->params['key'];
227
-			$secret = empty($this->params['secret']) ? null : $this->params['secret'];
228
-			$sessionToken = empty($this->params['session_token']) ? null : $this->params['session_token'];
229
-
230
-			if ($key && $secret) {
231
-				return Create::promiseFor(
232
-					// a null sessionToken match the default signature of the constructor
233
-					new Credentials($key, $secret, $sessionToken)
234
-				);
235
-			}
236
-
237
-			$msg = 'Could not find parameters set for credentials in config file.';
238
-			return new RejectedPromise(new CredentialsException($msg));
239
-		};
240
-	}
241
-
242
-	protected function getCertificateBundlePath(): ?string {
243
-		if ((int)($this->params['use_nextcloud_bundle'] ?? '0')) {
244
-			/** @var ICertificateManager $certManager */
245
-			$certManager = Server::get(ICertificateManager::class);
246
-			// since we store the certificate bundles on the primary storage, we can't get the bundle while setting up the primary storage
247
-			if (!isset($this->params['primary_storage'])) {
248
-				return $certManager->getAbsoluteBundlePath();
249
-			} else {
250
-				return $certManager->getDefaultCertificatesBundlePath();
251
-			}
252
-		} else {
253
-			return null;
254
-		}
255
-	}
256
-
257
-	protected function getSSECKey(): ?string {
258
-		if (isset($this->params['sse_c_key']) && !empty($this->params['sse_c_key'])) {
259
-			return $this->params['sse_c_key'];
260
-		}
261
-
262
-		return null;
263
-	}
264
-
265
-	protected function getSSECParameters(bool $copy = false): array {
266
-		$key = $this->getSSECKey();
267
-
268
-		if ($key === null) {
269
-			return [];
270
-		}
271
-
272
-		$rawKey = base64_decode($key);
273
-		if ($copy) {
274
-			return [
275
-				'CopySourceSSECustomerAlgorithm' => 'AES256',
276
-				'CopySourceSSECustomerKey' => $rawKey,
277
-				'CopySourceSSECustomerKeyMD5' => md5($rawKey, true)
278
-			];
279
-		}
280
-		return [
281
-			'SSECustomerAlgorithm' => 'AES256',
282
-			'SSECustomerKey' => $rawKey,
283
-			'SSECustomerKeyMD5' => md5($rawKey, true)
284
-		];
285
-	}
27
+    use S3ConfigTrait;
28
+
29
+    protected string $id;
30
+
31
+    protected bool $test;
32
+
33
+    protected ?S3Client $connection = null;
34
+
35
+    private ?ICache $existingBucketsCache = null;
36
+
37
+    protected function parseParams($params) {
38
+        if (empty($params['bucket'])) {
39
+            throw new \Exception('Bucket has to be configured.');
40
+        }
41
+
42
+        $this->id = 'amazon::' . $params['bucket'];
43
+
44
+        $this->test = isset($params['test']);
45
+        $this->bucket = $params['bucket'];
46
+        // Default to 5 like the S3 SDK does
47
+        $this->concurrency = $params['concurrency'] ?? 5;
48
+        $this->proxy = $params['proxy'] ?? false;
49
+        $this->connectTimeout = $params['connect_timeout'] ?? 5;
50
+        $this->timeout = $params['timeout'] ?? 15;
51
+        $this->storageClass = !empty($params['storageClass']) ? $params['storageClass'] : 'STANDARD';
52
+        $this->uploadPartSize = $params['uploadPartSize'] ?? 524288000;
53
+        $this->putSizeLimit = $params['putSizeLimit'] ?? 104857600;
54
+        $this->copySizeLimit = $params['copySizeLimit'] ?? 5242880000;
55
+        $this->useMultipartCopy = (bool)($params['useMultipartCopy'] ?? true);
56
+        $this->retriesMaxAttempts = $params['retriesMaxAttempts'] ?? 5;
57
+        $params['region'] = empty($params['region']) ? 'eu-west-1' : $params['region'];
58
+        $params['hostname'] = empty($params['hostname']) ? 's3.' . $params['region'] . '.amazonaws.com' : $params['hostname'];
59
+        $params['s3-accelerate'] = $params['hostname'] === 's3-accelerate.amazonaws.com' || $params['hostname'] === 's3-accelerate.dualstack.amazonaws.com';
60
+        if (!isset($params['port']) || $params['port'] === '') {
61
+            $params['port'] = (isset($params['use_ssl']) && $params['use_ssl'] === false) ? 80 : 443;
62
+        }
63
+        $params['verify_bucket_exists'] = $params['verify_bucket_exists'] ?? true;
64
+
65
+        if ($params['s3-accelerate']) {
66
+            $params['verify_bucket_exists'] = false;
67
+        }
68
+
69
+        $this->params = $params;
70
+    }
71
+
72
+    public function getBucket() {
73
+        return $this->bucket;
74
+    }
75
+
76
+    public function getProxy() {
77
+        return $this->proxy;
78
+    }
79
+
80
+    /**
81
+     * Returns the connection
82
+     *
83
+     * @return S3Client connected client
84
+     * @throws \Exception if connection could not be made
85
+     */
86
+    public function getConnection() {
87
+        if ($this->connection !== null) {
88
+            return $this->connection;
89
+        }
90
+
91
+        if ($this->existingBucketsCache === null) {
92
+            $this->existingBucketsCache = Server::get(ICacheFactory::class)
93
+                ->createLocal('s3-bucket-exists-cache');
94
+        }
95
+
96
+        $scheme = (isset($this->params['use_ssl']) && $this->params['use_ssl'] === false) ? 'http' : 'https';
97
+        $base_url = $scheme . '://' . $this->params['hostname'] . ':' . $this->params['port'] . '/';
98
+
99
+        // Adding explicit credential provider to the beginning chain.
100
+        // Including default credential provider (skipping AWS shared config files).
101
+        $provider = CredentialProvider::memoize(
102
+            CredentialProvider::chain(
103
+                $this->paramCredentialProvider(),
104
+                CredentialProvider::defaultProvider(['use_aws_shared_config_files' => false])
105
+            )
106
+        );
107
+
108
+        $options = [
109
+            'version' => $this->params['version'] ?? 'latest',
110
+            'credentials' => $provider,
111
+            'endpoint' => $base_url,
112
+            'region' => $this->params['region'],
113
+            'use_path_style_endpoint' => isset($this->params['use_path_style']) ? $this->params['use_path_style'] : false,
114
+            'signature_provider' => \Aws\or_chain([self::class, 'legacySignatureProvider'], ClientResolver::_default_signature_provider()),
115
+            'csm' => false,
116
+            'use_arn_region' => false,
117
+            'http' => [
118
+                'verify' => $this->getCertificateBundlePath(),
119
+                'connect_timeout' => $this->connectTimeout,
120
+            ],
121
+            'use_aws_shared_config_files' => false,
122
+            'retries' => [
123
+                'mode' => 'standard',
124
+                'max_attempts' => $this->retriesMaxAttempts,
125
+            ],
126
+        ];
127
+
128
+        if ($this->params['s3-accelerate']) {
129
+            $options['use_accelerate_endpoint'] = true;
130
+        } else {
131
+            $options['endpoint'] = $base_url;
132
+        }
133
+
134
+        if (isset($this->params['request_checksum_calculation'])) {
135
+            $options['request_checksum_calculation'] = $this->params['request_checksum_calculation'];
136
+        }
137
+
138
+        if (isset($this->params['response_checksum_validation'])) {
139
+            $options['response_checksum_validation'] = $this->params['response_checksum_validation'];
140
+        }
141
+
142
+        if ($this->getProxy()) {
143
+            $options['http']['proxy'] = $this->getProxy();
144
+        }
145
+        if (isset($this->params['legacy_auth']) && $this->params['legacy_auth']) {
146
+            $options['signature_version'] = 'v2';
147
+        }
148
+        $this->connection = new S3Client($options);
149
+
150
+        try {
151
+            $logger = Server::get(LoggerInterface::class);
152
+            if (!$this->connection::isBucketDnsCompatible($this->bucket)) {
153
+                $logger->debug('Bucket "' . $this->bucket . '" This bucket name is not dns compatible, it may contain invalid characters.',
154
+                    ['app' => 'objectstore']);
155
+            }
156
+
157
+            if ($this->params['verify_bucket_exists']) {
158
+                $cacheKey = $this->params['hostname'] . $this->bucket;
159
+                $exist = $this->existingBucketsCache->get($cacheKey) === 1;
160
+
161
+                if (!$exist) {
162
+                    if (!$this->connection->doesBucketExist($this->bucket)) {
163
+                        try {
164
+                            $logger->info('Bucket "' . $this->bucket . '" does not exist - creating it.', ['app' => 'objectstore']);
165
+                            if (!$this->connection::isBucketDnsCompatible($this->bucket)) {
166
+                                throw new StorageNotAvailableException('The bucket will not be created because the name is not dns compatible, please correct it: ' . $this->bucket);
167
+                            }
168
+                            $this->connection->createBucket(['Bucket' => $this->bucket]);
169
+                            Server::get(IEventDispatcher::class)
170
+                                ->dispatchTyped(new BucketCreatedEvent(
171
+                                    $this->bucket,
172
+                                    $options['endpoint'],
173
+                                    $options['region'],
174
+                                    $options['version']
175
+                                ));
176
+                            $this->testTimeout();
177
+                        } catch (S3Exception $e) {
178
+                            $logger->debug('Invalid remote storage.', [
179
+                                'exception' => $e,
180
+                                'app' => 'objectstore',
181
+                            ]);
182
+                            if ($e->getAwsErrorCode() !== 'BucketAlreadyOwnedByYou') {
183
+                                throw new StorageNotAvailableException('Creation of bucket "' . $this->bucket . '" failed. ' . $e->getMessage());
184
+                            }
185
+                        }
186
+                    }
187
+                    $this->existingBucketsCache->set($cacheKey, 1);
188
+                }
189
+            }
190
+
191
+            // google cloud's s3 compatibility doesn't like the EncodingType parameter
192
+            if (strpos($base_url, 'storage.googleapis.com')) {
193
+                $this->connection->getHandlerList()->remove('s3.auto_encode');
194
+            }
195
+        } catch (S3Exception $e) {
196
+            throw new StorageNotAvailableException('S3 service is unable to handle request: ' . $e->getMessage());
197
+        }
198
+
199
+        return $this->connection;
200
+    }
201
+
202
+    /**
203
+     * when running the tests wait to let the buckets catch up
204
+     */
205
+    private function testTimeout() {
206
+        if ($this->test) {
207
+            sleep($this->timeout);
208
+        }
209
+    }
210
+
211
+    public static function legacySignatureProvider($version, $service, $region) {
212
+        switch ($version) {
213
+            case 'v2':
214
+            case 's3':
215
+                return new S3Signature();
216
+            default:
217
+                return null;
218
+        }
219
+    }
220
+
221
+    /**
222
+     * This function creates a credential provider based on user parameter file
223
+     */
224
+    protected function paramCredentialProvider(): callable {
225
+        return function () {
226
+            $key = empty($this->params['key']) ? null : $this->params['key'];
227
+            $secret = empty($this->params['secret']) ? null : $this->params['secret'];
228
+            $sessionToken = empty($this->params['session_token']) ? null : $this->params['session_token'];
229
+
230
+            if ($key && $secret) {
231
+                return Create::promiseFor(
232
+                    // a null sessionToken match the default signature of the constructor
233
+                    new Credentials($key, $secret, $sessionToken)
234
+                );
235
+            }
236
+
237
+            $msg = 'Could not find parameters set for credentials in config file.';
238
+            return new RejectedPromise(new CredentialsException($msg));
239
+        };
240
+    }
241
+
242
+    protected function getCertificateBundlePath(): ?string {
243
+        if ((int)($this->params['use_nextcloud_bundle'] ?? '0')) {
244
+            /** @var ICertificateManager $certManager */
245
+            $certManager = Server::get(ICertificateManager::class);
246
+            // since we store the certificate bundles on the primary storage, we can't get the bundle while setting up the primary storage
247
+            if (!isset($this->params['primary_storage'])) {
248
+                return $certManager->getAbsoluteBundlePath();
249
+            } else {
250
+                return $certManager->getDefaultCertificatesBundlePath();
251
+            }
252
+        } else {
253
+            return null;
254
+        }
255
+    }
256
+
257
+    protected function getSSECKey(): ?string {
258
+        if (isset($this->params['sse_c_key']) && !empty($this->params['sse_c_key'])) {
259
+            return $this->params['sse_c_key'];
260
+        }
261
+
262
+        return null;
263
+    }
264
+
265
+    protected function getSSECParameters(bool $copy = false): array {
266
+        $key = $this->getSSECKey();
267
+
268
+        if ($key === null) {
269
+            return [];
270
+        }
271
+
272
+        $rawKey = base64_decode($key);
273
+        if ($copy) {
274
+            return [
275
+                'CopySourceSSECustomerAlgorithm' => 'AES256',
276
+                'CopySourceSSECustomerKey' => $rawKey,
277
+                'CopySourceSSECustomerKeyMD5' => md5($rawKey, true)
278
+            ];
279
+        }
280
+        return [
281
+            'SSECustomerAlgorithm' => 'AES256',
282
+            'SSECustomerKey' => $rawKey,
283
+            'SSECustomerKeyMD5' => md5($rawKey, true)
284
+        ];
285
+    }
286 286
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -39,7 +39,7 @@  discard block
 block discarded – undo
39 39
 			throw new \Exception('Bucket has to be configured.');
40 40
 		}
41 41
 
42
-		$this->id = 'amazon::' . $params['bucket'];
42
+		$this->id = 'amazon::'.$params['bucket'];
43 43
 
44 44
 		$this->test = isset($params['test']);
45 45
 		$this->bucket = $params['bucket'];
@@ -52,10 +52,10 @@  discard block
 block discarded – undo
52 52
 		$this->uploadPartSize = $params['uploadPartSize'] ?? 524288000;
53 53
 		$this->putSizeLimit = $params['putSizeLimit'] ?? 104857600;
54 54
 		$this->copySizeLimit = $params['copySizeLimit'] ?? 5242880000;
55
-		$this->useMultipartCopy = (bool)($params['useMultipartCopy'] ?? true);
55
+		$this->useMultipartCopy = (bool) ($params['useMultipartCopy'] ?? true);
56 56
 		$this->retriesMaxAttempts = $params['retriesMaxAttempts'] ?? 5;
57 57
 		$params['region'] = empty($params['region']) ? 'eu-west-1' : $params['region'];
58
-		$params['hostname'] = empty($params['hostname']) ? 's3.' . $params['region'] . '.amazonaws.com' : $params['hostname'];
58
+		$params['hostname'] = empty($params['hostname']) ? 's3.'.$params['region'].'.amazonaws.com' : $params['hostname'];
59 59
 		$params['s3-accelerate'] = $params['hostname'] === 's3-accelerate.amazonaws.com' || $params['hostname'] === 's3-accelerate.dualstack.amazonaws.com';
60 60
 		if (!isset($params['port']) || $params['port'] === '') {
61 61
 			$params['port'] = (isset($params['use_ssl']) && $params['use_ssl'] === false) ? 80 : 443;
@@ -94,7 +94,7 @@  discard block
 block discarded – undo
94 94
 		}
95 95
 
96 96
 		$scheme = (isset($this->params['use_ssl']) && $this->params['use_ssl'] === false) ? 'http' : 'https';
97
-		$base_url = $scheme . '://' . $this->params['hostname'] . ':' . $this->params['port'] . '/';
97
+		$base_url = $scheme.'://'.$this->params['hostname'].':'.$this->params['port'].'/';
98 98
 
99 99
 		// Adding explicit credential provider to the beginning chain.
100 100
 		// Including default credential provider (skipping AWS shared config files).
@@ -150,20 +150,20 @@  discard block
 block discarded – undo
150 150
 		try {
151 151
 			$logger = Server::get(LoggerInterface::class);
152 152
 			if (!$this->connection::isBucketDnsCompatible($this->bucket)) {
153
-				$logger->debug('Bucket "' . $this->bucket . '" This bucket name is not dns compatible, it may contain invalid characters.',
153
+				$logger->debug('Bucket "'.$this->bucket.'" This bucket name is not dns compatible, it may contain invalid characters.',
154 154
 					['app' => 'objectstore']);
155 155
 			}
156 156
 
157 157
 			if ($this->params['verify_bucket_exists']) {
158
-				$cacheKey = $this->params['hostname'] . $this->bucket;
158
+				$cacheKey = $this->params['hostname'].$this->bucket;
159 159
 				$exist = $this->existingBucketsCache->get($cacheKey) === 1;
160 160
 
161 161
 				if (!$exist) {
162 162
 					if (!$this->connection->doesBucketExist($this->bucket)) {
163 163
 						try {
164
-							$logger->info('Bucket "' . $this->bucket . '" does not exist - creating it.', ['app' => 'objectstore']);
164
+							$logger->info('Bucket "'.$this->bucket.'" does not exist - creating it.', ['app' => 'objectstore']);
165 165
 							if (!$this->connection::isBucketDnsCompatible($this->bucket)) {
166
-								throw new StorageNotAvailableException('The bucket will not be created because the name is not dns compatible, please correct it: ' . $this->bucket);
166
+								throw new StorageNotAvailableException('The bucket will not be created because the name is not dns compatible, please correct it: '.$this->bucket);
167 167
 							}
168 168
 							$this->connection->createBucket(['Bucket' => $this->bucket]);
169 169
 							Server::get(IEventDispatcher::class)
@@ -180,7 +180,7 @@  discard block
 block discarded – undo
180 180
 								'app' => 'objectstore',
181 181
 							]);
182 182
 							if ($e->getAwsErrorCode() !== 'BucketAlreadyOwnedByYou') {
183
-								throw new StorageNotAvailableException('Creation of bucket "' . $this->bucket . '" failed. ' . $e->getMessage());
183
+								throw new StorageNotAvailableException('Creation of bucket "'.$this->bucket.'" failed. '.$e->getMessage());
184 184
 							}
185 185
 						}
186 186
 					}
@@ -193,7 +193,7 @@  discard block
 block discarded – undo
193 193
 				$this->connection->getHandlerList()->remove('s3.auto_encode');
194 194
 			}
195 195
 		} catch (S3Exception $e) {
196
-			throw new StorageNotAvailableException('S3 service is unable to handle request: ' . $e->getMessage());
196
+			throw new StorageNotAvailableException('S3 service is unable to handle request: '.$e->getMessage());
197 197
 		}
198 198
 
199 199
 		return $this->connection;
@@ -222,7 +222,7 @@  discard block
 block discarded – undo
222 222
 	 * This function creates a credential provider based on user parameter file
223 223
 	 */
224 224
 	protected function paramCredentialProvider(): callable {
225
-		return function () {
225
+		return function() {
226 226
 			$key = empty($this->params['key']) ? null : $this->params['key'];
227 227
 			$secret = empty($this->params['secret']) ? null : $this->params['secret'];
228 228
 			$sessionToken = empty($this->params['session_token']) ? null : $this->params['session_token'];
@@ -240,7 +240,7 @@  discard block
 block discarded – undo
240 240
 	}
241 241
 
242 242
 	protected function getCertificateBundlePath(): ?string {
243
-		if ((int)($this->params['use_nextcloud_bundle'] ?? '0')) {
243
+		if ((int) ($this->params['use_nextcloud_bundle'] ?? '0')) {
244 244
 			/** @var ICertificateManager $certManager */
245 245
 			$certManager = Server::get(ICertificateManager::class);
246 246
 			// since we store the certificate bundles on the primary storage, we can't get the bundle while setting up the primary storage
Please login to merge, or discard this patch.
lib/private/Security/CertificateManager.php 2 patches
Indentation   +233 added lines, -233 removed lines patch added patch discarded remove patch
@@ -19,237 +19,237 @@
 block discarded – undo
19 19
  * Manage trusted certificates for users
20 20
  */
21 21
 class CertificateManager implements ICertificateManager {
22
-	private ?string $bundlePath = null;
23
-
24
-	public function __construct(
25
-		protected View $view,
26
-		protected IConfig $config,
27
-		protected LoggerInterface $logger,
28
-		protected ISecureRandom $random,
29
-	) {
30
-	}
31
-
32
-	/**
33
-	 * Returns all certificates trusted by the user
34
-	 *
35
-	 * @return \OCP\ICertificate[]
36
-	 */
37
-	public function listCertificates(): array {
38
-		if (!$this->config->getSystemValueBool('installed', false)) {
39
-			return [];
40
-		}
41
-
42
-		$path = $this->getPathToCertificates() . 'uploads/';
43
-		if (!$this->view->is_dir($path)) {
44
-			return [];
45
-		}
46
-		$result = [];
47
-		$handle = $this->view->opendir($path);
48
-		if (!is_resource($handle)) {
49
-			return [];
50
-		}
51
-		while (false !== ($file = readdir($handle))) {
52
-			if ($file != '.' && $file != '..') {
53
-				try {
54
-					$content = $this->view->file_get_contents($path . $file);
55
-					if ($content !== false) {
56
-						$result[] = new Certificate($content, $file);
57
-					} else {
58
-						$this->logger->error("Failed to read certificate from $path");
59
-					}
60
-				} catch (\Exception $e) {
61
-					$this->logger->error("Failed to read certificate from $path", ['exception' => $e]);
62
-				}
63
-			}
64
-		}
65
-		closedir($handle);
66
-		return $result;
67
-	}
68
-
69
-	private function hasCertificates(): bool {
70
-		if (!$this->config->getSystemValueBool('installed', false)) {
71
-			return false;
72
-		}
73
-
74
-		$path = $this->getPathToCertificates() . 'uploads/';
75
-		if (!$this->view->is_dir($path)) {
76
-			return false;
77
-		}
78
-		$result = [];
79
-		$handle = $this->view->opendir($path);
80
-		if (!is_resource($handle)) {
81
-			return false;
82
-		}
83
-		while (false !== ($file = readdir($handle))) {
84
-			if ($file !== '.' && $file !== '..') {
85
-				return true;
86
-			}
87
-		}
88
-		closedir($handle);
89
-		return false;
90
-	}
91
-
92
-	/**
93
-	 * create the certificate bundle of all trusted certificated
94
-	 */
95
-	public function createCertificateBundle(): void {
96
-		$path = $this->getPathToCertificates();
97
-		$certs = $this->listCertificates();
98
-
99
-		if (!$this->view->file_exists($path)) {
100
-			$this->view->mkdir($path);
101
-		}
102
-
103
-		$defaultCertificates = file_get_contents($this->getDefaultCertificatesBundlePath());
104
-		if (strlen($defaultCertificates) < 1024) { // sanity check to verify that we have some content for our bundle
105
-			// log as exception so we have a stacktrace
106
-			$e = new \Exception('Shipped ca-bundle is empty, refusing to create certificate bundle');
107
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
108
-			return;
109
-		}
110
-
111
-		$certPath = $path . 'rootcerts.crt';
112
-		$tmpPath = $certPath . '.tmp' . $this->random->generate(10, ISecureRandom::CHAR_DIGITS);
113
-		$fhCerts = $this->view->fopen($tmpPath, 'w');
114
-
115
-		if (!is_resource($fhCerts)) {
116
-			throw new \RuntimeException('Unable to open file handler to create certificate bundle "' . $tmpPath . '".');
117
-		}
118
-
119
-		// Write user certificates
120
-		foreach ($certs as $cert) {
121
-			$file = $path . '/uploads/' . $cert->getName();
122
-			$data = $this->view->file_get_contents($file);
123
-			if (strpos($data, 'BEGIN CERTIFICATE')) {
124
-				fwrite($fhCerts, $data);
125
-				fwrite($fhCerts, "\r\n");
126
-			}
127
-		}
128
-
129
-		// Append the default certificates
130
-		fwrite($fhCerts, $defaultCertificates);
131
-
132
-		// Append the system certificate bundle
133
-		$systemBundle = $this->getCertificateBundle();
134
-		if ($systemBundle !== $certPath && $this->view->file_exists($systemBundle)) {
135
-			$systemCertificates = $this->view->file_get_contents($systemBundle);
136
-			fwrite($fhCerts, $systemCertificates);
137
-		}
138
-
139
-		fclose($fhCerts);
140
-
141
-		$this->view->rename($tmpPath, $certPath);
142
-	}
143
-
144
-	/**
145
-	 * Save the certificate and re-generate the certificate bundle
146
-	 *
147
-	 * @param string $certificate the certificate data
148
-	 * @param string $name the filename for the certificate
149
-	 * @throws \Exception If the certificate could not get added
150
-	 */
151
-	public function addCertificate(string $certificate, string $name): ICertificate {
152
-		$path = $this->getPathToCertificates() . 'uploads/' . $name;
153
-		$directory = dirname($path);
154
-
155
-		$this->view->verifyPath($directory, basename($path));
156
-		$this->bundlePath = null;
157
-
158
-		if (!$this->view->file_exists($directory)) {
159
-			$this->view->mkdir($directory);
160
-		}
161
-
162
-		try {
163
-			$certificateObject = new Certificate($certificate, $name);
164
-			$this->view->file_put_contents($path, $certificate);
165
-			$this->createCertificateBundle();
166
-			return $certificateObject;
167
-		} catch (\Exception $e) {
168
-			throw $e;
169
-		}
170
-	}
171
-
172
-	/**
173
-	 * Remove the certificate and re-generate the certificate bundle
174
-	 */
175
-	public function removeCertificate(string $name): bool {
176
-		$path = $this->getPathToCertificates() . 'uploads/' . $name;
177
-
178
-		try {
179
-			$this->view->verifyPath(dirname($path), basename($path));
180
-		} catch (\Exception) {
181
-			return false;
182
-		}
183
-
184
-		$this->bundlePath = null;
185
-		if ($this->view->file_exists($path)) {
186
-			$this->view->unlink($path);
187
-			$this->createCertificateBundle();
188
-		}
189
-		return true;
190
-	}
191
-
192
-	/**
193
-	 * Get the path to the certificate bundle
194
-	 */
195
-	public function getCertificateBundle(): string {
196
-		return $this->getPathToCertificates() . 'rootcerts.crt';
197
-	}
198
-
199
-	/**
200
-	 * Get the full local path to the certificate bundle
201
-	 * @throws \Exception when getting bundle path fails
202
-	 */
203
-	public function getAbsoluteBundlePath(): string {
204
-		try {
205
-			if ($this->bundlePath === null) {
206
-				if (!$this->hasCertificates()) {
207
-					$this->bundlePath = $this->getDefaultCertificatesBundlePath();
208
-				} else {
209
-					if ($this->needsRebundling()) {
210
-						$this->createCertificateBundle();
211
-					}
212
-
213
-					$certificateBundle = $this->getCertificateBundle();
214
-					$this->bundlePath = $this->view->getLocalFile($certificateBundle) ?: null;
215
-
216
-					if ($this->bundlePath === null) {
217
-						throw new \RuntimeException('Unable to get certificate bundle "' . $certificateBundle . '".');
218
-					}
219
-				}
220
-			}
221
-			return $this->bundlePath;
222
-		} catch (\Exception $e) {
223
-			$this->logger->error('Failed to get absolute bundle path. Fallback to default ca-bundle.crt', ['exception' => $e]);
224
-			return $this->getDefaultCertificatesBundlePath();
225
-		}
226
-	}
227
-
228
-	private function getPathToCertificates(): string {
229
-		return '/files_external/';
230
-	}
231
-
232
-	/**
233
-	 * Check if we need to re-bundle the certificates because one of the sources has updated
234
-	 */
235
-	private function needsRebundling(): bool {
236
-		$targetBundle = $this->getCertificateBundle();
237
-		if (!$this->view->file_exists($targetBundle)) {
238
-			return true;
239
-		}
240
-
241
-		$sourceMTime = $this->getFilemtimeOfCaBundle();
242
-		return $sourceMTime > $this->view->filemtime($targetBundle);
243
-	}
244
-
245
-	/**
246
-	 * get mtime of ca-bundle shipped by Nextcloud
247
-	 */
248
-	protected function getFilemtimeOfCaBundle(): int {
249
-		return filemtime($this->getDefaultCertificatesBundlePath());
250
-	}
251
-
252
-	public function getDefaultCertificatesBundlePath(): string {
253
-		return $this->config->getSystemValueString('default_certificates_bundle_path', \OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
254
-	}
22
+    private ?string $bundlePath = null;
23
+
24
+    public function __construct(
25
+        protected View $view,
26
+        protected IConfig $config,
27
+        protected LoggerInterface $logger,
28
+        protected ISecureRandom $random,
29
+    ) {
30
+    }
31
+
32
+    /**
33
+     * Returns all certificates trusted by the user
34
+     *
35
+     * @return \OCP\ICertificate[]
36
+     */
37
+    public function listCertificates(): array {
38
+        if (!$this->config->getSystemValueBool('installed', false)) {
39
+            return [];
40
+        }
41
+
42
+        $path = $this->getPathToCertificates() . 'uploads/';
43
+        if (!$this->view->is_dir($path)) {
44
+            return [];
45
+        }
46
+        $result = [];
47
+        $handle = $this->view->opendir($path);
48
+        if (!is_resource($handle)) {
49
+            return [];
50
+        }
51
+        while (false !== ($file = readdir($handle))) {
52
+            if ($file != '.' && $file != '..') {
53
+                try {
54
+                    $content = $this->view->file_get_contents($path . $file);
55
+                    if ($content !== false) {
56
+                        $result[] = new Certificate($content, $file);
57
+                    } else {
58
+                        $this->logger->error("Failed to read certificate from $path");
59
+                    }
60
+                } catch (\Exception $e) {
61
+                    $this->logger->error("Failed to read certificate from $path", ['exception' => $e]);
62
+                }
63
+            }
64
+        }
65
+        closedir($handle);
66
+        return $result;
67
+    }
68
+
69
+    private function hasCertificates(): bool {
70
+        if (!$this->config->getSystemValueBool('installed', false)) {
71
+            return false;
72
+        }
73
+
74
+        $path = $this->getPathToCertificates() . 'uploads/';
75
+        if (!$this->view->is_dir($path)) {
76
+            return false;
77
+        }
78
+        $result = [];
79
+        $handle = $this->view->opendir($path);
80
+        if (!is_resource($handle)) {
81
+            return false;
82
+        }
83
+        while (false !== ($file = readdir($handle))) {
84
+            if ($file !== '.' && $file !== '..') {
85
+                return true;
86
+            }
87
+        }
88
+        closedir($handle);
89
+        return false;
90
+    }
91
+
92
+    /**
93
+     * create the certificate bundle of all trusted certificated
94
+     */
95
+    public function createCertificateBundle(): void {
96
+        $path = $this->getPathToCertificates();
97
+        $certs = $this->listCertificates();
98
+
99
+        if (!$this->view->file_exists($path)) {
100
+            $this->view->mkdir($path);
101
+        }
102
+
103
+        $defaultCertificates = file_get_contents($this->getDefaultCertificatesBundlePath());
104
+        if (strlen($defaultCertificates) < 1024) { // sanity check to verify that we have some content for our bundle
105
+            // log as exception so we have a stacktrace
106
+            $e = new \Exception('Shipped ca-bundle is empty, refusing to create certificate bundle');
107
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
108
+            return;
109
+        }
110
+
111
+        $certPath = $path . 'rootcerts.crt';
112
+        $tmpPath = $certPath . '.tmp' . $this->random->generate(10, ISecureRandom::CHAR_DIGITS);
113
+        $fhCerts = $this->view->fopen($tmpPath, 'w');
114
+
115
+        if (!is_resource($fhCerts)) {
116
+            throw new \RuntimeException('Unable to open file handler to create certificate bundle "' . $tmpPath . '".');
117
+        }
118
+
119
+        // Write user certificates
120
+        foreach ($certs as $cert) {
121
+            $file = $path . '/uploads/' . $cert->getName();
122
+            $data = $this->view->file_get_contents($file);
123
+            if (strpos($data, 'BEGIN CERTIFICATE')) {
124
+                fwrite($fhCerts, $data);
125
+                fwrite($fhCerts, "\r\n");
126
+            }
127
+        }
128
+
129
+        // Append the default certificates
130
+        fwrite($fhCerts, $defaultCertificates);
131
+
132
+        // Append the system certificate bundle
133
+        $systemBundle = $this->getCertificateBundle();
134
+        if ($systemBundle !== $certPath && $this->view->file_exists($systemBundle)) {
135
+            $systemCertificates = $this->view->file_get_contents($systemBundle);
136
+            fwrite($fhCerts, $systemCertificates);
137
+        }
138
+
139
+        fclose($fhCerts);
140
+
141
+        $this->view->rename($tmpPath, $certPath);
142
+    }
143
+
144
+    /**
145
+     * Save the certificate and re-generate the certificate bundle
146
+     *
147
+     * @param string $certificate the certificate data
148
+     * @param string $name the filename for the certificate
149
+     * @throws \Exception If the certificate could not get added
150
+     */
151
+    public function addCertificate(string $certificate, string $name): ICertificate {
152
+        $path = $this->getPathToCertificates() . 'uploads/' . $name;
153
+        $directory = dirname($path);
154
+
155
+        $this->view->verifyPath($directory, basename($path));
156
+        $this->bundlePath = null;
157
+
158
+        if (!$this->view->file_exists($directory)) {
159
+            $this->view->mkdir($directory);
160
+        }
161
+
162
+        try {
163
+            $certificateObject = new Certificate($certificate, $name);
164
+            $this->view->file_put_contents($path, $certificate);
165
+            $this->createCertificateBundle();
166
+            return $certificateObject;
167
+        } catch (\Exception $e) {
168
+            throw $e;
169
+        }
170
+    }
171
+
172
+    /**
173
+     * Remove the certificate and re-generate the certificate bundle
174
+     */
175
+    public function removeCertificate(string $name): bool {
176
+        $path = $this->getPathToCertificates() . 'uploads/' . $name;
177
+
178
+        try {
179
+            $this->view->verifyPath(dirname($path), basename($path));
180
+        } catch (\Exception) {
181
+            return false;
182
+        }
183
+
184
+        $this->bundlePath = null;
185
+        if ($this->view->file_exists($path)) {
186
+            $this->view->unlink($path);
187
+            $this->createCertificateBundle();
188
+        }
189
+        return true;
190
+    }
191
+
192
+    /**
193
+     * Get the path to the certificate bundle
194
+     */
195
+    public function getCertificateBundle(): string {
196
+        return $this->getPathToCertificates() . 'rootcerts.crt';
197
+    }
198
+
199
+    /**
200
+     * Get the full local path to the certificate bundle
201
+     * @throws \Exception when getting bundle path fails
202
+     */
203
+    public function getAbsoluteBundlePath(): string {
204
+        try {
205
+            if ($this->bundlePath === null) {
206
+                if (!$this->hasCertificates()) {
207
+                    $this->bundlePath = $this->getDefaultCertificatesBundlePath();
208
+                } else {
209
+                    if ($this->needsRebundling()) {
210
+                        $this->createCertificateBundle();
211
+                    }
212
+
213
+                    $certificateBundle = $this->getCertificateBundle();
214
+                    $this->bundlePath = $this->view->getLocalFile($certificateBundle) ?: null;
215
+
216
+                    if ($this->bundlePath === null) {
217
+                        throw new \RuntimeException('Unable to get certificate bundle "' . $certificateBundle . '".');
218
+                    }
219
+                }
220
+            }
221
+            return $this->bundlePath;
222
+        } catch (\Exception $e) {
223
+            $this->logger->error('Failed to get absolute bundle path. Fallback to default ca-bundle.crt', ['exception' => $e]);
224
+            return $this->getDefaultCertificatesBundlePath();
225
+        }
226
+    }
227
+
228
+    private function getPathToCertificates(): string {
229
+        return '/files_external/';
230
+    }
231
+
232
+    /**
233
+     * Check if we need to re-bundle the certificates because one of the sources has updated
234
+     */
235
+    private function needsRebundling(): bool {
236
+        $targetBundle = $this->getCertificateBundle();
237
+        if (!$this->view->file_exists($targetBundle)) {
238
+            return true;
239
+        }
240
+
241
+        $sourceMTime = $this->getFilemtimeOfCaBundle();
242
+        return $sourceMTime > $this->view->filemtime($targetBundle);
243
+    }
244
+
245
+    /**
246
+     * get mtime of ca-bundle shipped by Nextcloud
247
+     */
248
+    protected function getFilemtimeOfCaBundle(): int {
249
+        return filemtime($this->getDefaultCertificatesBundlePath());
250
+    }
251
+
252
+    public function getDefaultCertificatesBundlePath(): string {
253
+        return $this->config->getSystemValueString('default_certificates_bundle_path', \OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
254
+    }
255 255
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -39,7 +39,7 @@  discard block
 block discarded – undo
39 39
 			return [];
40 40
 		}
41 41
 
42
-		$path = $this->getPathToCertificates() . 'uploads/';
42
+		$path = $this->getPathToCertificates().'uploads/';
43 43
 		if (!$this->view->is_dir($path)) {
44 44
 			return [];
45 45
 		}
@@ -51,7 +51,7 @@  discard block
 block discarded – undo
51 51
 		while (false !== ($file = readdir($handle))) {
52 52
 			if ($file != '.' && $file != '..') {
53 53
 				try {
54
-					$content = $this->view->file_get_contents($path . $file);
54
+					$content = $this->view->file_get_contents($path.$file);
55 55
 					if ($content !== false) {
56 56
 						$result[] = new Certificate($content, $file);
57 57
 					} else {
@@ -71,7 +71,7 @@  discard block
 block discarded – undo
71 71
 			return false;
72 72
 		}
73 73
 
74
-		$path = $this->getPathToCertificates() . 'uploads/';
74
+		$path = $this->getPathToCertificates().'uploads/';
75 75
 		if (!$this->view->is_dir($path)) {
76 76
 			return false;
77 77
 		}
@@ -108,17 +108,17 @@  discard block
 block discarded – undo
108 108
 			return;
109 109
 		}
110 110
 
111
-		$certPath = $path . 'rootcerts.crt';
112
-		$tmpPath = $certPath . '.tmp' . $this->random->generate(10, ISecureRandom::CHAR_DIGITS);
111
+		$certPath = $path.'rootcerts.crt';
112
+		$tmpPath = $certPath.'.tmp'.$this->random->generate(10, ISecureRandom::CHAR_DIGITS);
113 113
 		$fhCerts = $this->view->fopen($tmpPath, 'w');
114 114
 
115 115
 		if (!is_resource($fhCerts)) {
116
-			throw new \RuntimeException('Unable to open file handler to create certificate bundle "' . $tmpPath . '".');
116
+			throw new \RuntimeException('Unable to open file handler to create certificate bundle "'.$tmpPath.'".');
117 117
 		}
118 118
 
119 119
 		// Write user certificates
120 120
 		foreach ($certs as $cert) {
121
-			$file = $path . '/uploads/' . $cert->getName();
121
+			$file = $path.'/uploads/'.$cert->getName();
122 122
 			$data = $this->view->file_get_contents($file);
123 123
 			if (strpos($data, 'BEGIN CERTIFICATE')) {
124 124
 				fwrite($fhCerts, $data);
@@ -149,7 +149,7 @@  discard block
 block discarded – undo
149 149
 	 * @throws \Exception If the certificate could not get added
150 150
 	 */
151 151
 	public function addCertificate(string $certificate, string $name): ICertificate {
152
-		$path = $this->getPathToCertificates() . 'uploads/' . $name;
152
+		$path = $this->getPathToCertificates().'uploads/'.$name;
153 153
 		$directory = dirname($path);
154 154
 
155 155
 		$this->view->verifyPath($directory, basename($path));
@@ -173,7 +173,7 @@  discard block
 block discarded – undo
173 173
 	 * Remove the certificate and re-generate the certificate bundle
174 174
 	 */
175 175
 	public function removeCertificate(string $name): bool {
176
-		$path = $this->getPathToCertificates() . 'uploads/' . $name;
176
+		$path = $this->getPathToCertificates().'uploads/'.$name;
177 177
 
178 178
 		try {
179 179
 			$this->view->verifyPath(dirname($path), basename($path));
@@ -193,7 +193,7 @@  discard block
 block discarded – undo
193 193
 	 * Get the path to the certificate bundle
194 194
 	 */
195 195
 	public function getCertificateBundle(): string {
196
-		return $this->getPathToCertificates() . 'rootcerts.crt';
196
+		return $this->getPathToCertificates().'rootcerts.crt';
197 197
 	}
198 198
 
199 199
 	/**
@@ -214,7 +214,7 @@  discard block
 block discarded – undo
214 214
 					$this->bundlePath = $this->view->getLocalFile($certificateBundle) ?: null;
215 215
 
216 216
 					if ($this->bundlePath === null) {
217
-						throw new \RuntimeException('Unable to get certificate bundle "' . $certificateBundle . '".');
217
+						throw new \RuntimeException('Unable to get certificate bundle "'.$certificateBundle.'".');
218 218
 					}
219 219
 				}
220 220
 			}
@@ -250,6 +250,6 @@  discard block
 block discarded – undo
250 250
 	}
251 251
 
252 252
 	public function getDefaultCertificatesBundlePath(): string {
253
-		return $this->config->getSystemValueString('default_certificates_bundle_path', \OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
253
+		return $this->config->getSystemValueString('default_certificates_bundle_path', \OC::$SERVERROOT.'/resources/config/ca-bundle.crt');
254 254
 	}
255 255
 }
Please login to merge, or discard this patch.
config/config.sample.php 2 patches
Indentation   +2857 added lines, -2857 removed lines patch added patch discarded remove patch
@@ -29,2861 +29,2861 @@
 block discarded – undo
29 29
 $CONFIG = [
30 30
 
31 31
 
32
-	/**
33
-	 * Default Parameters
34
-	 *
35
-	 * These parameters are configured by the Nextcloud installer, and are required
36
-	 * for your Nextcloud server to operate.
37
-	 */
38
-
39
-
40
-	/**
41
-	 * This is a unique identifier for your Nextcloud installation, created
42
-	 * automatically by the installer. This example is for documentation only,
43
-	 * and you should never use it because it will not work. A valid ``instanceid``
44
-	 * is created when you install Nextcloud.
45
-	 */
46
-	'instanceid' => '',
47
-
48
-	/**
49
-	 * This is a unique identifier for your server.
50
-	 * It is useful when your Nextcloud instance is using different PHP servers.
51
-	 * Once it's set it shouldn't be changed.
52
-	 *
53
-	 * Value must be an integer, comprised between 0 and 1023.
54
-	 *
55
-	 * When config.php is shared between different servers, this value should be overriden with "NC_serverid=<int>" on each server.
56
-	 * Note that it must be overriden for CLI and for your webserver.
57
-	 *
58
-	 * Example for CLI: NC_serverid=42 occ config:list system
59
-	 */
60
-	'serverid' => -1,
61
-
62
-	/**
63
-	 * The salt used to hash all passwords, auto-generated by the Nextcloud
64
-	 * installer. (There are also per-user salts.) If you lose this salt, you lose
65
-	 * all your passwords. This example is for documentation only, and you should
66
-	 * never use it.
67
-	 *
68
-	 * @deprecated 9.0.0 This salt is deprecated and only used for legacy-compatibility,
69
-	 * developers should *NOT* use this value for anything nowadays.
70
-	 */
71
-	'passwordsalt' => '',
72
-
73
-	/**
74
-	 * Secret used by Nextcloud for various purposes, e.g., to encrypt data. If you
75
-	 * lose this string, there will be data corruption.
76
-	 */
77
-	'secret' => '',
78
-
79
-	/**
80
-	 * Your list of trusted domains that users can log into. Specifying trusted
81
-	 * domains prevents host header poisoning. Do not remove this, as it performs
82
-	 * necessary security checks.
83
-	 *
84
-	 * You can specify:
85
-	 *
86
-	 * - The exact hostname of your host or virtual host, e.g. ``demo.example.org``.
87
-	 * - The exact hostname with permitted port, e.g. ``demo.example.org:443``. This disallows all other ports on this host
88
-	 * - Use ``*`` as a wildcard, e.g., ``ubos-raspberry-pi*.local`` will allow ``ubos-raspberry-pi.local`` and ``ubos-raspberry-pi-2.local``
89
-	 * - The IP address with or without permitted port, e.g. ``[2001:db8::1]:8080`` Using TLS certificates where ``commonName=<IP address>`` is deprecated
90
-	 */
91
-	'trusted_domains' => [
92
-		'demo.example.org',
93
-		'otherdomain.example.org',
94
-		'10.111.112.113',
95
-		'[2001:db8::1]'
96
-	],
97
-
98
-	/**
99
-	 * The validity domain for cookies, for example ``''`` (cookies will be sent only
100
-	 * the domain that defined it, e.g. ``'demo.example.org'``), ``'demo.example.org'``
101
-	 * (cookies will be valid for the domain and all subdomains), ...
102
-	 *
103
-	 * Defaults to ``''`` (safe option)
104
-	 */
105
-	'cookie_domain' => '',
106
-
107
-	/**
108
-	 * Where user files are stored. The SQLite database is also stored here, when
109
-	 * you use SQLite.
110
-	 *
111
-	 * Default to ``data/`` in the Nextcloud directory.
112
-	 */
113
-	'datadirectory' => '/var/www/nextcloud/data',
114
-
115
-	/**
116
-	 * The current version number of your Nextcloud installation. This is set up
117
-	 * during installation and update, so you shouldn't need to change it.
118
-	 */
119
-	'version' => '',
120
-
121
-	/**
122
-	 * Identifies the database used with this installation. See also config option
123
-	 * ``supportedDatabases``
124
-	 *
125
-	 * Available:
126
-	 *  - sqlite3 (SQLite3)
127
-	 *  - mysql (MySQL/MariaDB)
128
-	 *  - pgsql (PostgreSQL)
129
-	 *
130
-	 * Defaults to ``sqlite3``
131
-	 */
132
-	'dbtype' => 'sqlite3',
133
-
134
-	/**
135
-	 * Your host server name, for example ``localhost``, ``hostname``,
136
-	 * ``hostname.example.com``, or the IP address.
137
-	 * To specify a port, use ``hostname:####``; for IPv6 addresses, use the URI notation ``[ip]:port``.
138
-	 * To specify a Unix socket, use ``localhost:/path/to/directory/containing/socket`` or
139
-	 * ``:/path/to/directory/containing/socket``, e.g., ``localhost:/run/postgresql/``.
140
-	 */
141
-	'dbhost' => '',
142
-
143
-	/**
144
-	 * The name of the Nextcloud database, which is set during installation. You
145
-	 * should not need to change this.
146
-	 */
147
-	'dbname' => 'nextcloud',
148
-
149
-	/**
150
-	 * The user that Nextcloud uses to write to the database. This must be unique
151
-	 * across Nextcloud instances using the same SQL database. This is set up during
152
-	 * installation, so you shouldn't need to change it.
153
-	 */
154
-	'dbuser' => '',
155
-
156
-	/**
157
-	 * The password for the database user. This is set up during installation, so
158
-	 * you shouldn't need to change it.
159
-	 */
160
-	'dbpassword' => '',
161
-
162
-	/**
163
-	 * Prefix for the Nextcloud tables in the database.
164
-	 *
165
-	 * Default to ``oc_``
166
-	 */
167
-	'dbtableprefix' => 'oc_',
168
-
169
-	/**
170
-	 * Enable persistent connections to the database.
171
-	 * This setting uses the ``persistent`` option from Doctrine DBAL, which in turn
172
-	 * uses the PDO::ATTR_PERSISTENT option from the PDO driver.
173
-	 */
174
-	'dbpersistent' => '',
175
-
176
-	/**
177
-	 * Specify read-only replicas to be used by Nextcloud when querying the database
178
-	 */
179
-	'dbreplica' => [
180
-		['user' => 'nextcloud', 'password' => 'password1', 'host' => 'replica1', 'dbname' => ''],
181
-		['user' => 'nextcloud', 'password' => 'password2', 'host' => 'replica2', 'dbname' => ''],
182
-	],
183
-
184
-	/**
185
-	 * Add request ID to the database query in a comment.
186
-	 *
187
-	 * This can be enabled to assist in mapping database logs to Nextcloud logs.
188
-	 */
189
-	'db.log_request_id' => false,
190
-
191
-	/**
192
-	 * Indicates whether the Nextcloud instance was installed successfully; ``true``
193
-	 * indicates a successful installation, and ``false`` indicates an unsuccessful
194
-	 * installation.
195
-	 *
196
-	 * Defaults to ``false``
197
-	 */
198
-	'installed' => false,
199
-
200
-
201
-	/**
202
-	 * User Experience
203
-	 *
204
-	 * These optional parameters control some aspects of the user interface. Default
205
-	 * values, where present, are shown.
206
-	 */
207
-
208
-	/**
209
-	 * This sets the default language on your Nextcloud server, using ISO_639-1
210
-	 * language codes such as ``en`` for English, ``de`` for German, and ``fr`` for
211
-	 * French. The default_language parameter is only used when the browser does
212
-	 * not send any language, and the user hasn’t configured their own language
213
-	 * preferences.
214
-	 *
215
-	 * Nextcloud has two distinguished language codes for German, ``de`` and ``de_DE``.
216
-	 * ``de`` is used for informal German and ``de_DE`` for formal German. By setting
217
-	 * this value to ``de_DE``, you can enforce the formal version of German unless
218
-	 * the user has chosen something different explicitly.
219
-	 *
220
-	 * Defaults to ``en``
221
-	 */
222
-	'default_language' => 'en',
223
-
224
-	/**
225
-	 * With this setting, a language can be forced for all users. If a language is
226
-	 * forced, the users are also unable to change their language in the personal
227
-	 * settings. If users shall be unable to change their language, but users have
228
-	 * different languages, this value can be set to ``true`` instead of a language
229
-	 * code.
230
-	 *
231
-	 * Defaults to ``false``
232
-	 */
233
-	'force_language' => 'en',
234
-
235
-	/**
236
-	 * This sets the default locale on your Nextcloud server, using ISO_639
237
-	 * language codes such as ``en`` for English, ``de`` for German, and ``fr`` for
238
-	 * French, and ISO-3166 country codes such as ``GB``, ``US``, ``CA``, as defined
239
-	 * in RFC 5646. It overrides automatic locale detection on public pages like
240
-	 * login or shared items. User's locale preferences configured under "personal
241
-	 * -> locale" override this setting after they have logged in.
242
-	 *
243
-	 * Defaults to ``en``
244
-	 */
245
-	'default_locale' => 'en_US',
246
-
247
-	/**
248
-	 * With this setting, it is possible to reduce the languages available in the
249
-	 * language chooser. The languages have to be set as array values using ISO_639-1
250
-	 * language codes such as ``en`` for English, ``de`` for German, etc.
251
-	 *
252
-	 * For example: Set to ``['de', 'fr']`` to only allow German and French languages.
253
-	 */
254
-	'reduce_to_languages' => [],
255
-
256
-	/**
257
-	 * This sets the default region for phone numbers on your Nextcloud server,
258
-	 * using ISO 3166-1 country codes such as ``DE`` for Germany, ``FR`` for France, …
259
-	 * It is required to allow inserting phone numbers in the user profiles starting
260
-	 * without the country code (e.g., +49 for Germany).
261
-	 *
262
-	 * No default value!
263
-	 */
264
-	'default_phone_region' => 'GB',
265
-
266
-	/**
267
-	 * With this setting, a locale can be forced for all users. If a locale is
268
-	 * forced, the users are also unable to change their locale in the personal
269
-	 * settings. If users shall be unable to change their locale, but users have
270
-	 * different languages, this value can be set to ``true`` instead of a locale
271
-	 * code.
272
-	 *
273
-	 * Defaults to ``false``
274
-	 */
275
-	'force_locale' => 'en_US',
276
-
277
-	/**
278
-	 * This sets the default timezone on your Nextcloud server, using IANA
279
-	 * identifiers like ``Europe/Berlin`` or ``Pacific/Auckland``. The default
280
-	 * timezone parameter is only used when the timezone of the user cannot be
281
-	 * determined.
282
-	 *
283
-	 * Defaults to ``UTC``
284
-	 */
285
-	'default_timezone' => 'Europe/Berlin',
286
-
287
-	/**
288
-	 * ``true`` enables the Help menu item in the user menu (top right of the
289
-	 * Nextcloud Web interface).
290
-	 * ``false`` removes the Help item.
291
-	 */
292
-	'knowledgebaseenabled' => true,
293
-
294
-	/**
295
-	 * ``true`` embeds the documentation in an iframe inside Nextcloud.
296
-	 * ``false`` only shows buttons to the online documentation.
297
-	 */
298
-	'knowledgebase.embedded' => false,
299
-
300
-	/**
301
-	 * ``true`` allows users to change their display names (on their Personal
302
-	 * pages), and ``false`` prevents them from changing their display names.
303
-	 */
304
-	'allow_user_to_change_display_name' => true,
305
-
306
-	/**
307
-	 * The directory where the skeleton files are located. These files will be
308
-	 * copied to the data directory of new users. Set empty string to not copy any
309
-	 * skeleton files. If unset and templatedirectory is an empty string, shipped
310
-	 * templates will be used to create a template directory for the user.
311
-	 * ``{lang}`` can be used as a placeholder for the language of the user.
312
-	 * If the directory does not exist, it falls back to non-dialect (from ``de_DE``
313
-	 * to ``de``). If that does not exist either, it falls back to ``default``
314
-	 *
315
-	 * Defaults to ``core/skeleton`` in the Nextcloud directory.
316
-	 */
317
-	'skeletondirectory' => '/path/to/nextcloud/core/skeleton',
318
-
319
-	/**
320
-	 * The directory where the template files are located. These files will be
321
-	 * copied to the template directory of new users. Set empty string to not copy any
322
-	 * template files.
323
-	 * ``{lang}`` can be used as a placeholder for the language of the user.
324
-	 * If the directory does not exist, it falls back to non-dialect (from ``de_DE``
325
-	 * to ``de``). If that does not exist either, it falls back to ``default``
326
-	 *
327
-	 * To disable creating a template directory, set both skeletondirectory and
328
-	 * templatedirectory to empty strings.
329
-	 */
330
-	'templatedirectory' => '/path/to/nextcloud/templates',
331
-
332
-	/**
333
-	 * User session
334
-	 */
335
-
336
-	/**
337
-	 * Lifetime of the remember login cookie. This should be larger than the
338
-	 * session_lifetime. If it is set to 0, remember me is disabled.
339
-	 *
340
-	 * Defaults to ``60*60*24*15`` seconds (15 days)
341
-	 */
342
-	'remember_login_cookie_lifetime' => 60 * 60 * 24 * 15,
343
-
344
-	/**
345
-	 * The lifetime of a session after inactivity.
346
-	 *
347
-	 * The maximum possible time is limited by the ``session.gc_maxlifetime`` php.ini setting
348
-	 * which would overwrite this option if it is less than the value in the ``config.php``
349
-	 *
350
-	 * Defaults to ``60*60*24`` seconds (24 hours)
351
-	 */
352
-	'session_lifetime' => 60 * 60 * 24,
353
-
354
-	/**
355
-	 * The timeout in seconds for requests to servers made by the DAV component (e.g., needed for federated shares).
356
-	 */
357
-	'davstorage.request_timeout' => 30,
358
-
359
-	/**
360
-	 * The timeout in seconds for synchronizing address books, e.g., federated system address books (as run by ``occ federation:sync-addressbooks``).
361
-	 *
362
-	 * Defaults to ``30`` seconds
363
-	 */
364
-	'carddav_sync_request_timeout' => 30,
365
-
366
-	/**
367
-	 * The limit applied to the synchronization report request, e.g. federated system address books (as run by ``occ federation:sync-addressbooks``).
368
-	 */
369
-	'carddav_sync_request_truncation' => 2500,
370
-
371
-	/**
372
-	 * ``true`` enables a relaxed session timeout, where the session timeout would no longer be
373
-	 * handled by Nextcloud but by either the PHP garbage collection or the expiration of
374
-	 * potential other session backends like Redis.
375
-	 *
376
-	 * This may lead to sessions being available for longer than what ``session_lifetime`` uses but
377
-	 * comes with performance benefits as sessions are no longer a locking operation for concurrent
378
-	 * requests.
379
-	 */
380
-	'session_relaxed_expiry' => false,
381
-
382
-	/**
383
-	 * Enable or disable session keep-alive when a user is logged in to the Web UI.
384
-	 * Enabling this sends a "heartbeat" to the server to keep it from timing out.
385
-	 *
386
-	 * Defaults to ``true``
387
-	 */
388
-	'session_keepalive' => true,
389
-
390
-	/**
391
-	 * Enable or disable the automatic logout after session_lifetime, even if session
392
-	 * keepalive is enabled. This will make sure that an inactive browser will log itself out
393
-	 * even if requests to the server might extend the session lifetime.
394
-	 *
395
-	 * NOTE: The logout is handled on the client side. This is not a way to
396
-	 * limit the duration of potentially compromised sessions.
397
-	 *
398
-	 * Defaults to ``false``
399
-	 */
400
-	'auto_logout' => false,
401
-
402
-	/**
403
-	 * Enforce token authentication for clients, which blocks requests using the user
404
-	 * password for enhanced security. Users need to generate tokens in personal settings
405
-	 * which can be used as passwords on their clients.
406
-	 *
407
-	 * Defaults to ``false``
408
-	 */
409
-	'token_auth_enforced' => false,
410
-
411
-	/**
412
-	 * The interval at which token activity should be updated.
413
-	 * Increasing this value means that the last activity on the security page gets
414
-	 * more outdated.
415
-	 *
416
-	 * Tokens are still checked every 5 minutes for validity
417
-	 * max value: 300
418
-	 *
419
-	 * Defaults to ``60``
420
-	 */
421
-	'token_auth_activity_update' => 60,
422
-
423
-	/**
424
-	 * Whether the brute force protection shipped with Nextcloud should be enabled or not.
425
-	 *
426
-	 * WARNING: Disabling this is discouraged for security reasons.
427
-	 *
428
-	 * Defaults to ``true``
429
-	 */
430
-	'auth.bruteforce.protection.enabled' => true,
431
-
432
-	/**
433
-	 * Whether the brute force protection should write into the database even when a memory cache is available
434
-	 *
435
-	 * Using the database is most likely worse for performance, but makes investigating
436
-	 * issues a lot easier as it's possible to look directly at the table to see all
437
-	 * logged remote addresses and actions.
438
-	 *
439
-	 * Defaults to ``false``
440
-	 */
441
-	'auth.bruteforce.protection.force.database' => false,
442
-
443
-	/**
444
-	 * Whether the brute force protection shipped with Nextcloud should be set to testing mode.
445
-	 *
446
-	 * In testing mode, brute force attempts are still recorded, but the requests do
447
-	 * not sleep/wait for the specified time. They will still abort with
448
-	 * "429 Too Many Requests" when the maximum delay is reached.
449
-	 * Enabling this is discouraged for security reasons
450
-	 * and should only be done for debugging and on CI when running tests.
451
-	 *
452
-	 * Defaults to ``false``
453
-	 */
454
-	'auth.bruteforce.protection.testing' => false,
455
-
456
-	/**
457
-	 * Brute force protection: maximum number of attempts before blocking
458
-	 *
459
-	 * When more than max-attempts login requests are sent to Nextcloud, requests
460
-	 * will abort with "429 Too Many Requests".
461
-	 * For security reasons, change it only if you know what you are doing.
462
-	 *
463
-	 * Defaults to ``10``
464
-	 */
465
-	'auth.bruteforce.max-attempts' => 10,
466
-
467
-	/**
468
-	 * Whether the rate limit protection shipped with Nextcloud should be enabled or not.
469
-	 *
470
-	 * WARNING: Disabling this is discouraged for security reasons.
471
-	 *
472
-	 * Defaults to ``true``
473
-	 */
474
-	'ratelimit.protection.enabled' => true,
475
-
476
-	/**
477
-	 * Overwrite the individual rate limit for a specific route
478
-	 *
479
-	 * From time to time it can be necessary to extend the rate limit of a specific route,
480
-	 * depending on your usage pattern or when you script some actions.
481
-	 * Instead of completely disabling the rate limit or excluding an IP address from the
482
-	 * rate limit, the following config allows to overwrite the rate limit duration and period.
483
-	 *
484
-	 * The first level key is the name of the route. You can find the route name from a URL
485
-	 * using the ``occ router:list`` command of your server.
486
-	 *
487
-	 * You can also specify different limits for logged-in users with the ``user`` key
488
-	 * and not-logged-in users with the ``anon`` key. However, if there is no specific ``user`` limit,
489
-	 * the ``anon`` limit is also applied for logged-in users.
490
-	 *
491
-	 * Defaults to empty array ``[]``
492
-	 */
493
-	'ratelimit_overwrite' => [
494
-		'profile.profilepage.index' => [
495
-			'user' => ['limit' => 300, 'period' => 3600],
496
-			'anon' => ['limit' => 1, 'period' => 300],
497
-		]
498
-	],
499
-
500
-	/**
501
-	 * Size of subnet used to normalize IPv6
502
-	 *
503
-	 * For Brute Force Protection and Rate Limiting, IPv6 addresses are truncated using subnet size.
504
-	 * It defaults to /56, but you can set it between /32 and /64
505
-	 *
506
-	 * Defaults to ``56``
507
-	 */
508
-	'security.ipv6_normalized_subnet_size' => 56,
509
-
510
-	/**
511
-	 * By default, WebAuthn is available, but it can be explicitly disabled by admins
512
-	 */
513
-	'auth.webauthn.enabled' => true,
514
-
515
-	/**
516
-	 * Whether encrypted passwords should be stored in the database
517
-	 *
518
-	 * The passwords are only decrypted using the login token stored uniquely in the
519
-	 * clients and allow connecting to external storages, autoconfiguring mail accounts in
520
-	 * the mail app, and periodically checking if the password is still valid.
521
-	 *
522
-	 * This might be desirable to disable this functionality when using one-time
523
-	 * passwords or when having a password policy enforcing long passwords (> 300
524
-	 * characters).
525
-	 *
526
-	 * By default, the passwords are stored encrypted in the database.
527
-	 *
528
-	 * WARNING: If disabled, password changes on the user backend (e.g., on LDAP) no
529
-	 * longer log connected clients out automatically. Users can still disconnect
530
-	 * the clients by deleting the app token from the security settings.
531
-	 */
532
-	'auth.storeCryptedPassword' => true,
533
-
534
-	/**
535
-	 * By default, the login form is always available. There are cases (SSO) where an
536
-	 * admin wants to avoid users entering their credentials to the system if the SSO
537
-	 * app is unavailable.
538
-	 *
539
-	 * This will show an error. But the direct login still works with adding ``?direct=1``
540
-	 */
541
-	'hide_login_form' => false,
542
-
543
-	/**
544
-	 * If your user backend does not allow password resets (e.g., when it's a
545
-	 * read-only user backend like LDAP), you can specify a custom link, where the
546
-	 * user is redirected to, when clicking the "Reset password" link after a failed
547
-	 * login attempt.
548
-	 * In case you do not want to provide any link, replace the URL with ``'disabled'``
549
-	 */
550
-	'lost_password_link' => 'https://example.org/link/to/password/reset',
551
-
552
-	/**
553
-	 * URL to use as target for the logo link in the header (top-left logo)
554
-	 *
555
-	 * Defaults to the base URL of your Nextcloud instance
556
-	 */
557
-	'logo_url' => 'https://example.org',
558
-
559
-	/**
560
-	 * Mail Parameters
561
-	 *
562
-	 * These configure the email settings for Nextcloud notifications and password
563
-	 * resets.
564
-	 */
565
-
566
-	/**
567
-	 * The return address that you want to appear on emails sent by the Nextcloud
568
-	 * server, for example ``[email protected]``, substituting your own domain,
569
-	 * of course.
570
-	 */
571
-	'mail_domain' => 'example.com',
572
-
573
-	/**
574
-	 * FROM address that overrides the built-in ``sharing-noreply`` and
575
-	 * ``lostpassword-noreply`` FROM addresses.
576
-	 *
577
-	 * Defaults to different FROM addresses depending on the feature.
578
-	 */
579
-	'mail_from_address' => 'nextcloud',
580
-
581
-	/**
582
-	 * Enable SMTP class debugging.
583
-	 * NOTE: ``loglevel`` will likely need to be adjusted too. See docs:
584
-	 *   https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/email_configuration.html#enabling-debug-mode
585
-	 *
586
-	 * Defaults to ``false``
587
-	 */
588
-	'mail_smtpdebug' => false,
589
-
590
-	/**
591
-	 * Which mode to use for sending mail: ``sendmail``, ``smtp``, ``qmail``, or ``null``.
592
-	 *
593
-	 * If you are using local or remote SMTP, set this to ``smtp``.
594
-	 *
595
-	 * For the ``sendmail`` option, you need an installed and working email system on
596
-	 * the server, with ``/usr/sbin/sendmail`` installed on your Unix system.
597
-	 *
598
-	 * For ``qmail``, the binary is /var/qmail/bin/sendmail, and it must be installed
599
-	 * on your Unix system.
600
-	 *
601
-	 * Use the string ``null`` to send no mails (disable mail delivery). This can be
602
-	 * useful if mails should be sent via APIs and rendering messages is not necessary.
603
-	 *
604
-	 * Defaults to ``smtp``
605
-	 */
606
-	'mail_smtpmode' => 'smtp',
607
-
608
-	/**
609
-	 * This depends on ``mail_smtpmode``. Specify the IP address of your mail
610
-	 * server host. This may contain multiple hosts separated by a semicolon. If
611
-	 * you need to specify the port number, append it to the IP address separated by
612
-	 * a colon, like this: ``127.0.0.1:24``.
613
-	 *
614
-	 * Defaults to ``127.0.0.1``
615
-	 */
616
-	'mail_smtphost' => '127.0.0.1',
617
-
618
-	/**
619
-	 * This depends on ``mail_smtpmode``. Specify the port for sending mail.
620
-	 *
621
-	 * Defaults to ``25``
622
-	 */
623
-	'mail_smtpport' => 25,
624
-
625
-	/**
626
-	 * This depends on ``mail_smtpmode``. This sets the SMTP server timeout, in
627
-	 * seconds. You may need to increase this if you are running an anti-malware or
628
-	 * spam scanner.
629
-	 *
630
-	 * Defaults to ``10`` seconds
631
-	 */
632
-	'mail_smtptimeout' => 10,
633
-
634
-	/**
635
-	 * This depends on ``mail_smtpmode``. Specify ``ssl`` when you are using SSL/TLS. Any other value will be ignored.
636
-	 *
637
-	 * If the server advertises STARTTLS capabilities, they might be used, but they cannot be enforced by
638
-	 * this config option.
639
-	 *
640
-	 * Defaults to ``''`` (empty string)
641
-	 */
642
-	'mail_smtpsecure' => '',
643
-
644
-	/**
645
-	 * This depends on ``mail_smtpmode``. Change this to ``true`` if your mail
646
-	 * server requires authentication.
647
-	 *
648
-	 * Defaults to ``false``
649
-	 */
650
-	'mail_smtpauth' => false,
651
-
652
-	/**
653
-	 * This depends on ``mail_smtpauth``. Specify the username for authenticating to
654
-	 * the SMTP server.
655
-	 *
656
-	 * Defaults to ``''`` (empty string)
657
-	 */
658
-	'mail_smtpname' => '',
659
-
660
-	/**
661
-	 * This depends on ``mail_smtpauth``. Specify the password for authenticating to
662
-	 * the SMTP server.
663
-	 *
664
-	 * Default to ``''`` (empty string)
665
-	 */
666
-	'mail_smtppassword' => '',
667
-
668
-	/**
669
-	 * Replaces the default mail template layout. This can be utilized if the
670
-	 * options to modify the mail texts with the theming app are not enough.
671
-	 * The class must extend ``\OC\Mail\EMailTemplate``
672
-	 */
673
-	'mail_template_class' => '\OC\Mail\EMailTemplate',
674
-
675
-	/**
676
-	 * Email will be sent by default with an HTML and a plain text body. This option
677
-	 * allows sending only plain text emails.
678
-	 */
679
-	'mail_send_plaintext_only' => false,
680
-
681
-	/**
682
-	 * This depends on ``mail_smtpmode``. Array of additional streams options that
683
-	 * will be passed to underlying Swift mailer implementation.
684
-	 * Defaults to an empty array.
685
-	 */
686
-	'mail_smtpstreamoptions' => [],
687
-
688
-	/**
689
-	 * Which mode is used for sendmail/qmail: ``smtp`` or ``pipe``.
690
-	 *
691
-	 * For ``smtp``, the sendmail binary is started with the parameter ``-bs``:
692
-	 *   - Use the SMTP protocol on standard input and output.
693
-	 *
694
-	 * For ``pipe``, the binary is started with the parameters ``-t``:
695
-	 *   - Read message from STDIN and extract recipients.
696
-	 *
697
-	 * Defaults to ``smtp``
698
-	 */
699
-	'mail_sendmailmode' => 'smtp',
700
-
701
-	/**
702
-	 * Proxy Configurations
703
-	 */
704
-
705
-	/**
706
-	 * The automatic hostname detection of Nextcloud can fail in certain reverse
707
-	 * proxy and CLI/cron situations. This option allows you to manually override
708
-	 * the automatic detection; for example, ``www.example.com``, or specify the port
709
-	 * ``www.example.com:8080``.
710
-	 */
711
-	'overwritehost' => '',
712
-
713
-	/**
714
-	 * When generating URLs, Nextcloud attempts to detect whether the server is
715
-	 * accessed via ``https`` or ``http``. However, if Nextcloud is behind a proxy
716
-	 * and the proxy handles the ``https`` calls, Nextcloud would not know that
717
-	 * ``ssl`` is in use, which would result in incorrect URLs being generated.
718
-	 * Valid values are ``http`` and ``https``.
719
-	 */
720
-	'overwriteprotocol' => '',
721
-
722
-	/**
723
-	 * Nextcloud attempts to detect the webroot for generating URLs automatically.
724
-	 * For example, if ``www.example.com/nextcloud`` is the URL pointing to the
725
-	 * Nextcloud instance, the webroot is ``/nextcloud``. When proxies are in use,
726
-	 * it may be difficult for Nextcloud to detect this parameter, resulting in
727
-	 * invalid URLs.
728
-	 */
729
-	'overwritewebroot' => '',
730
-
731
-	/**
732
-	 * This option allows you to define a manual override condition as a regular
733
-	 * expression for the remote IP address. For example, defining a range of IP
734
-	 * addresses starting with ``10.0.0.`` and ending with 1 to 3:
735
-	 * ``^10\.0\.0\.[1-3]$``
736
-	 *
737
-	 * Defaults to ``''`` (empty string)
738
-	 */
739
-	'overwritecondaddr' => '',
740
-
741
-	/**
742
-	 * Use this configuration parameter to specify the base URL for any URLs which
743
-	 * are generated within Nextcloud using any kind of command line tools (cron or
744
-	 * occ). The value should contain the full base URL:
745
-	 * ``https://www.example.com/nextcloud``
746
-	 * Please make sure to set the value to the URL that your users mainly use to access this Nextcloud.
747
-	 * Otherwise, there might be problems with the URL generation via cron.
748
-	 *
749
-	 * Defaults to ``''`` (empty string)
750
-	 */
751
-	'overwrite.cli.url' => '',
752
-
753
-	/**
754
-	 * To have clean URLs without ``/index.php``, this parameter needs to be configured.
755
-	 *
756
-	 * This parameter will be written as "RewriteBase" on update and installation of
757
-	 * Nextcloud to your ``.htaccess`` file. While this value is often simply the URL
758
-	 * path of the Nextcloud installation, it cannot be set automatically properly in
759
-	 * every scenario and needs thus some manual configuration.
760
-	 *
761
-	 * In a standard Apache setup, this usually equals the folder that Nextcloud is
762
-	 * accessible at. So if Nextcloud is accessible via ``https://mycloud.org/nextcloud``,
763
-	 * the correct value would most likely be ``/nextcloud``. If Nextcloud is running
764
-	 * under ``https://mycloud.org/``, then it would be ``/``.
765
-	 *
766
-	 * Note that the above rule is not valid in every case, as there are some rare setup
767
-	 * cases where this may not apply. However, to avoid any update problems, this
768
-	 * configuration value is explicitly opt-in.
769
-	 *
770
-	 * After setting this value, run ``occ maintenance:update:htaccess``. Now, when the
771
-	 * following conditions are met, Nextcloud URLs won't contain ``index.php``:
772
-	 *
773
-	 * - ``mod_rewrite`` is installed
774
-	 * - ``mod_env`` is installed
775
-	 *
776
-	 * Defaults to ``''`` (empty string)
777
-	 */
778
-	'htaccess.RewriteBase' => '/',
779
-
780
-	/**
781
-	 * For server setups that don't have ``mod_env`` enabled or restricted (e.g., suEXEC),
782
-	 * this parameter has to be set to true and will assume mod_rewrite.
783
-	 *
784
-	 * Please check if ``mod_rewrite`` is active and functional before setting this
785
-	 * parameter, and you updated your .htaccess with ``occ maintenance:update:htaccess``.
786
-	 * Otherwise, your Nextcloud installation might not be reachable any more.
787
-	 * For example, try accessing resources by leaving out ``index.php`` in the URL.
788
-	 */
789
-	'htaccess.IgnoreFrontController' => false,
790
-
791
-	/**
792
-	 * The URL of your proxy server, for example, ``proxy.example.com:8081``.
793
-	 *
794
-	 * NOTE: Guzzle (the HTTP library used by Nextcloud) reads the environment
795
-	 * variables ``HTTP_PROXY`` (only for CLI requests), ``HTTPS_PROXY``, and ``NO_PROXY`` by default.
796
-	 *
797
-	 * If you configure a proxy with Nextcloud, any default configuration by Guzzle
798
-	 * is overwritten. Make sure to set ``proxyexclude`` accordingly if necessary.
799
-	 *
800
-	 * Defaults to ``''`` (empty string)
801
-	 */
802
-	'proxy' => '',
803
-
804
-	/**
805
-	 * The optional authentication for the proxy to use to connect to the internet.
806
-	 * The format is: ``username:password``.
807
-	 *
808
-	 * Defaults to ``''`` (empty string)
809
-	 */
810
-	'proxyuserpwd' => '',
811
-
812
-	/**
813
-	 * List of hostnames that should not be proxied to.
814
-	 * For example: ``['.mit.edu', 'foo.com']``.
815
-	 *
816
-	 * Hint: Use something like ``explode(',', getenv('NO_PROXY'))`` to sync this
817
-	 * value with the global ``NO_PROXY`` option.
818
-	 *
819
-	 * Defaults to empty array.
820
-	 */
821
-	'proxyexclude' => [],
822
-
823
-	/**
824
-	 * Allow remote servers with local addresses, e.g., in federated shares, webcal services, and more
825
-	 *
826
-	 * Defaults to ``false``
827
-	 */
828
-	'allow_local_remote_servers' => true,
829
-
830
-	/**
831
-	 * Deleted Items (trash bin)
832
-	 *
833
-	 * These parameters control the Deleted files app.
834
-	 */
835
-
836
-	/**
837
-	 * If the trash bin app is enabled (default), this setting defines the policy
838
-	 * for when files and folders in the trash bin will be permanently deleted.
839
-	 *
840
-	 * If the user quota limit is exceeded due to deleted files in the trash bin,
841
-	 * retention settings will be ignored and files will be cleaned up until
842
-	 * the quota requirements are met.
843
-	 *
844
-	 * The app allows for two settings, a minimum time for trash bin retention,
845
-	 * and a maximum time for trash bin retention.
846
-	 *
847
-	 * Minimum time is the number of days a file will be kept, after which it
848
-	 * *may be* deleted. A file may be deleted after the minimum number of days
849
-	 * has expired if space is needed. The file will not be deleted if space is
850
-	 * not needed.
851
-	 *
852
-	 * Whether "space is needed" depends on whether a user quota is defined or not:
853
-	 *
854
-	 *  * If no user quota is defined, the available space on the Nextcloud data
855
-	 *    partition sets the limit for the trashbin
856
-	 *    (issues: see https://github.com/nextcloud/server/issues/28451).
857
-	 *  * If a user quota is defined, 50% of the user's remaining quota space sets
858
-	 *    the limit for the trashbin.
859
-	 *
860
-	 * Maximum time is the number of days at which it is *guaranteed
861
-	 * to be* deleted. There is no further dependency on the available space.
862
-	 *
863
-	 * Both minimum and maximum times can be set together to explicitly define
864
-	 * file and folder deletion. For migration purposes, this setting is installed
865
-	 * initially set to "auto", which is equivalent to the default setting in
866
-	 * Nextcloud.
867
-	 *
868
-	 * Available values (D1 and D2 are configurable numbers):
869
-	 *
870
-	 * * ``auto``
871
-	 *     | Default setting. Keeps files and folders in the trash bin for at least **30** days.
872
-	 *     | Then, **if space is needed**, deletes trashed files anytime after that.
873
-	 * * ``D1, auto``
874
-	 *     | Keeps files and folders in the trash bin for at least **D1** days.
875
-	 *     | Then, **if space is needed**, deletes trashed files anytime after that.
876
-	 * * ``auto, D2``
877
-	 *     | **If space is needed**, deletes trashed files anytime.
878
-	 *     | After **D2** days, delete all trashed files automatically
879
-	 * * ``D1, D2``
880
-	 *     | Keeps files and folders in the trash bin for at least **D1** days.
881
-	 *     | Then, after **D2** days, delete all trashed files automatically.
882
-	 * * ``disabled``
883
-	 *     | Trash bin auto clean is disabled, files and folders will be kept forever.
884
-	 *
885
-	 * Defaults to ``auto``
886
-	 */
887
-	'trashbin_retention_obligation' => 'auto',
888
-
889
-
890
-	/**
891
-	 * File versions
892
-	 *
893
-	 * These parameters control the Versions app.
894
-	 */
895
-
896
-	/**
897
-	 * If the versions app is enabled (default), this setting defines the policy
898
-	 * for when versions will be permanently deleted.
899
-	 * The app allows for two settings, a minimum time for version retention,
900
-	 * and a maximum time for version retention.
901
-	 * Minimum time is the number of days a version will be kept, after which it
902
-	 * may be deleted. Maximum time is the number of days at which it is guaranteed
903
-	 * to be deleted.
904
-	 * Both minimum and maximum times can be set together to explicitly define
905
-	 * version deletion. For migration purposes, this setting is installed
906
-	 * initially set to "auto", which is equivalent to the default setting in
907
-	 * Nextcloud.
908
-	 *
909
-	 * Available values:
910
-	 *
911
-	 * * ``auto``
912
-	 *     default setting. Automatically expire versions according to expire
913
-	 *     rules. Please refer to :doc:`../configuration_files/file_versioning` for
914
-	 *     more information.
915
-	 * * ``D, auto``
916
-	 *     keep versions at least for D days, apply expiration rules to all versions
917
-	 *     that are older than D days
918
-	 * * ``auto, D``
919
-	 *     delete all versions that are older than D days automatically, delete
920
-	 *     other versions according to expire rules
921
-	 * * ``D1, D2``
922
-	 *     keep versions for at least D1 days and delete when exceeds D2 days
923
-	 * * ``disabled``
924
-	 *     versions auto clean disabled, versions will be kept forever
925
-	 *
926
-	 * Defaults to ``auto``
927
-	 */
928
-	'versions_retention_obligation' => 'auto',
929
-
930
-	/**
931
-	 * Nextcloud Verifications
932
-	 *
933
-	 * Nextcloud performs several verification checks. There are two options,
934
-	 * ``true`` and ``false``.
935
-	 */
936
-
937
-	/**
938
-	 * Checks an app before install whether it uses private APIs instead of the
939
-	 * proper public APIs. If this is set to true, it will only allow installing or
940
-	 * enabling apps that pass this check.
941
-	 *
942
-	 * Defaults to ``false``
943
-	 */
944
-	'appcodechecker' => true,
945
-
946
-	/**
947
-	 * Check if Nextcloud is up-to-date and shows a notification if a new version is
948
-	 * available. It sends current version, PHP version, installation and last update
949
-	 * time, and release channel to the updater server which responds with the latest
950
-	 * available version based on those metrics.
951
-	 *
952
-	 * Defaults to ``true``
953
-	 */
954
-	'updatechecker' => true,
955
-
956
-	/**
957
-	 * URL that Nextcloud should use to look for updates
958
-	 *
959
-	 * Defaults to ``https://updates.nextcloud.com/updater_server/``
960
-	 */
961
-	'updater.server.url' => 'https://updates.nextcloud.com/updater_server/',
962
-
963
-	/**
964
-	 * The channel that Nextcloud should use to look for updates
965
-	 *
966
-	 * Supported values:
967
-	 *
968
-	 * - ``daily``
969
-	 * - ``beta``
970
-	 * - ``stable``
971
-	 */
972
-	'updater.release.channel' => 'stable',
973
-
974
-	/**
975
-	 * Is Nextcloud connected to the Internet or running in a closed network?
976
-	 *
977
-	 * Defaults to ``true``
978
-	 */
979
-	'has_internet_connection' => true,
980
-
981
-	/**
982
-	 * Which domains to request to determine the availability of an Internet
983
-	 * connection. If none of these hosts are reachable, the administration panel
984
-	 * will show a warning. Set to an empty list to not do any such checks (warning
985
-	 * will still be shown).
986
-	 * If no protocol is provided, both http and https will be tested.
987
-	 * For example, ``http://www.nextcloud.com`` and ``https://www.nextcloud.com``
988
-	 * will be tested for ``www.nextcloud.com``
989
-	 * If a protocol is provided, only this one will be tested.
990
-	 *
991
-	 * Defaults to the following domains:
992
-	 *
993
-	 *  - https://www.nextcloud.com
994
-	 *  - https://www.startpage.com
995
-	 *  - https://www.eff.org
996
-	 *  - https://www.edri.org
997
-	 */
998
-	'connectivity_check_domains' => [
999
-		'https://www.nextcloud.com',
1000
-		'https://www.startpage.com',
1001
-		'https://www.eff.org',
1002
-		'https://www.edri.org'
1003
-	],
1004
-
1005
-	/**
1006
-	 * Allows Nextcloud to verify a working .well-known URL redirects. This is done
1007
-	 * by attempting to make a request from JS to
1008
-	 * ``https://example.tld/.well-known/caldav/``
1009
-	 *
1010
-	 * Defaults to ``true``
1011
-	 */
1012
-	'check_for_working_wellknown_setup' => true,
1013
-
1014
-	/**
1015
-	 * This is a crucial security check on Apache servers that should always be set
1016
-	 * to ``true``. This verifies that the ``.htaccess`` file is writable and works.
1017
-	 * If it is not, then any options controlled by ``.htaccess``, such as large
1018
-	 * file uploads, will not work. It also runs checks on the ``data/`` directory,
1019
-	 * which verifies that it can't be accessed directly through the Web server.
1020
-	 *
1021
-	 * Defaults to ``true``
1022
-	 */
1023
-	'check_for_working_htaccess' => true,
1024
-
1025
-	/**
1026
-	 * In rare setups (e.g., on OpenShift or Docker on Windows), the permissions check
1027
-	 * might block the installation while the underlying system offers no means to
1028
-	 * "correct" the permissions. In this case, set the value to false.
1029
-	 *
1030
-	 * In regular cases, if issues with permissions are encountered, they should be
1031
-	 * adjusted accordingly. Changing the flag is discouraged.
1032
-	 *
1033
-	 * Defaults to ``true``
1034
-	 */
1035
-	'check_data_directory_permissions' => true,
1036
-
1037
-	/**
1038
-	 * In certain environments, it is desired to have a read-only configuration file.
1039
-	 * When this switch is set to ``true``, writing to the config file will be
1040
-	 * forbidden. Therefore, it will not be possible to configure all options via
1041
-	 * the Web interface. Furthermore, when updating Nextcloud, it is required to
1042
-	 * make the configuration file writable again and to set this switch to ``false``
1043
-	 * for the update process.
1044
-	 *
1045
-	 * Defaults to ``false``
1046
-	 */
1047
-	'config_is_read_only' => false,
1048
-
1049
-	/**
1050
-	 * Logging
1051
-	 */
1052
-
1053
-	/**
1054
-	 * This parameter determines where the Nextcloud logs are sent.
1055
-	 *
1056
-	 * - ``file``: the logs are written to file ``nextcloud.log`` in the default Nextcloud data directory. The log file can be changed with parameter ``logfile``.
1057
-	 * - ``syslog``: the logs are sent to the system log. This requires a syslog daemon to be active.
1058
-	 * - ``errorlog``: the logs are sent to the PHP ``error_log`` function.
1059
-	 * - ``systemd``: the logs are sent to the Systemd journal. This requires a system that runs Systemd and the Systemd journal. The PHP extension ``systemd`` must be installed and active.
1060
-	 *
1061
-	 * Defaults to ``file``
1062
-	 */
1063
-	'log_type' => 'file',
1064
-
1065
-	/**
1066
-	 * This parameter determines where the audit logs are sent. See ``log_type`` for more information.
1067
-	 *
1068
-	 * Defaults to ``file``
1069
-	 */
1070
-	'log_type_audit' => 'file',
1071
-
1072
-	/**
1073
-	 * Name of the file to which the Nextcloud logs are written if parameter
1074
-	 * ``log_type`` is set to ``file``.
1075
-	 *
1076
-	 * Defaults to ``[datadirectory]/nextcloud.log``
1077
-	 */
1078
-	'logfile' => '/var/log/nextcloud.log',
1079
-
1080
-	/**
1081
-	 * Name of the file to which the audit logs are written if parameter
1082
-	 * ``log_type`` is set to ``file``.
1083
-	 *
1084
-	 * Defaults to ``[datadirectory]/audit.log``
1085
-	 */
1086
-	'logfile_audit' => '/var/log/audit.log',
1087
-
1088
-	/**
1089
-	 * Log file mode for the Nextcloud logging type in octal notation.
1090
-	 *
1091
-	 * Defaults to ``0640`` (writable by user, readable by group).
1092
-	 */
1093
-	'logfilemode' => 0640,
1094
-
1095
-	/**
1096
-	 * Loglevel to start logging at. Valid values are:
1097
-	 *
1098
-	 * - ``0`` = Debug
1099
-	 * - ``1`` = Info
1100
-	 * - ``2`` = Warning
1101
-	 * - ``3`` = Error
1102
-	 * - ``4`` = Fatal.
1103
-	 *
1104
-	 *
1105
-	 * Defaults to ``2`` (Warning)
1106
-	 */
1107
-	'loglevel' => 2,
1108
-
1109
-	/**
1110
-	 * Loglevel used by the frontend to start logging at. The same values as
1111
-	 * for ``loglevel`` can be used. If not set, it defaults to the value
1112
-	 * configured for ``loglevel`` or Warning if that is not set either.
1113
-	 *
1114
-	 * Defaults to ``2``
1115
-	 */
1116
-	'loglevel_frontend' => 2,
1117
-
1118
-	/**
1119
-	 * Loglevel used by the dirty database query detection. Useful to identify
1120
-	 * potential database bugs in production. Set this to loglevel or higher to
1121
-	 * see dirty queries in the logs.
1122
-	 *
1123
-	 * Defaults to ``0`` (debug)
1124
-	 */
1125
-	'loglevel_dirty_database_queries' => 0,
1126
-
1127
-	/**
1128
-	 * If you maintain different instances and aggregate the logs, you may want
1129
-	 * to distinguish between them. ``syslog_tag`` can be set per instance
1130
-	 * with a unique ID. Only available if ``log_type`` is set to ``syslog`` or
1131
-	 * ``systemd``.
1132
-	 *
1133
-	 * The default value is ``Nextcloud``.
1134
-	 */
1135
-	'syslog_tag' => 'Nextcloud',
1136
-
1137
-	/**
1138
-	 * If you maintain different instances and aggregate the logs, you may want
1139
-	 * to distinguish between them. ``syslog_tag_audit`` can be set per instance
1140
-	 * with a unique ID. Only available if ``log_type`` is set to ``syslog`` or
1141
-	 * ``systemd``.
1142
-	 *
1143
-	 * The default value is the value of ``syslog_tag``.
1144
-	 */
1145
-	'syslog_tag_audit' => 'Nextcloud',
1146
-
1147
-	/**
1148
-	 * Log condition for log level increase based on conditions. Once one of these
1149
-	 * conditions is met, the required log level is set to debug. This allows
1150
-	 * debugging specific requests, users, or apps
1151
-	 *
1152
-	 * Supported conditions:
1153
-	 *  - ``shared_secret``: if a request parameter with the name `log_secret` is set to
1154
-	 *                this value, the condition is met
1155
-	 *  - ``users``:  if the current request is done by one of the specified users,
1156
-	 *                this condition is met
1157
-	 *  - ``apps``:   if the log message is invoked by one of the specified apps,
1158
-	 *                this condition is met
1159
-	 *  - ``matches``: if all the conditions inside a group match,
1160
-	 *                this condition is met. This allows logging only entries to an app
1161
-	 *                by a few users.
1162
-	 *
1163
-	 * Defaults to an empty array.
1164
-	 */
1165
-	'log.condition' => [
1166
-		'shared_secret' => '57b58edb6637fe3059b3595cf9c41b9',
1167
-		'users' => ['sample-user'],
1168
-		'apps' => ['files'],
1169
-		'matches' => [
1170
-			[
1171
-				'shared_secret' => '57b58edb6637fe3059b3595cf9c41b9',
1172
-				'users' => ['sample-user'],
1173
-				'apps' => ['files'],
1174
-				'loglevel' => 1,
1175
-				'message' => 'contains substring'
1176
-			],
1177
-		],
1178
-	],
1179
-
1180
-	/**
1181
-	 * Enables logging a backtrace with each log line. Normally, only Exceptions
1182
-	 * carry backtrace information, which are logged automatically. This
1183
-	 * switch turns them on for any log message. Enabling this option will lead
1184
-	 * to increased log data size.
1185
-	 *
1186
-	 * Defaults to ``false``.
1187
-	 */
1188
-	'log.backtrace' => false,
1189
-
1190
-	/**
1191
-	 * This uses PHP.date formatting; see https://www.php.net/manual/en/function.date.php
1192
-	 *
1193
-	 * Defaults to ISO 8601 ``2005-08-15T15:52:01+00:00``, see ``\DateTime::ATOM``
1194
-	 * https://www.php.net/manual/en/class.datetimeinterface.php#datetimeinterface.constants.atom
1195
-	 */
1196
-	'logdateformat' => 'F d, Y H:i:s',
1197
-
1198
-	/**
1199
-	 * The timezone for logfiles. See https://www.php.net/manual/en/timezones.php
1200
-	 *
1201
-	 * Defaults to ``UTC``
1202
-	 */
1203
-	'logtimezone' => 'Europe/Berlin',
1204
-
1205
-	/**
1206
-	 * Append all database queries and parameters to the log file. Use this only for
1207
-	 * debugging, as your logfile will become huge.
1208
-	 */
1209
-	'log_query' => false,
1210
-
1211
-	/**
1212
-	 * Enables log rotation and limits the total size of logfiles. Set it to 0 for
1213
-	 * no rotation. Specify a size in bytes, for example, 104857600 (100 megabytes
1214
-	 * = 100 * 1024 * 1024 bytes). A new logfile is created with a new name when the
1215
-	 * old logfile reaches your limit. If a rotated log file is already present, it
1216
-	 * will be overwritten.
1217
-	 *
1218
-	 * Defaults to 100 MB
1219
-	 */
1220
-	'log_rotate_size' => 100 * 1024 * 1024,
1221
-
1222
-	/**
1223
-	 * Enable built-in profiler. Helpful when trying to debug performance
1224
-	 * issues.
1225
-	 *
1226
-	 * Note that this has a performance impact and shouldn't be enabled
1227
-	 * on production.
1228
-	 */
1229
-	'profiler' => false,
1230
-
1231
-	/**
1232
-	 * Enable profiling for individual requests if profiling single requests is enabled or the secret is passed.
1233
-	 * This requires the excimer extension to be installed. Be careful with this, as it can generate a lot of data.
1234
-	 *
1235
-	 * The profile data will be stored as a JSON file in the profiling.path directory that can be analyzed with speedscope.
1236
-	 *
1237
-	 * Defaults to ``false``
1238
-	 */
1239
-	'profiling.request' => false,
1240
-
1241
-	/**
1242
-	 * The rate at which profiling data is collected for individual requests.
1243
-	 * A lower value means more data points but higher overhead.
1244
-	 *
1245
-	 * Defaults to ``0.001``
1246
-	 */
1247
-	'profiling.request.rate' => 0.001,
1248
-
1249
-	/**
1250
-	 * A secret token that can be passed via ?profile_secret=<secret> to enable profiling for a specific request.
1251
-	 * This allows profiling specific requests in production without enabling it globally.
1252
-	 *
1253
-	 * No default value.
1254
-	 */
1255
-	'profiling.secret' => '',
1256
-
1257
-	/**
1258
-	 * Enable sampling-based profiling. This collects profiling data periodically rather than per-request.
1259
-	 * This requires the excimer extension to be installed. Be careful with this, as it can generate a lot of data.
1260
-	 *
1261
-	 * The profile data will be stored as a plain text file in the profiling.path directory that can be analyzed with speedscope.
1262
-	 *
1263
-	 * Defaults to ``false``
1264
-	 */
1265
-	'profiling.sample' => false,
1266
-
1267
-	/**
1268
-	 * The rate at which sampling profiling data is collected in seconds.
1269
-	 * A lower value means more frequent samples but higher overhead.
1270
-	 *
1271
-	 * Defaults to ``1``
1272
-	 */
1273
-	'profiling.sample.rate' => 1,
1274
-
1275
-	/**
1276
-	 * How often (in minutes) the sample log files are rotated.
1277
-	 *
1278
-	 * Defaults to ``60``
1279
-	 */
1280
-	'profiling.sample.rotation' => 60,
1281
-
1282
-	/**
1283
-	 * The directory where profiling data is stored.
1284
-	 *
1285
-	 * Note that this directory must be writable by the web server user and will not be cleaned up automatically.
1286
-	 */
1287
-	'profiling.path' => '/tmp',
1288
-
1289
-
1290
-	/**
1291
-	 * Alternate Code Locations
1292
-	 *
1293
-	 * Some Nextcloud code may be stored in alternate locations.
1294
-	 */
1295
-
1296
-	/**
1297
-	 * This section is for configuring the download links for Nextcloud clients, as
1298
-	 * seen in the first-run wizard and on Personal pages.
1299
-	 *
1300
-	 * Defaults to:
1301
-	 *
1302
-	 * - Desktop client: ``https://nextcloud.com/install/#install-clients``
1303
-	 * - Android client: ``https://play.google.com/store/apps/details?id=com.nextcloud.client``
1304
-	 * - iOS client: ``https://itunes.apple.com/us/app/nextcloud/id1125420102?mt=8``
1305
-	 * - iOS client app ID: ``1125420102``
1306
-	 * - F-Droid client: ``https://f-droid.org/packages/com.nextcloud.client/``
1307
-	 */
1308
-	'customclient_desktop'
1309
-		=> 'https://nextcloud.com/install/#install-clients',
1310
-	'customclient_android'
1311
-		=> 'https://play.google.com/store/apps/details?id=com.nextcloud.client',
1312
-	'customclient_ios'
1313
-		=> 'https://itunes.apple.com/us/app/nextcloud/id1125420102?mt=8',
1314
-	'customclient_ios_appid'
1315
-		=> '1125420102',
1316
-	'customclient_fdroid'
1317
-		=> 'https://f-droid.org/packages/com.nextcloud.client/',
1318
-
1319
-	/**
1320
-	 * Activity
1321
-	 *
1322
-	 * Options for the activity app.
1323
-	 */
1324
-
1325
-	/**
1326
-	 * Retention of activities.
1327
-	 *
1328
-	 * A daily cron job deletes all activities for all users which are older than
1329
-	 * the number of days specified here.
1330
-	 *
1331
-	 * Defaults to ``365``
1332
-	 */
1333
-	'activity_expire_days' => 365,
1334
-
1335
-	/**
1336
-	 * Activities in Team Folders and External Storages.
1337
-	 *
1338
-	 * By default, activities in team folders or external storages are only generated
1339
-	 * for the current user. This is due to a limitations in current implementations.
1340
-	 * This config flag makes activities in group folders and external storages work
1341
-	 * like in normal shares (when set to ``true``).
1342
-	 *
1343
-	 * WARNING: Enabling this comes with some CRITICAL trade-offs:
1344
-	 *
1345
-	 * - If team folder "Advanced Permissions" (ACLs) are used, activities do not
1346
-	 *   respect the permissions and therefore all users see all activities, even
1347
-	 *   for files and directories they do not have access to.
1348
-	 * - Users who had access to a team folder, share, or external storage can see
1349
-	 *   activities in their stream and emails that happen after they are removed
1350
-	 *   until they log in again.
1351
-	 * - Users who are newly added to a team folder, share, or external storage
1352
-	 *   cannot see activities in their stream or emails that happen after they
1353
-	 *   are added until they log in again.
1354
-	 *
1355
-	 * Defaults to ``false``
1356
-	 */
1357
-	'activity_use_cached_mountpoints' => false,
1358
-
1359
-	/**
1360
-	 * Apps
1361
-	 *
1362
-	 * Options for the Apps folder, Apps store, and App code checker.
1363
-	 */
1364
-
1365
-	/**
1366
-	 * Set the default app to open on login. The entry IDs can be retrieved from
1367
-	 * the Navigations OCS API endpoint: https://docs.nextcloud.com/server/latest/developer_manual/_static/openapi.html#/operations/core-navigation-get-apps-navigation.
1368
-	 * You can use a comma-separated list of app names, so if the first
1369
-	 * app is not enabled for a user, then Nextcloud will try the second one, and so
1370
-	 * on. If no enabled apps are found, it defaults to the dashboard app.
1371
-	 *
1372
-	 * Defaults to ``dashboard,files``
1373
-	 */
1374
-	'defaultapp' => 'dashboard,files',
1375
-
1376
-	/**
1377
-	 * When enabled, admins may install apps from the Nextcloud app store.
1378
-	 *
1379
-	 * Defaults to ``true``
1380
-	 */
1381
-	'appstoreenabled' => true,
1382
-
1383
-	/**
1384
-	 * Enables the installation of apps from a self-hosted apps store.
1385
-	 * Requires that at least one of the configured apps directories is writable.
1386
-	 *
1387
-	 * Defaults to ``https://apps.nextcloud.com/api/v1``
1388
-	 */
1389
-	'appstoreurl' => 'https://apps.nextcloud.com/api/v1',
1390
-
1391
-	/**
1392
-	 * Filters allowed installable apps from the appstore.
1393
-	 * Empty array will prevent all apps from the store to be found.
1394
-	 */
1395
-	'appsallowlist' => [],
1396
-
1397
-	/**
1398
-	 * Use the ``apps_paths`` parameter to set the location of the Apps directory,
1399
-	 * which should be scanned for available apps, and where user-specific apps
1400
-	 * should be installed from the Apps store. The ``path`` defines the absolute
1401
-	 * file system path to the app folder. The key ``url`` defines the HTTP Web path
1402
-	 * to that folder, starting from the Nextcloud webroot. The key ``writable``
1403
-	 * indicates if a Web server can write files to that folder.
1404
-	 */
1405
-	'apps_paths' => [
1406
-		[
1407
-			'path' => '/var/www/nextcloud/apps',
1408
-			'url' => '/apps',
1409
-			'writable' => true,
1410
-		],
1411
-	],
1412
-
1413
-	/**
1414
-	 * @see appcodechecker
1415
-	 */
1416
-
1417
-	/**
1418
-	 * Previews
1419
-	 *
1420
-	 * Nextcloud supports generating previews for various file types, such as images, audio files, and text files.
1421
-	 * These options control enabling and disabling previews, and thumbnail size.
1422
-	 */
1423
-
1424
-	/**
1425
-	 * By default, Nextcloud can generate previews for the following filetypes:
1426
-	 *
1427
-	 * - Image files
1428
-	 * - Text documents
1429
-	 *
1430
-	 * Valid values are ``true``, to enable previews, or
1431
-	 * ``false``, to disable previews
1432
-	 *
1433
-	 * Defaults to ``true``
1434
-	 */
1435
-	'enable_previews' => true,
1436
-
1437
-	/**
1438
-	 * Number of all preview requests being processed concurrently,
1439
-	 * including previews that need to be newly generated, and those that have
1440
-	 * been generated.
1441
-	 *
1442
-	 * This should be greater than ``preview_concurrency_new``.
1443
-	 * If unspecified, defaults to twice the value of ``preview_concurrency_new``.
1444
-	 */
1445
-	'preview_concurrency_all' => 8,
1446
-
1447
-	/**
1448
-	 * Number of new previews that are being concurrently generated.
1449
-	 *
1450
-	 * Depending on the max preview size set by ``preview_max_x`` and ``preview_max_y``,
1451
-	 * the generation process can consume considerable CPU and memory resources.
1452
-	 * It's recommended to limit this to be no greater than the number of CPU cores.
1453
-	 * If unspecified, defaults to the number of CPU cores, or 4 if that cannot
1454
-	 * be determined.
1455
-	 */
1456
-	'preview_concurrency_new' => 4,
1457
-
1458
-	/**
1459
-	 * The maximum width, in pixels, of a preview. A value of ``null`` means there
1460
-	 * is no limit.
1461
-	 *
1462
-	 * Defaults to ``4096``
1463
-	 */
1464
-	'preview_max_x' => 4096,
1465
-	/**
1466
-	 * The maximum height, in pixels, of a preview. A value of ``null`` means there
1467
-	 * is no limit.
1468
-	 *
1469
-	 * Defaults to ``4096``
1470
-	 */
1471
-	'preview_max_y' => 4096,
1472
-
1473
-	/**
1474
-	 * Max file size for generating image previews with imagegd (default behavior).
1475
-	 * If the image is bigger, it'll try other preview generators, but will most
1476
-	 * likely either show the default mimetype icon or not display the image at all.
1477
-	 * Set to ``-1`` for no limit and try to generate image previews on all file sizes.
1478
-	 *
1479
-	 * Defaults to ``50`` megabytes
1480
-	 */
1481
-	'preview_max_filesize_image' => 50,
1482
-
1483
-	/**
1484
-	 * Max memory for generating image previews with imagegd (default behavior)
1485
-	 * Reads the image dimensions from the header and assumes 32 bits per pixel.
1486
-	 * If creating the image would allocate more memory, preview generation will
1487
-	 * be disabled and the default mimetype icon is shown. Set to ``-1`` for no limit.
1488
-	 *
1489
-	 * Defaults to ``256`` megabytes
1490
-	 */
1491
-	'preview_max_memory' => 256,
1492
-
1493
-	/**
1494
-	 * Custom path for LibreOffice/OpenOffice binary
1495
-	 *
1496
-	 *
1497
-	 * Defaults to ``''`` (empty string)
1498
-	 */
1499
-	'preview_libreoffice_path' => '/usr/bin/libreoffice',
1500
-
1501
-	/**
1502
-	 * Custom path for ffmpeg binary
1503
-	 *
1504
-	 * Defaults to ``null`` and falls back to searching ``ffmpeg``
1505
-	 * in the configured ``PATH`` environment
1506
-	 */
1507
-	'preview_ffmpeg_path' => '/usr/bin/ffmpeg',
1508
-
1509
-	/**
1510
-	 * Custom path for ffprobe binary
1511
-	 *
1512
-	 * Defaults to ``null`` and falls back to using the same path as ffmpeg.
1513
-	 * ffprobe is typically packaged with ffmpeg and is required for
1514
-	 * enhanced preview generation for HDR videos.
1515
-	 */
1516
-	'preview_ffprobe_path' => '/usr/bin/ffprobe',
1517
-
1518
-	/**
1519
-	 * Set the URL of the Imaginary service to send image previews to.
1520
-	 * Also requires the ``OC\Preview\Imaginary`` provider to be enabled in the
1521
-	 * ``enabledPreviewProviders`` array, to create previews for these mimetypes: bmp,
1522
-	 * x-bitmap, png, jpeg, gif, heic, heif, svg+xml, tiff, webp, and illustrator.
1523
-	 *
1524
-	 * If you want Imaginary to also create preview images from PDF documents, you
1525
-	 * have to add the ``OC\Preview\ImaginaryPDF`` provider as well.
1526
-	 *
1527
-	 * See https://github.com/h2non/imaginary
1528
-	 */
1529
-	'preview_imaginary_url' => 'http://previews_hpb:8088/',
1530
-
1531
-	/**
1532
-	 * If you want to set an API key for Imaginary.
1533
-	 */
1534
-	'preview_imaginary_key' => 'secret',
1535
-
1536
-	/**
1537
-	 * Only register providers that have been explicitly enabled
1538
-	 *
1539
-	 * The following providers are disabled by default due to performance or privacy
1540
-	 * concerns:
1541
-	 *
1542
-	 *  - ``OC\Preview\EMF``
1543
-	 *  - ``OC\Preview\Font``
1544
-	 *  - ``OC\Preview\HEIC``
1545
-	 *  - ``OC\Preview\Illustrator``
1546
-	 *  - ``OC\Preview\Movie``
1547
-	 *  - ``OC\Preview\MP3``
1548
-	 *  - ``OC\Preview\MSOffice2003``
1549
-	 *  - ``OC\Preview\MSOffice2007``
1550
-	 *  - ``OC\Preview\MSOfficeDoc``
1551
-	 *  - ``OC\Preview\PDF``
1552
-	 *  - ``OC\Preview\Photoshop``
1553
-	 *  - ``OC\Preview\Postscript``
1554
-	 *  - ``OC\Preview\SGI``
1555
-	 *  - ``OC\Preview\StarOffice``
1556
-	 *  - ``OC\Preview\SVG``
1557
-	 *  - ``OC\Preview\TGA``
1558
-	 *  - ``OC\Preview\TIFF``
1559
-	 *
1560
-	 * The following providers are disabled by default, because they provide an alternative to the built-in providers:
1561
-	 *   - ``OC\Preview\Imaginary``
1562
-	 *   - ``OC\Preview\ImaginaryPDF``
1563
-	 *
1564
-	 * Defaults to the following providers:
1565
-	 *
1566
-	 *  - ``OC\Preview\PNG``
1567
-	 *  - ``OC\Preview\JPEG``
1568
-	 *  - ``OC\Preview\GIF``
1569
-	 *  - ``OC\Preview\BMP``
1570
-	 *  - ``OC\Preview\XBitmap``
1571
-	 *  - ``OC\Preview\Krita``
1572
-	 *  - ``OC\Preview\WebP``
1573
-	 *  - ``OC\Preview\MarkDown``
1574
-	 *  - ``OC\Preview\TXT``
1575
-	 *  - ``OC\Preview\OpenDocument``
1576
-	 *
1577
-	 */
1578
-	'enabledPreviewProviders' => [
1579
-		'OC\Preview\PNG',
1580
-		'OC\Preview\JPEG',
1581
-		'OC\Preview\GIF',
1582
-		'OC\Preview\BMP',
1583
-		'OC\Preview\XBitmap',
1584
-		'OC\Preview\Krita',
1585
-		'OC\Preview\WebP',
1586
-		'OC\Preview\MarkDown',
1587
-		'OC\Preview\TXT',
1588
-		'OC\Preview\OpenDocument',
1589
-	],
1590
-
1591
-	/**
1592
-	 * Maximum file size for metadata generation.
1593
-	 * If a file exceeds this size, metadata generation will be skipped.
1594
-	 *
1595
-	 * NOTE: memory equivalent to this size will be used for metadata generation.
1596
-	 *
1597
-	 * Default: 256 megabytes.
1598
-	 */
1599
-	'metadata_max_filesize' => 256,
1600
-
1601
-	/**
1602
-	 * Maximum file size for file conversion.
1603
-	 * If a file exceeds this size, the file will not be converted.
1604
-	 *
1605
-	 * Default: 100 MiB
1606
-	 */
1607
-	'max_file_conversion_filesize' => 100,
1608
-
1609
-	/**
1610
-	 * LDAP
1611
-	 *
1612
-	 * Global settings used by LDAP User and Group Backend
1613
-	 */
1614
-
1615
-	/**
1616
-	 * Defines the interval in minutes for the background job that checks user
1617
-	 * existence and marks them as ready to be cleaned up. The number is always
1618
-	 * minutes. Setting it to 0 disables the feature.
1619
-	 * See command line (occ) methods ``ldap:show-remnants`` and ``user:delete``
1620
-	 *
1621
-	 * Defaults to ``51`` minutes
1622
-	 */
1623
-	'ldapUserCleanupInterval' => 51,
1624
-
1625
-	/**
1626
-	 * Sort groups in the user settings by name instead of the user count
1627
-	 *
1628
-	 * By enabling this, the user count beside the group name is disabled as well.
1629
-	 *
1630
-	 * @deprecated 29.0.0 Use the frontend instead or set the app config value ``group.sortBy`` for ``core`` to ``2``
1631
-	 */
1632
-	'sort_groups_by_name' => false,
1633
-
1634
-	/**
1635
-	 * Comments
1636
-	 *
1637
-	 * Global settings for the Comments infrastructure
1638
-	 */
1639
-
1640
-	/**
1641
-	 * Replaces the default Comments Manager Factory. This can be utilized if an
1642
-	 * own or 3rd-party CommentsManager should be used that – for instance – uses the
1643
-	 * filesystem instead of the database to keep the comments.
1644
-	 *
1645
-	 * Defaults to ``\OC\Comments\ManagerFactory``
1646
-	 */
1647
-	'comments.managerFactory' => '\OC\Comments\ManagerFactory',
1648
-
1649
-	/**
1650
-	 * Replaces the default System Tags Manager Factory. This can be utilized if an
1651
-	 * own or 3rd-party SystemTagsManager should be used that – for instance – uses the
1652
-	 * filesystem instead of the database to keep the tags.
1653
-	 *
1654
-	 * Defaults to ``\OC\SystemTag\ManagerFactory``
1655
-	 */
1656
-	'systemtags.managerFactory' => '\OC\SystemTag\ManagerFactory',
1657
-
1658
-	/**
1659
-	 * Maintenance
1660
-	 *
1661
-	 * These options are for halting user activity when you are performing server
1662
-	 * maintenance.
1663
-	 */
1664
-
1665
-	/**
1666
-	 * Enable maintenance mode to disable Nextcloud
1667
-	 *
1668
-	 * If you want to prevent users from logging in to Nextcloud before you start
1669
-	 * doing some maintenance work, you need to set the value of the maintenance
1670
-	 * parameter to true. Please keep in mind that users who are already logged in
1671
-	 * are kicked out of Nextcloud instantly.
1672
-	 *
1673
-	 * Defaults to ``false``
1674
-	 */
1675
-	'maintenance' => false,
1676
-
1677
-	/**
1678
-	 * UTC Hour for maintenance windows
1679
-	 *
1680
-	 * Some background jobs only run once a day. When an hour is defined for this config,
1681
-	 * the background jobs which advertise themselves as not time sensitive will be
1682
-	 * delayed during the "working" hours and only run in the 4 hours after the given time.
1683
-	 * This is, e.g., used for activity expiration, suspicious login training, and update checks.
1684
-	 *
1685
-	 * A value of 1, e.g., will only run these background jobs between 01:00am UTC and 05:00am UTC.
1686
-	 *
1687
-	 * Defaults to ``100`` which disables the feature
1688
-	 */
1689
-	'maintenance_window_start' => 1,
1690
-
1691
-	/**
1692
-	 * Log all LDAP requests into a file
1693
-	 *
1694
-	 * Warning: This heavily decreases the performance of the server and is only
1695
-	 * meant to debug/profile the LDAP interaction manually.
1696
-	 * Also, it might log sensitive data into a plain text file.
1697
-	 */
1698
-	'ldap_log_file' => '',
1699
-
1700
-	/**
1701
-	 * SSL
1702
-	 */
1703
-
1704
-	/**
1705
-	 * Extra SSL options to be used for configuration.
1706
-	 *
1707
-	 * Defaults to an empty array.
1708
-	 */
1709
-	'openssl' => [
1710
-		'config' => '/absolute/location/of/openssl.cnf',
1711
-	],
1712
-
1713
-	/**
1714
-	 * Memory caching backend configuration
1715
-	 *
1716
-	 * Available cache backends:
1717
-	 *
1718
-	 * * ``\OC\Memcache\APCu``       APC user backend
1719
-	 * * ``\OC\Memcache\ArrayCache`` In-memory array-based backend (not recommended)
1720
-	 * * ``\OC\Memcache\Memcached``  Memcached backend
1721
-	 * * ``\OC\Memcache\Redis``      Redis backend
1722
-	 *
1723
-	 * Advice on choosing between the various backends:
1724
-	 *
1725
-	 * * APCu should be easiest to install. Almost all distributions have packages.
1726
-	 *   Use this for single user environment for all caches.
1727
-	 * * Use Redis or Memcached for distributed environments.
1728
-	 *   For the local cache (you can configure two) take APCu.
1729
-	 */
1730
-
1731
-	/**
1732
-	 * Memory caching backend for locally stored data
1733
-	 *
1734
-	 * * Used for host-specific data, e.g., file paths
1735
-	 *
1736
-	 * Defaults to ``none``
1737
-	 */
1738
-	'memcache.local' => '\\OC\\Memcache\\APCu',
1739
-
1740
-	/**
1741
-	 * Memory caching backend for distributed data
1742
-	 *
1743
-	 * * Used for installation-specific data, e.g., database caching
1744
-	 * * If unset, defaults to the value of memcache.local
1745
-	 *
1746
-	 * Defaults to ``none``
1747
-	 */
1748
-	'memcache.distributed' => '\\OC\\Memcache\\Memcached',
1749
-
1750
-	/**
1751
-	 * Connection details for Redis to use for memory caching in a single server configuration.
1752
-	 *
1753
-	 * For enhanced security, it is recommended to configure Redis
1754
-	 * to require a password. See http://redis.io/topics/security
1755
-	 * for more information.
1756
-	 *
1757
-	 * We also support Redis SSL/TLS encryption as of version 6.
1758
-	 * See https://redis.io/topics/encryption for more information.
1759
-	 */
1760
-	'redis' => [
1761
-		'host' => 'localhost', // can also be a Unix domain socket: '/tmp/redis.sock'
1762
-		'port' => 6379,
1763
-		'timeout' => 0.0,
1764
-		'read_timeout' => 0.0,
1765
-		'user' => '', // Optional: if not defined, no password will be used.
1766
-		'password' => '', // Optional: if not defined, no password will be used.
1767
-		'dbindex' => 0, // Optional: if undefined, SELECT will not run and will use Redis Server's default DB Index.
1768
-		// If Redis in-transit encryption is enabled, provide certificates
1769
-		// SSL context https://www.php.net/manual/en/context.ssl.php
1770
-		'ssl_context' => [
1771
-			'local_cert' => '/certs/redis.crt',
1772
-			'local_pk' => '/certs/redis.key',
1773
-			'cafile' => '/certs/ca.crt'
1774
-		]
1775
-	],
1776
-
1777
-	/**
1778
-	 * Connection details for a Redis Cluster.
1779
-	 *
1780
-	 * Redis Cluster support requires the PHP module phpredis in version 3.0.0 or
1781
-	 * higher.
1782
-	 *
1783
-	 * Available failover modes:
1784
-	 *  - ``\RedisCluster::FAILOVER_NONE`` - only send commands to master nodes (default)
1785
-	 *  - ``\RedisCluster::FAILOVER_ERROR`` - failover to slaves for read commands if master is unavailable (recommended)
1786
-	 *  - ``\RedisCluster::FAILOVER_DISTRIBUTE`` - randomly distribute read commands across master and slaves
1787
-	 *
1788
-	 * WARNING: ``\RedisCluster::FAILOVER_DISTRIBUTE`` is a not recommended setting, and we strongly
1789
-	 * suggest to not use it if you use Redis for file locking. Due to the way Redis
1790
-	 * is synchronized, it could happen that the read for an existing lock is
1791
-	 * scheduled to a slave that is not fully synchronized with the connected master
1792
-	 * which then causes a FileLocked exception.
1793
-	 *
1794
-	 * See https://redis.io/topics/cluster-spec for details about the Redis cluster
1795
-	 *
1796
-	 * Authentication works with phpredis version 4.2.1+. See
1797
-	 * https://github.com/phpredis/phpredis/commit/c5994f2a42b8a348af92d3acb4edff1328ad8ce1
1798
-	 */
1799
-	'redis.cluster' => [
1800
-		'seeds' => [ // provide some or all of the cluster servers to bootstrap discovery, port required
1801
-			'localhost:7000',
1802
-			'localhost:7001',
1803
-		],
1804
-		'timeout' => 0.0,
1805
-		'read_timeout' => 0.0,
1806
-		'failover_mode' => \RedisCluster::FAILOVER_ERROR,
1807
-		'user' => '', // Optional: if not defined, no password will be used.
1808
-		'password' => '', // Optional: if not defined, no password will be used.
1809
-		// If Redis in-transit encryption is enabled, provide certificates
1810
-		// SSL context https://www.php.net/manual/en/context.ssl.php
1811
-		'ssl_context' => [
1812
-			'local_cert' => '/certs/redis.crt',
1813
-			'local_pk' => '/certs/redis.key',
1814
-			'cafile' => '/certs/ca.crt'
1815
-		]
1816
-	],
1817
-
1818
-
1819
-	/**
1820
-	 * Server details for one or more Memcached servers to use for memory caching.
1821
-	 */
1822
-	'memcached_servers' => [
1823
-		// hostname, port and optional weight
1824
-		// or path and port 0 for Unix socket. Also see:
1825
-		// https://www.php.net/manual/en/memcached.addservers.php
1826
-		// https://www.php.net/manual/en/memcached.addserver.php
1827
-		['localhost', 11211],
1828
-		//array('other.host.local', 11211),
1829
-	],
1830
-
1831
-	/**
1832
-	 * Connection options for Memcached
1833
-	 */
1834
-	'memcached_options' => [
1835
-		// Set timeouts to 50ms
1836
-		\Memcached::OPT_CONNECT_TIMEOUT => 50,
1837
-		\Memcached::OPT_RETRY_TIMEOUT => 50,
1838
-		\Memcached::OPT_SEND_TIMEOUT => 50,
1839
-		\Memcached::OPT_RECV_TIMEOUT => 50,
1840
-		\Memcached::OPT_POLL_TIMEOUT => 50,
1841
-
1842
-		// Enable compression
1843
-		\Memcached::OPT_COMPRESSION => true,
1844
-
1845
-		// Turn on consistent hashing
1846
-		\Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
1847
-
1848
-		// Enable Binary Protocol
1849
-		\Memcached::OPT_BINARY_PROTOCOL => true,
1850
-
1851
-		// Binary serializer will be enabled if the igbinary PECL module is available
1852
-		//\Memcached::OPT_SERIALIZER => \Memcached::SERIALIZER_IGBINARY,
1853
-	],
1854
-
1855
-
1856
-	/**
1857
-	 * Location of the cache folder, defaults to ``data/$user/cache`` where
1858
-	 * ``$user`` is the current user. When specified, the format will change to
1859
-	 * ``$cache_path/$user`` where ``$cache_path`` is the configured cache directory
1860
-	 * and ``$user`` is the user.
1861
-	 *
1862
-	 * Defaults to ``''`` (empty string)
1863
-	 */
1864
-	'cache_path' => '',
1865
-
1866
-	/**
1867
-	 * TTL of chunks located in the cache folder before they're removed by
1868
-	 * garbage collection (in seconds). Increase this value if users have
1869
-	 * issues uploading very large files via the Nextcloud Client as upload isn't
1870
-	 * completed within one day.
1871
-	 *
1872
-	 * Defaults to ``60*60*24`` (1 day)
1873
-	 */
1874
-	'cache_chunk_gc_ttl' => 60 * 60 * 24,
1875
-
1876
-	/**
1877
-	 * Enable caching of the app config values.
1878
-	 * If enabled the app config will be cached locally for a short TTL,
1879
-	 * reducing database load significantly on larger setups.
1880
-	 *
1881
-	 * Defaults to ``true``
1882
-	 */
1883
-	'cache_app_config' => true,
1884
-
1885
-	/**
1886
-	 * Using Object Store with Nextcloud
1887
-	 */
1888
-
1889
-	/**
1890
-	 * This example shows how to configure Nextcloud to store all files in a
1891
-	 * Swift object storage.
1892
-	 *
1893
-	 * It is important to note that Nextcloud in object store mode will expect
1894
-	 * exclusive access to the object store container because it only stores the
1895
-	 * binary data for each file. The metadata is currently kept in the local
1896
-	 * database for performance reasons.
1897
-	 *
1898
-	 * WARNING: The current implementation is incompatible with any app that uses
1899
-	 * direct file I/O and circumvents our virtual filesystem. That includes
1900
-	 * Encryption and Gallery. Gallery will store thumbnails directly in the
1901
-	 * filesystem, and encryption will cause severe overhead because key files need
1902
-	 * to be fetched in addition to any requested file.
1903
-	 *
1904
-	 */
1905
-	'objectstore' => [
1906
-		'class' => 'OC\\Files\\ObjectStore\\Swift',
1907
-		'arguments' => [
1908
-			// trystack will use your Facebook ID as the username
1909
-			'username' => 'facebook100000123456789',
1910
-			// in the trystack dashboard, go to user -> settings -> API Password to
1911
-			// generate a password
1912
-			'password' => 'Secr3tPaSSWoRdt7',
1913
-			// must already exist in the objectstore, name can be different
1914
-			'container' => 'nextcloud',
1915
-			// prefix to prepend to the fileid, default is 'oid:urn:'
1916
-			'objectPrefix' => 'oid:urn:',
1917
-			// create the container if it does not exist. default is false
1918
-			'autocreate' => true,
1919
-			// required, dev-/trystack defaults to 'RegionOne'
1920
-			'region' => 'RegionOne',
1921
-			// The Identity / Keystone endpoint
1922
-			'url' => 'http://8.21.28.222:5000/v2.0',
1923
-			// uploadPartSize: size of the uploaded chunks, defaults to 524288000
1924
-			'uploadPartSize' => 524288000,
1925
-			// required on dev-/trystack
1926
-			'tenantName' => 'facebook100000123456789',
1927
-			// dev-/trystack uses swift by default, the lib defaults to 'cloudFiles'
1928
-			// if omitted
1929
-			'serviceName' => 'swift',
1930
-			// The Interface / URL Type, optional
1931
-			'urlType' => 'internal',
1932
-			// Maximum amount of data that can be uploaded
1933
-			'totalSizeLimit' => 1024 * 1024 * 1024,
1934
-		],
1935
-	],
1936
-
1937
-	/**
1938
-	 * To use Swift V3
1939
-	 */
1940
-	'objectstore' => [
1941
-		'class' => 'OC\\Files\\ObjectStore\\Swift',
1942
-		'arguments' => [
1943
-			'autocreate' => true,
1944
-			'user' => [
1945
-				'name' => 'swift',
1946
-				'password' => 'swift',
1947
-				'domain' => [
1948
-					'name' => 'default',
1949
-				],
1950
-			],
1951
-			'scope' => [
1952
-				'project' => [
1953
-					'name' => 'service',
1954
-					'domain' => [
1955
-						'name' => 'default',
1956
-					],
1957
-				],
1958
-			],
1959
-			'tenantName' => 'service',
1960
-			'serviceName' => 'swift',
1961
-			'region' => 'regionOne',
1962
-			'url' => 'http://yourswifthost:5000/v3',
1963
-			'bucket' => 'nextcloud',
1964
-		],
1965
-	],
1966
-
1967
-	/**
1968
-	 * To use S3 object storage
1969
-	 */
1970
-	'objectstore' => [
1971
-		'class' => 'OC\\Files\\ObjectStore\\S3',
1972
-		'arguments' => [
1973
-			'bucket' => 'nextcloud',
1974
-			'key' => 'your-access-key',
1975
-			'secret' => 'your-secret-key',
1976
-			'hostname' => 's3.example.com',
1977
-			'port' => 443,
1978
-			'use_ssl' => true,
1979
-			'region' => 'us-east-1',
1980
-			// optional: Maximum number of retry attempts for failed S3 requests
1981
-			// Default: 5
1982
-			'retriesMaxAttempts' => 5,
1983
-		],
1984
-	],
1985
-
1986
-	/**
1987
-	 * If this is set to true and a multibucket object store is configured, then
1988
-	 * newly created previews are put into 256 dedicated buckets.
1989
-	 *
1990
-	 * Those buckets are named like the multibucket version but with the postfix
1991
-	 * ``-preview-NUMBER`` where NUMBER is between 0 and 255.
1992
-	 *
1993
-	 * Keep in mind that only previews of files are put in there that don't have
1994
-	 * some already. Otherwise, the old bucket will be used.
1995
-	 *
1996
-	 * To migrate existing previews to this new multibucket distribution of previews,
1997
-	 * use the occ command ``preview:repair``. For now, this will only migrate
1998
-	 * previews that were generated before Nextcloud 19 in the flat
1999
-	 * ``appdata_INSTANCEID/previews/FILEID`` folder structure.
2000
-	 */
2001
-	'objectstore.multibucket.preview-distribution' => false,
2002
-
2003
-
2004
-	/**
2005
-	 * Sharing
2006
-	 *
2007
-	 * Global settings for Sharing
2008
-	 */
2009
-
2010
-	/**
2011
-	 * Replaces the default Share Provider Factory. This can be utilized if
2012
-	 * own or 3rd-party Share Providers are used that – for instance – use the
2013
-	 * filesystem instead of the database to keep the share information.
2014
-	 *
2015
-	 * Defaults to ``\OC\Share20\ProviderFactory``
2016
-	 */
2017
-	'sharing.managerFactory' => '\OC\Share20\ProviderFactory',
2018
-
2019
-	/**
2020
-	 * Enables expiration for link share passwords sent by email (sharebymail).
2021
-	 * The passwords will expire after the configured interval; the users can
2022
-	 * still request a new one on the public link page.
2023
-	 */
2024
-	'sharing.enable_mail_link_password_expiration' => false,
2025
-
2026
-	/**
2027
-	 * Expiration interval for passwords, in seconds.
2028
-	 */
2029
-	'sharing.mail_link_password_expiration_interval' => 3600,
2030
-
2031
-	/**
2032
-	 * Define max number of results returned by the search for auto-completion of
2033
-	 * users, groups, etc. The value must not be lower than 0 (for unlimited).
2034
-	 *
2035
-	 * If more, different sources are requested (e.g., different user backends; or
2036
-	 * both users and groups), the value is applied per source and might not be
2037
-	 * truncated after collecting the results. I.e., more results can appear than
2038
-	 * configured here.
2039
-	 *
2040
-	 * Default is 25.
2041
-	 */
2042
-	'sharing.maxAutocompleteResults' => 25,
2043
-
2044
-	/**
2045
-	 * Define the minimum length of the search string before we start auto-completion
2046
-	 * Default is no limit (value set to 0)
2047
-	 */
2048
-	'sharing.minSearchStringLength' => 0,
2049
-
2050
-	/**
2051
-	 * Set to true to enable that internal shares need to be accepted by the users by default.
2052
-	 * Users can change this for their account in their personal sharing settings
2053
-	 */
2054
-	'sharing.enable_share_accept' => false,
2055
-
2056
-	/**
2057
-	 * Set to ``true`` to enforce that internal shares need to be accepted
2058
-	 */
2059
-	'sharing.force_share_accept' => false,
2060
-
2061
-	/**
2062
-	 * Set to ``false`` to prevent users from setting a custom share_folder
2063
-	 */
2064
-	'sharing.allow_custom_share_folder' => true,
2065
-
2066
-	/**
2067
-	 * Define a default folder for shared files and folders other than root.
2068
-	 * Changes to this value will only have effect on new shares.
2069
-	 *
2070
-	 * Defaults to ``/``
2071
-	 */
2072
-	'share_folder' => '/',
2073
-
2074
-	/**
2075
-	 * Set to ``false`` to stop sending a mail when users receive a share
2076
-	 */
2077
-	'sharing.enable_share_mail' => true,
2078
-
2079
-	/**
2080
-	 * Set to true to enable the feature to add exceptions for share password enforcement
2081
-	 */
2082
-	'sharing.allow_disabled_password_enforcement_groups' => false,
2083
-
2084
-	/**
2085
-	 * Set to true to always transfer incoming shares by default
2086
-	 * when running ``occ files:transfer-ownership``.
2087
-	 * Defaults to ``false``, so incoming shares are not transferred if not specifically requested
2088
-	 * by a command line argument.
2089
-	 */
2090
-	'transferIncomingShares' => false,
2091
-
2092
-	/**
2093
-	 * Federated Cloud Sharing
2094
-	 */
2095
-
2096
-	/**
2097
-	 * Allow self-signed certificates for federated shares
2098
-	 */
2099
-	'sharing.federation.allowSelfSignedCertificates' => false,
2100
-
2101
-	/**
2102
-	 * Hashing
2103
-	 */
2104
-
2105
-	/**
2106
-	 * By default, Nextcloud will use the Argon2 password hashing if available.
2107
-	 * However, if for whatever reason you want to stick with the PASSWORD_DEFAULT
2108
-	 * of your PHP version, then set the setting to true.
2109
-	 *
2110
-	 * Nextcloud uses the Argon2 algorithm (with PHP >= 7.2) to create hashes by its
2111
-	 * own and exposes its configuration options as following. More information can
2112
-	 * be found at: https://www.php.net/manual/en/function.password-hash.php
2113
-	 */
2114
-	'hashing_default_password' => false,
2115
-
2116
-	/**
2117
-	 * The number of CPU threads to be used by the algorithm for computing a hash.
2118
-	 * The value must be an integer, and the minimum value is ``1``. Rationally, it does
2119
-	 * not help to provide a number higher than the available threads on the machine.
2120
-	 * Values that undershoot the minimum will be ignored in favor of the minimum.
2121
-	 */
2122
-	'hashingThreads' => PASSWORD_ARGON2_DEFAULT_THREADS,
2123
-
2124
-	/**
2125
-	 * The memory in KiB to be used by the algorithm for computing a hash. The value
2126
-	 * must be an integer, and the minimum value is 8 times the number of CPU threads.
2127
-	 * Values that undershoot the minimum will be ignored in favor of the minimum.
2128
-	 */
2129
-	'hashingMemoryCost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST,
2130
-
2131
-	/**
2132
-	 * The number of iterations that are used by the algorithm for computing a hash.
2133
-	 * The value must be an integer, and the minimum value is ``1``. Values that
2134
-	 * undershoot the minimum will be ignored in favor of the minimum.
2135
-	 */
2136
-	'hashingTimeCost' => PASSWORD_ARGON2_DEFAULT_TIME_COST,
2137
-
2138
-	/**
2139
-	 * The hashing cost used by hashes generated by Nextcloud
2140
-	 * Using a higher value requires more time and CPU power to calculate the hashes
2141
-	 */
2142
-	'hashingCost' => 10,
2143
-
2144
-	/**
2145
-	 * All other configuration options
2146
-	 */
2147
-
2148
-	/**
2149
-	 * Additional driver options for the database connection, e.g., to enable SSL
2150
-	 * encryption in MySQL or specify a custom wait timeout on a cheap hoster.
2151
-	 *
2152
-	 * When setting up TLS/SSL for encrypting the connections, you need to ensure that
2153
-	 * the passed keys and certificates are readable by the PHP process. In addition,
2154
-	 * ``PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT`` might need to be set to false, if the
2155
-	 * database server's certificate CN does not match with the hostname used to connect.
2156
-	 * The standard behavior here is different from the MySQL/MariaDB CLI client, which
2157
-	 * does not verify the server cert except ``--ssl-verify-server-cert`` is passed manually.
2158
-	 */
2159
-	'dbdriveroptions' => [
2160
-		PDO::MYSQL_ATTR_SSL_CA => '/file/path/to/ca_cert.pem',
2161
-		PDO::MYSQL_ATTR_SSL_KEY => '/file/path/to/mysql-client-key.pem',
2162
-		PDO::MYSQL_ATTR_SSL_CERT => '/file/path/to/mysql-client-cert.pem',
2163
-		PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false,
2164
-		PDO::MYSQL_ATTR_INIT_COMMAND => 'SET wait_timeout = 28800'
2165
-	],
2166
-
2167
-	/**
2168
-	 * SQLite3 journal mode can be specified using this configuration parameter -
2169
-	 * can be ``'WAL'`` or ``'DELETE'``. See https://www.sqlite.org/wal.html for more details.
2170
-	 */
2171
-	'sqlite.journal_mode' => 'DELETE',
2172
-
2173
-	/**
2174
-	 * During setup, if requirements are met (see below), this setting is set to true
2175
-	 * to enable MySQL to handle 4-byte characters instead of 3-byte characters.
2176
-	 *
2177
-	 * To convert an existing 3-byte setup to a 4-byte setup, configure the MySQL
2178
-	 * parameters as described below and run the migration command:
2179
-	 * ``./occ db:convert-mysql-charset``
2180
-	 * This config setting will be automatically updated after a successful migration.
2181
-	 *
2182
-	 * Refer to the documentation for more details.
2183
-	 *
2184
-	 * MySQL requires specific settings for longer indexes (> 767 bytes), which are
2185
-	 * necessary for 4-byte character support::
2186
-	 *
2187
-	 *     [mysqld]
2188
-	 *     innodb_large_prefix=ON
2189
-	 *     innodb_file_format=Barracuda
2190
-	 *     innodb_file_per_table=ON
2191
-	 *
2192
-	 * Tables will be created with:
2193
-	 *  * character set: ``utf8mb4``
2194
-	 *  * collation:     ``utf8mb4_bin``
2195
-	 *  * row_format:    ``dynamic``
2196
-	 *
2197
-	 * See:
2198
-	 *  * https://dev.mysql.com/doc/refman/5.7/en/charset-unicode-utf8mb4.html
2199
-	 *  * https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix
2200
-	 *  * https://mariadb.com/kb/en/mariadb/xtradbinnodb-server-system-variables/#innodb_large_prefix
2201
-	 *  * http://www.tocker.ca/2013/10/31/benchmarking-innodb-page-compression-performance.html
2202
-	 *  * http://mechanics.flite.com/blog/2014/07/29/using-innodb-large-prefix-to-avoid-error-1071/
2203
-	 */
2204
-	'mysql.utf8mb4' => false,
2205
-
2206
-	/**
2207
-	 * For search queries in the database, a default collation is chosen based on the
2208
-	 * character set. In some cases, a different collation is desired, such as for
2209
-	 * accent-sensitive searches.
2210
-	 *
2211
-	 * MariaDB and MySQL share some collations, but also have incompatible ones,
2212
-	 * depending on the database server version.
2213
-	 *
2214
-	 * This option allows overriding the automatic collation choice. Example::
2215
-	 *
2216
-	 *     'mysql.collation' => 'utf8mb4_0900_as_ci',
2217
-	 *
2218
-	 * This setting does not affect table creation or setup, where utf8[mb4]_bin is
2219
-	 * always used. It applies only to SQL queries using LIKE comparison operators.
2220
-	 */
2221
-	'mysql.collation' => null,
2222
-
2223
-	/**
2224
-	 * PostgreSQL SSL connection
2225
-	 */
2226
-	'pgsql_ssl' => [
2227
-		'mode' => '',
2228
-		'cert' => '',
2229
-		'rootcert' => '',
2230
-		'key' => '',
2231
-		'crl' => '',
2232
-	],
2233
-
2234
-	/**
2235
-	 * Database types supported for installation.
2236
-	 *
2237
-	 * Available:
2238
-	 *  - sqlite (SQLite3)
2239
-	 *  - mysql (MySQL)
2240
-	 *  - pgsql (PostgreSQL)
2241
-	 *  - oci (Oracle)
2242
-	 *
2243
-	 * Defaults to:
2244
-	 *  - sqlite (SQLite3)
2245
-	 *  - mysql (MySQL)
2246
-	 *  - pgsql (PostgreSQL)
2247
-	 */
2248
-	'supportedDatabases' => [
2249
-		'sqlite',
2250
-		'mysql',
2251
-		'pgsql',
2252
-		'oci',
2253
-	],
2254
-
2255
-	/**
2256
-	 * Override the location where Nextcloud stores temporary files. Useful in setups
2257
-	 * where the system temporary directory is on a limited-space ramdisk, restricted,
2258
-	 * or when using external storage that does not support streaming.
2259
-	 *
2260
-	 * The web server user/PHP must have write access to this directory. Ensure that
2261
-	 * PHP configuration recognizes this as a valid temporary directory by setting
2262
-	 * the TMP, TMPDIR, and TEMP environment variables accordingly. Additional
2263
-	 * permissions may be required for AppArmor or SELinux.
2264
-	 */
2265
-	'tempdirectory' => '/tmp/nextcloudtemp',
2266
-
2267
-	/**
2268
-	 * Override the location where Nextcloud stores update files during updates.
2269
-	 * Useful when the default ``datadirectory`` is on a network disk like NFS or is
2270
-	 * otherwise restricted. Defaults to the value of ``datadirectory`` if unset.
2271
-	 *
2272
-	 * If set, the directory must be located outside the Nextcloud installation
2273
-	 * directory and writable by the web server user.
2274
-	 */
2275
-	'updatedirectory' => '',
2276
-
2277
-	/**
2278
-	 * Block specific files or filenames, disallowing uploads or access (read and write).
2279
-	 * ``.htaccess`` is blocked by default.
2280
-	 *
2281
-	 * WARNING: Use this only if you understand the implications.
2282
-	 *
2283
-	 * NOTE: This list is case-insensitive.
2284
-	 *
2285
-	 * Defaults to ``['.htaccess']``
2286
-	 */
2287
-	'forbidden_filenames' => ['.htaccess'],
2288
-
2289
-	/**
2290
-	 * Disallow uploads of files with specific basenames. Matching existing files
2291
-	 * cannot be updated, and no new files can be created in matching folders.
2292
-	 *
2293
-	 * The basename is the filename without the extension, e.g., for "archive.tar.gz",
2294
-	 * the basename is "archive".
2295
-	 *
2296
-	 * NOTE: This list is case-insensitive.
2297
-	 *
2298
-	 * Defaults to ``[]`` (empty array)
2299
-	 */
2300
-	'forbidden_filename_basenames' => [],
2301
-
2302
-	/**
2303
-	 * Block specific characters in filenames. Useful for filesystems or operating
2304
-	 * systems (e.g., Windows) that do not support certain characters. Matching
2305
-	 * existing files cannot be updated, and no new files can be created in matching
2306
-	 * folders.
2307
-	 *
2308
-	 * The ``/`` and ``\`` characters, as well as ASCII characters [0-31], are always
2309
-	 * forbidden.
2310
-	 *
2311
-	 * Example for Windows: ``['?', '<', '>', ':', '*', '|', '"']``
2312
-	 * See: https://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits
2313
-	 *
2314
-	 * Defaults to ``[]`` (empty array)
2315
-	 */
2316
-	'forbidden_filename_characters' => [],
2317
-
2318
-	/**
2319
-	 * Deny specific file extensions. Matching existing files cannot be updated, and
2320
-	 * no new files can be created in matching folders.
2321
-	 *
2322
-	 * The ``'.part'`` extension is always forbidden, as it is used internally by Nextcloud.
2323
-	 *
2324
-	 * Defaults to ``['.filepart', '.part']``
2325
-	 */
2326
-	'forbidden_filename_extensions' => ['.part', '.filepart'],
2327
-
2328
-	/**
2329
-	 * Specify the name of a theme to apply to Nextcloud. Themes are located in
2330
-	 * ``nextcloud/themes/`` by default.
2331
-	 *
2332
-	 * Defaults to the theming app, included since Nextcloud 9.
2333
-	 */
2334
-	'theme' => '',
2335
-
2336
-	/**
2337
-	 * Enforce a specific user theme, disabling user theming settings. Must be a
2338
-	 * valid ITheme ID, e.g., ``dark``, ``dark-highcontrast``, ``default``, ``light``,
2339
-	 * ``light-highcontrast``, ``opendyslexic``.
2340
-	 */
2341
-	'enforce_theme' => '',
2342
-
2343
-	/**
2344
-	 * Enable or disable Progressive Web App (PWA) functionality, which allows
2345
-	 * browsers to open web applications in dedicated windows.
2346
-	 *
2347
-	 * Defaults to ``true``
2348
-	 */
2349
-	'theming.standalone_window.enabled' => true,
2350
-
2351
-	/**
2352
-	 * Specify the default cipher for encrypting files. Supported ciphers:
2353
-	 *  - AES-256-CTR
2354
-	 *  - AES-128-CTR
2355
-	 *  - AES-256-CFB
2356
-	 *  - AES-128-CFB
2357
-	 *
2358
-	 * Defaults to ``AES-256-CTR``
2359
-	 */
2360
-	'cipher' => 'AES-256-CTR',
2361
-
2362
-	/**
2363
-	 * Use the legacy base64 format for encrypted files instead of the more
2364
-	 * space-efficient binary format. This affects only newly written files; existing
2365
-	 * encrypted files remain readable regardless of the format.
2366
-	 *
2367
-	 * Defaults to ``false``
2368
-	 */
2369
-	'encryption.use_legacy_base64_encoding' => false,
2370
-
2371
-	/**
2372
-	 * Specify the minimum Nextcloud desktop client version allowed to sync with this
2373
-	 * server. Connections from earlier clients will be denied. Defaults to the
2374
-	 * minimum officially supported version at the time of this server release.
2375
-	 *
2376
-	 * Changing this may cause older, unsupported clients to malfunction, potentially
2377
-	 * leading to data loss or unexpected behavior.
2378
-	 *
2379
-	 * Defaults to ``3.1.0``
2380
-	 */
2381
-	'minimum.supported.desktop.version' => '3.1.0',
2382
-
2383
-	/**
2384
-	 * Specify the maximum Nextcloud desktop client version allowed to sync with this
2385
-	 * server. Connections from later clients will be denied.
2386
-	 *
2387
-	 * Defaults to ``99.99.99``
2388
-	 */
2389
-	'maximum.supported.desktop.version' => '99.99.99',
2390
-
2391
-	/**
2392
-	 * Allow local storage to contain symlinks.
2393
-	 * WARNING: Not recommended, as this allows Nextcloud to access files outside the
2394
-	 * data directory, posing a potential security risk.
2395
-	 *
2396
-	 * Defaults to ``false``
2397
-	 */
2398
-	'localstorage.allowsymlinks' => false,
2399
-
2400
-	/**
2401
-	 * Nextcloud overrides umask to ensure suitable access permissions regardless of
2402
-	 * web server or PHP-FPM configuration. Modifying this value has security
2403
-	 * implications and may cause issues with the installation.
2404
-	 *
2405
-	 * Most installations should not modify this value.
2406
-	 *
2407
-	 * Defaults to ``0022``
2408
-	 */
2409
-	'localstorage.umask' => 0022,
2410
-
2411
-	/**
2412
-	 * Allow storage systems that do not support modifying existing files to overcome
2413
-	 * this limitation by removing files before overwriting.
2414
-	 *
2415
-	 * Defaults to ``false``
2416
-	 */
2417
-	'localstorage.unlink_on_truncate' => false,
2418
-
2419
-	/**
2420
-	 * EXPERIMENTAL: Include external storage in quota calculations.
2421
-	 *
2422
-	 * Defaults to ``false``
2423
-	 */
2424
-	'quota_include_external_storage' => false,
2425
-
2426
-	/**
2427
-	 * When an external storage is unavailable (e.g., due to failed authentication),
2428
-	 * it is flagged as such for a specified duration. For authentication failures,
2429
-	 * this delay can be customized to reduce the likelihood of account lockouts in
2430
-	 * systems like Active Directory.
2431
-	 *
2432
-	 * Defaults to ``1800`` seconds (30 minutes)
2433
-	 */
2434
-	'external_storage.auth_availability_delay' => 1800,
2435
-
2436
-	/**
2437
-	 * Allow creation of external storages of type "Local" via the web interface and
2438
-	 * APIs. When disabled, local storages can still be created using the occ command::
2439
-	 *
2440
-	 *      occ files_external:create /mountpoint local null::null -c datadir=/path/to/data
2441
-	 *
2442
-	 * Defaults to ``true``
2443
-	 */
2444
-	'files_external_allow_create_new_local' => true,
2445
-
2446
-	/**
2447
-	 * Specify how often the local filesystem (Nextcloud data/ directory and NFS
2448
-	 * mounts in data/) is checked for changes made outside Nextcloud. This does not
2449
-	 * apply to external storage.
2450
-	 *
2451
-	 * - ``0`` -> Never check the filesystem for outside changes, improving performance when no external changes are expected.
2452
-	 * - ``1`` -> Check each file or folder at most once per request, recommended for general use if outside changes are possible.
2453
-	 *
2454
-	 * Defaults to ``0``
2455
-	 */
2456
-	'filesystem_check_changes' => 0,
2457
-
2458
-	/**
2459
-	 * Store part files created during upload in the same storage as the upload
2460
-	 * target. Setting this to false stores part files in the root of the user's
2461
-	 * folder, which may be necessary for external storage with limited rename
2462
-	 * capabilities.
2463
-	 *
2464
-	 * Defaults to ``true``
2465
-	 */
2466
-	'part_file_in_storage' => true,
2467
-
2468
-	/**
2469
-	 * Specify the location of the ``mount.json`` file.
2470
-	 *
2471
-	 * Defaults to ``data/mount.json`` in the Nextcloud directory.
2472
-	 */
2473
-	'mount_file' => '/var/www/nextcloud/data/mount.json',
2474
-
2475
-	/**
2476
-	 * Prevent Nextcloud from updating the cache due to filesystem changes for all
2477
-	 * storage.
2478
-	 *
2479
-	 * Defaults to ``false``
2480
-	 */
2481
-	'filesystem_cache_readonly' => false,
2482
-
2483
-	/**
2484
-	 * List of trusted proxy servers. Supported formats:
2485
-	 *
2486
-	 * - IPv4 addresses, e.g., ``192.168.2.123``
2487
-	 * - IPv4 ranges in CIDR notation, e.g., ``192.168.2.0/24``
2488
-	 * - IPv6 addresses, e.g., ``fd9e:21a7:a92c:2323::1``
2489
-	 * - IPv6 ranges in CIDR notation, e.g., ``2001:db8:85a3:8d3:1319:8a20::/95``
2490
-	 *
2491
-	 * If a request's ``REMOTE_ADDR`` matches an address here, it is treated as a proxy,
2492
-	 * and the client IP is read from the HTTP header specified in
2493
-	 * ``forwarded_for_headers`` instead of ``REMOTE_ADDR``.
2494
-	 *
2495
-	 * Ensure ``forwarded_for_headers`` is configured if ``trusted_proxies`` is set.
2496
-	 *
2497
-	 * Defaults to ``[]`` (empty array)
2498
-	 */
2499
-	'trusted_proxies' => ['203.0.113.45', '198.51.100.128', '192.168.2.0/24'],
2500
-
2501
-	/**
2502
-	 * Headers trusted as containing the client IP address when used with
2503
-	 * ``trusted_proxies``. For example, use ``HTTP_X_FORWARDED_FOR`` for the
2504
-	 * ``X-Forwarded-For`` header.
2505
-	 *
2506
-	 * Incorrect configuration allows clients to spoof their IP address, bypassing
2507
-	 * access controls and rendering logs unreliable.
2508
-	 *
2509
-	 * Defaults to ``['HTTP_X_FORWARDED_FOR']``
2510
-	 */
2511
-	'forwarded_for_headers' => ['HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR'],
2512
-
2513
-	/**
2514
-	 * List of trusted IP ranges for admin actions. If non-empty, all admin actions
2515
-	 * must originate from IPs within these ranges.
2516
-	 *
2517
-	 * Supported formats:
2518
-	 * - IPv4 addresses or ranges, e.g., ``192.0.2.42/32``, ``233.252.0.0/24``
2519
-	 * - IPv6 addresses or ranges, e.g., ``2001:db8::13:37/64``
2520
-	 *
2521
-	 * Defaults to ``[]`` (empty array)
2522
-	 */
2523
-	'allowed_admin_ranges' => ['192.0.2.42/32', '233.252.0.0/24', '2001:db8::13:37/64'],
2524
-
2525
-	/**
2526
-	 * Maximum file size (in megabytes) for animating GIFs on public sharing pages.
2527
-	 * If a GIF exceeds this size, a static preview is shown.
2528
-	 *
2529
-	 * Set to ``-1`` for no limit.
2530
-	 *
2531
-	 * Defaults to ``10`` megabytes
2532
-	 */
2533
-	'max_filesize_animated_gifs_public_sharing' => 10,
2534
-
2535
-	/**
2536
-	 * Set the lock's time-to-live (TTL) in seconds. Locks older than this are
2537
-	 * automatically cleaned up.
2538
-	 *
2539
-	 * Defaults to ``3600`` seconds (1 hour) or the PHP ``max_execution_time``,
2540
-	 * whichever is higher.
2541
-	 */
2542
-	'filelocking.ttl' => 60 * 60,
2543
-
2544
-	/**
2545
-	 * Memory caching backend for file locking. Redis is highly recommended to avoid
2546
-	 * data loss, as many memcache backends may evict values unexpectedly.
2547
-	 *
2548
-	 * Defaults to ``none``
2549
-	 */
2550
-	'memcache.locking' => '\\OC\\Memcache\\Redis',
2551
-
2552
-	/**
2553
-	 * Enable debug logging for file locking. This can generate a large volume of log
2554
-	 * entries, potentially causing performance degradation and large log files on
2555
-	 * busy instances.
2556
-	 *
2557
-	 * Use with ``log.condition`` to limit logging in production environments.
2558
-	 *
2559
-	 * Defaults to ``false``
2560
-	 */
2561
-	'filelocking.debug' => false,
2562
-
2563
-	/**
2564
-	 * Disable the web-based updater.
2565
-	 *
2566
-	 * Defaults to ``false``
2567
-	 */
2568
-	'upgrade.disable-web' => false,
2569
-
2570
-	/**
2571
-	 * Customize the CLI upgrade documentation link.
2572
-	 */
2573
-	'upgrade.cli-upgrade-link' => '',
2574
-
2575
-	/**
2576
-	 * Additional line(s) (string or array of strings)
2577
-	 * that will be added to .user.ini on each update by the updater.
2578
-	 *
2579
-	 * Defaults to ``''`` (empty string)
2580
-	 */
2581
-	'user_ini_additional_lines' => '',
2582
-
2583
-	/**
2584
-	 * Customize the server logs documentation link for exception handling.
2585
-	 */
2586
-	'documentation_url.server_logs' => '',
2587
-
2588
-	/**
2589
-	 * Enable debugging mode for Nextcloud. Only use for local development, not in
2590
-	 * production, as it disables minification and outputs additional debug
2591
-	 * information.
2592
-	 *
2593
-	 * Defaults to ``false``
2594
-	 */
2595
-	'debug' => false,
2596
-
2597
-	/**
2598
-	 * Set the data fingerprint for the current data served. Used by clients to
2599
-	 * detect if a backup has been restored. Update this by running::
2600
-	 *
2601
-	 *      occ maintenance:data-fingerprint
2602
-	 *
2603
-	 * Changing or deleting this value may cause connected clients to stall until
2604
-	 * conflicts are resolved.
2605
-	 *
2606
-	 * Defaults to ``''`` (empty string)
2607
-	 */
2608
-	'data-fingerprint' => '',
2609
-
2610
-	/**
2611
-	 * This entry serves as a warning if the sample configuration was copied.
2612
-	 * DO NOT ADD THIS TO YOUR CONFIGURATION!
2613
-	 *
2614
-	 * Ensure all settings are modified only after consulting the documentation.
2615
-	 */
2616
-	'copied_sample_config' => true,
2617
-
2618
-	/**
2619
-	 * Use a custom lookup server to publish user data.
2620
-	 *
2621
-	 * Defaults to ``https://lookup.nextcloud.com``
2622
-	 */
2623
-	'lookup_server' => 'https://lookup.nextcloud.com',
2624
-
2625
-	/**
2626
-	 * Enable Nextcloud's Global Scale architecture.
2627
-	 *
2628
-	 * Defaults to ``false``
2629
-	 */
2630
-	'gs.enabled' => false,
2631
-
2632
-	/**
2633
-	 * Configure federation for Global Scale setups. Set to ``global`` to allow
2634
-	 * federation outside the environment.
2635
-	 *
2636
-	 * Defaults to ``internal``
2637
-	 */
2638
-	'gs.federation' => 'internal',
2639
-
2640
-	/**
2641
-	 * List of user agents exempt from SameSite cookie protection due to non-standard
2642
-	 * HTTP behavior.
2643
-	 *
2644
-	 * WARNING: Use only if you understand the implications.
2645
-	 *
2646
-	 * Defaults to:
2647
-	 *
2648
-	 * - ``/^WebDAVFS/`` (OS X Finder)
2649
-	 * - ``/^Microsoft-WebDAV-MiniRedir/`` (Windows WebDAV drive)
2650
-	 */
2651
-	'csrf.optout' => [
2652
-		'/^WebDAVFS/', // OS X Finder
2653
-		'/^Microsoft-WebDAV-MiniRedir/', // Windows WebDAV drive
2654
-	],
2655
-
2656
-	/**
2657
-	 * Specify allowed user agents for Login Flow V2 using regular expressions.
2658
-	 * User agents not matching this list are denied access to Login Flow V2.
2659
-	 *
2660
-	 * WARNING: Use only if you understand the implications.
2661
-	 *
2662
-	 * Example: Allow only the Nextcloud Android app::
2663
-	 *
2664
-	 *    'core.login_flow_v2.allowed_user_agents' => ['/Nextcloud-android/i'],
2665
-	 *
2666
-	 * Defaults to ``[]`` (empty array)
2667
-	 */
2668
-	'core.login_flow_v2.allowed_user_agents' => [],
2669
-
2670
-	/**
2671
-	 * Show or hide the "simple sign up" link on public pages.
2672
-	 * See: https://nextcloud.com/signup/
2673
-	 *
2674
-	 * Defaults to ``true``
2675
-	 */
2676
-	'simpleSignUpLink.shown' => true,
2677
-
2678
-	/**
2679
-	 * Enable or disable autocompletion for the login form. Disabling this prevents
2680
-	 * browsers from remembering login credentials, which may be required for
2681
-	 * compliance with certain security policies.
2682
-	 *
2683
-	 * Defaults to ``true``
2684
-	 */
2685
-	'login_form_autocomplete' => true,
2686
-
2687
-	/**
2688
-	 * Set a timeout (in seconds) for the login form. After this period, the form is
2689
-	 * reset to prevent password leaks on public devices if the user forgets to clear
2690
-	 * it.
2691
-	 *
2692
-	 * A value of 0 disables the timeout.
2693
-	 *
2694
-	 * Defaults to ``300`` seconds (5 minutes)
2695
-	 */
2696
-	'login_form_timeout' => 300,
2697
-
2698
-	/**
2699
-	 * Suppress warnings for outdated or unsupported browsers. When enabled, users
2700
-	 * can bypass the warning after reading it.
2701
-	 *
2702
-	 * Set to ``true`` to disable the warning.
2703
-	 *
2704
-	 * Defaults to ``false``
2705
-	 */
2706
-	'no_unsupported_browser_warning' => false,
2707
-
2708
-	/**
2709
-	 * Disable background scanning of files. When enabled, a background job runs
2710
-	 * every 10 minutes to sync the filesystem and database for up to 500 users with
2711
-	 * unscanned files (size < 0 in filecache).
2712
-	 *
2713
-	 * Defaults to ``false``
2714
-	 */
2715
-	'files_no_background_scan' => false,
2716
-
2717
-	/**
2718
-	 * Log all database queries to a file.
2719
-	 *
2720
-	 * WARNING: This significantly reduces server performance and is intended only
2721
-	 * for debugging or profiling query interactions. Sensitive data may be logged in
2722
-	 * plain text.
2723
-	 */
2724
-	'query_log_file' => '',
2725
-
2726
-	/**
2727
-	 * Prefix all queries with the request ID when set to `yes`.
2728
-	 *
2729
-	 * Requires ``query_log_file`` to be set.
2730
-	 */
2731
-	'query_log_file_requestid' => '',
2732
-
2733
-	/**
2734
-	 * Include all query parameters in the query log when set to `yes`.
2735
-	 *
2736
-	 * Requires ``query_log_file`` to be set.
2737
-	 * WARNING: This may log sensitive data in plain text.
2738
-	 */
2739
-	'query_log_file_parameters' => '',
2740
-
2741
-	/**
2742
-	 * Include a backtrace in the query log when set to `yes`.
2743
-	 *
2744
-	 * Requires ``query_log_file`` to be set.
2745
-	 */
2746
-	'query_log_file_backtrace' => '',
2747
-
2748
-	/**
2749
-	 * Log all Redis requests to a file.
2750
-	 *
2751
-	 * WARNING: This significantly reduces server performance and is intended only
2752
-	 * for debugging or profiling Redis interactions. Sensitive data may be logged in
2753
-	 * plain text.
2754
-	 */
2755
-	'redis_log_file' => '',
2756
-
2757
-	/**
2758
-	 * Enable diagnostics event logging. Logs timings of common execution steps at
2759
-	 * debug level. Use with ``log.condition`` to enable conditionally in production.
2760
-	 *
2761
-	 * Defaults to ``true``
2762
-	 */
2763
-	'diagnostics.logging' => true,
2764
-
2765
-	/**
2766
-	 * Limit diagnostics event logging to events longer than the specified threshold
2767
-	 * (in milliseconds). A value of 0 disables diagnostics event logging.
2768
-	 */
2769
-	'diagnostics.logging.threshold' => 0,
2770
-
2771
-	/**
2772
-	 * Enable profiling globally.
2773
-	 *
2774
-	 * Defaults to ``true``
2775
-	 */
2776
-	'profile.enabled' => true,
2777
-
2778
-	/**
2779
-	 * Override default scopes for account data. Valid properties and scopes are
2780
-	 * defined in ``OCP\Accounts\IAccountManager``. Values are merged with defaults
2781
-	 * from ``OC\Accounts\AccountManager``.
2782
-	 *
2783
-	 * Example: Set phone property to private scope:
2784
-	 * ``[\OCP\Accounts\IAccountManager::PROPERTY_PHONE => \OCP\Accounts\IAccountManager::SCOPE_PRIVATE]``
2785
-	 */
2786
-	'account_manager.default_property_scope' => [],
2787
-
2788
-	/**
2789
-	 * Enable the deprecated Projects feature, superseded by Related Resources since
2790
-	 * Nextcloud 25.
2791
-	 *
2792
-	 * Defaults to ``false``
2793
-	 */
2794
-	'projects.enabled' => false,
2795
-
2796
-	/**
2797
-	 * Enable the bulk upload feature.
2798
-	 *
2799
-	 * Defaults to ``true``
2800
-	 */
2801
-	'bulkupload.enabled' => true,
2802
-
2803
-	/**
2804
-	 * Enable fetching Open Graph metadata from remote URLs.
2805
-	 *
2806
-	 * Defaults to ``true``
2807
-	 */
2808
-	'reference_opengraph' => true,
2809
-
2810
-	/**
2811
-	 * Enable the legacy unified search.
2812
-	 *
2813
-	 * Defaults to ``false``
2814
-	 */
2815
-	'unified_search.enabled' => false,
2816
-
2817
-	/**
2818
-	 * Enable features that do not yet comply with accessibility standards.
2819
-	 *
2820
-	 * Defaults to ``true``
2821
-	 */
2822
-	'enable_non-accessible_features' => true,
2823
-
2824
-	/**
2825
-	 * Directories where Nextcloud searches for external binaries (e.g., LibreOffice,
2826
-	 * sendmail, ffmpeg).
2827
-	 *
2828
-	 * Defaults to:
2829
-	 * - /usr/local/sbin
2830
-	 * - /usr/local/bin
2831
-	 * - /usr/sbin
2832
-	 * - /usr/bin
2833
-	 * - /sbin
2834
-	 * - /bin
2835
-	 * - /opt/bin
2836
-	 */
2837
-	'binary_search_paths' => [
2838
-		'/usr/local/sbin',
2839
-		'/usr/local/bin',
2840
-		'/usr/sbin',
2841
-		'/usr/bin',
2842
-		'/sbin',
2843
-		'/bin',
2844
-		'/opt/bin',
2845
-	],
2846
-
2847
-	/**
2848
-	 * Maximum chunk size for chunked uploads (in bytes). Larger chunks increase
2849
-	 * throughput but yield diminishing returns above 100 MiB. Services like
2850
-	 * Cloudflare may limit to 100 MiB.
2851
-	 *
2852
-	 * Defaults to ``100 * 1024 * 1024`` (100 MiB)
2853
-	 */
2854
-	'files.chunked_upload.max_size' => 100 * 1024 * 1024,
2855
-
2856
-	/**
2857
-	 * Maximum number of chunks uploaded in parallel during chunked uploads. Higher
2858
-	 * counts increase throughput but consume more server resources, with diminishing
2859
-	 * returns.
2860
-	 *
2861
-	 * Defaults to ``5``
2862
-	 */
2863
-	'files.chunked_upload.max_parallel_count' => 5,
2864
-
2865
-	/**
2866
-	 * Allow users to manually delete files from their trashbin. Automated deletions
2867
-	 * (e.g., due to low quota) are unaffected.
2868
-	 *
2869
-	 * Defaults to ``true``
2870
-	 */
2871
-	'files.trash.delete' => true,
2872
-
2873
-	/**
2874
-	 * Enable PHP 8.4 lazy objects for Dependency Injection to improve performance by
2875
-	 * avoiding instantiation of unused objects.
2876
-	 *
2877
-	 * Defaults to ``true``
2878
-	 */
2879
-	'enable_lazy_objects' => true,
2880
-
2881
-	/**
2882
-	 * Change the default certificates bundle used for trusting certificates.
2883
-	 *
2884
-	 * Nextcloud ships its own up-to-date certificates bundle, but in certain cases admins may wish to specify a different bundle, for example the one shipped by their distro.
2885
-	 *
2886
-	 * Defaults to `\OC::$SERVERROOT . '/resources/config/ca-bundle.crt'`.
2887
-	 */
2888
-	'default_certificates_bundle_path' => \OC::$SERVERROOT . '/resources/config/ca-bundle.crt',
32
+    /**
33
+     * Default Parameters
34
+     *
35
+     * These parameters are configured by the Nextcloud installer, and are required
36
+     * for your Nextcloud server to operate.
37
+     */
38
+
39
+
40
+    /**
41
+     * This is a unique identifier for your Nextcloud installation, created
42
+     * automatically by the installer. This example is for documentation only,
43
+     * and you should never use it because it will not work. A valid ``instanceid``
44
+     * is created when you install Nextcloud.
45
+     */
46
+    'instanceid' => '',
47
+
48
+    /**
49
+     * This is a unique identifier for your server.
50
+     * It is useful when your Nextcloud instance is using different PHP servers.
51
+     * Once it's set it shouldn't be changed.
52
+     *
53
+     * Value must be an integer, comprised between 0 and 1023.
54
+     *
55
+     * When config.php is shared between different servers, this value should be overriden with "NC_serverid=<int>" on each server.
56
+     * Note that it must be overriden for CLI and for your webserver.
57
+     *
58
+     * Example for CLI: NC_serverid=42 occ config:list system
59
+     */
60
+    'serverid' => -1,
61
+
62
+    /**
63
+     * The salt used to hash all passwords, auto-generated by the Nextcloud
64
+     * installer. (There are also per-user salts.) If you lose this salt, you lose
65
+     * all your passwords. This example is for documentation only, and you should
66
+     * never use it.
67
+     *
68
+     * @deprecated 9.0.0 This salt is deprecated and only used for legacy-compatibility,
69
+     * developers should *NOT* use this value for anything nowadays.
70
+     */
71
+    'passwordsalt' => '',
72
+
73
+    /**
74
+     * Secret used by Nextcloud for various purposes, e.g., to encrypt data. If you
75
+     * lose this string, there will be data corruption.
76
+     */
77
+    'secret' => '',
78
+
79
+    /**
80
+     * Your list of trusted domains that users can log into. Specifying trusted
81
+     * domains prevents host header poisoning. Do not remove this, as it performs
82
+     * necessary security checks.
83
+     *
84
+     * You can specify:
85
+     *
86
+     * - The exact hostname of your host or virtual host, e.g. ``demo.example.org``.
87
+     * - The exact hostname with permitted port, e.g. ``demo.example.org:443``. This disallows all other ports on this host
88
+     * - Use ``*`` as a wildcard, e.g., ``ubos-raspberry-pi*.local`` will allow ``ubos-raspberry-pi.local`` and ``ubos-raspberry-pi-2.local``
89
+     * - The IP address with or without permitted port, e.g. ``[2001:db8::1]:8080`` Using TLS certificates where ``commonName=<IP address>`` is deprecated
90
+     */
91
+    'trusted_domains' => [
92
+        'demo.example.org',
93
+        'otherdomain.example.org',
94
+        '10.111.112.113',
95
+        '[2001:db8::1]'
96
+    ],
97
+
98
+    /**
99
+     * The validity domain for cookies, for example ``''`` (cookies will be sent only
100
+     * the domain that defined it, e.g. ``'demo.example.org'``), ``'demo.example.org'``
101
+     * (cookies will be valid for the domain and all subdomains), ...
102
+     *
103
+     * Defaults to ``''`` (safe option)
104
+     */
105
+    'cookie_domain' => '',
106
+
107
+    /**
108
+     * Where user files are stored. The SQLite database is also stored here, when
109
+     * you use SQLite.
110
+     *
111
+     * Default to ``data/`` in the Nextcloud directory.
112
+     */
113
+    'datadirectory' => '/var/www/nextcloud/data',
114
+
115
+    /**
116
+     * The current version number of your Nextcloud installation. This is set up
117
+     * during installation and update, so you shouldn't need to change it.
118
+     */
119
+    'version' => '',
120
+
121
+    /**
122
+     * Identifies the database used with this installation. See also config option
123
+     * ``supportedDatabases``
124
+     *
125
+     * Available:
126
+     *  - sqlite3 (SQLite3)
127
+     *  - mysql (MySQL/MariaDB)
128
+     *  - pgsql (PostgreSQL)
129
+     *
130
+     * Defaults to ``sqlite3``
131
+     */
132
+    'dbtype' => 'sqlite3',
133
+
134
+    /**
135
+     * Your host server name, for example ``localhost``, ``hostname``,
136
+     * ``hostname.example.com``, or the IP address.
137
+     * To specify a port, use ``hostname:####``; for IPv6 addresses, use the URI notation ``[ip]:port``.
138
+     * To specify a Unix socket, use ``localhost:/path/to/directory/containing/socket`` or
139
+     * ``:/path/to/directory/containing/socket``, e.g., ``localhost:/run/postgresql/``.
140
+     */
141
+    'dbhost' => '',
142
+
143
+    /**
144
+     * The name of the Nextcloud database, which is set during installation. You
145
+     * should not need to change this.
146
+     */
147
+    'dbname' => 'nextcloud',
148
+
149
+    /**
150
+     * The user that Nextcloud uses to write to the database. This must be unique
151
+     * across Nextcloud instances using the same SQL database. This is set up during
152
+     * installation, so you shouldn't need to change it.
153
+     */
154
+    'dbuser' => '',
155
+
156
+    /**
157
+     * The password for the database user. This is set up during installation, so
158
+     * you shouldn't need to change it.
159
+     */
160
+    'dbpassword' => '',
161
+
162
+    /**
163
+     * Prefix for the Nextcloud tables in the database.
164
+     *
165
+     * Default to ``oc_``
166
+     */
167
+    'dbtableprefix' => 'oc_',
168
+
169
+    /**
170
+     * Enable persistent connections to the database.
171
+     * This setting uses the ``persistent`` option from Doctrine DBAL, which in turn
172
+     * uses the PDO::ATTR_PERSISTENT option from the PDO driver.
173
+     */
174
+    'dbpersistent' => '',
175
+
176
+    /**
177
+     * Specify read-only replicas to be used by Nextcloud when querying the database
178
+     */
179
+    'dbreplica' => [
180
+        ['user' => 'nextcloud', 'password' => 'password1', 'host' => 'replica1', 'dbname' => ''],
181
+        ['user' => 'nextcloud', 'password' => 'password2', 'host' => 'replica2', 'dbname' => ''],
182
+    ],
183
+
184
+    /**
185
+     * Add request ID to the database query in a comment.
186
+     *
187
+     * This can be enabled to assist in mapping database logs to Nextcloud logs.
188
+     */
189
+    'db.log_request_id' => false,
190
+
191
+    /**
192
+     * Indicates whether the Nextcloud instance was installed successfully; ``true``
193
+     * indicates a successful installation, and ``false`` indicates an unsuccessful
194
+     * installation.
195
+     *
196
+     * Defaults to ``false``
197
+     */
198
+    'installed' => false,
199
+
200
+
201
+    /**
202
+     * User Experience
203
+     *
204
+     * These optional parameters control some aspects of the user interface. Default
205
+     * values, where present, are shown.
206
+     */
207
+
208
+    /**
209
+     * This sets the default language on your Nextcloud server, using ISO_639-1
210
+     * language codes such as ``en`` for English, ``de`` for German, and ``fr`` for
211
+     * French. The default_language parameter is only used when the browser does
212
+     * not send any language, and the user hasn’t configured their own language
213
+     * preferences.
214
+     *
215
+     * Nextcloud has two distinguished language codes for German, ``de`` and ``de_DE``.
216
+     * ``de`` is used for informal German and ``de_DE`` for formal German. By setting
217
+     * this value to ``de_DE``, you can enforce the formal version of German unless
218
+     * the user has chosen something different explicitly.
219
+     *
220
+     * Defaults to ``en``
221
+     */
222
+    'default_language' => 'en',
223
+
224
+    /**
225
+     * With this setting, a language can be forced for all users. If a language is
226
+     * forced, the users are also unable to change their language in the personal
227
+     * settings. If users shall be unable to change their language, but users have
228
+     * different languages, this value can be set to ``true`` instead of a language
229
+     * code.
230
+     *
231
+     * Defaults to ``false``
232
+     */
233
+    'force_language' => 'en',
234
+
235
+    /**
236
+     * This sets the default locale on your Nextcloud server, using ISO_639
237
+     * language codes such as ``en`` for English, ``de`` for German, and ``fr`` for
238
+     * French, and ISO-3166 country codes such as ``GB``, ``US``, ``CA``, as defined
239
+     * in RFC 5646. It overrides automatic locale detection on public pages like
240
+     * login or shared items. User's locale preferences configured under "personal
241
+     * -> locale" override this setting after they have logged in.
242
+     *
243
+     * Defaults to ``en``
244
+     */
245
+    'default_locale' => 'en_US',
246
+
247
+    /**
248
+     * With this setting, it is possible to reduce the languages available in the
249
+     * language chooser. The languages have to be set as array values using ISO_639-1
250
+     * language codes such as ``en`` for English, ``de`` for German, etc.
251
+     *
252
+     * For example: Set to ``['de', 'fr']`` to only allow German and French languages.
253
+     */
254
+    'reduce_to_languages' => [],
255
+
256
+    /**
257
+     * This sets the default region for phone numbers on your Nextcloud server,
258
+     * using ISO 3166-1 country codes such as ``DE`` for Germany, ``FR`` for France, …
259
+     * It is required to allow inserting phone numbers in the user profiles starting
260
+     * without the country code (e.g., +49 for Germany).
261
+     *
262
+     * No default value!
263
+     */
264
+    'default_phone_region' => 'GB',
265
+
266
+    /**
267
+     * With this setting, a locale can be forced for all users. If a locale is
268
+     * forced, the users are also unable to change their locale in the personal
269
+     * settings. If users shall be unable to change their locale, but users have
270
+     * different languages, this value can be set to ``true`` instead of a locale
271
+     * code.
272
+     *
273
+     * Defaults to ``false``
274
+     */
275
+    'force_locale' => 'en_US',
276
+
277
+    /**
278
+     * This sets the default timezone on your Nextcloud server, using IANA
279
+     * identifiers like ``Europe/Berlin`` or ``Pacific/Auckland``. The default
280
+     * timezone parameter is only used when the timezone of the user cannot be
281
+     * determined.
282
+     *
283
+     * Defaults to ``UTC``
284
+     */
285
+    'default_timezone' => 'Europe/Berlin',
286
+
287
+    /**
288
+     * ``true`` enables the Help menu item in the user menu (top right of the
289
+     * Nextcloud Web interface).
290
+     * ``false`` removes the Help item.
291
+     */
292
+    'knowledgebaseenabled' => true,
293
+
294
+    /**
295
+     * ``true`` embeds the documentation in an iframe inside Nextcloud.
296
+     * ``false`` only shows buttons to the online documentation.
297
+     */
298
+    'knowledgebase.embedded' => false,
299
+
300
+    /**
301
+     * ``true`` allows users to change their display names (on their Personal
302
+     * pages), and ``false`` prevents them from changing their display names.
303
+     */
304
+    'allow_user_to_change_display_name' => true,
305
+
306
+    /**
307
+     * The directory where the skeleton files are located. These files will be
308
+     * copied to the data directory of new users. Set empty string to not copy any
309
+     * skeleton files. If unset and templatedirectory is an empty string, shipped
310
+     * templates will be used to create a template directory for the user.
311
+     * ``{lang}`` can be used as a placeholder for the language of the user.
312
+     * If the directory does not exist, it falls back to non-dialect (from ``de_DE``
313
+     * to ``de``). If that does not exist either, it falls back to ``default``
314
+     *
315
+     * Defaults to ``core/skeleton`` in the Nextcloud directory.
316
+     */
317
+    'skeletondirectory' => '/path/to/nextcloud/core/skeleton',
318
+
319
+    /**
320
+     * The directory where the template files are located. These files will be
321
+     * copied to the template directory of new users. Set empty string to not copy any
322
+     * template files.
323
+     * ``{lang}`` can be used as a placeholder for the language of the user.
324
+     * If the directory does not exist, it falls back to non-dialect (from ``de_DE``
325
+     * to ``de``). If that does not exist either, it falls back to ``default``
326
+     *
327
+     * To disable creating a template directory, set both skeletondirectory and
328
+     * templatedirectory to empty strings.
329
+     */
330
+    'templatedirectory' => '/path/to/nextcloud/templates',
331
+
332
+    /**
333
+     * User session
334
+     */
335
+
336
+    /**
337
+     * Lifetime of the remember login cookie. This should be larger than the
338
+     * session_lifetime. If it is set to 0, remember me is disabled.
339
+     *
340
+     * Defaults to ``60*60*24*15`` seconds (15 days)
341
+     */
342
+    'remember_login_cookie_lifetime' => 60 * 60 * 24 * 15,
343
+
344
+    /**
345
+     * The lifetime of a session after inactivity.
346
+     *
347
+     * The maximum possible time is limited by the ``session.gc_maxlifetime`` php.ini setting
348
+     * which would overwrite this option if it is less than the value in the ``config.php``
349
+     *
350
+     * Defaults to ``60*60*24`` seconds (24 hours)
351
+     */
352
+    'session_lifetime' => 60 * 60 * 24,
353
+
354
+    /**
355
+     * The timeout in seconds for requests to servers made by the DAV component (e.g., needed for federated shares).
356
+     */
357
+    'davstorage.request_timeout' => 30,
358
+
359
+    /**
360
+     * The timeout in seconds for synchronizing address books, e.g., federated system address books (as run by ``occ federation:sync-addressbooks``).
361
+     *
362
+     * Defaults to ``30`` seconds
363
+     */
364
+    'carddav_sync_request_timeout' => 30,
365
+
366
+    /**
367
+     * The limit applied to the synchronization report request, e.g. federated system address books (as run by ``occ federation:sync-addressbooks``).
368
+     */
369
+    'carddav_sync_request_truncation' => 2500,
370
+
371
+    /**
372
+     * ``true`` enables a relaxed session timeout, where the session timeout would no longer be
373
+     * handled by Nextcloud but by either the PHP garbage collection or the expiration of
374
+     * potential other session backends like Redis.
375
+     *
376
+     * This may lead to sessions being available for longer than what ``session_lifetime`` uses but
377
+     * comes with performance benefits as sessions are no longer a locking operation for concurrent
378
+     * requests.
379
+     */
380
+    'session_relaxed_expiry' => false,
381
+
382
+    /**
383
+     * Enable or disable session keep-alive when a user is logged in to the Web UI.
384
+     * Enabling this sends a "heartbeat" to the server to keep it from timing out.
385
+     *
386
+     * Defaults to ``true``
387
+     */
388
+    'session_keepalive' => true,
389
+
390
+    /**
391
+     * Enable or disable the automatic logout after session_lifetime, even if session
392
+     * keepalive is enabled. This will make sure that an inactive browser will log itself out
393
+     * even if requests to the server might extend the session lifetime.
394
+     *
395
+     * NOTE: The logout is handled on the client side. This is not a way to
396
+     * limit the duration of potentially compromised sessions.
397
+     *
398
+     * Defaults to ``false``
399
+     */
400
+    'auto_logout' => false,
401
+
402
+    /**
403
+     * Enforce token authentication for clients, which blocks requests using the user
404
+     * password for enhanced security. Users need to generate tokens in personal settings
405
+     * which can be used as passwords on their clients.
406
+     *
407
+     * Defaults to ``false``
408
+     */
409
+    'token_auth_enforced' => false,
410
+
411
+    /**
412
+     * The interval at which token activity should be updated.
413
+     * Increasing this value means that the last activity on the security page gets
414
+     * more outdated.
415
+     *
416
+     * Tokens are still checked every 5 minutes for validity
417
+     * max value: 300
418
+     *
419
+     * Defaults to ``60``
420
+     */
421
+    'token_auth_activity_update' => 60,
422
+
423
+    /**
424
+     * Whether the brute force protection shipped with Nextcloud should be enabled or not.
425
+     *
426
+     * WARNING: Disabling this is discouraged for security reasons.
427
+     *
428
+     * Defaults to ``true``
429
+     */
430
+    'auth.bruteforce.protection.enabled' => true,
431
+
432
+    /**
433
+     * Whether the brute force protection should write into the database even when a memory cache is available
434
+     *
435
+     * Using the database is most likely worse for performance, but makes investigating
436
+     * issues a lot easier as it's possible to look directly at the table to see all
437
+     * logged remote addresses and actions.
438
+     *
439
+     * Defaults to ``false``
440
+     */
441
+    'auth.bruteforce.protection.force.database' => false,
442
+
443
+    /**
444
+     * Whether the brute force protection shipped with Nextcloud should be set to testing mode.
445
+     *
446
+     * In testing mode, brute force attempts are still recorded, but the requests do
447
+     * not sleep/wait for the specified time. They will still abort with
448
+     * "429 Too Many Requests" when the maximum delay is reached.
449
+     * Enabling this is discouraged for security reasons
450
+     * and should only be done for debugging and on CI when running tests.
451
+     *
452
+     * Defaults to ``false``
453
+     */
454
+    'auth.bruteforce.protection.testing' => false,
455
+
456
+    /**
457
+     * Brute force protection: maximum number of attempts before blocking
458
+     *
459
+     * When more than max-attempts login requests are sent to Nextcloud, requests
460
+     * will abort with "429 Too Many Requests".
461
+     * For security reasons, change it only if you know what you are doing.
462
+     *
463
+     * Defaults to ``10``
464
+     */
465
+    'auth.bruteforce.max-attempts' => 10,
466
+
467
+    /**
468
+     * Whether the rate limit protection shipped with Nextcloud should be enabled or not.
469
+     *
470
+     * WARNING: Disabling this is discouraged for security reasons.
471
+     *
472
+     * Defaults to ``true``
473
+     */
474
+    'ratelimit.protection.enabled' => true,
475
+
476
+    /**
477
+     * Overwrite the individual rate limit for a specific route
478
+     *
479
+     * From time to time it can be necessary to extend the rate limit of a specific route,
480
+     * depending on your usage pattern or when you script some actions.
481
+     * Instead of completely disabling the rate limit or excluding an IP address from the
482
+     * rate limit, the following config allows to overwrite the rate limit duration and period.
483
+     *
484
+     * The first level key is the name of the route. You can find the route name from a URL
485
+     * using the ``occ router:list`` command of your server.
486
+     *
487
+     * You can also specify different limits for logged-in users with the ``user`` key
488
+     * and not-logged-in users with the ``anon`` key. However, if there is no specific ``user`` limit,
489
+     * the ``anon`` limit is also applied for logged-in users.
490
+     *
491
+     * Defaults to empty array ``[]``
492
+     */
493
+    'ratelimit_overwrite' => [
494
+        'profile.profilepage.index' => [
495
+            'user' => ['limit' => 300, 'period' => 3600],
496
+            'anon' => ['limit' => 1, 'period' => 300],
497
+        ]
498
+    ],
499
+
500
+    /**
501
+     * Size of subnet used to normalize IPv6
502
+     *
503
+     * For Brute Force Protection and Rate Limiting, IPv6 addresses are truncated using subnet size.
504
+     * It defaults to /56, but you can set it between /32 and /64
505
+     *
506
+     * Defaults to ``56``
507
+     */
508
+    'security.ipv6_normalized_subnet_size' => 56,
509
+
510
+    /**
511
+     * By default, WebAuthn is available, but it can be explicitly disabled by admins
512
+     */
513
+    'auth.webauthn.enabled' => true,
514
+
515
+    /**
516
+     * Whether encrypted passwords should be stored in the database
517
+     *
518
+     * The passwords are only decrypted using the login token stored uniquely in the
519
+     * clients and allow connecting to external storages, autoconfiguring mail accounts in
520
+     * the mail app, and periodically checking if the password is still valid.
521
+     *
522
+     * This might be desirable to disable this functionality when using one-time
523
+     * passwords or when having a password policy enforcing long passwords (> 300
524
+     * characters).
525
+     *
526
+     * By default, the passwords are stored encrypted in the database.
527
+     *
528
+     * WARNING: If disabled, password changes on the user backend (e.g., on LDAP) no
529
+     * longer log connected clients out automatically. Users can still disconnect
530
+     * the clients by deleting the app token from the security settings.
531
+     */
532
+    'auth.storeCryptedPassword' => true,
533
+
534
+    /**
535
+     * By default, the login form is always available. There are cases (SSO) where an
536
+     * admin wants to avoid users entering their credentials to the system if the SSO
537
+     * app is unavailable.
538
+     *
539
+     * This will show an error. But the direct login still works with adding ``?direct=1``
540
+     */
541
+    'hide_login_form' => false,
542
+
543
+    /**
544
+     * If your user backend does not allow password resets (e.g., when it's a
545
+     * read-only user backend like LDAP), you can specify a custom link, where the
546
+     * user is redirected to, when clicking the "Reset password" link after a failed
547
+     * login attempt.
548
+     * In case you do not want to provide any link, replace the URL with ``'disabled'``
549
+     */
550
+    'lost_password_link' => 'https://example.org/link/to/password/reset',
551
+
552
+    /**
553
+     * URL to use as target for the logo link in the header (top-left logo)
554
+     *
555
+     * Defaults to the base URL of your Nextcloud instance
556
+     */
557
+    'logo_url' => 'https://example.org',
558
+
559
+    /**
560
+     * Mail Parameters
561
+     *
562
+     * These configure the email settings for Nextcloud notifications and password
563
+     * resets.
564
+     */
565
+
566
+    /**
567
+     * The return address that you want to appear on emails sent by the Nextcloud
568
+     * server, for example ``[email protected]``, substituting your own domain,
569
+     * of course.
570
+     */
571
+    'mail_domain' => 'example.com',
572
+
573
+    /**
574
+     * FROM address that overrides the built-in ``sharing-noreply`` and
575
+     * ``lostpassword-noreply`` FROM addresses.
576
+     *
577
+     * Defaults to different FROM addresses depending on the feature.
578
+     */
579
+    'mail_from_address' => 'nextcloud',
580
+
581
+    /**
582
+     * Enable SMTP class debugging.
583
+     * NOTE: ``loglevel`` will likely need to be adjusted too. See docs:
584
+     *   https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/email_configuration.html#enabling-debug-mode
585
+     *
586
+     * Defaults to ``false``
587
+     */
588
+    'mail_smtpdebug' => false,
589
+
590
+    /**
591
+     * Which mode to use for sending mail: ``sendmail``, ``smtp``, ``qmail``, or ``null``.
592
+     *
593
+     * If you are using local or remote SMTP, set this to ``smtp``.
594
+     *
595
+     * For the ``sendmail`` option, you need an installed and working email system on
596
+     * the server, with ``/usr/sbin/sendmail`` installed on your Unix system.
597
+     *
598
+     * For ``qmail``, the binary is /var/qmail/bin/sendmail, and it must be installed
599
+     * on your Unix system.
600
+     *
601
+     * Use the string ``null`` to send no mails (disable mail delivery). This can be
602
+     * useful if mails should be sent via APIs and rendering messages is not necessary.
603
+     *
604
+     * Defaults to ``smtp``
605
+     */
606
+    'mail_smtpmode' => 'smtp',
607
+
608
+    /**
609
+     * This depends on ``mail_smtpmode``. Specify the IP address of your mail
610
+     * server host. This may contain multiple hosts separated by a semicolon. If
611
+     * you need to specify the port number, append it to the IP address separated by
612
+     * a colon, like this: ``127.0.0.1:24``.
613
+     *
614
+     * Defaults to ``127.0.0.1``
615
+     */
616
+    'mail_smtphost' => '127.0.0.1',
617
+
618
+    /**
619
+     * This depends on ``mail_smtpmode``. Specify the port for sending mail.
620
+     *
621
+     * Defaults to ``25``
622
+     */
623
+    'mail_smtpport' => 25,
624
+
625
+    /**
626
+     * This depends on ``mail_smtpmode``. This sets the SMTP server timeout, in
627
+     * seconds. You may need to increase this if you are running an anti-malware or
628
+     * spam scanner.
629
+     *
630
+     * Defaults to ``10`` seconds
631
+     */
632
+    'mail_smtptimeout' => 10,
633
+
634
+    /**
635
+     * This depends on ``mail_smtpmode``. Specify ``ssl`` when you are using SSL/TLS. Any other value will be ignored.
636
+     *
637
+     * If the server advertises STARTTLS capabilities, they might be used, but they cannot be enforced by
638
+     * this config option.
639
+     *
640
+     * Defaults to ``''`` (empty string)
641
+     */
642
+    'mail_smtpsecure' => '',
643
+
644
+    /**
645
+     * This depends on ``mail_smtpmode``. Change this to ``true`` if your mail
646
+     * server requires authentication.
647
+     *
648
+     * Defaults to ``false``
649
+     */
650
+    'mail_smtpauth' => false,
651
+
652
+    /**
653
+     * This depends on ``mail_smtpauth``. Specify the username for authenticating to
654
+     * the SMTP server.
655
+     *
656
+     * Defaults to ``''`` (empty string)
657
+     */
658
+    'mail_smtpname' => '',
659
+
660
+    /**
661
+     * This depends on ``mail_smtpauth``. Specify the password for authenticating to
662
+     * the SMTP server.
663
+     *
664
+     * Default to ``''`` (empty string)
665
+     */
666
+    'mail_smtppassword' => '',
667
+
668
+    /**
669
+     * Replaces the default mail template layout. This can be utilized if the
670
+     * options to modify the mail texts with the theming app are not enough.
671
+     * The class must extend ``\OC\Mail\EMailTemplate``
672
+     */
673
+    'mail_template_class' => '\OC\Mail\EMailTemplate',
674
+
675
+    /**
676
+     * Email will be sent by default with an HTML and a plain text body. This option
677
+     * allows sending only plain text emails.
678
+     */
679
+    'mail_send_plaintext_only' => false,
680
+
681
+    /**
682
+     * This depends on ``mail_smtpmode``. Array of additional streams options that
683
+     * will be passed to underlying Swift mailer implementation.
684
+     * Defaults to an empty array.
685
+     */
686
+    'mail_smtpstreamoptions' => [],
687
+
688
+    /**
689
+     * Which mode is used for sendmail/qmail: ``smtp`` or ``pipe``.
690
+     *
691
+     * For ``smtp``, the sendmail binary is started with the parameter ``-bs``:
692
+     *   - Use the SMTP protocol on standard input and output.
693
+     *
694
+     * For ``pipe``, the binary is started with the parameters ``-t``:
695
+     *   - Read message from STDIN and extract recipients.
696
+     *
697
+     * Defaults to ``smtp``
698
+     */
699
+    'mail_sendmailmode' => 'smtp',
700
+
701
+    /**
702
+     * Proxy Configurations
703
+     */
704
+
705
+    /**
706
+     * The automatic hostname detection of Nextcloud can fail in certain reverse
707
+     * proxy and CLI/cron situations. This option allows you to manually override
708
+     * the automatic detection; for example, ``www.example.com``, or specify the port
709
+     * ``www.example.com:8080``.
710
+     */
711
+    'overwritehost' => '',
712
+
713
+    /**
714
+     * When generating URLs, Nextcloud attempts to detect whether the server is
715
+     * accessed via ``https`` or ``http``. However, if Nextcloud is behind a proxy
716
+     * and the proxy handles the ``https`` calls, Nextcloud would not know that
717
+     * ``ssl`` is in use, which would result in incorrect URLs being generated.
718
+     * Valid values are ``http`` and ``https``.
719
+     */
720
+    'overwriteprotocol' => '',
721
+
722
+    /**
723
+     * Nextcloud attempts to detect the webroot for generating URLs automatically.
724
+     * For example, if ``www.example.com/nextcloud`` is the URL pointing to the
725
+     * Nextcloud instance, the webroot is ``/nextcloud``. When proxies are in use,
726
+     * it may be difficult for Nextcloud to detect this parameter, resulting in
727
+     * invalid URLs.
728
+     */
729
+    'overwritewebroot' => '',
730
+
731
+    /**
732
+     * This option allows you to define a manual override condition as a regular
733
+     * expression for the remote IP address. For example, defining a range of IP
734
+     * addresses starting with ``10.0.0.`` and ending with 1 to 3:
735
+     * ``^10\.0\.0\.[1-3]$``
736
+     *
737
+     * Defaults to ``''`` (empty string)
738
+     */
739
+    'overwritecondaddr' => '',
740
+
741
+    /**
742
+     * Use this configuration parameter to specify the base URL for any URLs which
743
+     * are generated within Nextcloud using any kind of command line tools (cron or
744
+     * occ). The value should contain the full base URL:
745
+     * ``https://www.example.com/nextcloud``
746
+     * Please make sure to set the value to the URL that your users mainly use to access this Nextcloud.
747
+     * Otherwise, there might be problems with the URL generation via cron.
748
+     *
749
+     * Defaults to ``''`` (empty string)
750
+     */
751
+    'overwrite.cli.url' => '',
752
+
753
+    /**
754
+     * To have clean URLs without ``/index.php``, this parameter needs to be configured.
755
+     *
756
+     * This parameter will be written as "RewriteBase" on update and installation of
757
+     * Nextcloud to your ``.htaccess`` file. While this value is often simply the URL
758
+     * path of the Nextcloud installation, it cannot be set automatically properly in
759
+     * every scenario and needs thus some manual configuration.
760
+     *
761
+     * In a standard Apache setup, this usually equals the folder that Nextcloud is
762
+     * accessible at. So if Nextcloud is accessible via ``https://mycloud.org/nextcloud``,
763
+     * the correct value would most likely be ``/nextcloud``. If Nextcloud is running
764
+     * under ``https://mycloud.org/``, then it would be ``/``.
765
+     *
766
+     * Note that the above rule is not valid in every case, as there are some rare setup
767
+     * cases where this may not apply. However, to avoid any update problems, this
768
+     * configuration value is explicitly opt-in.
769
+     *
770
+     * After setting this value, run ``occ maintenance:update:htaccess``. Now, when the
771
+     * following conditions are met, Nextcloud URLs won't contain ``index.php``:
772
+     *
773
+     * - ``mod_rewrite`` is installed
774
+     * - ``mod_env`` is installed
775
+     *
776
+     * Defaults to ``''`` (empty string)
777
+     */
778
+    'htaccess.RewriteBase' => '/',
779
+
780
+    /**
781
+     * For server setups that don't have ``mod_env`` enabled or restricted (e.g., suEXEC),
782
+     * this parameter has to be set to true and will assume mod_rewrite.
783
+     *
784
+     * Please check if ``mod_rewrite`` is active and functional before setting this
785
+     * parameter, and you updated your .htaccess with ``occ maintenance:update:htaccess``.
786
+     * Otherwise, your Nextcloud installation might not be reachable any more.
787
+     * For example, try accessing resources by leaving out ``index.php`` in the URL.
788
+     */
789
+    'htaccess.IgnoreFrontController' => false,
790
+
791
+    /**
792
+     * The URL of your proxy server, for example, ``proxy.example.com:8081``.
793
+     *
794
+     * NOTE: Guzzle (the HTTP library used by Nextcloud) reads the environment
795
+     * variables ``HTTP_PROXY`` (only for CLI requests), ``HTTPS_PROXY``, and ``NO_PROXY`` by default.
796
+     *
797
+     * If you configure a proxy with Nextcloud, any default configuration by Guzzle
798
+     * is overwritten. Make sure to set ``proxyexclude`` accordingly if necessary.
799
+     *
800
+     * Defaults to ``''`` (empty string)
801
+     */
802
+    'proxy' => '',
803
+
804
+    /**
805
+     * The optional authentication for the proxy to use to connect to the internet.
806
+     * The format is: ``username:password``.
807
+     *
808
+     * Defaults to ``''`` (empty string)
809
+     */
810
+    'proxyuserpwd' => '',
811
+
812
+    /**
813
+     * List of hostnames that should not be proxied to.
814
+     * For example: ``['.mit.edu', 'foo.com']``.
815
+     *
816
+     * Hint: Use something like ``explode(',', getenv('NO_PROXY'))`` to sync this
817
+     * value with the global ``NO_PROXY`` option.
818
+     *
819
+     * Defaults to empty array.
820
+     */
821
+    'proxyexclude' => [],
822
+
823
+    /**
824
+     * Allow remote servers with local addresses, e.g., in federated shares, webcal services, and more
825
+     *
826
+     * Defaults to ``false``
827
+     */
828
+    'allow_local_remote_servers' => true,
829
+
830
+    /**
831
+     * Deleted Items (trash bin)
832
+     *
833
+     * These parameters control the Deleted files app.
834
+     */
835
+
836
+    /**
837
+     * If the trash bin app is enabled (default), this setting defines the policy
838
+     * for when files and folders in the trash bin will be permanently deleted.
839
+     *
840
+     * If the user quota limit is exceeded due to deleted files in the trash bin,
841
+     * retention settings will be ignored and files will be cleaned up until
842
+     * the quota requirements are met.
843
+     *
844
+     * The app allows for two settings, a minimum time for trash bin retention,
845
+     * and a maximum time for trash bin retention.
846
+     *
847
+     * Minimum time is the number of days a file will be kept, after which it
848
+     * *may be* deleted. A file may be deleted after the minimum number of days
849
+     * has expired if space is needed. The file will not be deleted if space is
850
+     * not needed.
851
+     *
852
+     * Whether "space is needed" depends on whether a user quota is defined or not:
853
+     *
854
+     *  * If no user quota is defined, the available space on the Nextcloud data
855
+     *    partition sets the limit for the trashbin
856
+     *    (issues: see https://github.com/nextcloud/server/issues/28451).
857
+     *  * If a user quota is defined, 50% of the user's remaining quota space sets
858
+     *    the limit for the trashbin.
859
+     *
860
+     * Maximum time is the number of days at which it is *guaranteed
861
+     * to be* deleted. There is no further dependency on the available space.
862
+     *
863
+     * Both minimum and maximum times can be set together to explicitly define
864
+     * file and folder deletion. For migration purposes, this setting is installed
865
+     * initially set to "auto", which is equivalent to the default setting in
866
+     * Nextcloud.
867
+     *
868
+     * Available values (D1 and D2 are configurable numbers):
869
+     *
870
+     * * ``auto``
871
+     *     | Default setting. Keeps files and folders in the trash bin for at least **30** days.
872
+     *     | Then, **if space is needed**, deletes trashed files anytime after that.
873
+     * * ``D1, auto``
874
+     *     | Keeps files and folders in the trash bin for at least **D1** days.
875
+     *     | Then, **if space is needed**, deletes trashed files anytime after that.
876
+     * * ``auto, D2``
877
+     *     | **If space is needed**, deletes trashed files anytime.
878
+     *     | After **D2** days, delete all trashed files automatically
879
+     * * ``D1, D2``
880
+     *     | Keeps files and folders in the trash bin for at least **D1** days.
881
+     *     | Then, after **D2** days, delete all trashed files automatically.
882
+     * * ``disabled``
883
+     *     | Trash bin auto clean is disabled, files and folders will be kept forever.
884
+     *
885
+     * Defaults to ``auto``
886
+     */
887
+    'trashbin_retention_obligation' => 'auto',
888
+
889
+
890
+    /**
891
+     * File versions
892
+     *
893
+     * These parameters control the Versions app.
894
+     */
895
+
896
+    /**
897
+     * If the versions app is enabled (default), this setting defines the policy
898
+     * for when versions will be permanently deleted.
899
+     * The app allows for two settings, a minimum time for version retention,
900
+     * and a maximum time for version retention.
901
+     * Minimum time is the number of days a version will be kept, after which it
902
+     * may be deleted. Maximum time is the number of days at which it is guaranteed
903
+     * to be deleted.
904
+     * Both minimum and maximum times can be set together to explicitly define
905
+     * version deletion. For migration purposes, this setting is installed
906
+     * initially set to "auto", which is equivalent to the default setting in
907
+     * Nextcloud.
908
+     *
909
+     * Available values:
910
+     *
911
+     * * ``auto``
912
+     *     default setting. Automatically expire versions according to expire
913
+     *     rules. Please refer to :doc:`../configuration_files/file_versioning` for
914
+     *     more information.
915
+     * * ``D, auto``
916
+     *     keep versions at least for D days, apply expiration rules to all versions
917
+     *     that are older than D days
918
+     * * ``auto, D``
919
+     *     delete all versions that are older than D days automatically, delete
920
+     *     other versions according to expire rules
921
+     * * ``D1, D2``
922
+     *     keep versions for at least D1 days and delete when exceeds D2 days
923
+     * * ``disabled``
924
+     *     versions auto clean disabled, versions will be kept forever
925
+     *
926
+     * Defaults to ``auto``
927
+     */
928
+    'versions_retention_obligation' => 'auto',
929
+
930
+    /**
931
+     * Nextcloud Verifications
932
+     *
933
+     * Nextcloud performs several verification checks. There are two options,
934
+     * ``true`` and ``false``.
935
+     */
936
+
937
+    /**
938
+     * Checks an app before install whether it uses private APIs instead of the
939
+     * proper public APIs. If this is set to true, it will only allow installing or
940
+     * enabling apps that pass this check.
941
+     *
942
+     * Defaults to ``false``
943
+     */
944
+    'appcodechecker' => true,
945
+
946
+    /**
947
+     * Check if Nextcloud is up-to-date and shows a notification if a new version is
948
+     * available. It sends current version, PHP version, installation and last update
949
+     * time, and release channel to the updater server which responds with the latest
950
+     * available version based on those metrics.
951
+     *
952
+     * Defaults to ``true``
953
+     */
954
+    'updatechecker' => true,
955
+
956
+    /**
957
+     * URL that Nextcloud should use to look for updates
958
+     *
959
+     * Defaults to ``https://updates.nextcloud.com/updater_server/``
960
+     */
961
+    'updater.server.url' => 'https://updates.nextcloud.com/updater_server/',
962
+
963
+    /**
964
+     * The channel that Nextcloud should use to look for updates
965
+     *
966
+     * Supported values:
967
+     *
968
+     * - ``daily``
969
+     * - ``beta``
970
+     * - ``stable``
971
+     */
972
+    'updater.release.channel' => 'stable',
973
+
974
+    /**
975
+     * Is Nextcloud connected to the Internet or running in a closed network?
976
+     *
977
+     * Defaults to ``true``
978
+     */
979
+    'has_internet_connection' => true,
980
+
981
+    /**
982
+     * Which domains to request to determine the availability of an Internet
983
+     * connection. If none of these hosts are reachable, the administration panel
984
+     * will show a warning. Set to an empty list to not do any such checks (warning
985
+     * will still be shown).
986
+     * If no protocol is provided, both http and https will be tested.
987
+     * For example, ``http://www.nextcloud.com`` and ``https://www.nextcloud.com``
988
+     * will be tested for ``www.nextcloud.com``
989
+     * If a protocol is provided, only this one will be tested.
990
+     *
991
+     * Defaults to the following domains:
992
+     *
993
+     *  - https://www.nextcloud.com
994
+     *  - https://www.startpage.com
995
+     *  - https://www.eff.org
996
+     *  - https://www.edri.org
997
+     */
998
+    'connectivity_check_domains' => [
999
+        'https://www.nextcloud.com',
1000
+        'https://www.startpage.com',
1001
+        'https://www.eff.org',
1002
+        'https://www.edri.org'
1003
+    ],
1004
+
1005
+    /**
1006
+     * Allows Nextcloud to verify a working .well-known URL redirects. This is done
1007
+     * by attempting to make a request from JS to
1008
+     * ``https://example.tld/.well-known/caldav/``
1009
+     *
1010
+     * Defaults to ``true``
1011
+     */
1012
+    'check_for_working_wellknown_setup' => true,
1013
+
1014
+    /**
1015
+     * This is a crucial security check on Apache servers that should always be set
1016
+     * to ``true``. This verifies that the ``.htaccess`` file is writable and works.
1017
+     * If it is not, then any options controlled by ``.htaccess``, such as large
1018
+     * file uploads, will not work. It also runs checks on the ``data/`` directory,
1019
+     * which verifies that it can't be accessed directly through the Web server.
1020
+     *
1021
+     * Defaults to ``true``
1022
+     */
1023
+    'check_for_working_htaccess' => true,
1024
+
1025
+    /**
1026
+     * In rare setups (e.g., on OpenShift or Docker on Windows), the permissions check
1027
+     * might block the installation while the underlying system offers no means to
1028
+     * "correct" the permissions. In this case, set the value to false.
1029
+     *
1030
+     * In regular cases, if issues with permissions are encountered, they should be
1031
+     * adjusted accordingly. Changing the flag is discouraged.
1032
+     *
1033
+     * Defaults to ``true``
1034
+     */
1035
+    'check_data_directory_permissions' => true,
1036
+
1037
+    /**
1038
+     * In certain environments, it is desired to have a read-only configuration file.
1039
+     * When this switch is set to ``true``, writing to the config file will be
1040
+     * forbidden. Therefore, it will not be possible to configure all options via
1041
+     * the Web interface. Furthermore, when updating Nextcloud, it is required to
1042
+     * make the configuration file writable again and to set this switch to ``false``
1043
+     * for the update process.
1044
+     *
1045
+     * Defaults to ``false``
1046
+     */
1047
+    'config_is_read_only' => false,
1048
+
1049
+    /**
1050
+     * Logging
1051
+     */
1052
+
1053
+    /**
1054
+     * This parameter determines where the Nextcloud logs are sent.
1055
+     *
1056
+     * - ``file``: the logs are written to file ``nextcloud.log`` in the default Nextcloud data directory. The log file can be changed with parameter ``logfile``.
1057
+     * - ``syslog``: the logs are sent to the system log. This requires a syslog daemon to be active.
1058
+     * - ``errorlog``: the logs are sent to the PHP ``error_log`` function.
1059
+     * - ``systemd``: the logs are sent to the Systemd journal. This requires a system that runs Systemd and the Systemd journal. The PHP extension ``systemd`` must be installed and active.
1060
+     *
1061
+     * Defaults to ``file``
1062
+     */
1063
+    'log_type' => 'file',
1064
+
1065
+    /**
1066
+     * This parameter determines where the audit logs are sent. See ``log_type`` for more information.
1067
+     *
1068
+     * Defaults to ``file``
1069
+     */
1070
+    'log_type_audit' => 'file',
1071
+
1072
+    /**
1073
+     * Name of the file to which the Nextcloud logs are written if parameter
1074
+     * ``log_type`` is set to ``file``.
1075
+     *
1076
+     * Defaults to ``[datadirectory]/nextcloud.log``
1077
+     */
1078
+    'logfile' => '/var/log/nextcloud.log',
1079
+
1080
+    /**
1081
+     * Name of the file to which the audit logs are written if parameter
1082
+     * ``log_type`` is set to ``file``.
1083
+     *
1084
+     * Defaults to ``[datadirectory]/audit.log``
1085
+     */
1086
+    'logfile_audit' => '/var/log/audit.log',
1087
+
1088
+    /**
1089
+     * Log file mode for the Nextcloud logging type in octal notation.
1090
+     *
1091
+     * Defaults to ``0640`` (writable by user, readable by group).
1092
+     */
1093
+    'logfilemode' => 0640,
1094
+
1095
+    /**
1096
+     * Loglevel to start logging at. Valid values are:
1097
+     *
1098
+     * - ``0`` = Debug
1099
+     * - ``1`` = Info
1100
+     * - ``2`` = Warning
1101
+     * - ``3`` = Error
1102
+     * - ``4`` = Fatal.
1103
+     *
1104
+     *
1105
+     * Defaults to ``2`` (Warning)
1106
+     */
1107
+    'loglevel' => 2,
1108
+
1109
+    /**
1110
+     * Loglevel used by the frontend to start logging at. The same values as
1111
+     * for ``loglevel`` can be used. If not set, it defaults to the value
1112
+     * configured for ``loglevel`` or Warning if that is not set either.
1113
+     *
1114
+     * Defaults to ``2``
1115
+     */
1116
+    'loglevel_frontend' => 2,
1117
+
1118
+    /**
1119
+     * Loglevel used by the dirty database query detection. Useful to identify
1120
+     * potential database bugs in production. Set this to loglevel or higher to
1121
+     * see dirty queries in the logs.
1122
+     *
1123
+     * Defaults to ``0`` (debug)
1124
+     */
1125
+    'loglevel_dirty_database_queries' => 0,
1126
+
1127
+    /**
1128
+     * If you maintain different instances and aggregate the logs, you may want
1129
+     * to distinguish between them. ``syslog_tag`` can be set per instance
1130
+     * with a unique ID. Only available if ``log_type`` is set to ``syslog`` or
1131
+     * ``systemd``.
1132
+     *
1133
+     * The default value is ``Nextcloud``.
1134
+     */
1135
+    'syslog_tag' => 'Nextcloud',
1136
+
1137
+    /**
1138
+     * If you maintain different instances and aggregate the logs, you may want
1139
+     * to distinguish between them. ``syslog_tag_audit`` can be set per instance
1140
+     * with a unique ID. Only available if ``log_type`` is set to ``syslog`` or
1141
+     * ``systemd``.
1142
+     *
1143
+     * The default value is the value of ``syslog_tag``.
1144
+     */
1145
+    'syslog_tag_audit' => 'Nextcloud',
1146
+
1147
+    /**
1148
+     * Log condition for log level increase based on conditions. Once one of these
1149
+     * conditions is met, the required log level is set to debug. This allows
1150
+     * debugging specific requests, users, or apps
1151
+     *
1152
+     * Supported conditions:
1153
+     *  - ``shared_secret``: if a request parameter with the name `log_secret` is set to
1154
+     *                this value, the condition is met
1155
+     *  - ``users``:  if the current request is done by one of the specified users,
1156
+     *                this condition is met
1157
+     *  - ``apps``:   if the log message is invoked by one of the specified apps,
1158
+     *                this condition is met
1159
+     *  - ``matches``: if all the conditions inside a group match,
1160
+     *                this condition is met. This allows logging only entries to an app
1161
+     *                by a few users.
1162
+     *
1163
+     * Defaults to an empty array.
1164
+     */
1165
+    'log.condition' => [
1166
+        'shared_secret' => '57b58edb6637fe3059b3595cf9c41b9',
1167
+        'users' => ['sample-user'],
1168
+        'apps' => ['files'],
1169
+        'matches' => [
1170
+            [
1171
+                'shared_secret' => '57b58edb6637fe3059b3595cf9c41b9',
1172
+                'users' => ['sample-user'],
1173
+                'apps' => ['files'],
1174
+                'loglevel' => 1,
1175
+                'message' => 'contains substring'
1176
+            ],
1177
+        ],
1178
+    ],
1179
+
1180
+    /**
1181
+     * Enables logging a backtrace with each log line. Normally, only Exceptions
1182
+     * carry backtrace information, which are logged automatically. This
1183
+     * switch turns them on for any log message. Enabling this option will lead
1184
+     * to increased log data size.
1185
+     *
1186
+     * Defaults to ``false``.
1187
+     */
1188
+    'log.backtrace' => false,
1189
+
1190
+    /**
1191
+     * This uses PHP.date formatting; see https://www.php.net/manual/en/function.date.php
1192
+     *
1193
+     * Defaults to ISO 8601 ``2005-08-15T15:52:01+00:00``, see ``\DateTime::ATOM``
1194
+     * https://www.php.net/manual/en/class.datetimeinterface.php#datetimeinterface.constants.atom
1195
+     */
1196
+    'logdateformat' => 'F d, Y H:i:s',
1197
+
1198
+    /**
1199
+     * The timezone for logfiles. See https://www.php.net/manual/en/timezones.php
1200
+     *
1201
+     * Defaults to ``UTC``
1202
+     */
1203
+    'logtimezone' => 'Europe/Berlin',
1204
+
1205
+    /**
1206
+     * Append all database queries and parameters to the log file. Use this only for
1207
+     * debugging, as your logfile will become huge.
1208
+     */
1209
+    'log_query' => false,
1210
+
1211
+    /**
1212
+     * Enables log rotation and limits the total size of logfiles. Set it to 0 for
1213
+     * no rotation. Specify a size in bytes, for example, 104857600 (100 megabytes
1214
+     * = 100 * 1024 * 1024 bytes). A new logfile is created with a new name when the
1215
+     * old logfile reaches your limit. If a rotated log file is already present, it
1216
+     * will be overwritten.
1217
+     *
1218
+     * Defaults to 100 MB
1219
+     */
1220
+    'log_rotate_size' => 100 * 1024 * 1024,
1221
+
1222
+    /**
1223
+     * Enable built-in profiler. Helpful when trying to debug performance
1224
+     * issues.
1225
+     *
1226
+     * Note that this has a performance impact and shouldn't be enabled
1227
+     * on production.
1228
+     */
1229
+    'profiler' => false,
1230
+
1231
+    /**
1232
+     * Enable profiling for individual requests if profiling single requests is enabled or the secret is passed.
1233
+     * This requires the excimer extension to be installed. Be careful with this, as it can generate a lot of data.
1234
+     *
1235
+     * The profile data will be stored as a JSON file in the profiling.path directory that can be analyzed with speedscope.
1236
+     *
1237
+     * Defaults to ``false``
1238
+     */
1239
+    'profiling.request' => false,
1240
+
1241
+    /**
1242
+     * The rate at which profiling data is collected for individual requests.
1243
+     * A lower value means more data points but higher overhead.
1244
+     *
1245
+     * Defaults to ``0.001``
1246
+     */
1247
+    'profiling.request.rate' => 0.001,
1248
+
1249
+    /**
1250
+     * A secret token that can be passed via ?profile_secret=<secret> to enable profiling for a specific request.
1251
+     * This allows profiling specific requests in production without enabling it globally.
1252
+     *
1253
+     * No default value.
1254
+     */
1255
+    'profiling.secret' => '',
1256
+
1257
+    /**
1258
+     * Enable sampling-based profiling. This collects profiling data periodically rather than per-request.
1259
+     * This requires the excimer extension to be installed. Be careful with this, as it can generate a lot of data.
1260
+     *
1261
+     * The profile data will be stored as a plain text file in the profiling.path directory that can be analyzed with speedscope.
1262
+     *
1263
+     * Defaults to ``false``
1264
+     */
1265
+    'profiling.sample' => false,
1266
+
1267
+    /**
1268
+     * The rate at which sampling profiling data is collected in seconds.
1269
+     * A lower value means more frequent samples but higher overhead.
1270
+     *
1271
+     * Defaults to ``1``
1272
+     */
1273
+    'profiling.sample.rate' => 1,
1274
+
1275
+    /**
1276
+     * How often (in minutes) the sample log files are rotated.
1277
+     *
1278
+     * Defaults to ``60``
1279
+     */
1280
+    'profiling.sample.rotation' => 60,
1281
+
1282
+    /**
1283
+     * The directory where profiling data is stored.
1284
+     *
1285
+     * Note that this directory must be writable by the web server user and will not be cleaned up automatically.
1286
+     */
1287
+    'profiling.path' => '/tmp',
1288
+
1289
+
1290
+    /**
1291
+     * Alternate Code Locations
1292
+     *
1293
+     * Some Nextcloud code may be stored in alternate locations.
1294
+     */
1295
+
1296
+    /**
1297
+     * This section is for configuring the download links for Nextcloud clients, as
1298
+     * seen in the first-run wizard and on Personal pages.
1299
+     *
1300
+     * Defaults to:
1301
+     *
1302
+     * - Desktop client: ``https://nextcloud.com/install/#install-clients``
1303
+     * - Android client: ``https://play.google.com/store/apps/details?id=com.nextcloud.client``
1304
+     * - iOS client: ``https://itunes.apple.com/us/app/nextcloud/id1125420102?mt=8``
1305
+     * - iOS client app ID: ``1125420102``
1306
+     * - F-Droid client: ``https://f-droid.org/packages/com.nextcloud.client/``
1307
+     */
1308
+    'customclient_desktop'
1309
+        => 'https://nextcloud.com/install/#install-clients',
1310
+    'customclient_android'
1311
+        => 'https://play.google.com/store/apps/details?id=com.nextcloud.client',
1312
+    'customclient_ios'
1313
+        => 'https://itunes.apple.com/us/app/nextcloud/id1125420102?mt=8',
1314
+    'customclient_ios_appid'
1315
+        => '1125420102',
1316
+    'customclient_fdroid'
1317
+        => 'https://f-droid.org/packages/com.nextcloud.client/',
1318
+
1319
+    /**
1320
+     * Activity
1321
+     *
1322
+     * Options for the activity app.
1323
+     */
1324
+
1325
+    /**
1326
+     * Retention of activities.
1327
+     *
1328
+     * A daily cron job deletes all activities for all users which are older than
1329
+     * the number of days specified here.
1330
+     *
1331
+     * Defaults to ``365``
1332
+     */
1333
+    'activity_expire_days' => 365,
1334
+
1335
+    /**
1336
+     * Activities in Team Folders and External Storages.
1337
+     *
1338
+     * By default, activities in team folders or external storages are only generated
1339
+     * for the current user. This is due to a limitations in current implementations.
1340
+     * This config flag makes activities in group folders and external storages work
1341
+     * like in normal shares (when set to ``true``).
1342
+     *
1343
+     * WARNING: Enabling this comes with some CRITICAL trade-offs:
1344
+     *
1345
+     * - If team folder "Advanced Permissions" (ACLs) are used, activities do not
1346
+     *   respect the permissions and therefore all users see all activities, even
1347
+     *   for files and directories they do not have access to.
1348
+     * - Users who had access to a team folder, share, or external storage can see
1349
+     *   activities in their stream and emails that happen after they are removed
1350
+     *   until they log in again.
1351
+     * - Users who are newly added to a team folder, share, or external storage
1352
+     *   cannot see activities in their stream or emails that happen after they
1353
+     *   are added until they log in again.
1354
+     *
1355
+     * Defaults to ``false``
1356
+     */
1357
+    'activity_use_cached_mountpoints' => false,
1358
+
1359
+    /**
1360
+     * Apps
1361
+     *
1362
+     * Options for the Apps folder, Apps store, and App code checker.
1363
+     */
1364
+
1365
+    /**
1366
+     * Set the default app to open on login. The entry IDs can be retrieved from
1367
+     * the Navigations OCS API endpoint: https://docs.nextcloud.com/server/latest/developer_manual/_static/openapi.html#/operations/core-navigation-get-apps-navigation.
1368
+     * You can use a comma-separated list of app names, so if the first
1369
+     * app is not enabled for a user, then Nextcloud will try the second one, and so
1370
+     * on. If no enabled apps are found, it defaults to the dashboard app.
1371
+     *
1372
+     * Defaults to ``dashboard,files``
1373
+     */
1374
+    'defaultapp' => 'dashboard,files',
1375
+
1376
+    /**
1377
+     * When enabled, admins may install apps from the Nextcloud app store.
1378
+     *
1379
+     * Defaults to ``true``
1380
+     */
1381
+    'appstoreenabled' => true,
1382
+
1383
+    /**
1384
+     * Enables the installation of apps from a self-hosted apps store.
1385
+     * Requires that at least one of the configured apps directories is writable.
1386
+     *
1387
+     * Defaults to ``https://apps.nextcloud.com/api/v1``
1388
+     */
1389
+    'appstoreurl' => 'https://apps.nextcloud.com/api/v1',
1390
+
1391
+    /**
1392
+     * Filters allowed installable apps from the appstore.
1393
+     * Empty array will prevent all apps from the store to be found.
1394
+     */
1395
+    'appsallowlist' => [],
1396
+
1397
+    /**
1398
+     * Use the ``apps_paths`` parameter to set the location of the Apps directory,
1399
+     * which should be scanned for available apps, and where user-specific apps
1400
+     * should be installed from the Apps store. The ``path`` defines the absolute
1401
+     * file system path to the app folder. The key ``url`` defines the HTTP Web path
1402
+     * to that folder, starting from the Nextcloud webroot. The key ``writable``
1403
+     * indicates if a Web server can write files to that folder.
1404
+     */
1405
+    'apps_paths' => [
1406
+        [
1407
+            'path' => '/var/www/nextcloud/apps',
1408
+            'url' => '/apps',
1409
+            'writable' => true,
1410
+        ],
1411
+    ],
1412
+
1413
+    /**
1414
+     * @see appcodechecker
1415
+     */
1416
+
1417
+    /**
1418
+     * Previews
1419
+     *
1420
+     * Nextcloud supports generating previews for various file types, such as images, audio files, and text files.
1421
+     * These options control enabling and disabling previews, and thumbnail size.
1422
+     */
1423
+
1424
+    /**
1425
+     * By default, Nextcloud can generate previews for the following filetypes:
1426
+     *
1427
+     * - Image files
1428
+     * - Text documents
1429
+     *
1430
+     * Valid values are ``true``, to enable previews, or
1431
+     * ``false``, to disable previews
1432
+     *
1433
+     * Defaults to ``true``
1434
+     */
1435
+    'enable_previews' => true,
1436
+
1437
+    /**
1438
+     * Number of all preview requests being processed concurrently,
1439
+     * including previews that need to be newly generated, and those that have
1440
+     * been generated.
1441
+     *
1442
+     * This should be greater than ``preview_concurrency_new``.
1443
+     * If unspecified, defaults to twice the value of ``preview_concurrency_new``.
1444
+     */
1445
+    'preview_concurrency_all' => 8,
1446
+
1447
+    /**
1448
+     * Number of new previews that are being concurrently generated.
1449
+     *
1450
+     * Depending on the max preview size set by ``preview_max_x`` and ``preview_max_y``,
1451
+     * the generation process can consume considerable CPU and memory resources.
1452
+     * It's recommended to limit this to be no greater than the number of CPU cores.
1453
+     * If unspecified, defaults to the number of CPU cores, or 4 if that cannot
1454
+     * be determined.
1455
+     */
1456
+    'preview_concurrency_new' => 4,
1457
+
1458
+    /**
1459
+     * The maximum width, in pixels, of a preview. A value of ``null`` means there
1460
+     * is no limit.
1461
+     *
1462
+     * Defaults to ``4096``
1463
+     */
1464
+    'preview_max_x' => 4096,
1465
+    /**
1466
+     * The maximum height, in pixels, of a preview. A value of ``null`` means there
1467
+     * is no limit.
1468
+     *
1469
+     * Defaults to ``4096``
1470
+     */
1471
+    'preview_max_y' => 4096,
1472
+
1473
+    /**
1474
+     * Max file size for generating image previews with imagegd (default behavior).
1475
+     * If the image is bigger, it'll try other preview generators, but will most
1476
+     * likely either show the default mimetype icon or not display the image at all.
1477
+     * Set to ``-1`` for no limit and try to generate image previews on all file sizes.
1478
+     *
1479
+     * Defaults to ``50`` megabytes
1480
+     */
1481
+    'preview_max_filesize_image' => 50,
1482
+
1483
+    /**
1484
+     * Max memory for generating image previews with imagegd (default behavior)
1485
+     * Reads the image dimensions from the header and assumes 32 bits per pixel.
1486
+     * If creating the image would allocate more memory, preview generation will
1487
+     * be disabled and the default mimetype icon is shown. Set to ``-1`` for no limit.
1488
+     *
1489
+     * Defaults to ``256`` megabytes
1490
+     */
1491
+    'preview_max_memory' => 256,
1492
+
1493
+    /**
1494
+     * Custom path for LibreOffice/OpenOffice binary
1495
+     *
1496
+     *
1497
+     * Defaults to ``''`` (empty string)
1498
+     */
1499
+    'preview_libreoffice_path' => '/usr/bin/libreoffice',
1500
+
1501
+    /**
1502
+     * Custom path for ffmpeg binary
1503
+     *
1504
+     * Defaults to ``null`` and falls back to searching ``ffmpeg``
1505
+     * in the configured ``PATH`` environment
1506
+     */
1507
+    'preview_ffmpeg_path' => '/usr/bin/ffmpeg',
1508
+
1509
+    /**
1510
+     * Custom path for ffprobe binary
1511
+     *
1512
+     * Defaults to ``null`` and falls back to using the same path as ffmpeg.
1513
+     * ffprobe is typically packaged with ffmpeg and is required for
1514
+     * enhanced preview generation for HDR videos.
1515
+     */
1516
+    'preview_ffprobe_path' => '/usr/bin/ffprobe',
1517
+
1518
+    /**
1519
+     * Set the URL of the Imaginary service to send image previews to.
1520
+     * Also requires the ``OC\Preview\Imaginary`` provider to be enabled in the
1521
+     * ``enabledPreviewProviders`` array, to create previews for these mimetypes: bmp,
1522
+     * x-bitmap, png, jpeg, gif, heic, heif, svg+xml, tiff, webp, and illustrator.
1523
+     *
1524
+     * If you want Imaginary to also create preview images from PDF documents, you
1525
+     * have to add the ``OC\Preview\ImaginaryPDF`` provider as well.
1526
+     *
1527
+     * See https://github.com/h2non/imaginary
1528
+     */
1529
+    'preview_imaginary_url' => 'http://previews_hpb:8088/',
1530
+
1531
+    /**
1532
+     * If you want to set an API key for Imaginary.
1533
+     */
1534
+    'preview_imaginary_key' => 'secret',
1535
+
1536
+    /**
1537
+     * Only register providers that have been explicitly enabled
1538
+     *
1539
+     * The following providers are disabled by default due to performance or privacy
1540
+     * concerns:
1541
+     *
1542
+     *  - ``OC\Preview\EMF``
1543
+     *  - ``OC\Preview\Font``
1544
+     *  - ``OC\Preview\HEIC``
1545
+     *  - ``OC\Preview\Illustrator``
1546
+     *  - ``OC\Preview\Movie``
1547
+     *  - ``OC\Preview\MP3``
1548
+     *  - ``OC\Preview\MSOffice2003``
1549
+     *  - ``OC\Preview\MSOffice2007``
1550
+     *  - ``OC\Preview\MSOfficeDoc``
1551
+     *  - ``OC\Preview\PDF``
1552
+     *  - ``OC\Preview\Photoshop``
1553
+     *  - ``OC\Preview\Postscript``
1554
+     *  - ``OC\Preview\SGI``
1555
+     *  - ``OC\Preview\StarOffice``
1556
+     *  - ``OC\Preview\SVG``
1557
+     *  - ``OC\Preview\TGA``
1558
+     *  - ``OC\Preview\TIFF``
1559
+     *
1560
+     * The following providers are disabled by default, because they provide an alternative to the built-in providers:
1561
+     *   - ``OC\Preview\Imaginary``
1562
+     *   - ``OC\Preview\ImaginaryPDF``
1563
+     *
1564
+     * Defaults to the following providers:
1565
+     *
1566
+     *  - ``OC\Preview\PNG``
1567
+     *  - ``OC\Preview\JPEG``
1568
+     *  - ``OC\Preview\GIF``
1569
+     *  - ``OC\Preview\BMP``
1570
+     *  - ``OC\Preview\XBitmap``
1571
+     *  - ``OC\Preview\Krita``
1572
+     *  - ``OC\Preview\WebP``
1573
+     *  - ``OC\Preview\MarkDown``
1574
+     *  - ``OC\Preview\TXT``
1575
+     *  - ``OC\Preview\OpenDocument``
1576
+     *
1577
+     */
1578
+    'enabledPreviewProviders' => [
1579
+        'OC\Preview\PNG',
1580
+        'OC\Preview\JPEG',
1581
+        'OC\Preview\GIF',
1582
+        'OC\Preview\BMP',
1583
+        'OC\Preview\XBitmap',
1584
+        'OC\Preview\Krita',
1585
+        'OC\Preview\WebP',
1586
+        'OC\Preview\MarkDown',
1587
+        'OC\Preview\TXT',
1588
+        'OC\Preview\OpenDocument',
1589
+    ],
1590
+
1591
+    /**
1592
+     * Maximum file size for metadata generation.
1593
+     * If a file exceeds this size, metadata generation will be skipped.
1594
+     *
1595
+     * NOTE: memory equivalent to this size will be used for metadata generation.
1596
+     *
1597
+     * Default: 256 megabytes.
1598
+     */
1599
+    'metadata_max_filesize' => 256,
1600
+
1601
+    /**
1602
+     * Maximum file size for file conversion.
1603
+     * If a file exceeds this size, the file will not be converted.
1604
+     *
1605
+     * Default: 100 MiB
1606
+     */
1607
+    'max_file_conversion_filesize' => 100,
1608
+
1609
+    /**
1610
+     * LDAP
1611
+     *
1612
+     * Global settings used by LDAP User and Group Backend
1613
+     */
1614
+
1615
+    /**
1616
+     * Defines the interval in minutes for the background job that checks user
1617
+     * existence and marks them as ready to be cleaned up. The number is always
1618
+     * minutes. Setting it to 0 disables the feature.
1619
+     * See command line (occ) methods ``ldap:show-remnants`` and ``user:delete``
1620
+     *
1621
+     * Defaults to ``51`` minutes
1622
+     */
1623
+    'ldapUserCleanupInterval' => 51,
1624
+
1625
+    /**
1626
+     * Sort groups in the user settings by name instead of the user count
1627
+     *
1628
+     * By enabling this, the user count beside the group name is disabled as well.
1629
+     *
1630
+     * @deprecated 29.0.0 Use the frontend instead or set the app config value ``group.sortBy`` for ``core`` to ``2``
1631
+     */
1632
+    'sort_groups_by_name' => false,
1633
+
1634
+    /**
1635
+     * Comments
1636
+     *
1637
+     * Global settings for the Comments infrastructure
1638
+     */
1639
+
1640
+    /**
1641
+     * Replaces the default Comments Manager Factory. This can be utilized if an
1642
+     * own or 3rd-party CommentsManager should be used that – for instance – uses the
1643
+     * filesystem instead of the database to keep the comments.
1644
+     *
1645
+     * Defaults to ``\OC\Comments\ManagerFactory``
1646
+     */
1647
+    'comments.managerFactory' => '\OC\Comments\ManagerFactory',
1648
+
1649
+    /**
1650
+     * Replaces the default System Tags Manager Factory. This can be utilized if an
1651
+     * own or 3rd-party SystemTagsManager should be used that – for instance – uses the
1652
+     * filesystem instead of the database to keep the tags.
1653
+     *
1654
+     * Defaults to ``\OC\SystemTag\ManagerFactory``
1655
+     */
1656
+    'systemtags.managerFactory' => '\OC\SystemTag\ManagerFactory',
1657
+
1658
+    /**
1659
+     * Maintenance
1660
+     *
1661
+     * These options are for halting user activity when you are performing server
1662
+     * maintenance.
1663
+     */
1664
+
1665
+    /**
1666
+     * Enable maintenance mode to disable Nextcloud
1667
+     *
1668
+     * If you want to prevent users from logging in to Nextcloud before you start
1669
+     * doing some maintenance work, you need to set the value of the maintenance
1670
+     * parameter to true. Please keep in mind that users who are already logged in
1671
+     * are kicked out of Nextcloud instantly.
1672
+     *
1673
+     * Defaults to ``false``
1674
+     */
1675
+    'maintenance' => false,
1676
+
1677
+    /**
1678
+     * UTC Hour for maintenance windows
1679
+     *
1680
+     * Some background jobs only run once a day. When an hour is defined for this config,
1681
+     * the background jobs which advertise themselves as not time sensitive will be
1682
+     * delayed during the "working" hours and only run in the 4 hours after the given time.
1683
+     * This is, e.g., used for activity expiration, suspicious login training, and update checks.
1684
+     *
1685
+     * A value of 1, e.g., will only run these background jobs between 01:00am UTC and 05:00am UTC.
1686
+     *
1687
+     * Defaults to ``100`` which disables the feature
1688
+     */
1689
+    'maintenance_window_start' => 1,
1690
+
1691
+    /**
1692
+     * Log all LDAP requests into a file
1693
+     *
1694
+     * Warning: This heavily decreases the performance of the server and is only
1695
+     * meant to debug/profile the LDAP interaction manually.
1696
+     * Also, it might log sensitive data into a plain text file.
1697
+     */
1698
+    'ldap_log_file' => '',
1699
+
1700
+    /**
1701
+     * SSL
1702
+     */
1703
+
1704
+    /**
1705
+     * Extra SSL options to be used for configuration.
1706
+     *
1707
+     * Defaults to an empty array.
1708
+     */
1709
+    'openssl' => [
1710
+        'config' => '/absolute/location/of/openssl.cnf',
1711
+    ],
1712
+
1713
+    /**
1714
+     * Memory caching backend configuration
1715
+     *
1716
+     * Available cache backends:
1717
+     *
1718
+     * * ``\OC\Memcache\APCu``       APC user backend
1719
+     * * ``\OC\Memcache\ArrayCache`` In-memory array-based backend (not recommended)
1720
+     * * ``\OC\Memcache\Memcached``  Memcached backend
1721
+     * * ``\OC\Memcache\Redis``      Redis backend
1722
+     *
1723
+     * Advice on choosing between the various backends:
1724
+     *
1725
+     * * APCu should be easiest to install. Almost all distributions have packages.
1726
+     *   Use this for single user environment for all caches.
1727
+     * * Use Redis or Memcached for distributed environments.
1728
+     *   For the local cache (you can configure two) take APCu.
1729
+     */
1730
+
1731
+    /**
1732
+     * Memory caching backend for locally stored data
1733
+     *
1734
+     * * Used for host-specific data, e.g., file paths
1735
+     *
1736
+     * Defaults to ``none``
1737
+     */
1738
+    'memcache.local' => '\\OC\\Memcache\\APCu',
1739
+
1740
+    /**
1741
+     * Memory caching backend for distributed data
1742
+     *
1743
+     * * Used for installation-specific data, e.g., database caching
1744
+     * * If unset, defaults to the value of memcache.local
1745
+     *
1746
+     * Defaults to ``none``
1747
+     */
1748
+    'memcache.distributed' => '\\OC\\Memcache\\Memcached',
1749
+
1750
+    /**
1751
+     * Connection details for Redis to use for memory caching in a single server configuration.
1752
+     *
1753
+     * For enhanced security, it is recommended to configure Redis
1754
+     * to require a password. See http://redis.io/topics/security
1755
+     * for more information.
1756
+     *
1757
+     * We also support Redis SSL/TLS encryption as of version 6.
1758
+     * See https://redis.io/topics/encryption for more information.
1759
+     */
1760
+    'redis' => [
1761
+        'host' => 'localhost', // can also be a Unix domain socket: '/tmp/redis.sock'
1762
+        'port' => 6379,
1763
+        'timeout' => 0.0,
1764
+        'read_timeout' => 0.0,
1765
+        'user' => '', // Optional: if not defined, no password will be used.
1766
+        'password' => '', // Optional: if not defined, no password will be used.
1767
+        'dbindex' => 0, // Optional: if undefined, SELECT will not run and will use Redis Server's default DB Index.
1768
+        // If Redis in-transit encryption is enabled, provide certificates
1769
+        // SSL context https://www.php.net/manual/en/context.ssl.php
1770
+        'ssl_context' => [
1771
+            'local_cert' => '/certs/redis.crt',
1772
+            'local_pk' => '/certs/redis.key',
1773
+            'cafile' => '/certs/ca.crt'
1774
+        ]
1775
+    ],
1776
+
1777
+    /**
1778
+     * Connection details for a Redis Cluster.
1779
+     *
1780
+     * Redis Cluster support requires the PHP module phpredis in version 3.0.0 or
1781
+     * higher.
1782
+     *
1783
+     * Available failover modes:
1784
+     *  - ``\RedisCluster::FAILOVER_NONE`` - only send commands to master nodes (default)
1785
+     *  - ``\RedisCluster::FAILOVER_ERROR`` - failover to slaves for read commands if master is unavailable (recommended)
1786
+     *  - ``\RedisCluster::FAILOVER_DISTRIBUTE`` - randomly distribute read commands across master and slaves
1787
+     *
1788
+     * WARNING: ``\RedisCluster::FAILOVER_DISTRIBUTE`` is a not recommended setting, and we strongly
1789
+     * suggest to not use it if you use Redis for file locking. Due to the way Redis
1790
+     * is synchronized, it could happen that the read for an existing lock is
1791
+     * scheduled to a slave that is not fully synchronized with the connected master
1792
+     * which then causes a FileLocked exception.
1793
+     *
1794
+     * See https://redis.io/topics/cluster-spec for details about the Redis cluster
1795
+     *
1796
+     * Authentication works with phpredis version 4.2.1+. See
1797
+     * https://github.com/phpredis/phpredis/commit/c5994f2a42b8a348af92d3acb4edff1328ad8ce1
1798
+     */
1799
+    'redis.cluster' => [
1800
+        'seeds' => [ // provide some or all of the cluster servers to bootstrap discovery, port required
1801
+            'localhost:7000',
1802
+            'localhost:7001',
1803
+        ],
1804
+        'timeout' => 0.0,
1805
+        'read_timeout' => 0.0,
1806
+        'failover_mode' => \RedisCluster::FAILOVER_ERROR,
1807
+        'user' => '', // Optional: if not defined, no password will be used.
1808
+        'password' => '', // Optional: if not defined, no password will be used.
1809
+        // If Redis in-transit encryption is enabled, provide certificates
1810
+        // SSL context https://www.php.net/manual/en/context.ssl.php
1811
+        'ssl_context' => [
1812
+            'local_cert' => '/certs/redis.crt',
1813
+            'local_pk' => '/certs/redis.key',
1814
+            'cafile' => '/certs/ca.crt'
1815
+        ]
1816
+    ],
1817
+
1818
+
1819
+    /**
1820
+     * Server details for one or more Memcached servers to use for memory caching.
1821
+     */
1822
+    'memcached_servers' => [
1823
+        // hostname, port and optional weight
1824
+        // or path and port 0 for Unix socket. Also see:
1825
+        // https://www.php.net/manual/en/memcached.addservers.php
1826
+        // https://www.php.net/manual/en/memcached.addserver.php
1827
+        ['localhost', 11211],
1828
+        //array('other.host.local', 11211),
1829
+    ],
1830
+
1831
+    /**
1832
+     * Connection options for Memcached
1833
+     */
1834
+    'memcached_options' => [
1835
+        // Set timeouts to 50ms
1836
+        \Memcached::OPT_CONNECT_TIMEOUT => 50,
1837
+        \Memcached::OPT_RETRY_TIMEOUT => 50,
1838
+        \Memcached::OPT_SEND_TIMEOUT => 50,
1839
+        \Memcached::OPT_RECV_TIMEOUT => 50,
1840
+        \Memcached::OPT_POLL_TIMEOUT => 50,
1841
+
1842
+        // Enable compression
1843
+        \Memcached::OPT_COMPRESSION => true,
1844
+
1845
+        // Turn on consistent hashing
1846
+        \Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
1847
+
1848
+        // Enable Binary Protocol
1849
+        \Memcached::OPT_BINARY_PROTOCOL => true,
1850
+
1851
+        // Binary serializer will be enabled if the igbinary PECL module is available
1852
+        //\Memcached::OPT_SERIALIZER => \Memcached::SERIALIZER_IGBINARY,
1853
+    ],
1854
+
1855
+
1856
+    /**
1857
+     * Location of the cache folder, defaults to ``data/$user/cache`` where
1858
+     * ``$user`` is the current user. When specified, the format will change to
1859
+     * ``$cache_path/$user`` where ``$cache_path`` is the configured cache directory
1860
+     * and ``$user`` is the user.
1861
+     *
1862
+     * Defaults to ``''`` (empty string)
1863
+     */
1864
+    'cache_path' => '',
1865
+
1866
+    /**
1867
+     * TTL of chunks located in the cache folder before they're removed by
1868
+     * garbage collection (in seconds). Increase this value if users have
1869
+     * issues uploading very large files via the Nextcloud Client as upload isn't
1870
+     * completed within one day.
1871
+     *
1872
+     * Defaults to ``60*60*24`` (1 day)
1873
+     */
1874
+    'cache_chunk_gc_ttl' => 60 * 60 * 24,
1875
+
1876
+    /**
1877
+     * Enable caching of the app config values.
1878
+     * If enabled the app config will be cached locally for a short TTL,
1879
+     * reducing database load significantly on larger setups.
1880
+     *
1881
+     * Defaults to ``true``
1882
+     */
1883
+    'cache_app_config' => true,
1884
+
1885
+    /**
1886
+     * Using Object Store with Nextcloud
1887
+     */
1888
+
1889
+    /**
1890
+     * This example shows how to configure Nextcloud to store all files in a
1891
+     * Swift object storage.
1892
+     *
1893
+     * It is important to note that Nextcloud in object store mode will expect
1894
+     * exclusive access to the object store container because it only stores the
1895
+     * binary data for each file. The metadata is currently kept in the local
1896
+     * database for performance reasons.
1897
+     *
1898
+     * WARNING: The current implementation is incompatible with any app that uses
1899
+     * direct file I/O and circumvents our virtual filesystem. That includes
1900
+     * Encryption and Gallery. Gallery will store thumbnails directly in the
1901
+     * filesystem, and encryption will cause severe overhead because key files need
1902
+     * to be fetched in addition to any requested file.
1903
+     *
1904
+     */
1905
+    'objectstore' => [
1906
+        'class' => 'OC\\Files\\ObjectStore\\Swift',
1907
+        'arguments' => [
1908
+            // trystack will use your Facebook ID as the username
1909
+            'username' => 'facebook100000123456789',
1910
+            // in the trystack dashboard, go to user -> settings -> API Password to
1911
+            // generate a password
1912
+            'password' => 'Secr3tPaSSWoRdt7',
1913
+            // must already exist in the objectstore, name can be different
1914
+            'container' => 'nextcloud',
1915
+            // prefix to prepend to the fileid, default is 'oid:urn:'
1916
+            'objectPrefix' => 'oid:urn:',
1917
+            // create the container if it does not exist. default is false
1918
+            'autocreate' => true,
1919
+            // required, dev-/trystack defaults to 'RegionOne'
1920
+            'region' => 'RegionOne',
1921
+            // The Identity / Keystone endpoint
1922
+            'url' => 'http://8.21.28.222:5000/v2.0',
1923
+            // uploadPartSize: size of the uploaded chunks, defaults to 524288000
1924
+            'uploadPartSize' => 524288000,
1925
+            // required on dev-/trystack
1926
+            'tenantName' => 'facebook100000123456789',
1927
+            // dev-/trystack uses swift by default, the lib defaults to 'cloudFiles'
1928
+            // if omitted
1929
+            'serviceName' => 'swift',
1930
+            // The Interface / URL Type, optional
1931
+            'urlType' => 'internal',
1932
+            // Maximum amount of data that can be uploaded
1933
+            'totalSizeLimit' => 1024 * 1024 * 1024,
1934
+        ],
1935
+    ],
1936
+
1937
+    /**
1938
+     * To use Swift V3
1939
+     */
1940
+    'objectstore' => [
1941
+        'class' => 'OC\\Files\\ObjectStore\\Swift',
1942
+        'arguments' => [
1943
+            'autocreate' => true,
1944
+            'user' => [
1945
+                'name' => 'swift',
1946
+                'password' => 'swift',
1947
+                'domain' => [
1948
+                    'name' => 'default',
1949
+                ],
1950
+            ],
1951
+            'scope' => [
1952
+                'project' => [
1953
+                    'name' => 'service',
1954
+                    'domain' => [
1955
+                        'name' => 'default',
1956
+                    ],
1957
+                ],
1958
+            ],
1959
+            'tenantName' => 'service',
1960
+            'serviceName' => 'swift',
1961
+            'region' => 'regionOne',
1962
+            'url' => 'http://yourswifthost:5000/v3',
1963
+            'bucket' => 'nextcloud',
1964
+        ],
1965
+    ],
1966
+
1967
+    /**
1968
+     * To use S3 object storage
1969
+     */
1970
+    'objectstore' => [
1971
+        'class' => 'OC\\Files\\ObjectStore\\S3',
1972
+        'arguments' => [
1973
+            'bucket' => 'nextcloud',
1974
+            'key' => 'your-access-key',
1975
+            'secret' => 'your-secret-key',
1976
+            'hostname' => 's3.example.com',
1977
+            'port' => 443,
1978
+            'use_ssl' => true,
1979
+            'region' => 'us-east-1',
1980
+            // optional: Maximum number of retry attempts for failed S3 requests
1981
+            // Default: 5
1982
+            'retriesMaxAttempts' => 5,
1983
+        ],
1984
+    ],
1985
+
1986
+    /**
1987
+     * If this is set to true and a multibucket object store is configured, then
1988
+     * newly created previews are put into 256 dedicated buckets.
1989
+     *
1990
+     * Those buckets are named like the multibucket version but with the postfix
1991
+     * ``-preview-NUMBER`` where NUMBER is between 0 and 255.
1992
+     *
1993
+     * Keep in mind that only previews of files are put in there that don't have
1994
+     * some already. Otherwise, the old bucket will be used.
1995
+     *
1996
+     * To migrate existing previews to this new multibucket distribution of previews,
1997
+     * use the occ command ``preview:repair``. For now, this will only migrate
1998
+     * previews that were generated before Nextcloud 19 in the flat
1999
+     * ``appdata_INSTANCEID/previews/FILEID`` folder structure.
2000
+     */
2001
+    'objectstore.multibucket.preview-distribution' => false,
2002
+
2003
+
2004
+    /**
2005
+     * Sharing
2006
+     *
2007
+     * Global settings for Sharing
2008
+     */
2009
+
2010
+    /**
2011
+     * Replaces the default Share Provider Factory. This can be utilized if
2012
+     * own or 3rd-party Share Providers are used that – for instance – use the
2013
+     * filesystem instead of the database to keep the share information.
2014
+     *
2015
+     * Defaults to ``\OC\Share20\ProviderFactory``
2016
+     */
2017
+    'sharing.managerFactory' => '\OC\Share20\ProviderFactory',
2018
+
2019
+    /**
2020
+     * Enables expiration for link share passwords sent by email (sharebymail).
2021
+     * The passwords will expire after the configured interval; the users can
2022
+     * still request a new one on the public link page.
2023
+     */
2024
+    'sharing.enable_mail_link_password_expiration' => false,
2025
+
2026
+    /**
2027
+     * Expiration interval for passwords, in seconds.
2028
+     */
2029
+    'sharing.mail_link_password_expiration_interval' => 3600,
2030
+
2031
+    /**
2032
+     * Define max number of results returned by the search for auto-completion of
2033
+     * users, groups, etc. The value must not be lower than 0 (for unlimited).
2034
+     *
2035
+     * If more, different sources are requested (e.g., different user backends; or
2036
+     * both users and groups), the value is applied per source and might not be
2037
+     * truncated after collecting the results. I.e., more results can appear than
2038
+     * configured here.
2039
+     *
2040
+     * Default is 25.
2041
+     */
2042
+    'sharing.maxAutocompleteResults' => 25,
2043
+
2044
+    /**
2045
+     * Define the minimum length of the search string before we start auto-completion
2046
+     * Default is no limit (value set to 0)
2047
+     */
2048
+    'sharing.minSearchStringLength' => 0,
2049
+
2050
+    /**
2051
+     * Set to true to enable that internal shares need to be accepted by the users by default.
2052
+     * Users can change this for their account in their personal sharing settings
2053
+     */
2054
+    'sharing.enable_share_accept' => false,
2055
+
2056
+    /**
2057
+     * Set to ``true`` to enforce that internal shares need to be accepted
2058
+     */
2059
+    'sharing.force_share_accept' => false,
2060
+
2061
+    /**
2062
+     * Set to ``false`` to prevent users from setting a custom share_folder
2063
+     */
2064
+    'sharing.allow_custom_share_folder' => true,
2065
+
2066
+    /**
2067
+     * Define a default folder for shared files and folders other than root.
2068
+     * Changes to this value will only have effect on new shares.
2069
+     *
2070
+     * Defaults to ``/``
2071
+     */
2072
+    'share_folder' => '/',
2073
+
2074
+    /**
2075
+     * Set to ``false`` to stop sending a mail when users receive a share
2076
+     */
2077
+    'sharing.enable_share_mail' => true,
2078
+
2079
+    /**
2080
+     * Set to true to enable the feature to add exceptions for share password enforcement
2081
+     */
2082
+    'sharing.allow_disabled_password_enforcement_groups' => false,
2083
+
2084
+    /**
2085
+     * Set to true to always transfer incoming shares by default
2086
+     * when running ``occ files:transfer-ownership``.
2087
+     * Defaults to ``false``, so incoming shares are not transferred if not specifically requested
2088
+     * by a command line argument.
2089
+     */
2090
+    'transferIncomingShares' => false,
2091
+
2092
+    /**
2093
+     * Federated Cloud Sharing
2094
+     */
2095
+
2096
+    /**
2097
+     * Allow self-signed certificates for federated shares
2098
+     */
2099
+    'sharing.federation.allowSelfSignedCertificates' => false,
2100
+
2101
+    /**
2102
+     * Hashing
2103
+     */
2104
+
2105
+    /**
2106
+     * By default, Nextcloud will use the Argon2 password hashing if available.
2107
+     * However, if for whatever reason you want to stick with the PASSWORD_DEFAULT
2108
+     * of your PHP version, then set the setting to true.
2109
+     *
2110
+     * Nextcloud uses the Argon2 algorithm (with PHP >= 7.2) to create hashes by its
2111
+     * own and exposes its configuration options as following. More information can
2112
+     * be found at: https://www.php.net/manual/en/function.password-hash.php
2113
+     */
2114
+    'hashing_default_password' => false,
2115
+
2116
+    /**
2117
+     * The number of CPU threads to be used by the algorithm for computing a hash.
2118
+     * The value must be an integer, and the minimum value is ``1``. Rationally, it does
2119
+     * not help to provide a number higher than the available threads on the machine.
2120
+     * Values that undershoot the minimum will be ignored in favor of the minimum.
2121
+     */
2122
+    'hashingThreads' => PASSWORD_ARGON2_DEFAULT_THREADS,
2123
+
2124
+    /**
2125
+     * The memory in KiB to be used by the algorithm for computing a hash. The value
2126
+     * must be an integer, and the minimum value is 8 times the number of CPU threads.
2127
+     * Values that undershoot the minimum will be ignored in favor of the minimum.
2128
+     */
2129
+    'hashingMemoryCost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST,
2130
+
2131
+    /**
2132
+     * The number of iterations that are used by the algorithm for computing a hash.
2133
+     * The value must be an integer, and the minimum value is ``1``. Values that
2134
+     * undershoot the minimum will be ignored in favor of the minimum.
2135
+     */
2136
+    'hashingTimeCost' => PASSWORD_ARGON2_DEFAULT_TIME_COST,
2137
+
2138
+    /**
2139
+     * The hashing cost used by hashes generated by Nextcloud
2140
+     * Using a higher value requires more time and CPU power to calculate the hashes
2141
+     */
2142
+    'hashingCost' => 10,
2143
+
2144
+    /**
2145
+     * All other configuration options
2146
+     */
2147
+
2148
+    /**
2149
+     * Additional driver options for the database connection, e.g., to enable SSL
2150
+     * encryption in MySQL or specify a custom wait timeout on a cheap hoster.
2151
+     *
2152
+     * When setting up TLS/SSL for encrypting the connections, you need to ensure that
2153
+     * the passed keys and certificates are readable by the PHP process. In addition,
2154
+     * ``PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT`` might need to be set to false, if the
2155
+     * database server's certificate CN does not match with the hostname used to connect.
2156
+     * The standard behavior here is different from the MySQL/MariaDB CLI client, which
2157
+     * does not verify the server cert except ``--ssl-verify-server-cert`` is passed manually.
2158
+     */
2159
+    'dbdriveroptions' => [
2160
+        PDO::MYSQL_ATTR_SSL_CA => '/file/path/to/ca_cert.pem',
2161
+        PDO::MYSQL_ATTR_SSL_KEY => '/file/path/to/mysql-client-key.pem',
2162
+        PDO::MYSQL_ATTR_SSL_CERT => '/file/path/to/mysql-client-cert.pem',
2163
+        PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false,
2164
+        PDO::MYSQL_ATTR_INIT_COMMAND => 'SET wait_timeout = 28800'
2165
+    ],
2166
+
2167
+    /**
2168
+     * SQLite3 journal mode can be specified using this configuration parameter -
2169
+     * can be ``'WAL'`` or ``'DELETE'``. See https://www.sqlite.org/wal.html for more details.
2170
+     */
2171
+    'sqlite.journal_mode' => 'DELETE',
2172
+
2173
+    /**
2174
+     * During setup, if requirements are met (see below), this setting is set to true
2175
+     * to enable MySQL to handle 4-byte characters instead of 3-byte characters.
2176
+     *
2177
+     * To convert an existing 3-byte setup to a 4-byte setup, configure the MySQL
2178
+     * parameters as described below and run the migration command:
2179
+     * ``./occ db:convert-mysql-charset``
2180
+     * This config setting will be automatically updated after a successful migration.
2181
+     *
2182
+     * Refer to the documentation for more details.
2183
+     *
2184
+     * MySQL requires specific settings for longer indexes (> 767 bytes), which are
2185
+     * necessary for 4-byte character support::
2186
+     *
2187
+     *     [mysqld]
2188
+     *     innodb_large_prefix=ON
2189
+     *     innodb_file_format=Barracuda
2190
+     *     innodb_file_per_table=ON
2191
+     *
2192
+     * Tables will be created with:
2193
+     *  * character set: ``utf8mb4``
2194
+     *  * collation:     ``utf8mb4_bin``
2195
+     *  * row_format:    ``dynamic``
2196
+     *
2197
+     * See:
2198
+     *  * https://dev.mysql.com/doc/refman/5.7/en/charset-unicode-utf8mb4.html
2199
+     *  * https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix
2200
+     *  * https://mariadb.com/kb/en/mariadb/xtradbinnodb-server-system-variables/#innodb_large_prefix
2201
+     *  * http://www.tocker.ca/2013/10/31/benchmarking-innodb-page-compression-performance.html
2202
+     *  * http://mechanics.flite.com/blog/2014/07/29/using-innodb-large-prefix-to-avoid-error-1071/
2203
+     */
2204
+    'mysql.utf8mb4' => false,
2205
+
2206
+    /**
2207
+     * For search queries in the database, a default collation is chosen based on the
2208
+     * character set. In some cases, a different collation is desired, such as for
2209
+     * accent-sensitive searches.
2210
+     *
2211
+     * MariaDB and MySQL share some collations, but also have incompatible ones,
2212
+     * depending on the database server version.
2213
+     *
2214
+     * This option allows overriding the automatic collation choice. Example::
2215
+     *
2216
+     *     'mysql.collation' => 'utf8mb4_0900_as_ci',
2217
+     *
2218
+     * This setting does not affect table creation or setup, where utf8[mb4]_bin is
2219
+     * always used. It applies only to SQL queries using LIKE comparison operators.
2220
+     */
2221
+    'mysql.collation' => null,
2222
+
2223
+    /**
2224
+     * PostgreSQL SSL connection
2225
+     */
2226
+    'pgsql_ssl' => [
2227
+        'mode' => '',
2228
+        'cert' => '',
2229
+        'rootcert' => '',
2230
+        'key' => '',
2231
+        'crl' => '',
2232
+    ],
2233
+
2234
+    /**
2235
+     * Database types supported for installation.
2236
+     *
2237
+     * Available:
2238
+     *  - sqlite (SQLite3)
2239
+     *  - mysql (MySQL)
2240
+     *  - pgsql (PostgreSQL)
2241
+     *  - oci (Oracle)
2242
+     *
2243
+     * Defaults to:
2244
+     *  - sqlite (SQLite3)
2245
+     *  - mysql (MySQL)
2246
+     *  - pgsql (PostgreSQL)
2247
+     */
2248
+    'supportedDatabases' => [
2249
+        'sqlite',
2250
+        'mysql',
2251
+        'pgsql',
2252
+        'oci',
2253
+    ],
2254
+
2255
+    /**
2256
+     * Override the location where Nextcloud stores temporary files. Useful in setups
2257
+     * where the system temporary directory is on a limited-space ramdisk, restricted,
2258
+     * or when using external storage that does not support streaming.
2259
+     *
2260
+     * The web server user/PHP must have write access to this directory. Ensure that
2261
+     * PHP configuration recognizes this as a valid temporary directory by setting
2262
+     * the TMP, TMPDIR, and TEMP environment variables accordingly. Additional
2263
+     * permissions may be required for AppArmor or SELinux.
2264
+     */
2265
+    'tempdirectory' => '/tmp/nextcloudtemp',
2266
+
2267
+    /**
2268
+     * Override the location where Nextcloud stores update files during updates.
2269
+     * Useful when the default ``datadirectory`` is on a network disk like NFS or is
2270
+     * otherwise restricted. Defaults to the value of ``datadirectory`` if unset.
2271
+     *
2272
+     * If set, the directory must be located outside the Nextcloud installation
2273
+     * directory and writable by the web server user.
2274
+     */
2275
+    'updatedirectory' => '',
2276
+
2277
+    /**
2278
+     * Block specific files or filenames, disallowing uploads or access (read and write).
2279
+     * ``.htaccess`` is blocked by default.
2280
+     *
2281
+     * WARNING: Use this only if you understand the implications.
2282
+     *
2283
+     * NOTE: This list is case-insensitive.
2284
+     *
2285
+     * Defaults to ``['.htaccess']``
2286
+     */
2287
+    'forbidden_filenames' => ['.htaccess'],
2288
+
2289
+    /**
2290
+     * Disallow uploads of files with specific basenames. Matching existing files
2291
+     * cannot be updated, and no new files can be created in matching folders.
2292
+     *
2293
+     * The basename is the filename without the extension, e.g., for "archive.tar.gz",
2294
+     * the basename is "archive".
2295
+     *
2296
+     * NOTE: This list is case-insensitive.
2297
+     *
2298
+     * Defaults to ``[]`` (empty array)
2299
+     */
2300
+    'forbidden_filename_basenames' => [],
2301
+
2302
+    /**
2303
+     * Block specific characters in filenames. Useful for filesystems or operating
2304
+     * systems (e.g., Windows) that do not support certain characters. Matching
2305
+     * existing files cannot be updated, and no new files can be created in matching
2306
+     * folders.
2307
+     *
2308
+     * The ``/`` and ``\`` characters, as well as ASCII characters [0-31], are always
2309
+     * forbidden.
2310
+     *
2311
+     * Example for Windows: ``['?', '<', '>', ':', '*', '|', '"']``
2312
+     * See: https://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits
2313
+     *
2314
+     * Defaults to ``[]`` (empty array)
2315
+     */
2316
+    'forbidden_filename_characters' => [],
2317
+
2318
+    /**
2319
+     * Deny specific file extensions. Matching existing files cannot be updated, and
2320
+     * no new files can be created in matching folders.
2321
+     *
2322
+     * The ``'.part'`` extension is always forbidden, as it is used internally by Nextcloud.
2323
+     *
2324
+     * Defaults to ``['.filepart', '.part']``
2325
+     */
2326
+    'forbidden_filename_extensions' => ['.part', '.filepart'],
2327
+
2328
+    /**
2329
+     * Specify the name of a theme to apply to Nextcloud. Themes are located in
2330
+     * ``nextcloud/themes/`` by default.
2331
+     *
2332
+     * Defaults to the theming app, included since Nextcloud 9.
2333
+     */
2334
+    'theme' => '',
2335
+
2336
+    /**
2337
+     * Enforce a specific user theme, disabling user theming settings. Must be a
2338
+     * valid ITheme ID, e.g., ``dark``, ``dark-highcontrast``, ``default``, ``light``,
2339
+     * ``light-highcontrast``, ``opendyslexic``.
2340
+     */
2341
+    'enforce_theme' => '',
2342
+
2343
+    /**
2344
+     * Enable or disable Progressive Web App (PWA) functionality, which allows
2345
+     * browsers to open web applications in dedicated windows.
2346
+     *
2347
+     * Defaults to ``true``
2348
+     */
2349
+    'theming.standalone_window.enabled' => true,
2350
+
2351
+    /**
2352
+     * Specify the default cipher for encrypting files. Supported ciphers:
2353
+     *  - AES-256-CTR
2354
+     *  - AES-128-CTR
2355
+     *  - AES-256-CFB
2356
+     *  - AES-128-CFB
2357
+     *
2358
+     * Defaults to ``AES-256-CTR``
2359
+     */
2360
+    'cipher' => 'AES-256-CTR',
2361
+
2362
+    /**
2363
+     * Use the legacy base64 format for encrypted files instead of the more
2364
+     * space-efficient binary format. This affects only newly written files; existing
2365
+     * encrypted files remain readable regardless of the format.
2366
+     *
2367
+     * Defaults to ``false``
2368
+     */
2369
+    'encryption.use_legacy_base64_encoding' => false,
2370
+
2371
+    /**
2372
+     * Specify the minimum Nextcloud desktop client version allowed to sync with this
2373
+     * server. Connections from earlier clients will be denied. Defaults to the
2374
+     * minimum officially supported version at the time of this server release.
2375
+     *
2376
+     * Changing this may cause older, unsupported clients to malfunction, potentially
2377
+     * leading to data loss or unexpected behavior.
2378
+     *
2379
+     * Defaults to ``3.1.0``
2380
+     */
2381
+    'minimum.supported.desktop.version' => '3.1.0',
2382
+
2383
+    /**
2384
+     * Specify the maximum Nextcloud desktop client version allowed to sync with this
2385
+     * server. Connections from later clients will be denied.
2386
+     *
2387
+     * Defaults to ``99.99.99``
2388
+     */
2389
+    'maximum.supported.desktop.version' => '99.99.99',
2390
+
2391
+    /**
2392
+     * Allow local storage to contain symlinks.
2393
+     * WARNING: Not recommended, as this allows Nextcloud to access files outside the
2394
+     * data directory, posing a potential security risk.
2395
+     *
2396
+     * Defaults to ``false``
2397
+     */
2398
+    'localstorage.allowsymlinks' => false,
2399
+
2400
+    /**
2401
+     * Nextcloud overrides umask to ensure suitable access permissions regardless of
2402
+     * web server or PHP-FPM configuration. Modifying this value has security
2403
+     * implications and may cause issues with the installation.
2404
+     *
2405
+     * Most installations should not modify this value.
2406
+     *
2407
+     * Defaults to ``0022``
2408
+     */
2409
+    'localstorage.umask' => 0022,
2410
+
2411
+    /**
2412
+     * Allow storage systems that do not support modifying existing files to overcome
2413
+     * this limitation by removing files before overwriting.
2414
+     *
2415
+     * Defaults to ``false``
2416
+     */
2417
+    'localstorage.unlink_on_truncate' => false,
2418
+
2419
+    /**
2420
+     * EXPERIMENTAL: Include external storage in quota calculations.
2421
+     *
2422
+     * Defaults to ``false``
2423
+     */
2424
+    'quota_include_external_storage' => false,
2425
+
2426
+    /**
2427
+     * When an external storage is unavailable (e.g., due to failed authentication),
2428
+     * it is flagged as such for a specified duration. For authentication failures,
2429
+     * this delay can be customized to reduce the likelihood of account lockouts in
2430
+     * systems like Active Directory.
2431
+     *
2432
+     * Defaults to ``1800`` seconds (30 minutes)
2433
+     */
2434
+    'external_storage.auth_availability_delay' => 1800,
2435
+
2436
+    /**
2437
+     * Allow creation of external storages of type "Local" via the web interface and
2438
+     * APIs. When disabled, local storages can still be created using the occ command::
2439
+     *
2440
+     *      occ files_external:create /mountpoint local null::null -c datadir=/path/to/data
2441
+     *
2442
+     * Defaults to ``true``
2443
+     */
2444
+    'files_external_allow_create_new_local' => true,
2445
+
2446
+    /**
2447
+     * Specify how often the local filesystem (Nextcloud data/ directory and NFS
2448
+     * mounts in data/) is checked for changes made outside Nextcloud. This does not
2449
+     * apply to external storage.
2450
+     *
2451
+     * - ``0`` -> Never check the filesystem for outside changes, improving performance when no external changes are expected.
2452
+     * - ``1`` -> Check each file or folder at most once per request, recommended for general use if outside changes are possible.
2453
+     *
2454
+     * Defaults to ``0``
2455
+     */
2456
+    'filesystem_check_changes' => 0,
2457
+
2458
+    /**
2459
+     * Store part files created during upload in the same storage as the upload
2460
+     * target. Setting this to false stores part files in the root of the user's
2461
+     * folder, which may be necessary for external storage with limited rename
2462
+     * capabilities.
2463
+     *
2464
+     * Defaults to ``true``
2465
+     */
2466
+    'part_file_in_storage' => true,
2467
+
2468
+    /**
2469
+     * Specify the location of the ``mount.json`` file.
2470
+     *
2471
+     * Defaults to ``data/mount.json`` in the Nextcloud directory.
2472
+     */
2473
+    'mount_file' => '/var/www/nextcloud/data/mount.json',
2474
+
2475
+    /**
2476
+     * Prevent Nextcloud from updating the cache due to filesystem changes for all
2477
+     * storage.
2478
+     *
2479
+     * Defaults to ``false``
2480
+     */
2481
+    'filesystem_cache_readonly' => false,
2482
+
2483
+    /**
2484
+     * List of trusted proxy servers. Supported formats:
2485
+     *
2486
+     * - IPv4 addresses, e.g., ``192.168.2.123``
2487
+     * - IPv4 ranges in CIDR notation, e.g., ``192.168.2.0/24``
2488
+     * - IPv6 addresses, e.g., ``fd9e:21a7:a92c:2323::1``
2489
+     * - IPv6 ranges in CIDR notation, e.g., ``2001:db8:85a3:8d3:1319:8a20::/95``
2490
+     *
2491
+     * If a request's ``REMOTE_ADDR`` matches an address here, it is treated as a proxy,
2492
+     * and the client IP is read from the HTTP header specified in
2493
+     * ``forwarded_for_headers`` instead of ``REMOTE_ADDR``.
2494
+     *
2495
+     * Ensure ``forwarded_for_headers`` is configured if ``trusted_proxies`` is set.
2496
+     *
2497
+     * Defaults to ``[]`` (empty array)
2498
+     */
2499
+    'trusted_proxies' => ['203.0.113.45', '198.51.100.128', '192.168.2.0/24'],
2500
+
2501
+    /**
2502
+     * Headers trusted as containing the client IP address when used with
2503
+     * ``trusted_proxies``. For example, use ``HTTP_X_FORWARDED_FOR`` for the
2504
+     * ``X-Forwarded-For`` header.
2505
+     *
2506
+     * Incorrect configuration allows clients to spoof their IP address, bypassing
2507
+     * access controls and rendering logs unreliable.
2508
+     *
2509
+     * Defaults to ``['HTTP_X_FORWARDED_FOR']``
2510
+     */
2511
+    'forwarded_for_headers' => ['HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR'],
2512
+
2513
+    /**
2514
+     * List of trusted IP ranges for admin actions. If non-empty, all admin actions
2515
+     * must originate from IPs within these ranges.
2516
+     *
2517
+     * Supported formats:
2518
+     * - IPv4 addresses or ranges, e.g., ``192.0.2.42/32``, ``233.252.0.0/24``
2519
+     * - IPv6 addresses or ranges, e.g., ``2001:db8::13:37/64``
2520
+     *
2521
+     * Defaults to ``[]`` (empty array)
2522
+     */
2523
+    'allowed_admin_ranges' => ['192.0.2.42/32', '233.252.0.0/24', '2001:db8::13:37/64'],
2524
+
2525
+    /**
2526
+     * Maximum file size (in megabytes) for animating GIFs on public sharing pages.
2527
+     * If a GIF exceeds this size, a static preview is shown.
2528
+     *
2529
+     * Set to ``-1`` for no limit.
2530
+     *
2531
+     * Defaults to ``10`` megabytes
2532
+     */
2533
+    'max_filesize_animated_gifs_public_sharing' => 10,
2534
+
2535
+    /**
2536
+     * Set the lock's time-to-live (TTL) in seconds. Locks older than this are
2537
+     * automatically cleaned up.
2538
+     *
2539
+     * Defaults to ``3600`` seconds (1 hour) or the PHP ``max_execution_time``,
2540
+     * whichever is higher.
2541
+     */
2542
+    'filelocking.ttl' => 60 * 60,
2543
+
2544
+    /**
2545
+     * Memory caching backend for file locking. Redis is highly recommended to avoid
2546
+     * data loss, as many memcache backends may evict values unexpectedly.
2547
+     *
2548
+     * Defaults to ``none``
2549
+     */
2550
+    'memcache.locking' => '\\OC\\Memcache\\Redis',
2551
+
2552
+    /**
2553
+     * Enable debug logging for file locking. This can generate a large volume of log
2554
+     * entries, potentially causing performance degradation and large log files on
2555
+     * busy instances.
2556
+     *
2557
+     * Use with ``log.condition`` to limit logging in production environments.
2558
+     *
2559
+     * Defaults to ``false``
2560
+     */
2561
+    'filelocking.debug' => false,
2562
+
2563
+    /**
2564
+     * Disable the web-based updater.
2565
+     *
2566
+     * Defaults to ``false``
2567
+     */
2568
+    'upgrade.disable-web' => false,
2569
+
2570
+    /**
2571
+     * Customize the CLI upgrade documentation link.
2572
+     */
2573
+    'upgrade.cli-upgrade-link' => '',
2574
+
2575
+    /**
2576
+     * Additional line(s) (string or array of strings)
2577
+     * that will be added to .user.ini on each update by the updater.
2578
+     *
2579
+     * Defaults to ``''`` (empty string)
2580
+     */
2581
+    'user_ini_additional_lines' => '',
2582
+
2583
+    /**
2584
+     * Customize the server logs documentation link for exception handling.
2585
+     */
2586
+    'documentation_url.server_logs' => '',
2587
+
2588
+    /**
2589
+     * Enable debugging mode for Nextcloud. Only use for local development, not in
2590
+     * production, as it disables minification and outputs additional debug
2591
+     * information.
2592
+     *
2593
+     * Defaults to ``false``
2594
+     */
2595
+    'debug' => false,
2596
+
2597
+    /**
2598
+     * Set the data fingerprint for the current data served. Used by clients to
2599
+     * detect if a backup has been restored. Update this by running::
2600
+     *
2601
+     *      occ maintenance:data-fingerprint
2602
+     *
2603
+     * Changing or deleting this value may cause connected clients to stall until
2604
+     * conflicts are resolved.
2605
+     *
2606
+     * Defaults to ``''`` (empty string)
2607
+     */
2608
+    'data-fingerprint' => '',
2609
+
2610
+    /**
2611
+     * This entry serves as a warning if the sample configuration was copied.
2612
+     * DO NOT ADD THIS TO YOUR CONFIGURATION!
2613
+     *
2614
+     * Ensure all settings are modified only after consulting the documentation.
2615
+     */
2616
+    'copied_sample_config' => true,
2617
+
2618
+    /**
2619
+     * Use a custom lookup server to publish user data.
2620
+     *
2621
+     * Defaults to ``https://lookup.nextcloud.com``
2622
+     */
2623
+    'lookup_server' => 'https://lookup.nextcloud.com',
2624
+
2625
+    /**
2626
+     * Enable Nextcloud's Global Scale architecture.
2627
+     *
2628
+     * Defaults to ``false``
2629
+     */
2630
+    'gs.enabled' => false,
2631
+
2632
+    /**
2633
+     * Configure federation for Global Scale setups. Set to ``global`` to allow
2634
+     * federation outside the environment.
2635
+     *
2636
+     * Defaults to ``internal``
2637
+     */
2638
+    'gs.federation' => 'internal',
2639
+
2640
+    /**
2641
+     * List of user agents exempt from SameSite cookie protection due to non-standard
2642
+     * HTTP behavior.
2643
+     *
2644
+     * WARNING: Use only if you understand the implications.
2645
+     *
2646
+     * Defaults to:
2647
+     *
2648
+     * - ``/^WebDAVFS/`` (OS X Finder)
2649
+     * - ``/^Microsoft-WebDAV-MiniRedir/`` (Windows WebDAV drive)
2650
+     */
2651
+    'csrf.optout' => [
2652
+        '/^WebDAVFS/', // OS X Finder
2653
+        '/^Microsoft-WebDAV-MiniRedir/', // Windows WebDAV drive
2654
+    ],
2655
+
2656
+    /**
2657
+     * Specify allowed user agents for Login Flow V2 using regular expressions.
2658
+     * User agents not matching this list are denied access to Login Flow V2.
2659
+     *
2660
+     * WARNING: Use only if you understand the implications.
2661
+     *
2662
+     * Example: Allow only the Nextcloud Android app::
2663
+     *
2664
+     *    'core.login_flow_v2.allowed_user_agents' => ['/Nextcloud-android/i'],
2665
+     *
2666
+     * Defaults to ``[]`` (empty array)
2667
+     */
2668
+    'core.login_flow_v2.allowed_user_agents' => [],
2669
+
2670
+    /**
2671
+     * Show or hide the "simple sign up" link on public pages.
2672
+     * See: https://nextcloud.com/signup/
2673
+     *
2674
+     * Defaults to ``true``
2675
+     */
2676
+    'simpleSignUpLink.shown' => true,
2677
+
2678
+    /**
2679
+     * Enable or disable autocompletion for the login form. Disabling this prevents
2680
+     * browsers from remembering login credentials, which may be required for
2681
+     * compliance with certain security policies.
2682
+     *
2683
+     * Defaults to ``true``
2684
+     */
2685
+    'login_form_autocomplete' => true,
2686
+
2687
+    /**
2688
+     * Set a timeout (in seconds) for the login form. After this period, the form is
2689
+     * reset to prevent password leaks on public devices if the user forgets to clear
2690
+     * it.
2691
+     *
2692
+     * A value of 0 disables the timeout.
2693
+     *
2694
+     * Defaults to ``300`` seconds (5 minutes)
2695
+     */
2696
+    'login_form_timeout' => 300,
2697
+
2698
+    /**
2699
+     * Suppress warnings for outdated or unsupported browsers. When enabled, users
2700
+     * can bypass the warning after reading it.
2701
+     *
2702
+     * Set to ``true`` to disable the warning.
2703
+     *
2704
+     * Defaults to ``false``
2705
+     */
2706
+    'no_unsupported_browser_warning' => false,
2707
+
2708
+    /**
2709
+     * Disable background scanning of files. When enabled, a background job runs
2710
+     * every 10 minutes to sync the filesystem and database for up to 500 users with
2711
+     * unscanned files (size < 0 in filecache).
2712
+     *
2713
+     * Defaults to ``false``
2714
+     */
2715
+    'files_no_background_scan' => false,
2716
+
2717
+    /**
2718
+     * Log all database queries to a file.
2719
+     *
2720
+     * WARNING: This significantly reduces server performance and is intended only
2721
+     * for debugging or profiling query interactions. Sensitive data may be logged in
2722
+     * plain text.
2723
+     */
2724
+    'query_log_file' => '',
2725
+
2726
+    /**
2727
+     * Prefix all queries with the request ID when set to `yes`.
2728
+     *
2729
+     * Requires ``query_log_file`` to be set.
2730
+     */
2731
+    'query_log_file_requestid' => '',
2732
+
2733
+    /**
2734
+     * Include all query parameters in the query log when set to `yes`.
2735
+     *
2736
+     * Requires ``query_log_file`` to be set.
2737
+     * WARNING: This may log sensitive data in plain text.
2738
+     */
2739
+    'query_log_file_parameters' => '',
2740
+
2741
+    /**
2742
+     * Include a backtrace in the query log when set to `yes`.
2743
+     *
2744
+     * Requires ``query_log_file`` to be set.
2745
+     */
2746
+    'query_log_file_backtrace' => '',
2747
+
2748
+    /**
2749
+     * Log all Redis requests to a file.
2750
+     *
2751
+     * WARNING: This significantly reduces server performance and is intended only
2752
+     * for debugging or profiling Redis interactions. Sensitive data may be logged in
2753
+     * plain text.
2754
+     */
2755
+    'redis_log_file' => '',
2756
+
2757
+    /**
2758
+     * Enable diagnostics event logging. Logs timings of common execution steps at
2759
+     * debug level. Use with ``log.condition`` to enable conditionally in production.
2760
+     *
2761
+     * Defaults to ``true``
2762
+     */
2763
+    'diagnostics.logging' => true,
2764
+
2765
+    /**
2766
+     * Limit diagnostics event logging to events longer than the specified threshold
2767
+     * (in milliseconds). A value of 0 disables diagnostics event logging.
2768
+     */
2769
+    'diagnostics.logging.threshold' => 0,
2770
+
2771
+    /**
2772
+     * Enable profiling globally.
2773
+     *
2774
+     * Defaults to ``true``
2775
+     */
2776
+    'profile.enabled' => true,
2777
+
2778
+    /**
2779
+     * Override default scopes for account data. Valid properties and scopes are
2780
+     * defined in ``OCP\Accounts\IAccountManager``. Values are merged with defaults
2781
+     * from ``OC\Accounts\AccountManager``.
2782
+     *
2783
+     * Example: Set phone property to private scope:
2784
+     * ``[\OCP\Accounts\IAccountManager::PROPERTY_PHONE => \OCP\Accounts\IAccountManager::SCOPE_PRIVATE]``
2785
+     */
2786
+    'account_manager.default_property_scope' => [],
2787
+
2788
+    /**
2789
+     * Enable the deprecated Projects feature, superseded by Related Resources since
2790
+     * Nextcloud 25.
2791
+     *
2792
+     * Defaults to ``false``
2793
+     */
2794
+    'projects.enabled' => false,
2795
+
2796
+    /**
2797
+     * Enable the bulk upload feature.
2798
+     *
2799
+     * Defaults to ``true``
2800
+     */
2801
+    'bulkupload.enabled' => true,
2802
+
2803
+    /**
2804
+     * Enable fetching Open Graph metadata from remote URLs.
2805
+     *
2806
+     * Defaults to ``true``
2807
+     */
2808
+    'reference_opengraph' => true,
2809
+
2810
+    /**
2811
+     * Enable the legacy unified search.
2812
+     *
2813
+     * Defaults to ``false``
2814
+     */
2815
+    'unified_search.enabled' => false,
2816
+
2817
+    /**
2818
+     * Enable features that do not yet comply with accessibility standards.
2819
+     *
2820
+     * Defaults to ``true``
2821
+     */
2822
+    'enable_non-accessible_features' => true,
2823
+
2824
+    /**
2825
+     * Directories where Nextcloud searches for external binaries (e.g., LibreOffice,
2826
+     * sendmail, ffmpeg).
2827
+     *
2828
+     * Defaults to:
2829
+     * - /usr/local/sbin
2830
+     * - /usr/local/bin
2831
+     * - /usr/sbin
2832
+     * - /usr/bin
2833
+     * - /sbin
2834
+     * - /bin
2835
+     * - /opt/bin
2836
+     */
2837
+    'binary_search_paths' => [
2838
+        '/usr/local/sbin',
2839
+        '/usr/local/bin',
2840
+        '/usr/sbin',
2841
+        '/usr/bin',
2842
+        '/sbin',
2843
+        '/bin',
2844
+        '/opt/bin',
2845
+    ],
2846
+
2847
+    /**
2848
+     * Maximum chunk size for chunked uploads (in bytes). Larger chunks increase
2849
+     * throughput but yield diminishing returns above 100 MiB. Services like
2850
+     * Cloudflare may limit to 100 MiB.
2851
+     *
2852
+     * Defaults to ``100 * 1024 * 1024`` (100 MiB)
2853
+     */
2854
+    'files.chunked_upload.max_size' => 100 * 1024 * 1024,
2855
+
2856
+    /**
2857
+     * Maximum number of chunks uploaded in parallel during chunked uploads. Higher
2858
+     * counts increase throughput but consume more server resources, with diminishing
2859
+     * returns.
2860
+     *
2861
+     * Defaults to ``5``
2862
+     */
2863
+    'files.chunked_upload.max_parallel_count' => 5,
2864
+
2865
+    /**
2866
+     * Allow users to manually delete files from their trashbin. Automated deletions
2867
+     * (e.g., due to low quota) are unaffected.
2868
+     *
2869
+     * Defaults to ``true``
2870
+     */
2871
+    'files.trash.delete' => true,
2872
+
2873
+    /**
2874
+     * Enable PHP 8.4 lazy objects for Dependency Injection to improve performance by
2875
+     * avoiding instantiation of unused objects.
2876
+     *
2877
+     * Defaults to ``true``
2878
+     */
2879
+    'enable_lazy_objects' => true,
2880
+
2881
+    /**
2882
+     * Change the default certificates bundle used for trusting certificates.
2883
+     *
2884
+     * Nextcloud ships its own up-to-date certificates bundle, but in certain cases admins may wish to specify a different bundle, for example the one shipped by their distro.
2885
+     *
2886
+     * Defaults to `\OC::$SERVERROOT . '/resources/config/ca-bundle.crt'`.
2887
+     */
2888
+    'default_certificates_bundle_path' => \OC::$SERVERROOT . '/resources/config/ca-bundle.crt',
2889 2889
 ];
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -1797,7 +1797,7 @@  discard block
 block discarded – undo
1797 1797
 	 * https://github.com/phpredis/phpredis/commit/c5994f2a42b8a348af92d3acb4edff1328ad8ce1
1798 1798
 	 */
1799 1799
 	'redis.cluster' => [
1800
-		'seeds' => [ // provide some or all of the cluster servers to bootstrap discovery, port required
1800
+		'seeds' => [// provide some or all of the cluster servers to bootstrap discovery, port required
1801 1801
 			'localhost:7000',
1802 1802
 			'localhost:7001',
1803 1803
 		],
@@ -2885,5 +2885,5 @@  discard block
 block discarded – undo
2885 2885
 	 *
2886 2886
 	 * Defaults to `\OC::$SERVERROOT . '/resources/config/ca-bundle.crt'`.
2887 2887
 	 */
2888
-	'default_certificates_bundle_path' => \OC::$SERVERROOT . '/resources/config/ca-bundle.crt',
2888
+	'default_certificates_bundle_path' => \OC::$SERVERROOT.'/resources/config/ca-bundle.crt',
2889 2889
 ];
Please login to merge, or discard this patch.
tests/lib/Http/Client/ClientTest.php 2 patches
Indentation   +584 added lines, -584 removed lines patch added patch discarded remove patch
@@ -26,588 +26,588 @@
 block discarded – undo
26 26
  * Class ClientTest
27 27
  */
28 28
 class ClientTest extends \Test\TestCase {
29
-	/** @var \GuzzleHttp\Client|MockObject */
30
-	private $guzzleClient;
31
-	/** @var CertificateManager|MockObject */
32
-	private $certificateManager;
33
-	/** @var Client */
34
-	private $client;
35
-	/** @var IConfig|MockObject */
36
-	private $config;
37
-	/** @var IRemoteHostValidator|MockObject */
38
-	private IRemoteHostValidator $remoteHostValidator;
39
-	private LoggerInterface $logger;
40
-	private ServerVersion $serverVersion;
41
-	/** @var array */
42
-	private $defaultRequestOptions;
43
-
44
-	protected function setUp(): void {
45
-		parent::setUp();
46
-		$this->config = $this->createMock(IConfig::class);
47
-		$this->guzzleClient = $this->createMock(\GuzzleHttp\Client::class);
48
-		$this->certificateManager = $this->createMock(ICertificateManager::class);
49
-		$this->remoteHostValidator = $this->createMock(IRemoteHostValidator::class);
50
-		$this->logger = $this->createMock(LoggerInterface::class);
51
-		$this->serverVersion = $this->createMock(ServerVersion::class);
52
-
53
-		$this->client = new Client(
54
-			$this->config,
55
-			$this->certificateManager,
56
-			$this->guzzleClient,
57
-			$this->remoteHostValidator,
58
-			$this->logger,
59
-			$this->serverVersion,
60
-		);
61
-	}
62
-
63
-	public function testGetProxyUri(): void {
64
-		$this->config
65
-			->method('getSystemValueString')
66
-			->with('proxy', '')
67
-			->willReturn('');
68
-		$this->assertNull(self::invokePrivate($this->client, 'getProxyUri'));
69
-	}
70
-
71
-	public function testGetProxyUriProxyHostEmptyPassword(): void {
72
-		$this->config
73
-			->method('getSystemValue')
74
-			->willReturnMap([
75
-				['proxyexclude', [], []],
76
-			]);
77
-
78
-		$this->config
79
-			->method('getSystemValueString')
80
-			->willReturnMap([
81
-				['proxy', '', 'foo'],
82
-				['proxyuserpwd', '', ''],
83
-			]);
84
-
85
-		$this->assertEquals([
86
-			'http' => 'foo',
87
-			'https' => 'foo'
88
-		], self::invokePrivate($this->client, 'getProxyUri'));
89
-	}
90
-
91
-	public function testGetProxyUriProxyHostWithPassword(): void {
92
-		$this->config
93
-			->expects($this->once())
94
-			->method('getSystemValue')
95
-			->with('proxyexclude', [])
96
-			->willReturn([]);
97
-		$this->config
98
-			->expects($this->exactly(2))
99
-			->method('getSystemValueString')
100
-			->willReturnMap([
101
-				['proxy', '', 'foo'],
102
-				['proxyuserpwd', '', 'username:password'],
103
-			]);
104
-		$this->assertEquals([
105
-			'http' => 'username:password@foo',
106
-			'https' => 'username:password@foo'
107
-		], self::invokePrivate($this->client, 'getProxyUri'));
108
-	}
109
-
110
-	public function testGetProxyUriProxyHostWithPasswordAndExclude(): void {
111
-		$this->config
112
-			->expects($this->once())
113
-			->method('getSystemValue')
114
-			->with('proxyexclude', [])
115
-			->willReturn(['bar']);
116
-		$this->config
117
-			->expects($this->exactly(2))
118
-			->method('getSystemValueString')
119
-			->willReturnMap([
120
-				['proxy', '', 'foo'],
121
-				['proxyuserpwd', '', 'username:password'],
122
-			]);
123
-		$this->assertEquals([
124
-			'http' => 'username:password@foo',
125
-			'https' => 'username:password@foo',
126
-			'no' => ['bar']
127
-		], self::invokePrivate($this->client, 'getProxyUri'));
128
-	}
129
-
130
-	public function testPreventLocalAddressThrowOnInvalidUri(): void {
131
-		$this->expectException(LocalServerException::class);
132
-		$this->expectExceptionMessage('Could not detect any host');
133
-
134
-		self::invokePrivate($this->client, 'preventLocalAddress', ['!@#$', []]);
135
-	}
136
-
137
-	public static function dataPreventLocalAddress(): array {
138
-		return [
139
-			['https://localhost/foo.bar'],
140
-			['https://localHost/foo.bar'],
141
-			['https://random-host/foo.bar'],
142
-			['https://[::1]/bla.blub'],
143
-			['https://[::]/bla.blub'],
144
-			['https://192.168.0.1'],
145
-			['https://172.16.42.1'],
146
-			['https://[fdf8:f53b:82e4::53]/secret.ics'],
147
-			['https://[fe80::200:5aee:feaa:20a2]/secret.ics'],
148
-			['https://[0:0:0:0:0:0:10.0.0.1]/secret.ics'],
149
-			['https://[0:0:0:0:0:ffff:127.0.0.0]/secret.ics'],
150
-			['https://10.0.0.1'],
151
-			['https://another-host.local'],
152
-			['https://service.localhost'],
153
-			['https://normal.host.com'],
154
-			['https://com.one-.nextcloud-one.com'],
155
-		];
156
-	}
157
-
158
-	/**
159
-	 * @param string $uri
160
-	 */
161
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
162
-	public function testPreventLocalAddressDisabledByGlobalConfig(string $uri): void {
163
-		$this->config->expects($this->once())
164
-			->method('getSystemValueBool')
165
-			->with('allow_local_remote_servers', false)
166
-			->willReturn(true);
167
-
168
-		self::invokePrivate($this->client, 'preventLocalAddress', [$uri, []]);
169
-	}
170
-
171
-	/**
172
-	 * @param string $uri
173
-	 */
174
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
175
-	public function testPreventLocalAddressDisabledByOption(string $uri): void {
176
-		$this->config->expects($this->never())
177
-			->method('getSystemValueBool');
178
-
179
-		self::invokePrivate($this->client, 'preventLocalAddress', [$uri, [
180
-			'nextcloud' => ['allow_local_address' => true],
181
-		]]);
182
-	}
183
-
184
-	/**
185
-	 * @param string $uri
186
-	 */
187
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
188
-	public function testPreventLocalAddressOnGet(string $uri): void {
189
-		$host = parse_url($uri, PHP_URL_HOST);
190
-		$this->expectException(LocalServerException::class);
191
-		$this->remoteHostValidator
192
-			->method('isValid')
193
-			->with($host)
194
-			->willReturn(false);
195
-
196
-		$this->client->get($uri);
197
-	}
198
-
199
-	/**
200
-	 * @param string $uri
201
-	 */
202
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
203
-	public function testPreventLocalAddressOnHead(string $uri): void {
204
-		$host = parse_url($uri, PHP_URL_HOST);
205
-		$this->expectException(LocalServerException::class);
206
-		$this->remoteHostValidator
207
-			->method('isValid')
208
-			->with($host)
209
-			->willReturn(false);
210
-
211
-		$this->client->head($uri);
212
-	}
213
-
214
-	/**
215
-	 * @param string $uri
216
-	 */
217
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
218
-	public function testPreventLocalAddressOnPost(string $uri): void {
219
-		$host = parse_url($uri, PHP_URL_HOST);
220
-		$this->expectException(LocalServerException::class);
221
-		$this->remoteHostValidator
222
-			->method('isValid')
223
-			->with($host)
224
-			->willReturn(false);
225
-
226
-		$this->client->post($uri);
227
-	}
228
-
229
-	/**
230
-	 * @param string $uri
231
-	 */
232
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
233
-	public function testPreventLocalAddressOnPut(string $uri): void {
234
-		$host = parse_url($uri, PHP_URL_HOST);
235
-		$this->expectException(LocalServerException::class);
236
-		$this->remoteHostValidator
237
-			->method('isValid')
238
-			->with($host)
239
-			->willReturn(false);
240
-
241
-		$this->client->put($uri);
242
-	}
243
-
244
-	/**
245
-	 * @param string $uri
246
-	 */
247
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
248
-	public function testPreventLocalAddressOnDelete(string $uri): void {
249
-		$host = parse_url($uri, PHP_URL_HOST);
250
-		$this->expectException(LocalServerException::class);
251
-		$this->remoteHostValidator
252
-			->method('isValid')
253
-			->with($host)
254
-			->willReturn(false);
255
-
256
-		$this->client->delete($uri);
257
-	}
258
-
259
-	private function setUpDefaultRequestOptions(): void {
260
-		$this->config
261
-			->method('getSystemValue')
262
-			->willReturnMap([
263
-				['proxyexclude', [], []],
264
-			]);
265
-		$this->config
266
-			->method('getSystemValueString')
267
-			->willReturnMap([
268
-				['proxy', '', 'foo'],
269
-				['proxyuserpwd', '', ''],
270
-			]);
271
-		$this->config
272
-			->method('getSystemValueBool')
273
-			->willReturnMap([
274
-				['installed', false, true],
275
-				['allow_local_remote_servers', false, true],
276
-			]);
277
-
278
-		$this->certificateManager
279
-			->expects($this->once())
280
-			->method('getAbsoluteBundlePath')
281
-			->with()
282
-			->willReturn('/my/path.crt');
283
-
284
-		$this->serverVersion->method('getVersionString')
285
-			->willReturn('123.45.6');
286
-
287
-		$this->defaultRequestOptions = [
288
-			'verify' => '/my/path.crt',
289
-			'proxy' => [
290
-				'http' => 'foo',
291
-				'https' => 'foo'
292
-			],
293
-			'headers' => [
294
-				'User-Agent' => 'Nextcloud-Server-Crawler/123.45.6',
295
-				'Accept-Encoding' => 'gzip',
296
-			],
297
-			'timeout' => 30,
298
-			'nextcloud' => [
299
-				'allow_local_address' => true,
300
-			],
301
-		];
302
-	}
303
-
304
-	public function testGet(): void {
305
-		$this->setUpDefaultRequestOptions();
306
-
307
-		$this->guzzleClient->method('request')
308
-			->with('get', 'http://localhost/', $this->defaultRequestOptions)
309
-			->willReturn(new Response(418));
310
-		$this->assertEquals(418, $this->client->get('http://localhost/', [])->getStatusCode());
311
-	}
312
-
313
-	public function testGetWithOptions(): void {
314
-		$this->setUpDefaultRequestOptions();
315
-
316
-		$options = array_merge($this->defaultRequestOptions, [
317
-			'verify' => false,
318
-			'proxy' => [
319
-				'http' => 'bar',
320
-				'https' => 'bar'
321
-			],
322
-		]);
323
-
324
-		$this->guzzleClient->method('request')
325
-			->with('get', 'http://localhost/', $options)
326
-			->willReturn(new Response(418));
327
-		$this->assertEquals(418, $this->client->get('http://localhost/', $options)->getStatusCode());
328
-	}
329
-
330
-	public function testPost(): void {
331
-		$this->setUpDefaultRequestOptions();
332
-
333
-		$this->guzzleClient->method('request')
334
-			->with('post', 'http://localhost/', $this->defaultRequestOptions)
335
-			->willReturn(new Response(418));
336
-		$this->assertEquals(418, $this->client->post('http://localhost/', [])->getStatusCode());
337
-	}
338
-
339
-	public function testPostWithOptions(): void {
340
-		$this->setUpDefaultRequestOptions();
341
-
342
-		$options = array_merge($this->defaultRequestOptions, [
343
-			'verify' => false,
344
-			'proxy' => [
345
-				'http' => 'bar',
346
-				'https' => 'bar'
347
-			],
348
-		]);
349
-
350
-		$this->guzzleClient->method('request')
351
-			->with('post', 'http://localhost/', $options)
352
-			->willReturn(new Response(418));
353
-		$this->assertEquals(418, $this->client->post('http://localhost/', $options)->getStatusCode());
354
-	}
355
-
356
-	public function testPut(): void {
357
-		$this->setUpDefaultRequestOptions();
358
-
359
-		$this->guzzleClient->method('request')
360
-			->with('put', 'http://localhost/', $this->defaultRequestOptions)
361
-			->willReturn(new Response(418));
362
-		$this->assertEquals(418, $this->client->put('http://localhost/', [])->getStatusCode());
363
-	}
364
-
365
-	public function testPutWithOptions(): void {
366
-		$this->setUpDefaultRequestOptions();
367
-
368
-		$options = array_merge($this->defaultRequestOptions, [
369
-			'verify' => false,
370
-			'proxy' => [
371
-				'http' => 'bar',
372
-				'https' => 'bar'
373
-			],
374
-		]);
375
-
376
-		$this->guzzleClient->method('request')
377
-			->with('put', 'http://localhost/', $options)
378
-			->willReturn(new Response(418));
379
-		$this->assertEquals(418, $this->client->put('http://localhost/', $options)->getStatusCode());
380
-	}
381
-
382
-	public function testDelete(): void {
383
-		$this->setUpDefaultRequestOptions();
384
-
385
-		$this->guzzleClient->method('request')
386
-			->with('delete', 'http://localhost/', $this->defaultRequestOptions)
387
-			->willReturn(new Response(418));
388
-		$this->assertEquals(418, $this->client->delete('http://localhost/', [])->getStatusCode());
389
-	}
390
-
391
-	public function testDeleteWithOptions(): void {
392
-		$this->setUpDefaultRequestOptions();
393
-
394
-		$options = array_merge($this->defaultRequestOptions, [
395
-			'verify' => false,
396
-			'proxy' => [
397
-				'http' => 'bar',
398
-				'https' => 'bar'
399
-			],
400
-		]);
401
-
402
-		$this->guzzleClient->method('request')
403
-			->with('delete', 'http://localhost/', $options)
404
-			->willReturn(new Response(418));
405
-		$this->assertEquals(418, $this->client->delete('http://localhost/', $options)->getStatusCode());
406
-	}
407
-
408
-	public function testOptions(): void {
409
-		$this->setUpDefaultRequestOptions();
410
-
411
-		$this->guzzleClient->method('request')
412
-			->with('options', 'http://localhost/', $this->defaultRequestOptions)
413
-			->willReturn(new Response(418));
414
-		$this->assertEquals(418, $this->client->options('http://localhost/', [])->getStatusCode());
415
-	}
416
-
417
-	public function testOptionsWithOptions(): void {
418
-		$this->setUpDefaultRequestOptions();
419
-
420
-		$options = array_merge($this->defaultRequestOptions, [
421
-			'verify' => false,
422
-			'proxy' => [
423
-				'http' => 'bar',
424
-				'https' => 'bar'
425
-			],
426
-		]);
427
-
428
-		$this->guzzleClient->method('request')
429
-			->with('options', 'http://localhost/', $options)
430
-			->willReturn(new Response(418));
431
-		$this->assertEquals(418, $this->client->options('http://localhost/', $options)->getStatusCode());
432
-	}
433
-
434
-	public function testHead(): void {
435
-		$this->setUpDefaultRequestOptions();
436
-
437
-		$this->guzzleClient->method('request')
438
-			->with('head', 'http://localhost/', $this->defaultRequestOptions)
439
-			->willReturn(new Response(418));
440
-		$this->assertEquals(418, $this->client->head('http://localhost/', [])->getStatusCode());
441
-	}
442
-
443
-	public function testHeadWithOptions(): void {
444
-		$this->setUpDefaultRequestOptions();
445
-
446
-		$options = array_merge($this->defaultRequestOptions, [
447
-			'verify' => false,
448
-			'proxy' => [
449
-				'http' => 'bar',
450
-				'https' => 'bar'
451
-			],
452
-		]);
453
-
454
-		$this->guzzleClient->method('request')
455
-			->with('head', 'http://localhost/', $options)
456
-			->willReturn(new Response(418));
457
-		$this->assertEquals(418, $this->client->head('http://localhost/', $options)->getStatusCode());
458
-	}
459
-
460
-	public function testSetDefaultOptionsWithNotInstalled(): void {
461
-		$this->config
462
-			->expects($this->exactly(2))
463
-			->method('getSystemValueBool')
464
-			->willReturnMap([
465
-				['installed', false, false],
466
-				['allow_local_remote_servers', false, false],
467
-			]);
468
-		$this->config
469
-			->expects($this->once())
470
-			->method('getSystemValueString')
471
-			->with('proxy', '')
472
-			->willReturn('');
473
-		$this->certificateManager
474
-			->expects($this->never())
475
-			->method('listCertificates');
476
-		$this->certificateManager
477
-			->expects($this->once())
478
-			->method('getDefaultCertificatesBundlePath')
479
-			->willReturn(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
480
-
481
-		$this->serverVersion->method('getVersionString')
482
-			->willReturn('123.45.6');
483
-
484
-		$this->assertEquals([
485
-			'verify' => \OC::$SERVERROOT . '/resources/config/ca-bundle.crt',
486
-			'headers' => [
487
-				'User-Agent' => 'Nextcloud-Server-Crawler/123.45.6',
488
-				'Accept-Encoding' => 'gzip',
489
-			],
490
-			'timeout' => 30,
491
-			'nextcloud' => [
492
-				'allow_local_address' => false,
493
-			],
494
-			'allow_redirects' => [
495
-				'on_redirect' => function (
496
-					\Psr\Http\Message\RequestInterface $request,
497
-					\Psr\Http\Message\ResponseInterface $response,
498
-					\Psr\Http\Message\UriInterface $uri,
499
-				): void {
500
-				},
501
-			],
502
-		], self::invokePrivate($this->client, 'buildRequestOptions', [[]]));
503
-	}
504
-
505
-	public function testSetDefaultOptionsWithProxy(): void {
506
-		$this->config
507
-			->expects($this->exactly(2))
508
-			->method('getSystemValueBool')
509
-			->willReturnMap([
510
-				['installed', false, true],
511
-				['allow_local_remote_servers', false, false],
512
-			]);
513
-		$this->config
514
-			->expects($this->once())
515
-			->method('getSystemValue')
516
-			->with('proxyexclude', [])
517
-			->willReturn([]);
518
-		$this->config
519
-			->expects($this->exactly(2))
520
-			->method('getSystemValueString')
521
-			->willReturnMap([
522
-				['proxy', '', 'foo'],
523
-				['proxyuserpwd', '', ''],
524
-			]);
525
-		$this->certificateManager
526
-			->expects($this->once())
527
-			->method('getAbsoluteBundlePath')
528
-			->with()
529
-			->willReturn('/my/path.crt');
530
-
531
-		$this->serverVersion->method('getVersionString')
532
-			->willReturn('123.45.6');
533
-
534
-		$this->assertEquals([
535
-			'verify' => '/my/path.crt',
536
-			'proxy' => [
537
-				'http' => 'foo',
538
-				'https' => 'foo'
539
-			],
540
-			'headers' => [
541
-				'User-Agent' => 'Nextcloud-Server-Crawler/123.45.6',
542
-				'Accept-Encoding' => 'gzip',
543
-			],
544
-			'timeout' => 30,
545
-			'nextcloud' => [
546
-				'allow_local_address' => false,
547
-			],
548
-			'allow_redirects' => [
549
-				'on_redirect' => function (
550
-					\Psr\Http\Message\RequestInterface $request,
551
-					\Psr\Http\Message\ResponseInterface $response,
552
-					\Psr\Http\Message\UriInterface $uri,
553
-				): void {
554
-				},
555
-			],
556
-		], self::invokePrivate($this->client, 'buildRequestOptions', [[]]));
557
-	}
558
-
559
-	public function testSetDefaultOptionsWithProxyAndExclude(): void {
560
-		$this->config
561
-			->expects($this->exactly(2))
562
-			->method('getSystemValueBool')
563
-			->willReturnMap([
564
-				['installed', false, true],
565
-				['allow_local_remote_servers', false, false],
566
-			]);
567
-		$this->config
568
-			->expects($this->once())
569
-			->method('getSystemValue')
570
-			->with('proxyexclude', [])
571
-			->willReturn(['bar']);
572
-		$this->config
573
-			->expects($this->exactly(2))
574
-			->method('getSystemValueString')
575
-			->willReturnMap([
576
-				['proxy', '', 'foo'],
577
-				['proxyuserpwd', '', ''],
578
-			]);
579
-		$this->certificateManager
580
-			->expects($this->once())
581
-			->method('getAbsoluteBundlePath')
582
-			->with()
583
-			->willReturn('/my/path.crt');
584
-
585
-		$this->serverVersion->method('getVersionString')
586
-			->willReturn('123.45.6');
587
-
588
-		$this->assertEquals([
589
-			'verify' => '/my/path.crt',
590
-			'proxy' => [
591
-				'http' => 'foo',
592
-				'https' => 'foo',
593
-				'no' => ['bar']
594
-			],
595
-			'headers' => [
596
-				'User-Agent' => 'Nextcloud-Server-Crawler/123.45.6',
597
-				'Accept-Encoding' => 'gzip',
598
-			],
599
-			'timeout' => 30,
600
-			'nextcloud' => [
601
-				'allow_local_address' => false,
602
-			],
603
-			'allow_redirects' => [
604
-				'on_redirect' => function (
605
-					\Psr\Http\Message\RequestInterface $request,
606
-					\Psr\Http\Message\ResponseInterface $response,
607
-					\Psr\Http\Message\UriInterface $uri,
608
-				): void {
609
-				},
610
-			],
611
-		], self::invokePrivate($this->client, 'buildRequestOptions', [[]]));
612
-	}
29
+    /** @var \GuzzleHttp\Client|MockObject */
30
+    private $guzzleClient;
31
+    /** @var CertificateManager|MockObject */
32
+    private $certificateManager;
33
+    /** @var Client */
34
+    private $client;
35
+    /** @var IConfig|MockObject */
36
+    private $config;
37
+    /** @var IRemoteHostValidator|MockObject */
38
+    private IRemoteHostValidator $remoteHostValidator;
39
+    private LoggerInterface $logger;
40
+    private ServerVersion $serverVersion;
41
+    /** @var array */
42
+    private $defaultRequestOptions;
43
+
44
+    protected function setUp(): void {
45
+        parent::setUp();
46
+        $this->config = $this->createMock(IConfig::class);
47
+        $this->guzzleClient = $this->createMock(\GuzzleHttp\Client::class);
48
+        $this->certificateManager = $this->createMock(ICertificateManager::class);
49
+        $this->remoteHostValidator = $this->createMock(IRemoteHostValidator::class);
50
+        $this->logger = $this->createMock(LoggerInterface::class);
51
+        $this->serverVersion = $this->createMock(ServerVersion::class);
52
+
53
+        $this->client = new Client(
54
+            $this->config,
55
+            $this->certificateManager,
56
+            $this->guzzleClient,
57
+            $this->remoteHostValidator,
58
+            $this->logger,
59
+            $this->serverVersion,
60
+        );
61
+    }
62
+
63
+    public function testGetProxyUri(): void {
64
+        $this->config
65
+            ->method('getSystemValueString')
66
+            ->with('proxy', '')
67
+            ->willReturn('');
68
+        $this->assertNull(self::invokePrivate($this->client, 'getProxyUri'));
69
+    }
70
+
71
+    public function testGetProxyUriProxyHostEmptyPassword(): void {
72
+        $this->config
73
+            ->method('getSystemValue')
74
+            ->willReturnMap([
75
+                ['proxyexclude', [], []],
76
+            ]);
77
+
78
+        $this->config
79
+            ->method('getSystemValueString')
80
+            ->willReturnMap([
81
+                ['proxy', '', 'foo'],
82
+                ['proxyuserpwd', '', ''],
83
+            ]);
84
+
85
+        $this->assertEquals([
86
+            'http' => 'foo',
87
+            'https' => 'foo'
88
+        ], self::invokePrivate($this->client, 'getProxyUri'));
89
+    }
90
+
91
+    public function testGetProxyUriProxyHostWithPassword(): void {
92
+        $this->config
93
+            ->expects($this->once())
94
+            ->method('getSystemValue')
95
+            ->with('proxyexclude', [])
96
+            ->willReturn([]);
97
+        $this->config
98
+            ->expects($this->exactly(2))
99
+            ->method('getSystemValueString')
100
+            ->willReturnMap([
101
+                ['proxy', '', 'foo'],
102
+                ['proxyuserpwd', '', 'username:password'],
103
+            ]);
104
+        $this->assertEquals([
105
+            'http' => 'username:password@foo',
106
+            'https' => 'username:password@foo'
107
+        ], self::invokePrivate($this->client, 'getProxyUri'));
108
+    }
109
+
110
+    public function testGetProxyUriProxyHostWithPasswordAndExclude(): void {
111
+        $this->config
112
+            ->expects($this->once())
113
+            ->method('getSystemValue')
114
+            ->with('proxyexclude', [])
115
+            ->willReturn(['bar']);
116
+        $this->config
117
+            ->expects($this->exactly(2))
118
+            ->method('getSystemValueString')
119
+            ->willReturnMap([
120
+                ['proxy', '', 'foo'],
121
+                ['proxyuserpwd', '', 'username:password'],
122
+            ]);
123
+        $this->assertEquals([
124
+            'http' => 'username:password@foo',
125
+            'https' => 'username:password@foo',
126
+            'no' => ['bar']
127
+        ], self::invokePrivate($this->client, 'getProxyUri'));
128
+    }
129
+
130
+    public function testPreventLocalAddressThrowOnInvalidUri(): void {
131
+        $this->expectException(LocalServerException::class);
132
+        $this->expectExceptionMessage('Could not detect any host');
133
+
134
+        self::invokePrivate($this->client, 'preventLocalAddress', ['!@#$', []]);
135
+    }
136
+
137
+    public static function dataPreventLocalAddress(): array {
138
+        return [
139
+            ['https://localhost/foo.bar'],
140
+            ['https://localHost/foo.bar'],
141
+            ['https://random-host/foo.bar'],
142
+            ['https://[::1]/bla.blub'],
143
+            ['https://[::]/bla.blub'],
144
+            ['https://192.168.0.1'],
145
+            ['https://172.16.42.1'],
146
+            ['https://[fdf8:f53b:82e4::53]/secret.ics'],
147
+            ['https://[fe80::200:5aee:feaa:20a2]/secret.ics'],
148
+            ['https://[0:0:0:0:0:0:10.0.0.1]/secret.ics'],
149
+            ['https://[0:0:0:0:0:ffff:127.0.0.0]/secret.ics'],
150
+            ['https://10.0.0.1'],
151
+            ['https://another-host.local'],
152
+            ['https://service.localhost'],
153
+            ['https://normal.host.com'],
154
+            ['https://com.one-.nextcloud-one.com'],
155
+        ];
156
+    }
157
+
158
+    /**
159
+     * @param string $uri
160
+     */
161
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
162
+    public function testPreventLocalAddressDisabledByGlobalConfig(string $uri): void {
163
+        $this->config->expects($this->once())
164
+            ->method('getSystemValueBool')
165
+            ->with('allow_local_remote_servers', false)
166
+            ->willReturn(true);
167
+
168
+        self::invokePrivate($this->client, 'preventLocalAddress', [$uri, []]);
169
+    }
170
+
171
+    /**
172
+     * @param string $uri
173
+     */
174
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
175
+    public function testPreventLocalAddressDisabledByOption(string $uri): void {
176
+        $this->config->expects($this->never())
177
+            ->method('getSystemValueBool');
178
+
179
+        self::invokePrivate($this->client, 'preventLocalAddress', [$uri, [
180
+            'nextcloud' => ['allow_local_address' => true],
181
+        ]]);
182
+    }
183
+
184
+    /**
185
+     * @param string $uri
186
+     */
187
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
188
+    public function testPreventLocalAddressOnGet(string $uri): void {
189
+        $host = parse_url($uri, PHP_URL_HOST);
190
+        $this->expectException(LocalServerException::class);
191
+        $this->remoteHostValidator
192
+            ->method('isValid')
193
+            ->with($host)
194
+            ->willReturn(false);
195
+
196
+        $this->client->get($uri);
197
+    }
198
+
199
+    /**
200
+     * @param string $uri
201
+     */
202
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
203
+    public function testPreventLocalAddressOnHead(string $uri): void {
204
+        $host = parse_url($uri, PHP_URL_HOST);
205
+        $this->expectException(LocalServerException::class);
206
+        $this->remoteHostValidator
207
+            ->method('isValid')
208
+            ->with($host)
209
+            ->willReturn(false);
210
+
211
+        $this->client->head($uri);
212
+    }
213
+
214
+    /**
215
+     * @param string $uri
216
+     */
217
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
218
+    public function testPreventLocalAddressOnPost(string $uri): void {
219
+        $host = parse_url($uri, PHP_URL_HOST);
220
+        $this->expectException(LocalServerException::class);
221
+        $this->remoteHostValidator
222
+            ->method('isValid')
223
+            ->with($host)
224
+            ->willReturn(false);
225
+
226
+        $this->client->post($uri);
227
+    }
228
+
229
+    /**
230
+     * @param string $uri
231
+     */
232
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
233
+    public function testPreventLocalAddressOnPut(string $uri): void {
234
+        $host = parse_url($uri, PHP_URL_HOST);
235
+        $this->expectException(LocalServerException::class);
236
+        $this->remoteHostValidator
237
+            ->method('isValid')
238
+            ->with($host)
239
+            ->willReturn(false);
240
+
241
+        $this->client->put($uri);
242
+    }
243
+
244
+    /**
245
+     * @param string $uri
246
+     */
247
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
248
+    public function testPreventLocalAddressOnDelete(string $uri): void {
249
+        $host = parse_url($uri, PHP_URL_HOST);
250
+        $this->expectException(LocalServerException::class);
251
+        $this->remoteHostValidator
252
+            ->method('isValid')
253
+            ->with($host)
254
+            ->willReturn(false);
255
+
256
+        $this->client->delete($uri);
257
+    }
258
+
259
+    private function setUpDefaultRequestOptions(): void {
260
+        $this->config
261
+            ->method('getSystemValue')
262
+            ->willReturnMap([
263
+                ['proxyexclude', [], []],
264
+            ]);
265
+        $this->config
266
+            ->method('getSystemValueString')
267
+            ->willReturnMap([
268
+                ['proxy', '', 'foo'],
269
+                ['proxyuserpwd', '', ''],
270
+            ]);
271
+        $this->config
272
+            ->method('getSystemValueBool')
273
+            ->willReturnMap([
274
+                ['installed', false, true],
275
+                ['allow_local_remote_servers', false, true],
276
+            ]);
277
+
278
+        $this->certificateManager
279
+            ->expects($this->once())
280
+            ->method('getAbsoluteBundlePath')
281
+            ->with()
282
+            ->willReturn('/my/path.crt');
283
+
284
+        $this->serverVersion->method('getVersionString')
285
+            ->willReturn('123.45.6');
286
+
287
+        $this->defaultRequestOptions = [
288
+            'verify' => '/my/path.crt',
289
+            'proxy' => [
290
+                'http' => 'foo',
291
+                'https' => 'foo'
292
+            ],
293
+            'headers' => [
294
+                'User-Agent' => 'Nextcloud-Server-Crawler/123.45.6',
295
+                'Accept-Encoding' => 'gzip',
296
+            ],
297
+            'timeout' => 30,
298
+            'nextcloud' => [
299
+                'allow_local_address' => true,
300
+            ],
301
+        ];
302
+    }
303
+
304
+    public function testGet(): void {
305
+        $this->setUpDefaultRequestOptions();
306
+
307
+        $this->guzzleClient->method('request')
308
+            ->with('get', 'http://localhost/', $this->defaultRequestOptions)
309
+            ->willReturn(new Response(418));
310
+        $this->assertEquals(418, $this->client->get('http://localhost/', [])->getStatusCode());
311
+    }
312
+
313
+    public function testGetWithOptions(): void {
314
+        $this->setUpDefaultRequestOptions();
315
+
316
+        $options = array_merge($this->defaultRequestOptions, [
317
+            'verify' => false,
318
+            'proxy' => [
319
+                'http' => 'bar',
320
+                'https' => 'bar'
321
+            ],
322
+        ]);
323
+
324
+        $this->guzzleClient->method('request')
325
+            ->with('get', 'http://localhost/', $options)
326
+            ->willReturn(new Response(418));
327
+        $this->assertEquals(418, $this->client->get('http://localhost/', $options)->getStatusCode());
328
+    }
329
+
330
+    public function testPost(): void {
331
+        $this->setUpDefaultRequestOptions();
332
+
333
+        $this->guzzleClient->method('request')
334
+            ->with('post', 'http://localhost/', $this->defaultRequestOptions)
335
+            ->willReturn(new Response(418));
336
+        $this->assertEquals(418, $this->client->post('http://localhost/', [])->getStatusCode());
337
+    }
338
+
339
+    public function testPostWithOptions(): void {
340
+        $this->setUpDefaultRequestOptions();
341
+
342
+        $options = array_merge($this->defaultRequestOptions, [
343
+            'verify' => false,
344
+            'proxy' => [
345
+                'http' => 'bar',
346
+                'https' => 'bar'
347
+            ],
348
+        ]);
349
+
350
+        $this->guzzleClient->method('request')
351
+            ->with('post', 'http://localhost/', $options)
352
+            ->willReturn(new Response(418));
353
+        $this->assertEquals(418, $this->client->post('http://localhost/', $options)->getStatusCode());
354
+    }
355
+
356
+    public function testPut(): void {
357
+        $this->setUpDefaultRequestOptions();
358
+
359
+        $this->guzzleClient->method('request')
360
+            ->with('put', 'http://localhost/', $this->defaultRequestOptions)
361
+            ->willReturn(new Response(418));
362
+        $this->assertEquals(418, $this->client->put('http://localhost/', [])->getStatusCode());
363
+    }
364
+
365
+    public function testPutWithOptions(): void {
366
+        $this->setUpDefaultRequestOptions();
367
+
368
+        $options = array_merge($this->defaultRequestOptions, [
369
+            'verify' => false,
370
+            'proxy' => [
371
+                'http' => 'bar',
372
+                'https' => 'bar'
373
+            ],
374
+        ]);
375
+
376
+        $this->guzzleClient->method('request')
377
+            ->with('put', 'http://localhost/', $options)
378
+            ->willReturn(new Response(418));
379
+        $this->assertEquals(418, $this->client->put('http://localhost/', $options)->getStatusCode());
380
+    }
381
+
382
+    public function testDelete(): void {
383
+        $this->setUpDefaultRequestOptions();
384
+
385
+        $this->guzzleClient->method('request')
386
+            ->with('delete', 'http://localhost/', $this->defaultRequestOptions)
387
+            ->willReturn(new Response(418));
388
+        $this->assertEquals(418, $this->client->delete('http://localhost/', [])->getStatusCode());
389
+    }
390
+
391
+    public function testDeleteWithOptions(): void {
392
+        $this->setUpDefaultRequestOptions();
393
+
394
+        $options = array_merge($this->defaultRequestOptions, [
395
+            'verify' => false,
396
+            'proxy' => [
397
+                'http' => 'bar',
398
+                'https' => 'bar'
399
+            ],
400
+        ]);
401
+
402
+        $this->guzzleClient->method('request')
403
+            ->with('delete', 'http://localhost/', $options)
404
+            ->willReturn(new Response(418));
405
+        $this->assertEquals(418, $this->client->delete('http://localhost/', $options)->getStatusCode());
406
+    }
407
+
408
+    public function testOptions(): void {
409
+        $this->setUpDefaultRequestOptions();
410
+
411
+        $this->guzzleClient->method('request')
412
+            ->with('options', 'http://localhost/', $this->defaultRequestOptions)
413
+            ->willReturn(new Response(418));
414
+        $this->assertEquals(418, $this->client->options('http://localhost/', [])->getStatusCode());
415
+    }
416
+
417
+    public function testOptionsWithOptions(): void {
418
+        $this->setUpDefaultRequestOptions();
419
+
420
+        $options = array_merge($this->defaultRequestOptions, [
421
+            'verify' => false,
422
+            'proxy' => [
423
+                'http' => 'bar',
424
+                'https' => 'bar'
425
+            ],
426
+        ]);
427
+
428
+        $this->guzzleClient->method('request')
429
+            ->with('options', 'http://localhost/', $options)
430
+            ->willReturn(new Response(418));
431
+        $this->assertEquals(418, $this->client->options('http://localhost/', $options)->getStatusCode());
432
+    }
433
+
434
+    public function testHead(): void {
435
+        $this->setUpDefaultRequestOptions();
436
+
437
+        $this->guzzleClient->method('request')
438
+            ->with('head', 'http://localhost/', $this->defaultRequestOptions)
439
+            ->willReturn(new Response(418));
440
+        $this->assertEquals(418, $this->client->head('http://localhost/', [])->getStatusCode());
441
+    }
442
+
443
+    public function testHeadWithOptions(): void {
444
+        $this->setUpDefaultRequestOptions();
445
+
446
+        $options = array_merge($this->defaultRequestOptions, [
447
+            'verify' => false,
448
+            'proxy' => [
449
+                'http' => 'bar',
450
+                'https' => 'bar'
451
+            ],
452
+        ]);
453
+
454
+        $this->guzzleClient->method('request')
455
+            ->with('head', 'http://localhost/', $options)
456
+            ->willReturn(new Response(418));
457
+        $this->assertEquals(418, $this->client->head('http://localhost/', $options)->getStatusCode());
458
+    }
459
+
460
+    public function testSetDefaultOptionsWithNotInstalled(): void {
461
+        $this->config
462
+            ->expects($this->exactly(2))
463
+            ->method('getSystemValueBool')
464
+            ->willReturnMap([
465
+                ['installed', false, false],
466
+                ['allow_local_remote_servers', false, false],
467
+            ]);
468
+        $this->config
469
+            ->expects($this->once())
470
+            ->method('getSystemValueString')
471
+            ->with('proxy', '')
472
+            ->willReturn('');
473
+        $this->certificateManager
474
+            ->expects($this->never())
475
+            ->method('listCertificates');
476
+        $this->certificateManager
477
+            ->expects($this->once())
478
+            ->method('getDefaultCertificatesBundlePath')
479
+            ->willReturn(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
480
+
481
+        $this->serverVersion->method('getVersionString')
482
+            ->willReturn('123.45.6');
483
+
484
+        $this->assertEquals([
485
+            'verify' => \OC::$SERVERROOT . '/resources/config/ca-bundle.crt',
486
+            'headers' => [
487
+                'User-Agent' => 'Nextcloud-Server-Crawler/123.45.6',
488
+                'Accept-Encoding' => 'gzip',
489
+            ],
490
+            'timeout' => 30,
491
+            'nextcloud' => [
492
+                'allow_local_address' => false,
493
+            ],
494
+            'allow_redirects' => [
495
+                'on_redirect' => function (
496
+                    \Psr\Http\Message\RequestInterface $request,
497
+                    \Psr\Http\Message\ResponseInterface $response,
498
+                    \Psr\Http\Message\UriInterface $uri,
499
+                ): void {
500
+                },
501
+            ],
502
+        ], self::invokePrivate($this->client, 'buildRequestOptions', [[]]));
503
+    }
504
+
505
+    public function testSetDefaultOptionsWithProxy(): void {
506
+        $this->config
507
+            ->expects($this->exactly(2))
508
+            ->method('getSystemValueBool')
509
+            ->willReturnMap([
510
+                ['installed', false, true],
511
+                ['allow_local_remote_servers', false, false],
512
+            ]);
513
+        $this->config
514
+            ->expects($this->once())
515
+            ->method('getSystemValue')
516
+            ->with('proxyexclude', [])
517
+            ->willReturn([]);
518
+        $this->config
519
+            ->expects($this->exactly(2))
520
+            ->method('getSystemValueString')
521
+            ->willReturnMap([
522
+                ['proxy', '', 'foo'],
523
+                ['proxyuserpwd', '', ''],
524
+            ]);
525
+        $this->certificateManager
526
+            ->expects($this->once())
527
+            ->method('getAbsoluteBundlePath')
528
+            ->with()
529
+            ->willReturn('/my/path.crt');
530
+
531
+        $this->serverVersion->method('getVersionString')
532
+            ->willReturn('123.45.6');
533
+
534
+        $this->assertEquals([
535
+            'verify' => '/my/path.crt',
536
+            'proxy' => [
537
+                'http' => 'foo',
538
+                'https' => 'foo'
539
+            ],
540
+            'headers' => [
541
+                'User-Agent' => 'Nextcloud-Server-Crawler/123.45.6',
542
+                'Accept-Encoding' => 'gzip',
543
+            ],
544
+            'timeout' => 30,
545
+            'nextcloud' => [
546
+                'allow_local_address' => false,
547
+            ],
548
+            'allow_redirects' => [
549
+                'on_redirect' => function (
550
+                    \Psr\Http\Message\RequestInterface $request,
551
+                    \Psr\Http\Message\ResponseInterface $response,
552
+                    \Psr\Http\Message\UriInterface $uri,
553
+                ): void {
554
+                },
555
+            ],
556
+        ], self::invokePrivate($this->client, 'buildRequestOptions', [[]]));
557
+    }
558
+
559
+    public function testSetDefaultOptionsWithProxyAndExclude(): void {
560
+        $this->config
561
+            ->expects($this->exactly(2))
562
+            ->method('getSystemValueBool')
563
+            ->willReturnMap([
564
+                ['installed', false, true],
565
+                ['allow_local_remote_servers', false, false],
566
+            ]);
567
+        $this->config
568
+            ->expects($this->once())
569
+            ->method('getSystemValue')
570
+            ->with('proxyexclude', [])
571
+            ->willReturn(['bar']);
572
+        $this->config
573
+            ->expects($this->exactly(2))
574
+            ->method('getSystemValueString')
575
+            ->willReturnMap([
576
+                ['proxy', '', 'foo'],
577
+                ['proxyuserpwd', '', ''],
578
+            ]);
579
+        $this->certificateManager
580
+            ->expects($this->once())
581
+            ->method('getAbsoluteBundlePath')
582
+            ->with()
583
+            ->willReturn('/my/path.crt');
584
+
585
+        $this->serverVersion->method('getVersionString')
586
+            ->willReturn('123.45.6');
587
+
588
+        $this->assertEquals([
589
+            'verify' => '/my/path.crt',
590
+            'proxy' => [
591
+                'http' => 'foo',
592
+                'https' => 'foo',
593
+                'no' => ['bar']
594
+            ],
595
+            'headers' => [
596
+                'User-Agent' => 'Nextcloud-Server-Crawler/123.45.6',
597
+                'Accept-Encoding' => 'gzip',
598
+            ],
599
+            'timeout' => 30,
600
+            'nextcloud' => [
601
+                'allow_local_address' => false,
602
+            ],
603
+            'allow_redirects' => [
604
+                'on_redirect' => function (
605
+                    \Psr\Http\Message\RequestInterface $request,
606
+                    \Psr\Http\Message\ResponseInterface $response,
607
+                    \Psr\Http\Message\UriInterface $uri,
608
+                ): void {
609
+                },
610
+            ],
611
+        ], self::invokePrivate($this->client, 'buildRequestOptions', [[]]));
612
+    }
613 613
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -476,13 +476,13 @@  discard block
 block discarded – undo
476 476
 		$this->certificateManager
477 477
 			->expects($this->once())
478 478
 			->method('getDefaultCertificatesBundlePath')
479
-			->willReturn(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
479
+			->willReturn(\OC::$SERVERROOT.'/resources/config/ca-bundle.crt');
480 480
 
481 481
 		$this->serverVersion->method('getVersionString')
482 482
 			->willReturn('123.45.6');
483 483
 
484 484
 		$this->assertEquals([
485
-			'verify' => \OC::$SERVERROOT . '/resources/config/ca-bundle.crt',
485
+			'verify' => \OC::$SERVERROOT.'/resources/config/ca-bundle.crt',
486 486
 			'headers' => [
487 487
 				'User-Agent' => 'Nextcloud-Server-Crawler/123.45.6',
488 488
 				'Accept-Encoding' => 'gzip',
@@ -492,7 +492,7 @@  discard block
 block discarded – undo
492 492
 				'allow_local_address' => false,
493 493
 			],
494 494
 			'allow_redirects' => [
495
-				'on_redirect' => function (
495
+				'on_redirect' => function(
496 496
 					\Psr\Http\Message\RequestInterface $request,
497 497
 					\Psr\Http\Message\ResponseInterface $response,
498 498
 					\Psr\Http\Message\UriInterface $uri,
@@ -546,7 +546,7 @@  discard block
 block discarded – undo
546 546
 				'allow_local_address' => false,
547 547
 			],
548 548
 			'allow_redirects' => [
549
-				'on_redirect' => function (
549
+				'on_redirect' => function(
550 550
 					\Psr\Http\Message\RequestInterface $request,
551 551
 					\Psr\Http\Message\ResponseInterface $response,
552 552
 					\Psr\Http\Message\UriInterface $uri,
@@ -601,7 +601,7 @@  discard block
 block discarded – undo
601 601
 				'allow_local_address' => false,
602 602
 			],
603 603
 			'allow_redirects' => [
604
-				'on_redirect' => function (
604
+				'on_redirect' => function(
605 605
 					\Psr\Http\Message\RequestInterface $request,
606 606
 					\Psr\Http\Message\ResponseInterface $response,
607 607
 					\Psr\Http\Message\UriInterface $uri,
Please login to merge, or discard this patch.
tests/lib/Security/CertificateManagerTest.php 2 patches
Indentation   +177 added lines, -177 removed lines patch added patch discarded remove patch
@@ -28,181 +28,181 @@
 block discarded – undo
28 28
  */
29 29
 #[\PHPUnit\Framework\Attributes\Group('DB')]
30 30
 class CertificateManagerTest extends \Test\TestCase {
31
-	use \Test\Traits\UserTrait;
32
-	use \Test\Traits\MountProviderTrait;
33
-
34
-	private CertificateManager $certificateManager;
35
-	private string $username;
36
-	private ISecureRandom&MockObject $random;
37
-
38
-	protected function setUp(): void {
39
-		parent::setUp();
40
-
41
-		$this->username = $this->getUniqueID('', 20);
42
-		$this->createUser($this->username, '');
43
-
44
-		$storage = new Temporary();
45
-		$this->registerMount($this->username, $storage, '/' . $this->username . '/');
46
-
47
-		\OC_Util::tearDownFS();
48
-		\OC_User::setUserId($this->username);
49
-		Filesystem::tearDown();
50
-		\OC_Util::setupFS($this->username);
51
-
52
-		$config = $this->createMock(IConfig::class);
53
-		$config->expects($this->any())->method('getSystemValueBool')
54
-			->with('installed', false)->willReturn(true);
55
-		$config
56
-			->expects($this->any())
57
-			->method('getSystemValueString')
58
-			->with('default_certificates_bundle_path', \OC::$SERVERROOT . '/resources/config/ca-bundle.crt')
59
-			->willReturn(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
60
-
61
-		$this->random = $this->createMock(ISecureRandom::class);
62
-		$this->random->method('generate')
63
-			->willReturn('random');
64
-
65
-		$this->certificateManager = new CertificateManager(
66
-			new View(),
67
-			$config,
68
-			$this->createMock(LoggerInterface::class),
69
-			$this->random
70
-		);
71
-	}
72
-
73
-	protected function tearDown(): void {
74
-		$user = Server::get(IUserManager::class)->get($this->username);
75
-		if ($user !== null) {
76
-			$user->delete();
77
-		}
78
-		parent::tearDown();
79
-	}
80
-
81
-	protected function assertEqualsArrays($expected, $actual) {
82
-		sort($expected);
83
-		sort($actual);
84
-
85
-		$this->assertEquals($expected, $actual);
86
-	}
87
-
88
-	public function testListCertificates(): void {
89
-		// Test empty certificate bundle
90
-		$this->assertSame([], $this->certificateManager->listCertificates());
91
-
92
-		// Add some certificates
93
-		$this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
94
-		$certificateStore = [];
95
-		$certificateStore[] = new Certificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
96
-		$this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
97
-
98
-		// Add another certificates
99
-		$this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
100
-		$certificateStore[] = new Certificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
101
-		$this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
102
-	}
103
-
104
-
105
-	public function testAddInvalidCertificate(): void {
106
-		$this->expectException(\Exception::class);
107
-		$this->expectExceptionMessage('Certificate could not get parsed.');
108
-
109
-		$this->certificateManager->addCertificate('InvalidCertificate', 'invalidCertificate');
110
-	}
111
-
112
-	public static function dangerousFileProvider(): array {
113
-		return [
114
-			['.htaccess'],
115
-			['../../foo.txt'],
116
-			['..\..\foo.txt'],
117
-		];
118
-	}
119
-
120
-	/**
121
-	 * @param string $filename
122
-	 */
123
-	#[\PHPUnit\Framework\Attributes\DataProvider('dangerousFileProvider')]
124
-	public function testAddDangerousFile($filename): void {
125
-		$this->expectException(InvalidPathException::class);
126
-		$this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), $filename);
127
-	}
128
-
129
-	public function testRemoveDangerousFile(): void {
130
-		$this->assertFalse($this->certificateManager->removeCertificate('../../foo.txt'));
131
-	}
132
-
133
-	public function testRemoveExistingFile(): void {
134
-		$this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
135
-		$this->assertTrue($this->certificateManager->removeCertificate('GoodCertificate'));
136
-	}
137
-
138
-	public function testGetCertificateBundle(): void {
139
-		$this->assertSame('/files_external/rootcerts.crt', $this->certificateManager->getCertificateBundle());
140
-	}
141
-
142
-	/**
143
-	 *
144
-	 * @param int $CaBundleMtime
145
-	 * @param int $targetBundleMtime
146
-	 * @param int $targetBundleExists
147
-	 * @param bool $expected
148
-	 */
149
-	#[\PHPUnit\Framework\Attributes\DataProvider('dataTestNeedRebundling')]
150
-	public function testNeedRebundling($CaBundleMtime,
151
-		$targetBundleMtime,
152
-		$targetBundleExists,
153
-		$expected,
154
-	): void {
155
-		$view = $this->getMockBuilder(View::class)
156
-			->disableOriginalConstructor()->getMock();
157
-		$config = $this->createMock(IConfig::class);
158
-
159
-		/** @var CertificateManager | \PHPUnit\Framework\MockObject\MockObject $certificateManager */
160
-		$certificateManager = $this->getMockBuilder('OC\Security\CertificateManager')
161
-			->setConstructorArgs([$view, $config, $this->createMock(LoggerInterface::class), $this->random])
162
-			->onlyMethods(['getFilemtimeOfCaBundle', 'getCertificateBundle'])
163
-			->getMock();
164
-
165
-		$certificateManager->expects($this->any())->method('getFilemtimeOfCaBundle')
166
-			->willReturn($CaBundleMtime);
167
-
168
-		$certificateManager->expects($this->once())->method('getCertificateBundle')
169
-			->willReturn('targetBundlePath');
170
-
171
-		$view->expects($this->any())->method('file_exists')
172
-			->with('targetBundlePath')
173
-			->willReturn($targetBundleExists);
174
-
175
-
176
-		$view->expects($this->any())->method('filemtime')
177
-			->willReturnCallback(function ($path) use ($targetBundleMtime) {
178
-				if ($path === 'targetBundlePath') {
179
-					return $targetBundleMtime;
180
-				}
181
-				throw new \Exception('unexpected path');
182
-			});
183
-
184
-
185
-		$this->assertSame($expected,
186
-			$this->invokePrivate($certificateManager, 'needsRebundling')
187
-		);
188
-	}
189
-
190
-	public static function dataTestNeedRebundling(): array {
191
-		return [
192
-			//values: CaBundleMtime, targetBundleMtime, targetBundleExists, expected
193
-
194
-			[10, 30, true, false],
195
-			[10, 15, true, false],
196
-			[10, 8, true, true],
197
-			[10, 30, true, false],
198
-			[10, 8, true, true],
199
-
200
-			// if no target bundle exists we always build a new one
201
-			[10, 30, false, true],
202
-			[10, 15, false, true],
203
-			[10, 8, false, true],
204
-			[10, 30, false, true],
205
-			[10, 8, false, true],
206
-		];
207
-	}
31
+    use \Test\Traits\UserTrait;
32
+    use \Test\Traits\MountProviderTrait;
33
+
34
+    private CertificateManager $certificateManager;
35
+    private string $username;
36
+    private ISecureRandom&MockObject $random;
37
+
38
+    protected function setUp(): void {
39
+        parent::setUp();
40
+
41
+        $this->username = $this->getUniqueID('', 20);
42
+        $this->createUser($this->username, '');
43
+
44
+        $storage = new Temporary();
45
+        $this->registerMount($this->username, $storage, '/' . $this->username . '/');
46
+
47
+        \OC_Util::tearDownFS();
48
+        \OC_User::setUserId($this->username);
49
+        Filesystem::tearDown();
50
+        \OC_Util::setupFS($this->username);
51
+
52
+        $config = $this->createMock(IConfig::class);
53
+        $config->expects($this->any())->method('getSystemValueBool')
54
+            ->with('installed', false)->willReturn(true);
55
+        $config
56
+            ->expects($this->any())
57
+            ->method('getSystemValueString')
58
+            ->with('default_certificates_bundle_path', \OC::$SERVERROOT . '/resources/config/ca-bundle.crt')
59
+            ->willReturn(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
60
+
61
+        $this->random = $this->createMock(ISecureRandom::class);
62
+        $this->random->method('generate')
63
+            ->willReturn('random');
64
+
65
+        $this->certificateManager = new CertificateManager(
66
+            new View(),
67
+            $config,
68
+            $this->createMock(LoggerInterface::class),
69
+            $this->random
70
+        );
71
+    }
72
+
73
+    protected function tearDown(): void {
74
+        $user = Server::get(IUserManager::class)->get($this->username);
75
+        if ($user !== null) {
76
+            $user->delete();
77
+        }
78
+        parent::tearDown();
79
+    }
80
+
81
+    protected function assertEqualsArrays($expected, $actual) {
82
+        sort($expected);
83
+        sort($actual);
84
+
85
+        $this->assertEquals($expected, $actual);
86
+    }
87
+
88
+    public function testListCertificates(): void {
89
+        // Test empty certificate bundle
90
+        $this->assertSame([], $this->certificateManager->listCertificates());
91
+
92
+        // Add some certificates
93
+        $this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
94
+        $certificateStore = [];
95
+        $certificateStore[] = new Certificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
96
+        $this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
97
+
98
+        // Add another certificates
99
+        $this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
100
+        $certificateStore[] = new Certificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
101
+        $this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
102
+    }
103
+
104
+
105
+    public function testAddInvalidCertificate(): void {
106
+        $this->expectException(\Exception::class);
107
+        $this->expectExceptionMessage('Certificate could not get parsed.');
108
+
109
+        $this->certificateManager->addCertificate('InvalidCertificate', 'invalidCertificate');
110
+    }
111
+
112
+    public static function dangerousFileProvider(): array {
113
+        return [
114
+            ['.htaccess'],
115
+            ['../../foo.txt'],
116
+            ['..\..\foo.txt'],
117
+        ];
118
+    }
119
+
120
+    /**
121
+     * @param string $filename
122
+     */
123
+    #[\PHPUnit\Framework\Attributes\DataProvider('dangerousFileProvider')]
124
+    public function testAddDangerousFile($filename): void {
125
+        $this->expectException(InvalidPathException::class);
126
+        $this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), $filename);
127
+    }
128
+
129
+    public function testRemoveDangerousFile(): void {
130
+        $this->assertFalse($this->certificateManager->removeCertificate('../../foo.txt'));
131
+    }
132
+
133
+    public function testRemoveExistingFile(): void {
134
+        $this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
135
+        $this->assertTrue($this->certificateManager->removeCertificate('GoodCertificate'));
136
+    }
137
+
138
+    public function testGetCertificateBundle(): void {
139
+        $this->assertSame('/files_external/rootcerts.crt', $this->certificateManager->getCertificateBundle());
140
+    }
141
+
142
+    /**
143
+     *
144
+     * @param int $CaBundleMtime
145
+     * @param int $targetBundleMtime
146
+     * @param int $targetBundleExists
147
+     * @param bool $expected
148
+     */
149
+    #[\PHPUnit\Framework\Attributes\DataProvider('dataTestNeedRebundling')]
150
+    public function testNeedRebundling($CaBundleMtime,
151
+        $targetBundleMtime,
152
+        $targetBundleExists,
153
+        $expected,
154
+    ): void {
155
+        $view = $this->getMockBuilder(View::class)
156
+            ->disableOriginalConstructor()->getMock();
157
+        $config = $this->createMock(IConfig::class);
158
+
159
+        /** @var CertificateManager | \PHPUnit\Framework\MockObject\MockObject $certificateManager */
160
+        $certificateManager = $this->getMockBuilder('OC\Security\CertificateManager')
161
+            ->setConstructorArgs([$view, $config, $this->createMock(LoggerInterface::class), $this->random])
162
+            ->onlyMethods(['getFilemtimeOfCaBundle', 'getCertificateBundle'])
163
+            ->getMock();
164
+
165
+        $certificateManager->expects($this->any())->method('getFilemtimeOfCaBundle')
166
+            ->willReturn($CaBundleMtime);
167
+
168
+        $certificateManager->expects($this->once())->method('getCertificateBundle')
169
+            ->willReturn('targetBundlePath');
170
+
171
+        $view->expects($this->any())->method('file_exists')
172
+            ->with('targetBundlePath')
173
+            ->willReturn($targetBundleExists);
174
+
175
+
176
+        $view->expects($this->any())->method('filemtime')
177
+            ->willReturnCallback(function ($path) use ($targetBundleMtime) {
178
+                if ($path === 'targetBundlePath') {
179
+                    return $targetBundleMtime;
180
+                }
181
+                throw new \Exception('unexpected path');
182
+            });
183
+
184
+
185
+        $this->assertSame($expected,
186
+            $this->invokePrivate($certificateManager, 'needsRebundling')
187
+        );
188
+    }
189
+
190
+    public static function dataTestNeedRebundling(): array {
191
+        return [
192
+            //values: CaBundleMtime, targetBundleMtime, targetBundleExists, expected
193
+
194
+            [10, 30, true, false],
195
+            [10, 15, true, false],
196
+            [10, 8, true, true],
197
+            [10, 30, true, false],
198
+            [10, 8, true, true],
199
+
200
+            // if no target bundle exists we always build a new one
201
+            [10, 30, false, true],
202
+            [10, 15, false, true],
203
+            [10, 8, false, true],
204
+            [10, 30, false, true],
205
+            [10, 8, false, true],
206
+        ];
207
+    }
208 208
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -42,7 +42,7 @@  discard block
 block discarded – undo
42 42
 		$this->createUser($this->username, '');
43 43
 
44 44
 		$storage = new Temporary();
45
-		$this->registerMount($this->username, $storage, '/' . $this->username . '/');
45
+		$this->registerMount($this->username, $storage, '/'.$this->username.'/');
46 46
 
47 47
 		\OC_Util::tearDownFS();
48 48
 		\OC_User::setUserId($this->username);
@@ -55,8 +55,8 @@  discard block
 block discarded – undo
55 55
 		$config
56 56
 			->expects($this->any())
57 57
 			->method('getSystemValueString')
58
-			->with('default_certificates_bundle_path', \OC::$SERVERROOT . '/resources/config/ca-bundle.crt')
59
-			->willReturn(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
58
+			->with('default_certificates_bundle_path', \OC::$SERVERROOT.'/resources/config/ca-bundle.crt')
59
+			->willReturn(\OC::$SERVERROOT.'/resources/config/ca-bundle.crt');
60 60
 
61 61
 		$this->random = $this->createMock(ISecureRandom::class);
62 62
 		$this->random->method('generate')
@@ -90,14 +90,14 @@  discard block
 block discarded – undo
90 90
 		$this->assertSame([], $this->certificateManager->listCertificates());
91 91
 
92 92
 		// Add some certificates
93
-		$this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
93
+		$this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
94 94
 		$certificateStore = [];
95
-		$certificateStore[] = new Certificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
95
+		$certificateStore[] = new Certificate(file_get_contents(__DIR__.'/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
96 96
 		$this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
97 97
 
98 98
 		// Add another certificates
99
-		$this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
100
-		$certificateStore[] = new Certificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
99
+		$this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
100
+		$certificateStore[] = new Certificate(file_get_contents(__DIR__.'/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
101 101
 		$this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
102 102
 	}
103 103
 
@@ -123,7 +123,7 @@  discard block
 block discarded – undo
123 123
 	#[\PHPUnit\Framework\Attributes\DataProvider('dangerousFileProvider')]
124 124
 	public function testAddDangerousFile($filename): void {
125 125
 		$this->expectException(InvalidPathException::class);
126
-		$this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), $filename);
126
+		$this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/expiredCertificate.crt'), $filename);
127 127
 	}
128 128
 
129 129
 	public function testRemoveDangerousFile(): void {
@@ -131,7 +131,7 @@  discard block
 block discarded – undo
131 131
 	}
132 132
 
133 133
 	public function testRemoveExistingFile(): void {
134
-		$this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
134
+		$this->certificateManager->addCertificate(file_get_contents(__DIR__.'/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
135 135
 		$this->assertTrue($this->certificateManager->removeCertificate('GoodCertificate'));
136 136
 	}
137 137
 
@@ -174,7 +174,7 @@  discard block
 block discarded – undo
174 174
 
175 175
 
176 176
 		$view->expects($this->any())->method('filemtime')
177
-			->willReturnCallback(function ($path) use ($targetBundleMtime) {
177
+			->willReturnCallback(function($path) use ($targetBundleMtime) {
178 178
 				if ($path === 'targetBundlePath') {
179 179
 					return $targetBundleMtime;
180 180
 				}
Please login to merge, or discard this patch.