Passed
Push — master ( fcf578...0af4e9 )
by Joas
15:31 queued 01:31
created
lib/public/IRequest.php 1 patch
Indentation   +254 added lines, -254 removed lines patch added patch discarded remove patch
@@ -62,258 +62,258 @@
 block discarded – undo
62 62
  * @since 6.0.0
63 63
  */
64 64
 interface IRequest {
65
-	/**
66
-	 * @since 9.1.0
67
-	 */
68
-	public const USER_AGENT_CLIENT_ANDROID = '/^Mozilla\/5\.0 \(Android\) (ownCloud|Nextcloud)\-android.*$/';
69
-
70
-	/**
71
-	 * @since 13.0.0
72
-	 */
73
-	public const USER_AGENT_TALK_ANDROID = '/^Mozilla\/5\.0 \(Android\) Nextcloud\-Talk v.*$/';
74
-
75
-	/**
76
-	 * @since 9.1.0
77
-	 */
78
-	public const USER_AGENT_CLIENT_DESKTOP = '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/';
79
-
80
-	/**
81
-	 * @since 9.1.0
82
-	 */
83
-	public const USER_AGENT_CLIENT_IOS = '/^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)\-iOS.*$/';
84
-
85
-	/**
86
-	 * @since 13.0.0
87
-	 */
88
-	public const USER_AGENT_TALK_IOS = '/^Mozilla\/5\.0 \(iOS\) Nextcloud\-Talk v.*$/';
89
-
90
-	/**
91
-	 * @since 13.0.1
92
-	 */
93
-	public const USER_AGENT_OUTLOOK_ADDON = '/^Mozilla\/5\.0 \([A-Za-z ]+\) Nextcloud\-Outlook v.*$/';
94
-
95
-	/**
96
-	 * @since 13.0.1
97
-	 */
98
-	public const USER_AGENT_THUNDERBIRD_ADDON = '/^Mozilla\/5\.0 \([A-Za-z ]+\) Nextcloud\-Thunderbird v.*$/';
99
-
100
-	/**
101
-	 * @since 26.0.0
102
-	 */
103
-	public const JSON_CONTENT_TYPE_REGEX = '/^application\/(?:[a-z0-9.-]+\+)?json\b/';
104
-
105
-	/**
106
-	 * @param string $name
107
-	 *
108
-	 * @psalm-taint-source input
109
-	 *
110
-	 * @return string
111
-	 * @since 6.0.0
112
-	 */
113
-	public function getHeader(string $name): string;
114
-
115
-	/**
116
-	 * Lets you access post and get parameters by the index
117
-	 * In case of json requests the encoded json body is accessed
118
-	 *
119
-	 * @psalm-taint-source input
120
-	 *
121
-	 * @param string $key the key which you want to access in the URL Parameter
122
-	 *                     placeholder, $_POST or $_GET array.
123
-	 *                     The priority how they're returned is the following:
124
-	 *                     1. URL parameters
125
-	 *                     2. POST parameters
126
-	 *                     3. GET parameters
127
-	 * @param mixed $default If the key is not found, this value will be returned
128
-	 * @return mixed the content of the array
129
-	 * @since 6.0.0
130
-	 */
131
-	public function getParam(string $key, $default = null);
132
-
133
-
134
-	/**
135
-	 * Returns all params that were received, be it from the request
136
-	 *
137
-	 * (as GET or POST) or through the URL by the route
138
-	 *
139
-	 * @psalm-taint-source input
140
-	 *
141
-	 * @return array the array with all parameters
142
-	 * @since 6.0.0
143
-	 */
144
-	public function getParams(): array;
145
-
146
-	/**
147
-	 * Returns the method of the request
148
-	 *
149
-	 * @return string the method of the request (POST, GET, etc)
150
-	 * @since 6.0.0
151
-	 */
152
-	public function getMethod(): string;
153
-
154
-	/**
155
-	 * Shortcut for accessing an uploaded file through the $_FILES array
156
-	 *
157
-	 * @param string $key the key that will be taken from the $_FILES array
158
-	 * @return array the file in the $_FILES element
159
-	 * @since 6.0.0
160
-	 */
161
-	public function getUploadedFile(string $key);
162
-
163
-
164
-	/**
165
-	 * Shortcut for getting env variables
166
-	 *
167
-	 * @param string $key the key that will be taken from the $_ENV array
168
-	 * @return array the value in the $_ENV element
169
-	 * @since 6.0.0
170
-	 */
171
-	public function getEnv(string $key);
172
-
173
-
174
-	/**
175
-	 * Shortcut for getting cookie variables
176
-	 *
177
-	 * @psalm-taint-source input
178
-	 *
179
-	 * @param string $key the key that will be taken from the $_COOKIE array
180
-	 * @return string|null the value in the $_COOKIE element
181
-	 * @since 6.0.0
182
-	 */
183
-	public function getCookie(string $key);
184
-
185
-
186
-	/**
187
-	 * Checks if the CSRF check was correct
188
-	 *
189
-	 * @return bool true if CSRF check passed
190
-	 * @since 6.0.0
191
-	 */
192
-	public function passesCSRFCheck(): bool;
193
-
194
-	/**
195
-	 * Checks if the strict cookie has been sent with the request if the request
196
-	 * is including any cookies.
197
-	 *
198
-	 * @return bool
199
-	 * @since 9.0.0
200
-	 */
201
-	public function passesStrictCookieCheck(): bool;
202
-
203
-	/**
204
-	 * Checks if the lax cookie has been sent with the request if the request
205
-	 * is including any cookies.
206
-	 *
207
-	 * @return bool
208
-	 * @since 9.0.0
209
-	 */
210
-	public function passesLaxCookieCheck(): bool;
211
-
212
-	/**
213
-	 * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
214
-	 * If `mod_unique_id` is installed this value will be taken.
215
-	 *
216
-	 * @return string
217
-	 * @since 8.1.0
218
-	 */
219
-	public function getId(): string;
220
-
221
-	/**
222
-	 * Returns the remote address, if the connection came from a trusted proxy
223
-	 * and `forwarded_for_headers` has been configured then the IP address
224
-	 * specified in this header will be returned instead.
225
-	 * Do always use this instead of $_SERVER['REMOTE_ADDR']
226
-	 *
227
-	 * @return string IP address
228
-	 * @since 8.1.0
229
-	 */
230
-	public function getRemoteAddress(): string;
231
-
232
-	/**
233
-	 * Returns the server protocol. It respects reverse proxy servers and load
234
-	 * balancers.
235
-	 *
236
-	 * @return string Server protocol (http or https)
237
-	 * @since 8.1.0
238
-	 */
239
-	public function getServerProtocol(): string;
240
-
241
-	/**
242
-	 * Returns the used HTTP protocol.
243
-	 *
244
-	 * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
245
-	 * @since 8.2.0
246
-	 */
247
-	public function getHttpProtocol(): string;
248
-
249
-	/**
250
-	 * Returns the request uri, even if the website uses one or more
251
-	 * reverse proxies
252
-	 *
253
-	 * @psalm-taint-source input
254
-	 *
255
-	 * @return string
256
-	 * @since 8.1.0
257
-	 */
258
-	public function getRequestUri(): string;
259
-
260
-	/**
261
-	 * Get raw PathInfo from request (not urldecoded)
262
-	 *
263
-	 * @psalm-taint-source input
264
-	 *
265
-	 * @throws \Exception
266
-	 * @return string Path info
267
-	 * @since 8.1.0
268
-	 */
269
-	public function getRawPathInfo(): string;
270
-
271
-	/**
272
-	 * Get PathInfo from request
273
-	 *
274
-	 * @psalm-taint-source input
275
-	 *
276
-	 * @throws \Exception
277
-	 * @return string|false Path info or false when not found
278
-	 * @since 8.1.0
279
-	 */
280
-	public function getPathInfo();
281
-
282
-	/**
283
-	 * Returns the script name, even if the website uses one or more
284
-	 * reverse proxies
285
-	 *
286
-	 * @return string the script name
287
-	 * @since 8.1.0
288
-	 */
289
-	public function getScriptName(): string;
290
-
291
-	/**
292
-	 * Checks whether the user agent matches a given regex
293
-	 *
294
-	 * @param array $agent array of agent names
295
-	 * @return bool true if at least one of the given agent matches, false otherwise
296
-	 * @since 8.1.0
297
-	 */
298
-	public function isUserAgent(array $agent): bool;
299
-
300
-	/**
301
-	 * Returns the unverified server host from the headers without checking
302
-	 * whether it is a trusted domain
303
-	 *
304
-	 * @psalm-taint-source input
305
-	 *
306
-	 * @return string Server host
307
-	 * @since 8.1.0
308
-	 */
309
-	public function getInsecureServerHost(): string;
310
-
311
-	/**
312
-	 * Returns the server host from the headers, or the first configured
313
-	 * trusted domain if the host isn't in the trusted list
314
-	 *
315
-	 * @return string Server host
316
-	 * @since 8.1.0
317
-	 */
318
-	public function getServerHost(): string;
65
+    /**
66
+     * @since 9.1.0
67
+     */
68
+    public const USER_AGENT_CLIENT_ANDROID = '/^Mozilla\/5\.0 \(Android\) (ownCloud|Nextcloud)\-android.*$/';
69
+
70
+    /**
71
+     * @since 13.0.0
72
+     */
73
+    public const USER_AGENT_TALK_ANDROID = '/^Mozilla\/5\.0 \(Android\) Nextcloud\-Talk v.*$/';
74
+
75
+    /**
76
+     * @since 9.1.0
77
+     */
78
+    public const USER_AGENT_CLIENT_DESKTOP = '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/';
79
+
80
+    /**
81
+     * @since 9.1.0
82
+     */
83
+    public const USER_AGENT_CLIENT_IOS = '/^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)\-iOS.*$/';
84
+
85
+    /**
86
+     * @since 13.0.0
87
+     */
88
+    public const USER_AGENT_TALK_IOS = '/^Mozilla\/5\.0 \(iOS\) Nextcloud\-Talk v.*$/';
89
+
90
+    /**
91
+     * @since 13.0.1
92
+     */
93
+    public const USER_AGENT_OUTLOOK_ADDON = '/^Mozilla\/5\.0 \([A-Za-z ]+\) Nextcloud\-Outlook v.*$/';
94
+
95
+    /**
96
+     * @since 13.0.1
97
+     */
98
+    public const USER_AGENT_THUNDERBIRD_ADDON = '/^Mozilla\/5\.0 \([A-Za-z ]+\) Nextcloud\-Thunderbird v.*$/';
99
+
100
+    /**
101
+     * @since 26.0.0
102
+     */
103
+    public const JSON_CONTENT_TYPE_REGEX = '/^application\/(?:[a-z0-9.-]+\+)?json\b/';
104
+
105
+    /**
106
+     * @param string $name
107
+     *
108
+     * @psalm-taint-source input
109
+     *
110
+     * @return string
111
+     * @since 6.0.0
112
+     */
113
+    public function getHeader(string $name): string;
114
+
115
+    /**
116
+     * Lets you access post and get parameters by the index
117
+     * In case of json requests the encoded json body is accessed
118
+     *
119
+     * @psalm-taint-source input
120
+     *
121
+     * @param string $key the key which you want to access in the URL Parameter
122
+     *                     placeholder, $_POST or $_GET array.
123
+     *                     The priority how they're returned is the following:
124
+     *                     1. URL parameters
125
+     *                     2. POST parameters
126
+     *                     3. GET parameters
127
+     * @param mixed $default If the key is not found, this value will be returned
128
+     * @return mixed the content of the array
129
+     * @since 6.0.0
130
+     */
131
+    public function getParam(string $key, $default = null);
132
+
133
+
134
+    /**
135
+     * Returns all params that were received, be it from the request
136
+     *
137
+     * (as GET or POST) or through the URL by the route
138
+     *
139
+     * @psalm-taint-source input
140
+     *
141
+     * @return array the array with all parameters
142
+     * @since 6.0.0
143
+     */
144
+    public function getParams(): array;
145
+
146
+    /**
147
+     * Returns the method of the request
148
+     *
149
+     * @return string the method of the request (POST, GET, etc)
150
+     * @since 6.0.0
151
+     */
152
+    public function getMethod(): string;
153
+
154
+    /**
155
+     * Shortcut for accessing an uploaded file through the $_FILES array
156
+     *
157
+     * @param string $key the key that will be taken from the $_FILES array
158
+     * @return array the file in the $_FILES element
159
+     * @since 6.0.0
160
+     */
161
+    public function getUploadedFile(string $key);
162
+
163
+
164
+    /**
165
+     * Shortcut for getting env variables
166
+     *
167
+     * @param string $key the key that will be taken from the $_ENV array
168
+     * @return array the value in the $_ENV element
169
+     * @since 6.0.0
170
+     */
171
+    public function getEnv(string $key);
172
+
173
+
174
+    /**
175
+     * Shortcut for getting cookie variables
176
+     *
177
+     * @psalm-taint-source input
178
+     *
179
+     * @param string $key the key that will be taken from the $_COOKIE array
180
+     * @return string|null the value in the $_COOKIE element
181
+     * @since 6.0.0
182
+     */
183
+    public function getCookie(string $key);
184
+
185
+
186
+    /**
187
+     * Checks if the CSRF check was correct
188
+     *
189
+     * @return bool true if CSRF check passed
190
+     * @since 6.0.0
191
+     */
192
+    public function passesCSRFCheck(): bool;
193
+
194
+    /**
195
+     * Checks if the strict cookie has been sent with the request if the request
196
+     * is including any cookies.
197
+     *
198
+     * @return bool
199
+     * @since 9.0.0
200
+     */
201
+    public function passesStrictCookieCheck(): bool;
202
+
203
+    /**
204
+     * Checks if the lax cookie has been sent with the request if the request
205
+     * is including any cookies.
206
+     *
207
+     * @return bool
208
+     * @since 9.0.0
209
+     */
210
+    public function passesLaxCookieCheck(): bool;
211
+
212
+    /**
213
+     * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
214
+     * If `mod_unique_id` is installed this value will be taken.
215
+     *
216
+     * @return string
217
+     * @since 8.1.0
218
+     */
219
+    public function getId(): string;
220
+
221
+    /**
222
+     * Returns the remote address, if the connection came from a trusted proxy
223
+     * and `forwarded_for_headers` has been configured then the IP address
224
+     * specified in this header will be returned instead.
225
+     * Do always use this instead of $_SERVER['REMOTE_ADDR']
226
+     *
227
+     * @return string IP address
228
+     * @since 8.1.0
229
+     */
230
+    public function getRemoteAddress(): string;
231
+
232
+    /**
233
+     * Returns the server protocol. It respects reverse proxy servers and load
234
+     * balancers.
235
+     *
236
+     * @return string Server protocol (http or https)
237
+     * @since 8.1.0
238
+     */
239
+    public function getServerProtocol(): string;
240
+
241
+    /**
242
+     * Returns the used HTTP protocol.
243
+     *
244
+     * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
245
+     * @since 8.2.0
246
+     */
247
+    public function getHttpProtocol(): string;
248
+
249
+    /**
250
+     * Returns the request uri, even if the website uses one or more
251
+     * reverse proxies
252
+     *
253
+     * @psalm-taint-source input
254
+     *
255
+     * @return string
256
+     * @since 8.1.0
257
+     */
258
+    public function getRequestUri(): string;
259
+
260
+    /**
261
+     * Get raw PathInfo from request (not urldecoded)
262
+     *
263
+     * @psalm-taint-source input
264
+     *
265
+     * @throws \Exception
266
+     * @return string Path info
267
+     * @since 8.1.0
268
+     */
269
+    public function getRawPathInfo(): string;
270
+
271
+    /**
272
+     * Get PathInfo from request
273
+     *
274
+     * @psalm-taint-source input
275
+     *
276
+     * @throws \Exception
277
+     * @return string|false Path info or false when not found
278
+     * @since 8.1.0
279
+     */
280
+    public function getPathInfo();
281
+
282
+    /**
283
+     * Returns the script name, even if the website uses one or more
284
+     * reverse proxies
285
+     *
286
+     * @return string the script name
287
+     * @since 8.1.0
288
+     */
289
+    public function getScriptName(): string;
290
+
291
+    /**
292
+     * Checks whether the user agent matches a given regex
293
+     *
294
+     * @param array $agent array of agent names
295
+     * @return bool true if at least one of the given agent matches, false otherwise
296
+     * @since 8.1.0
297
+     */
298
+    public function isUserAgent(array $agent): bool;
299
+
300
+    /**
301
+     * Returns the unverified server host from the headers without checking
302
+     * whether it is a trusted domain
303
+     *
304
+     * @psalm-taint-source input
305
+     *
306
+     * @return string Server host
307
+     * @since 8.1.0
308
+     */
309
+    public function getInsecureServerHost(): string;
310
+
311
+    /**
312
+     * Returns the server host from the headers, or the first configured
313
+     * trusted domain if the host isn't in the trusted list
314
+     *
315
+     * @return string Server host
316
+     * @since 8.1.0
317
+     */
318
+    public function getServerHost(): string;
319 319
 }
Please login to merge, or discard this patch.
lib/private/AppFramework/Http/Request.php 1 patch
Indentation   +800 added lines, -800 removed lines patch added patch discarded remove patch
@@ -65,804 +65,804 @@
 block discarded – undo
65 65
  * @property mixed[] server
66 66
  */
67 67
 class Request implements \ArrayAccess, \Countable, IRequest {
68
-	public const USER_AGENT_IE = '/(MSIE)|(Trident)/';
69
-	// Microsoft Edge User Agent from https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
70
-	public const USER_AGENT_MS_EDGE = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+ Edge\/[0-9.]+$/';
71
-	// Firefox User Agent from https://developer.mozilla.org/en-US/docs/Web/HTTP/Gecko_user_agent_string_reference
72
-	public const USER_AGENT_FIREFOX = '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/[0-9.]+$/';
73
-	// Chrome User Agent from https://developer.chrome.com/multidevice/user-agent
74
-	public const USER_AGENT_CHROME = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)( Ubuntu Chromium\/[0-9.]+|) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+( (Vivaldi|Brave|OPR)\/[0-9.]+|)$/';
75
-	// Safari User Agent from http://www.useragentstring.com/pages/Safari/
76
-	public const USER_AGENT_SAFARI = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/[0-9.]+ Safari\/[0-9.A-Z]+$/';
77
-	// Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent
78
-	public const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#';
79
-	public const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#';
80
-	public const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost|\[::1\])$/';
81
-
82
-	protected string $inputStream;
83
-	protected $content;
84
-	protected array $items = [];
85
-	protected array $allowedKeys = [
86
-		'get',
87
-		'post',
88
-		'files',
89
-		'server',
90
-		'env',
91
-		'cookies',
92
-		'urlParams',
93
-		'parameters',
94
-		'method',
95
-		'requesttoken',
96
-	];
97
-	protected IRequestId $requestId;
98
-	protected IConfig $config;
99
-	protected ?CsrfTokenManager $csrfTokenManager;
100
-
101
-	protected bool $contentDecoded = false;
102
-
103
-	/**
104
-	 * @param array $vars An associative array with the following optional values:
105
-	 *        - array 'urlParams' the parameters which were matched from the URL
106
-	 *        - array 'get' the $_GET array
107
-	 *        - array|string 'post' the $_POST array or JSON string
108
-	 *        - array 'files' the $_FILES array
109
-	 *        - array 'server' the $_SERVER array
110
-	 *        - array 'env' the $_ENV array
111
-	 *        - array 'cookies' the $_COOKIE array
112
-	 *        - string 'method' the request method (GET, POST etc)
113
-	 *        - string|false 'requesttoken' the requesttoken or false when not available
114
-	 * @param IRequestId $requestId
115
-	 * @param IConfig $config
116
-	 * @param CsrfTokenManager|null $csrfTokenManager
117
-	 * @param string $stream
118
-	 * @see https://www.php.net/manual/en/reserved.variables.php
119
-	 */
120
-	public function __construct(array $vars,
121
-								IRequestId $requestId,
122
-								IConfig $config,
123
-								CsrfTokenManager $csrfTokenManager = null,
124
-								string $stream = 'php://input') {
125
-		$this->inputStream = $stream;
126
-		$this->items['params'] = [];
127
-		$this->requestId = $requestId;
128
-		$this->config = $config;
129
-		$this->csrfTokenManager = $csrfTokenManager;
130
-
131
-		if (!array_key_exists('method', $vars)) {
132
-			$vars['method'] = 'GET';
133
-		}
134
-
135
-		foreach ($this->allowedKeys as $name) {
136
-			$this->items[$name] = $vars[$name] ?? [];
137
-		}
138
-
139
-		$this->items['parameters'] = array_merge(
140
-			$this->items['get'],
141
-			$this->items['post'],
142
-			$this->items['urlParams'],
143
-			$this->items['params']
144
-		);
145
-	}
146
-	/**
147
-	 * @param array $parameters
148
-	 */
149
-	public function setUrlParameters(array $parameters) {
150
-		$this->items['urlParams'] = $parameters;
151
-		$this->items['parameters'] = array_merge(
152
-			$this->items['parameters'],
153
-			$this->items['urlParams']
154
-		);
155
-	}
156
-
157
-	/**
158
-	 * Countable method
159
-	 * @return int
160
-	 */
161
-	public function count(): int {
162
-		return \count($this->items['parameters']);
163
-	}
164
-
165
-	/**
166
-	 * ArrayAccess methods
167
-	 *
168
-	 * Gives access to the combined GET, POST and urlParams arrays
169
-	 *
170
-	 * Examples:
171
-	 *
172
-	 * $var = $request['myvar'];
173
-	 *
174
-	 * or
175
-	 *
176
-	 * if(!isset($request['myvar']) {
177
-	 * 	// Do something
178
-	 * }
179
-	 *
180
-	 * $request['myvar'] = 'something'; // This throws an exception.
181
-	 *
182
-	 * @param string $offset The key to lookup
183
-	 * @return boolean
184
-	 */
185
-	public function offsetExists($offset): bool {
186
-		return isset($this->items['parameters'][$offset]);
187
-	}
188
-
189
-	/**
190
-	 * @see offsetExists
191
-	 * @param string $offset
192
-	 * @return mixed
193
-	 */
194
-	#[\ReturnTypeWillChange]
195
-	public function offsetGet($offset) {
196
-		return isset($this->items['parameters'][$offset])
197
-			? $this->items['parameters'][$offset]
198
-			: null;
199
-	}
200
-
201
-	/**
202
-	 * @see offsetExists
203
-	 * @param string $offset
204
-	 * @param mixed $value
205
-	 */
206
-	public function offsetSet($offset, $value): void {
207
-		throw new \RuntimeException('You cannot change the contents of the request object');
208
-	}
209
-
210
-	/**
211
-	 * @see offsetExists
212
-	 * @param string $offset
213
-	 */
214
-	public function offsetUnset($offset): void {
215
-		throw new \RuntimeException('You cannot change the contents of the request object');
216
-	}
217
-
218
-	/**
219
-	 * Magic property accessors
220
-	 * @param string $name
221
-	 * @param mixed $value
222
-	 */
223
-	public function __set($name, $value) {
224
-		throw new \RuntimeException('You cannot change the contents of the request object');
225
-	}
226
-
227
-	/**
228
-	 * Access request variables by method and name.
229
-	 * Examples:
230
-	 *
231
-	 * $request->post['myvar']; // Only look for POST variables
232
-	 * $request->myvar; or $request->{'myvar'}; or $request->{$myvar}
233
-	 * Looks in the combined GET, POST and urlParams array.
234
-	 *
235
-	 * If you access e.g. ->post but the current HTTP request method
236
-	 * is GET a \LogicException will be thrown.
237
-	 *
238
-	 * @param string $name The key to look for.
239
-	 * @throws \LogicException
240
-	 * @return mixed|null
241
-	 */
242
-	public function __get($name) {
243
-		switch ($name) {
244
-			case 'put':
245
-			case 'patch':
246
-			case 'get':
247
-			case 'post':
248
-				if ($this->method !== strtoupper($name)) {
249
-					throw new \LogicException(sprintf('%s cannot be accessed in a %s request.', $name, $this->method));
250
-				}
251
-				return $this->getContent();
252
-			case 'files':
253
-			case 'server':
254
-			case 'env':
255
-			case 'cookies':
256
-			case 'urlParams':
257
-			case 'method':
258
-				return isset($this->items[$name])
259
-					? $this->items[$name]
260
-					: null;
261
-			case 'parameters':
262
-			case 'params':
263
-				return $this->getContent();
264
-			default:
265
-				return isset($this[$name])
266
-					? $this[$name]
267
-					: null;
268
-		}
269
-	}
270
-
271
-	/**
272
-	 * @param string $name
273
-	 * @return bool
274
-	 */
275
-	public function __isset($name) {
276
-		if (\in_array($name, $this->allowedKeys, true)) {
277
-			return true;
278
-		}
279
-		return isset($this->items['parameters'][$name]);
280
-	}
281
-
282
-	/**
283
-	 * @param string $id
284
-	 */
285
-	public function __unset($id) {
286
-		throw new \RuntimeException('You cannot change the contents of the request object');
287
-	}
288
-
289
-	/**
290
-	 * Returns the value for a specific http header.
291
-	 *
292
-	 * This method returns an empty string if the header did not exist.
293
-	 *
294
-	 * @param string $name
295
-	 * @return string
296
-	 */
297
-	public function getHeader(string $name): string {
298
-		$name = strtoupper(str_replace('-', '_', $name));
299
-		if (isset($this->server['HTTP_' . $name])) {
300
-			return $this->server['HTTP_' . $name];
301
-		}
302
-
303
-		// There's a few headers that seem to end up in the top-level
304
-		// server array.
305
-		switch ($name) {
306
-			case 'CONTENT_TYPE':
307
-			case 'CONTENT_LENGTH':
308
-			case 'REMOTE_ADDR':
309
-				if (isset($this->server[$name])) {
310
-					return $this->server[$name];
311
-				}
312
-				break;
313
-		}
314
-
315
-		return '';
316
-	}
317
-
318
-	/**
319
-	 * Lets you access post and get parameters by the index
320
-	 * In case of json requests the encoded json body is accessed
321
-	 *
322
-	 * @param string $key the key which you want to access in the URL Parameter
323
-	 *                     placeholder, $_POST or $_GET array.
324
-	 *                     The priority how they're returned is the following:
325
-	 *                     1. URL parameters
326
-	 *                     2. POST parameters
327
-	 *                     3. GET parameters
328
-	 * @param mixed $default If the key is not found, this value will be returned
329
-	 * @return mixed the content of the array
330
-	 */
331
-	public function getParam(string $key, $default = null) {
332
-		return isset($this->parameters[$key])
333
-			? $this->parameters[$key]
334
-			: $default;
335
-	}
336
-
337
-	/**
338
-	 * Returns all params that were received, be it from the request
339
-	 * (as GET or POST) or through the URL by the route
340
-	 * @return array the array with all parameters
341
-	 */
342
-	public function getParams(): array {
343
-		return is_array($this->parameters) ? $this->parameters : [];
344
-	}
345
-
346
-	/**
347
-	 * Returns the method of the request
348
-	 * @return string the method of the request (POST, GET, etc)
349
-	 */
350
-	public function getMethod(): string {
351
-		return $this->method;
352
-	}
353
-
354
-	/**
355
-	 * Shortcut for accessing an uploaded file through the $_FILES array
356
-	 * @param string $key the key that will be taken from the $_FILES array
357
-	 * @return array the file in the $_FILES element
358
-	 */
359
-	public function getUploadedFile(string $key) {
360
-		return isset($this->files[$key]) ? $this->files[$key] : null;
361
-	}
362
-
363
-	/**
364
-	 * Shortcut for getting env variables
365
-	 * @param string $key the key that will be taken from the $_ENV array
366
-	 * @return array the value in the $_ENV element
367
-	 */
368
-	public function getEnv(string $key) {
369
-		return isset($this->env[$key]) ? $this->env[$key] : null;
370
-	}
371
-
372
-	/**
373
-	 * Shortcut for getting cookie variables
374
-	 * @param string $key the key that will be taken from the $_COOKIE array
375
-	 * @return string the value in the $_COOKIE element
376
-	 */
377
-	public function getCookie(string $key) {
378
-		return isset($this->cookies[$key]) ? $this->cookies[$key] : null;
379
-	}
380
-
381
-	/**
382
-	 * Returns the request body content.
383
-	 *
384
-	 * If the HTTP request method is PUT and the body
385
-	 * not application/x-www-form-urlencoded or application/json a stream
386
-	 * resource is returned, otherwise an array.
387
-	 *
388
-	 * @return array|string|resource The request body content or a resource to read the body stream.
389
-	 *
390
-	 * @throws \LogicException
391
-	 */
392
-	protected function getContent() {
393
-		// If the content can't be parsed into an array then return a stream resource.
394
-		if ($this->method === 'PUT'
395
-			&& $this->getHeader('Content-Length') !== '0'
396
-			&& $this->getHeader('Content-Length') !== ''
397
-			&& strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false
398
-			&& strpos($this->getHeader('Content-Type'), 'application/json') === false
399
-		) {
400
-			if ($this->content === false) {
401
-				throw new \LogicException(
402
-					'"put" can only be accessed once if not '
403
-					. 'application/x-www-form-urlencoded or application/json.'
404
-				);
405
-			}
406
-			$this->content = false;
407
-			return fopen($this->inputStream, 'rb');
408
-		} else {
409
-			$this->decodeContent();
410
-			return $this->items['parameters'];
411
-		}
412
-	}
413
-
414
-	/**
415
-	 * Attempt to decode the content and populate parameters
416
-	 */
417
-	protected function decodeContent() {
418
-		if ($this->contentDecoded) {
419
-			return;
420
-		}
421
-		$params = [];
422
-
423
-		// 'application/json' and other JSON-related content types must be decoded manually.
424
-		if (preg_match(self::JSON_CONTENT_TYPE_REGEX, $this->getHeader('Content-Type')) === 1) {
425
-			$params = json_decode(file_get_contents($this->inputStream), true);
426
-			if (\is_array($params) && \count($params) > 0) {
427
-				$this->items['params'] = $params;
428
-				if ($this->method === 'POST') {
429
-					$this->items['post'] = $params;
430
-				}
431
-			}
432
-			// Handle application/x-www-form-urlencoded for methods other than GET
433
-		// or post correctly
434
-		} elseif ($this->method !== 'GET'
435
-				&& $this->method !== 'POST'
436
-				&& strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) {
437
-			parse_str(file_get_contents($this->inputStream), $params);
438
-			if (\is_array($params)) {
439
-				$this->items['params'] = $params;
440
-			}
441
-		}
442
-
443
-		if (\is_array($params)) {
444
-			$this->items['parameters'] = array_merge($this->items['parameters'], $params);
445
-		}
446
-		$this->contentDecoded = true;
447
-	}
448
-
449
-
450
-	/**
451
-	 * Checks if the CSRF check was correct
452
-	 * @return bool true if CSRF check passed
453
-	 */
454
-	public function passesCSRFCheck(): bool {
455
-		if ($this->csrfTokenManager === null) {
456
-			return false;
457
-		}
458
-
459
-		if (!$this->passesStrictCookieCheck()) {
460
-			return false;
461
-		}
462
-
463
-		if (isset($this->items['get']['requesttoken'])) {
464
-			$token = $this->items['get']['requesttoken'];
465
-		} elseif (isset($this->items['post']['requesttoken'])) {
466
-			$token = $this->items['post']['requesttoken'];
467
-		} elseif (isset($this->items['server']['HTTP_REQUESTTOKEN'])) {
468
-			$token = $this->items['server']['HTTP_REQUESTTOKEN'];
469
-		} else {
470
-			//no token found.
471
-			return false;
472
-		}
473
-		$token = new CsrfToken($token);
474
-
475
-		return $this->csrfTokenManager->isTokenValid($token);
476
-	}
477
-
478
-	/**
479
-	 * Whether the cookie checks are required
480
-	 *
481
-	 * @return bool
482
-	 */
483
-	private function cookieCheckRequired(): bool {
484
-		if ($this->getHeader('OCS-APIREQUEST')) {
485
-			return false;
486
-		}
487
-		if ($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
488
-			return false;
489
-		}
490
-
491
-		return true;
492
-	}
493
-
494
-	/**
495
-	 * Wrapper around session_get_cookie_params
496
-	 *
497
-	 * @return array
498
-	 */
499
-	public function getCookieParams(): array {
500
-		return session_get_cookie_params();
501
-	}
502
-
503
-	/**
504
-	 * Appends the __Host- prefix to the cookie if applicable
505
-	 *
506
-	 * @param string $name
507
-	 * @return string
508
-	 */
509
-	protected function getProtectedCookieName(string $name): string {
510
-		$cookieParams = $this->getCookieParams();
511
-		$prefix = '';
512
-		if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
513
-			$prefix = '__Host-';
514
-		}
515
-
516
-		return $prefix.$name;
517
-	}
518
-
519
-	/**
520
-	 * Checks if the strict cookie has been sent with the request if the request
521
-	 * is including any cookies.
522
-	 *
523
-	 * @return bool
524
-	 * @since 9.1.0
525
-	 */
526
-	public function passesStrictCookieCheck(): bool {
527
-		if (!$this->cookieCheckRequired()) {
528
-			return true;
529
-		}
530
-
531
-		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict');
532
-		if ($this->getCookie($cookieName) === 'true'
533
-			&& $this->passesLaxCookieCheck()) {
534
-			return true;
535
-		}
536
-		return false;
537
-	}
538
-
539
-	/**
540
-	 * Checks if the lax cookie has been sent with the request if the request
541
-	 * is including any cookies.
542
-	 *
543
-	 * @return bool
544
-	 * @since 9.1.0
545
-	 */
546
-	public function passesLaxCookieCheck(): bool {
547
-		if (!$this->cookieCheckRequired()) {
548
-			return true;
549
-		}
550
-
551
-		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax');
552
-		if ($this->getCookie($cookieName) === 'true') {
553
-			return true;
554
-		}
555
-		return false;
556
-	}
557
-
558
-
559
-	/**
560
-	 * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
561
-	 * If `mod_unique_id` is installed this value will be taken.
562
-	 * @return string
563
-	 */
564
-	public function getId(): string {
565
-		return $this->requestId->getId();
566
-	}
567
-
568
-	/**
569
-	 * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
570
-	 * For details regarding what "match" means, refer to `matchesTrustedProxy`.
571
-	 * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
572
-	 */
573
-	protected function isTrustedProxy($trustedProxies, $remoteAddress) {
574
-		return IpUtils::checkIp($remoteAddress, $trustedProxies);
575
-	}
576
-
577
-	/**
578
-	 * Returns the remote address, if the connection came from a trusted proxy
579
-	 * and `forwarded_for_headers` has been configured then the IP address
580
-	 * specified in this header will be returned instead.
581
-	 * Do always use this instead of $_SERVER['REMOTE_ADDR']
582
-	 * @return string IP address
583
-	 */
584
-	public function getRemoteAddress(): string {
585
-		$remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
586
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
587
-
588
-		if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
589
-			$forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
590
-				'HTTP_X_FORWARDED_FOR'
591
-				// only have one default, so we cannot ship an insecure product out of the box
592
-			]);
593
-
594
-			foreach ($forwardedForHeaders as $header) {
595
-				if (isset($this->server[$header])) {
596
-					foreach (explode(',', $this->server[$header]) as $IP) {
597
-						$IP = trim($IP);
598
-
599
-						// remove brackets from IPv6 addresses
600
-						if (strpos($IP, '[') === 0 && substr($IP, -1) === ']') {
601
-							$IP = substr($IP, 1, -1);
602
-						}
603
-
604
-						if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
605
-							return $IP;
606
-						}
607
-					}
608
-				}
609
-			}
610
-		}
611
-
612
-		return $remoteAddress;
613
-	}
614
-
615
-	/**
616
-	 * Check overwrite condition
617
-	 * @param string $type
618
-	 * @return bool
619
-	 */
620
-	private function isOverwriteCondition(string $type = ''): bool {
621
-		$regex = '/' . $this->config->getSystemValue('overwritecondaddr', '')  . '/';
622
-		$remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
623
-		return $regex === '//' || preg_match($regex, $remoteAddr) === 1
624
-		|| $type !== 'protocol';
625
-	}
626
-
627
-	/**
628
-	 * Returns the server protocol. It respects one or more reverse proxies servers
629
-	 * and load balancers
630
-	 * @return string Server protocol (http or https)
631
-	 */
632
-	public function getServerProtocol(): string {
633
-		if ($this->config->getSystemValue('overwriteprotocol') !== ''
634
-			&& $this->isOverwriteCondition('protocol')) {
635
-			return $this->config->getSystemValue('overwriteprotocol');
636
-		}
637
-
638
-		if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
639
-			if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) {
640
-				$parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
641
-				$proto = strtolower(trim($parts[0]));
642
-			} else {
643
-				$proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']);
644
-			}
645
-
646
-			// Verify that the protocol is always HTTP or HTTPS
647
-			// default to http if an invalid value is provided
648
-			return $proto === 'https' ? 'https' : 'http';
649
-		}
650
-
651
-		if (isset($this->server['HTTPS'])
652
-			&& $this->server['HTTPS'] !== null
653
-			&& $this->server['HTTPS'] !== 'off'
654
-			&& $this->server['HTTPS'] !== '') {
655
-			return 'https';
656
-		}
657
-
658
-		return 'http';
659
-	}
660
-
661
-	/**
662
-	 * Returns the used HTTP protocol.
663
-	 *
664
-	 * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
665
-	 */
666
-	public function getHttpProtocol(): string {
667
-		$claimedProtocol = $this->server['SERVER_PROTOCOL'];
668
-
669
-		if (\is_string($claimedProtocol)) {
670
-			$claimedProtocol = strtoupper($claimedProtocol);
671
-		}
672
-
673
-		$validProtocols = [
674
-			'HTTP/1.0',
675
-			'HTTP/1.1',
676
-			'HTTP/2',
677
-		];
678
-
679
-		if (\in_array($claimedProtocol, $validProtocols, true)) {
680
-			return $claimedProtocol;
681
-		}
682
-
683
-		return 'HTTP/1.1';
684
-	}
685
-
686
-	/**
687
-	 * Returns the request uri, even if the website uses one or more
688
-	 * reverse proxies
689
-	 * @return string
690
-	 */
691
-	public function getRequestUri(): string {
692
-		$uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
693
-		if ($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
694
-			$uri = $this->getScriptName() . substr($uri, \strlen($this->server['SCRIPT_NAME']));
695
-		}
696
-		return $uri;
697
-	}
698
-
699
-	/**
700
-	 * Get raw PathInfo from request (not urldecoded)
701
-	 * @throws \Exception
702
-	 * @return string Path info
703
-	 */
704
-	public function getRawPathInfo(): string {
705
-		$requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
706
-		// remove too many slashes - can be caused by reverse proxy configuration
707
-		$requestUri = preg_replace('%/{2,}%', '/', $requestUri);
708
-
709
-		// Remove the query string from REQUEST_URI
710
-		if ($pos = strpos($requestUri, '?')) {
711
-			$requestUri = substr($requestUri, 0, $pos);
712
-		}
713
-
714
-		$scriptName = $this->server['SCRIPT_NAME'];
715
-		$pathInfo = $requestUri;
716
-
717
-		// strip off the script name's dir and file name
718
-		// FIXME: Sabre does not really belong here
719
-		[$path, $name] = \Sabre\Uri\split($scriptName);
720
-		if (!empty($path)) {
721
-			if ($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) {
722
-				$pathInfo = substr($pathInfo, \strlen($path));
723
-			} else {
724
-				throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
725
-			}
726
-		}
727
-		if ($name === null) {
728
-			$name = '';
729
-		}
730
-
731
-		if (strpos($pathInfo, '/'.$name) === 0) {
732
-			$pathInfo = substr($pathInfo, \strlen($name) + 1);
733
-		}
734
-		if ($name !== '' && strpos($pathInfo, $name) === 0) {
735
-			$pathInfo = substr($pathInfo, \strlen($name));
736
-		}
737
-		if ($pathInfo === false || $pathInfo === '/') {
738
-			return '';
739
-		} else {
740
-			return $pathInfo;
741
-		}
742
-	}
743
-
744
-	/**
745
-	 * Get PathInfo from request
746
-	 * @throws \Exception
747
-	 * @return string|false Path info or false when not found
748
-	 */
749
-	public function getPathInfo() {
750
-		$pathInfo = $this->getRawPathInfo();
751
-		return \Sabre\HTTP\decodePath($pathInfo);
752
-	}
753
-
754
-	/**
755
-	 * Returns the script name, even if the website uses one or more
756
-	 * reverse proxies
757
-	 * @return string the script name
758
-	 */
759
-	public function getScriptName(): string {
760
-		$name = $this->server['SCRIPT_NAME'];
761
-		$overwriteWebRoot = $this->config->getSystemValue('overwritewebroot');
762
-		if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) {
763
-			// FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
764
-			$serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -\strlen('lib/private/appframework/http/')));
765
-			$suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), \strlen($serverRoot)));
766
-			$name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
767
-		}
768
-		return $name;
769
-	}
770
-
771
-	/**
772
-	 * Checks whether the user agent matches a given regex
773
-	 * @param array $agent array of agent names
774
-	 * @return bool true if at least one of the given agent matches, false otherwise
775
-	 */
776
-	public function isUserAgent(array $agent): bool {
777
-		if (!isset($this->server['HTTP_USER_AGENT'])) {
778
-			return false;
779
-		}
780
-		foreach ($agent as $regex) {
781
-			if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) {
782
-				return true;
783
-			}
784
-		}
785
-		return false;
786
-	}
787
-
788
-	/**
789
-	 * Returns the unverified server host from the headers without checking
790
-	 * whether it is a trusted domain
791
-	 * @return string Server host
792
-	 */
793
-	public function getInsecureServerHost(): string {
794
-		if ($this->fromTrustedProxy() && $this->getOverwriteHost() !== null) {
795
-			return $this->getOverwriteHost();
796
-		}
797
-
798
-		$host = 'localhost';
799
-		if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_HOST'])) {
800
-			if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) {
801
-				$parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']);
802
-				$host = trim(current($parts));
803
-			} else {
804
-				$host = $this->server['HTTP_X_FORWARDED_HOST'];
805
-			}
806
-		} else {
807
-			if (isset($this->server['HTTP_HOST'])) {
808
-				$host = $this->server['HTTP_HOST'];
809
-			} elseif (isset($this->server['SERVER_NAME'])) {
810
-				$host = $this->server['SERVER_NAME'];
811
-			}
812
-		}
813
-
814
-		return $host;
815
-	}
816
-
817
-
818
-	/**
819
-	 * Returns the server host from the headers, or the first configured
820
-	 * trusted domain if the host isn't in the trusted list
821
-	 * @return string Server host
822
-	 */
823
-	public function getServerHost(): string {
824
-		// overwritehost is always trusted
825
-		$host = $this->getOverwriteHost();
826
-		if ($host !== null) {
827
-			return $host;
828
-		}
829
-
830
-		// get the host from the headers
831
-		$host = $this->getInsecureServerHost();
832
-
833
-		// Verify that the host is a trusted domain if the trusted domains
834
-		// are defined
835
-		// If no trusted domain is provided the first trusted domain is returned
836
-		$trustedDomainHelper = new TrustedDomainHelper($this->config);
837
-		if ($trustedDomainHelper->isTrustedDomain($host)) {
838
-			return $host;
839
-		}
840
-
841
-		$trustedList = (array)$this->config->getSystemValue('trusted_domains', []);
842
-		if (count($trustedList) > 0) {
843
-			return reset($trustedList);
844
-		}
845
-
846
-		return '';
847
-	}
848
-
849
-	/**
850
-	 * Returns the overwritehost setting from the config if set and
851
-	 * if the overwrite condition is met
852
-	 * @return string|null overwritehost value or null if not defined or the defined condition
853
-	 * isn't met
854
-	 */
855
-	private function getOverwriteHost() {
856
-		if ($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) {
857
-			return $this->config->getSystemValue('overwritehost');
858
-		}
859
-		return null;
860
-	}
861
-
862
-	private function fromTrustedProxy(): bool {
863
-		$remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
864
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
865
-
866
-		return \is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress);
867
-	}
68
+    public const USER_AGENT_IE = '/(MSIE)|(Trident)/';
69
+    // Microsoft Edge User Agent from https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
70
+    public const USER_AGENT_MS_EDGE = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+ Edge\/[0-9.]+$/';
71
+    // Firefox User Agent from https://developer.mozilla.org/en-US/docs/Web/HTTP/Gecko_user_agent_string_reference
72
+    public const USER_AGENT_FIREFOX = '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/[0-9.]+$/';
73
+    // Chrome User Agent from https://developer.chrome.com/multidevice/user-agent
74
+    public const USER_AGENT_CHROME = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)( Ubuntu Chromium\/[0-9.]+|) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+( (Vivaldi|Brave|OPR)\/[0-9.]+|)$/';
75
+    // Safari User Agent from http://www.useragentstring.com/pages/Safari/
76
+    public const USER_AGENT_SAFARI = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/[0-9.]+ Safari\/[0-9.A-Z]+$/';
77
+    // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent
78
+    public const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#';
79
+    public const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#';
80
+    public const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost|\[::1\])$/';
81
+
82
+    protected string $inputStream;
83
+    protected $content;
84
+    protected array $items = [];
85
+    protected array $allowedKeys = [
86
+        'get',
87
+        'post',
88
+        'files',
89
+        'server',
90
+        'env',
91
+        'cookies',
92
+        'urlParams',
93
+        'parameters',
94
+        'method',
95
+        'requesttoken',
96
+    ];
97
+    protected IRequestId $requestId;
98
+    protected IConfig $config;
99
+    protected ?CsrfTokenManager $csrfTokenManager;
100
+
101
+    protected bool $contentDecoded = false;
102
+
103
+    /**
104
+     * @param array $vars An associative array with the following optional values:
105
+     *        - array 'urlParams' the parameters which were matched from the URL
106
+     *        - array 'get' the $_GET array
107
+     *        - array|string 'post' the $_POST array or JSON string
108
+     *        - array 'files' the $_FILES array
109
+     *        - array 'server' the $_SERVER array
110
+     *        - array 'env' the $_ENV array
111
+     *        - array 'cookies' the $_COOKIE array
112
+     *        - string 'method' the request method (GET, POST etc)
113
+     *        - string|false 'requesttoken' the requesttoken or false when not available
114
+     * @param IRequestId $requestId
115
+     * @param IConfig $config
116
+     * @param CsrfTokenManager|null $csrfTokenManager
117
+     * @param string $stream
118
+     * @see https://www.php.net/manual/en/reserved.variables.php
119
+     */
120
+    public function __construct(array $vars,
121
+                                IRequestId $requestId,
122
+                                IConfig $config,
123
+                                CsrfTokenManager $csrfTokenManager = null,
124
+                                string $stream = 'php://input') {
125
+        $this->inputStream = $stream;
126
+        $this->items['params'] = [];
127
+        $this->requestId = $requestId;
128
+        $this->config = $config;
129
+        $this->csrfTokenManager = $csrfTokenManager;
130
+
131
+        if (!array_key_exists('method', $vars)) {
132
+            $vars['method'] = 'GET';
133
+        }
134
+
135
+        foreach ($this->allowedKeys as $name) {
136
+            $this->items[$name] = $vars[$name] ?? [];
137
+        }
138
+
139
+        $this->items['parameters'] = array_merge(
140
+            $this->items['get'],
141
+            $this->items['post'],
142
+            $this->items['urlParams'],
143
+            $this->items['params']
144
+        );
145
+    }
146
+    /**
147
+     * @param array $parameters
148
+     */
149
+    public function setUrlParameters(array $parameters) {
150
+        $this->items['urlParams'] = $parameters;
151
+        $this->items['parameters'] = array_merge(
152
+            $this->items['parameters'],
153
+            $this->items['urlParams']
154
+        );
155
+    }
156
+
157
+    /**
158
+     * Countable method
159
+     * @return int
160
+     */
161
+    public function count(): int {
162
+        return \count($this->items['parameters']);
163
+    }
164
+
165
+    /**
166
+     * ArrayAccess methods
167
+     *
168
+     * Gives access to the combined GET, POST and urlParams arrays
169
+     *
170
+     * Examples:
171
+     *
172
+     * $var = $request['myvar'];
173
+     *
174
+     * or
175
+     *
176
+     * if(!isset($request['myvar']) {
177
+     * 	// Do something
178
+     * }
179
+     *
180
+     * $request['myvar'] = 'something'; // This throws an exception.
181
+     *
182
+     * @param string $offset The key to lookup
183
+     * @return boolean
184
+     */
185
+    public function offsetExists($offset): bool {
186
+        return isset($this->items['parameters'][$offset]);
187
+    }
188
+
189
+    /**
190
+     * @see offsetExists
191
+     * @param string $offset
192
+     * @return mixed
193
+     */
194
+    #[\ReturnTypeWillChange]
195
+    public function offsetGet($offset) {
196
+        return isset($this->items['parameters'][$offset])
197
+            ? $this->items['parameters'][$offset]
198
+            : null;
199
+    }
200
+
201
+    /**
202
+     * @see offsetExists
203
+     * @param string $offset
204
+     * @param mixed $value
205
+     */
206
+    public function offsetSet($offset, $value): void {
207
+        throw new \RuntimeException('You cannot change the contents of the request object');
208
+    }
209
+
210
+    /**
211
+     * @see offsetExists
212
+     * @param string $offset
213
+     */
214
+    public function offsetUnset($offset): void {
215
+        throw new \RuntimeException('You cannot change the contents of the request object');
216
+    }
217
+
218
+    /**
219
+     * Magic property accessors
220
+     * @param string $name
221
+     * @param mixed $value
222
+     */
223
+    public function __set($name, $value) {
224
+        throw new \RuntimeException('You cannot change the contents of the request object');
225
+    }
226
+
227
+    /**
228
+     * Access request variables by method and name.
229
+     * Examples:
230
+     *
231
+     * $request->post['myvar']; // Only look for POST variables
232
+     * $request->myvar; or $request->{'myvar'}; or $request->{$myvar}
233
+     * Looks in the combined GET, POST and urlParams array.
234
+     *
235
+     * If you access e.g. ->post but the current HTTP request method
236
+     * is GET a \LogicException will be thrown.
237
+     *
238
+     * @param string $name The key to look for.
239
+     * @throws \LogicException
240
+     * @return mixed|null
241
+     */
242
+    public function __get($name) {
243
+        switch ($name) {
244
+            case 'put':
245
+            case 'patch':
246
+            case 'get':
247
+            case 'post':
248
+                if ($this->method !== strtoupper($name)) {
249
+                    throw new \LogicException(sprintf('%s cannot be accessed in a %s request.', $name, $this->method));
250
+                }
251
+                return $this->getContent();
252
+            case 'files':
253
+            case 'server':
254
+            case 'env':
255
+            case 'cookies':
256
+            case 'urlParams':
257
+            case 'method':
258
+                return isset($this->items[$name])
259
+                    ? $this->items[$name]
260
+                    : null;
261
+            case 'parameters':
262
+            case 'params':
263
+                return $this->getContent();
264
+            default:
265
+                return isset($this[$name])
266
+                    ? $this[$name]
267
+                    : null;
268
+        }
269
+    }
270
+
271
+    /**
272
+     * @param string $name
273
+     * @return bool
274
+     */
275
+    public function __isset($name) {
276
+        if (\in_array($name, $this->allowedKeys, true)) {
277
+            return true;
278
+        }
279
+        return isset($this->items['parameters'][$name]);
280
+    }
281
+
282
+    /**
283
+     * @param string $id
284
+     */
285
+    public function __unset($id) {
286
+        throw new \RuntimeException('You cannot change the contents of the request object');
287
+    }
288
+
289
+    /**
290
+     * Returns the value for a specific http header.
291
+     *
292
+     * This method returns an empty string if the header did not exist.
293
+     *
294
+     * @param string $name
295
+     * @return string
296
+     */
297
+    public function getHeader(string $name): string {
298
+        $name = strtoupper(str_replace('-', '_', $name));
299
+        if (isset($this->server['HTTP_' . $name])) {
300
+            return $this->server['HTTP_' . $name];
301
+        }
302
+
303
+        // There's a few headers that seem to end up in the top-level
304
+        // server array.
305
+        switch ($name) {
306
+            case 'CONTENT_TYPE':
307
+            case 'CONTENT_LENGTH':
308
+            case 'REMOTE_ADDR':
309
+                if (isset($this->server[$name])) {
310
+                    return $this->server[$name];
311
+                }
312
+                break;
313
+        }
314
+
315
+        return '';
316
+    }
317
+
318
+    /**
319
+     * Lets you access post and get parameters by the index
320
+     * In case of json requests the encoded json body is accessed
321
+     *
322
+     * @param string $key the key which you want to access in the URL Parameter
323
+     *                     placeholder, $_POST or $_GET array.
324
+     *                     The priority how they're returned is the following:
325
+     *                     1. URL parameters
326
+     *                     2. POST parameters
327
+     *                     3. GET parameters
328
+     * @param mixed $default If the key is not found, this value will be returned
329
+     * @return mixed the content of the array
330
+     */
331
+    public function getParam(string $key, $default = null) {
332
+        return isset($this->parameters[$key])
333
+            ? $this->parameters[$key]
334
+            : $default;
335
+    }
336
+
337
+    /**
338
+     * Returns all params that were received, be it from the request
339
+     * (as GET or POST) or through the URL by the route
340
+     * @return array the array with all parameters
341
+     */
342
+    public function getParams(): array {
343
+        return is_array($this->parameters) ? $this->parameters : [];
344
+    }
345
+
346
+    /**
347
+     * Returns the method of the request
348
+     * @return string the method of the request (POST, GET, etc)
349
+     */
350
+    public function getMethod(): string {
351
+        return $this->method;
352
+    }
353
+
354
+    /**
355
+     * Shortcut for accessing an uploaded file through the $_FILES array
356
+     * @param string $key the key that will be taken from the $_FILES array
357
+     * @return array the file in the $_FILES element
358
+     */
359
+    public function getUploadedFile(string $key) {
360
+        return isset($this->files[$key]) ? $this->files[$key] : null;
361
+    }
362
+
363
+    /**
364
+     * Shortcut for getting env variables
365
+     * @param string $key the key that will be taken from the $_ENV array
366
+     * @return array the value in the $_ENV element
367
+     */
368
+    public function getEnv(string $key) {
369
+        return isset($this->env[$key]) ? $this->env[$key] : null;
370
+    }
371
+
372
+    /**
373
+     * Shortcut for getting cookie variables
374
+     * @param string $key the key that will be taken from the $_COOKIE array
375
+     * @return string the value in the $_COOKIE element
376
+     */
377
+    public function getCookie(string $key) {
378
+        return isset($this->cookies[$key]) ? $this->cookies[$key] : null;
379
+    }
380
+
381
+    /**
382
+     * Returns the request body content.
383
+     *
384
+     * If the HTTP request method is PUT and the body
385
+     * not application/x-www-form-urlencoded or application/json a stream
386
+     * resource is returned, otherwise an array.
387
+     *
388
+     * @return array|string|resource The request body content or a resource to read the body stream.
389
+     *
390
+     * @throws \LogicException
391
+     */
392
+    protected function getContent() {
393
+        // If the content can't be parsed into an array then return a stream resource.
394
+        if ($this->method === 'PUT'
395
+            && $this->getHeader('Content-Length') !== '0'
396
+            && $this->getHeader('Content-Length') !== ''
397
+            && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false
398
+            && strpos($this->getHeader('Content-Type'), 'application/json') === false
399
+        ) {
400
+            if ($this->content === false) {
401
+                throw new \LogicException(
402
+                    '"put" can only be accessed once if not '
403
+                    . 'application/x-www-form-urlencoded or application/json.'
404
+                );
405
+            }
406
+            $this->content = false;
407
+            return fopen($this->inputStream, 'rb');
408
+        } else {
409
+            $this->decodeContent();
410
+            return $this->items['parameters'];
411
+        }
412
+    }
413
+
414
+    /**
415
+     * Attempt to decode the content and populate parameters
416
+     */
417
+    protected function decodeContent() {
418
+        if ($this->contentDecoded) {
419
+            return;
420
+        }
421
+        $params = [];
422
+
423
+        // 'application/json' and other JSON-related content types must be decoded manually.
424
+        if (preg_match(self::JSON_CONTENT_TYPE_REGEX, $this->getHeader('Content-Type')) === 1) {
425
+            $params = json_decode(file_get_contents($this->inputStream), true);
426
+            if (\is_array($params) && \count($params) > 0) {
427
+                $this->items['params'] = $params;
428
+                if ($this->method === 'POST') {
429
+                    $this->items['post'] = $params;
430
+                }
431
+            }
432
+            // Handle application/x-www-form-urlencoded for methods other than GET
433
+        // or post correctly
434
+        } elseif ($this->method !== 'GET'
435
+                && $this->method !== 'POST'
436
+                && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) {
437
+            parse_str(file_get_contents($this->inputStream), $params);
438
+            if (\is_array($params)) {
439
+                $this->items['params'] = $params;
440
+            }
441
+        }
442
+
443
+        if (\is_array($params)) {
444
+            $this->items['parameters'] = array_merge($this->items['parameters'], $params);
445
+        }
446
+        $this->contentDecoded = true;
447
+    }
448
+
449
+
450
+    /**
451
+     * Checks if the CSRF check was correct
452
+     * @return bool true if CSRF check passed
453
+     */
454
+    public function passesCSRFCheck(): bool {
455
+        if ($this->csrfTokenManager === null) {
456
+            return false;
457
+        }
458
+
459
+        if (!$this->passesStrictCookieCheck()) {
460
+            return false;
461
+        }
462
+
463
+        if (isset($this->items['get']['requesttoken'])) {
464
+            $token = $this->items['get']['requesttoken'];
465
+        } elseif (isset($this->items['post']['requesttoken'])) {
466
+            $token = $this->items['post']['requesttoken'];
467
+        } elseif (isset($this->items['server']['HTTP_REQUESTTOKEN'])) {
468
+            $token = $this->items['server']['HTTP_REQUESTTOKEN'];
469
+        } else {
470
+            //no token found.
471
+            return false;
472
+        }
473
+        $token = new CsrfToken($token);
474
+
475
+        return $this->csrfTokenManager->isTokenValid($token);
476
+    }
477
+
478
+    /**
479
+     * Whether the cookie checks are required
480
+     *
481
+     * @return bool
482
+     */
483
+    private function cookieCheckRequired(): bool {
484
+        if ($this->getHeader('OCS-APIREQUEST')) {
485
+            return false;
486
+        }
487
+        if ($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
488
+            return false;
489
+        }
490
+
491
+        return true;
492
+    }
493
+
494
+    /**
495
+     * Wrapper around session_get_cookie_params
496
+     *
497
+     * @return array
498
+     */
499
+    public function getCookieParams(): array {
500
+        return session_get_cookie_params();
501
+    }
502
+
503
+    /**
504
+     * Appends the __Host- prefix to the cookie if applicable
505
+     *
506
+     * @param string $name
507
+     * @return string
508
+     */
509
+    protected function getProtectedCookieName(string $name): string {
510
+        $cookieParams = $this->getCookieParams();
511
+        $prefix = '';
512
+        if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
513
+            $prefix = '__Host-';
514
+        }
515
+
516
+        return $prefix.$name;
517
+    }
518
+
519
+    /**
520
+     * Checks if the strict cookie has been sent with the request if the request
521
+     * is including any cookies.
522
+     *
523
+     * @return bool
524
+     * @since 9.1.0
525
+     */
526
+    public function passesStrictCookieCheck(): bool {
527
+        if (!$this->cookieCheckRequired()) {
528
+            return true;
529
+        }
530
+
531
+        $cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict');
532
+        if ($this->getCookie($cookieName) === 'true'
533
+            && $this->passesLaxCookieCheck()) {
534
+            return true;
535
+        }
536
+        return false;
537
+    }
538
+
539
+    /**
540
+     * Checks if the lax cookie has been sent with the request if the request
541
+     * is including any cookies.
542
+     *
543
+     * @return bool
544
+     * @since 9.1.0
545
+     */
546
+    public function passesLaxCookieCheck(): bool {
547
+        if (!$this->cookieCheckRequired()) {
548
+            return true;
549
+        }
550
+
551
+        $cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax');
552
+        if ($this->getCookie($cookieName) === 'true') {
553
+            return true;
554
+        }
555
+        return false;
556
+    }
557
+
558
+
559
+    /**
560
+     * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
561
+     * If `mod_unique_id` is installed this value will be taken.
562
+     * @return string
563
+     */
564
+    public function getId(): string {
565
+        return $this->requestId->getId();
566
+    }
567
+
568
+    /**
569
+     * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
570
+     * For details regarding what "match" means, refer to `matchesTrustedProxy`.
571
+     * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
572
+     */
573
+    protected function isTrustedProxy($trustedProxies, $remoteAddress) {
574
+        return IpUtils::checkIp($remoteAddress, $trustedProxies);
575
+    }
576
+
577
+    /**
578
+     * Returns the remote address, if the connection came from a trusted proxy
579
+     * and `forwarded_for_headers` has been configured then the IP address
580
+     * specified in this header will be returned instead.
581
+     * Do always use this instead of $_SERVER['REMOTE_ADDR']
582
+     * @return string IP address
583
+     */
584
+    public function getRemoteAddress(): string {
585
+        $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
586
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
587
+
588
+        if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
589
+            $forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
590
+                'HTTP_X_FORWARDED_FOR'
591
+                // only have one default, so we cannot ship an insecure product out of the box
592
+            ]);
593
+
594
+            foreach ($forwardedForHeaders as $header) {
595
+                if (isset($this->server[$header])) {
596
+                    foreach (explode(',', $this->server[$header]) as $IP) {
597
+                        $IP = trim($IP);
598
+
599
+                        // remove brackets from IPv6 addresses
600
+                        if (strpos($IP, '[') === 0 && substr($IP, -1) === ']') {
601
+                            $IP = substr($IP, 1, -1);
602
+                        }
603
+
604
+                        if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
605
+                            return $IP;
606
+                        }
607
+                    }
608
+                }
609
+            }
610
+        }
611
+
612
+        return $remoteAddress;
613
+    }
614
+
615
+    /**
616
+     * Check overwrite condition
617
+     * @param string $type
618
+     * @return bool
619
+     */
620
+    private function isOverwriteCondition(string $type = ''): bool {
621
+        $regex = '/' . $this->config->getSystemValue('overwritecondaddr', '')  . '/';
622
+        $remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
623
+        return $regex === '//' || preg_match($regex, $remoteAddr) === 1
624
+        || $type !== 'protocol';
625
+    }
626
+
627
+    /**
628
+     * Returns the server protocol. It respects one or more reverse proxies servers
629
+     * and load balancers
630
+     * @return string Server protocol (http or https)
631
+     */
632
+    public function getServerProtocol(): string {
633
+        if ($this->config->getSystemValue('overwriteprotocol') !== ''
634
+            && $this->isOverwriteCondition('protocol')) {
635
+            return $this->config->getSystemValue('overwriteprotocol');
636
+        }
637
+
638
+        if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
639
+            if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) {
640
+                $parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
641
+                $proto = strtolower(trim($parts[0]));
642
+            } else {
643
+                $proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']);
644
+            }
645
+
646
+            // Verify that the protocol is always HTTP or HTTPS
647
+            // default to http if an invalid value is provided
648
+            return $proto === 'https' ? 'https' : 'http';
649
+        }
650
+
651
+        if (isset($this->server['HTTPS'])
652
+            && $this->server['HTTPS'] !== null
653
+            && $this->server['HTTPS'] !== 'off'
654
+            && $this->server['HTTPS'] !== '') {
655
+            return 'https';
656
+        }
657
+
658
+        return 'http';
659
+    }
660
+
661
+    /**
662
+     * Returns the used HTTP protocol.
663
+     *
664
+     * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
665
+     */
666
+    public function getHttpProtocol(): string {
667
+        $claimedProtocol = $this->server['SERVER_PROTOCOL'];
668
+
669
+        if (\is_string($claimedProtocol)) {
670
+            $claimedProtocol = strtoupper($claimedProtocol);
671
+        }
672
+
673
+        $validProtocols = [
674
+            'HTTP/1.0',
675
+            'HTTP/1.1',
676
+            'HTTP/2',
677
+        ];
678
+
679
+        if (\in_array($claimedProtocol, $validProtocols, true)) {
680
+            return $claimedProtocol;
681
+        }
682
+
683
+        return 'HTTP/1.1';
684
+    }
685
+
686
+    /**
687
+     * Returns the request uri, even if the website uses one or more
688
+     * reverse proxies
689
+     * @return string
690
+     */
691
+    public function getRequestUri(): string {
692
+        $uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
693
+        if ($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
694
+            $uri = $this->getScriptName() . substr($uri, \strlen($this->server['SCRIPT_NAME']));
695
+        }
696
+        return $uri;
697
+    }
698
+
699
+    /**
700
+     * Get raw PathInfo from request (not urldecoded)
701
+     * @throws \Exception
702
+     * @return string Path info
703
+     */
704
+    public function getRawPathInfo(): string {
705
+        $requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
706
+        // remove too many slashes - can be caused by reverse proxy configuration
707
+        $requestUri = preg_replace('%/{2,}%', '/', $requestUri);
708
+
709
+        // Remove the query string from REQUEST_URI
710
+        if ($pos = strpos($requestUri, '?')) {
711
+            $requestUri = substr($requestUri, 0, $pos);
712
+        }
713
+
714
+        $scriptName = $this->server['SCRIPT_NAME'];
715
+        $pathInfo = $requestUri;
716
+
717
+        // strip off the script name's dir and file name
718
+        // FIXME: Sabre does not really belong here
719
+        [$path, $name] = \Sabre\Uri\split($scriptName);
720
+        if (!empty($path)) {
721
+            if ($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) {
722
+                $pathInfo = substr($pathInfo, \strlen($path));
723
+            } else {
724
+                throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
725
+            }
726
+        }
727
+        if ($name === null) {
728
+            $name = '';
729
+        }
730
+
731
+        if (strpos($pathInfo, '/'.$name) === 0) {
732
+            $pathInfo = substr($pathInfo, \strlen($name) + 1);
733
+        }
734
+        if ($name !== '' && strpos($pathInfo, $name) === 0) {
735
+            $pathInfo = substr($pathInfo, \strlen($name));
736
+        }
737
+        if ($pathInfo === false || $pathInfo === '/') {
738
+            return '';
739
+        } else {
740
+            return $pathInfo;
741
+        }
742
+    }
743
+
744
+    /**
745
+     * Get PathInfo from request
746
+     * @throws \Exception
747
+     * @return string|false Path info or false when not found
748
+     */
749
+    public function getPathInfo() {
750
+        $pathInfo = $this->getRawPathInfo();
751
+        return \Sabre\HTTP\decodePath($pathInfo);
752
+    }
753
+
754
+    /**
755
+     * Returns the script name, even if the website uses one or more
756
+     * reverse proxies
757
+     * @return string the script name
758
+     */
759
+    public function getScriptName(): string {
760
+        $name = $this->server['SCRIPT_NAME'];
761
+        $overwriteWebRoot = $this->config->getSystemValue('overwritewebroot');
762
+        if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) {
763
+            // FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
764
+            $serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -\strlen('lib/private/appframework/http/')));
765
+            $suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), \strlen($serverRoot)));
766
+            $name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
767
+        }
768
+        return $name;
769
+    }
770
+
771
+    /**
772
+     * Checks whether the user agent matches a given regex
773
+     * @param array $agent array of agent names
774
+     * @return bool true if at least one of the given agent matches, false otherwise
775
+     */
776
+    public function isUserAgent(array $agent): bool {
777
+        if (!isset($this->server['HTTP_USER_AGENT'])) {
778
+            return false;
779
+        }
780
+        foreach ($agent as $regex) {
781
+            if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) {
782
+                return true;
783
+            }
784
+        }
785
+        return false;
786
+    }
787
+
788
+    /**
789
+     * Returns the unverified server host from the headers without checking
790
+     * whether it is a trusted domain
791
+     * @return string Server host
792
+     */
793
+    public function getInsecureServerHost(): string {
794
+        if ($this->fromTrustedProxy() && $this->getOverwriteHost() !== null) {
795
+            return $this->getOverwriteHost();
796
+        }
797
+
798
+        $host = 'localhost';
799
+        if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_HOST'])) {
800
+            if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) {
801
+                $parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']);
802
+                $host = trim(current($parts));
803
+            } else {
804
+                $host = $this->server['HTTP_X_FORWARDED_HOST'];
805
+            }
806
+        } else {
807
+            if (isset($this->server['HTTP_HOST'])) {
808
+                $host = $this->server['HTTP_HOST'];
809
+            } elseif (isset($this->server['SERVER_NAME'])) {
810
+                $host = $this->server['SERVER_NAME'];
811
+            }
812
+        }
813
+
814
+        return $host;
815
+    }
816
+
817
+
818
+    /**
819
+     * Returns the server host from the headers, or the first configured
820
+     * trusted domain if the host isn't in the trusted list
821
+     * @return string Server host
822
+     */
823
+    public function getServerHost(): string {
824
+        // overwritehost is always trusted
825
+        $host = $this->getOverwriteHost();
826
+        if ($host !== null) {
827
+            return $host;
828
+        }
829
+
830
+        // get the host from the headers
831
+        $host = $this->getInsecureServerHost();
832
+
833
+        // Verify that the host is a trusted domain if the trusted domains
834
+        // are defined
835
+        // If no trusted domain is provided the first trusted domain is returned
836
+        $trustedDomainHelper = new TrustedDomainHelper($this->config);
837
+        if ($trustedDomainHelper->isTrustedDomain($host)) {
838
+            return $host;
839
+        }
840
+
841
+        $trustedList = (array)$this->config->getSystemValue('trusted_domains', []);
842
+        if (count($trustedList) > 0) {
843
+            return reset($trustedList);
844
+        }
845
+
846
+        return '';
847
+    }
848
+
849
+    /**
850
+     * Returns the overwritehost setting from the config if set and
851
+     * if the overwrite condition is met
852
+     * @return string|null overwritehost value or null if not defined or the defined condition
853
+     * isn't met
854
+     */
855
+    private function getOverwriteHost() {
856
+        if ($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) {
857
+            return $this->config->getSystemValue('overwritehost');
858
+        }
859
+        return null;
860
+    }
861
+
862
+    private function fromTrustedProxy(): bool {
863
+        $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
864
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
865
+
866
+        return \is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress);
867
+    }
868 868
 }
Please login to merge, or discard this patch.