Passed
Push — master ( e81fdf...9f1d49 )
by Robin
16:12 queued 13s
created
lib/private/URLGenerator.php 2 patches
Indentation   +283 added lines, -283 removed lines patch added patch discarded remove patch
@@ -55,287 +55,287 @@
 block discarded – undo
55 55
  * Class to generate URLs
56 56
  */
57 57
 class URLGenerator implements IURLGenerator {
58
-	/** @var IConfig */
59
-	private $config;
60
-	/** @var IUserSession */
61
-	public $userSession;
62
-	/** @var ICacheFactory */
63
-	private $cacheFactory;
64
-	/** @var IRequest */
65
-	private $request;
66
-	/** @var Router */
67
-	private $router;
68
-	/** @var null|string */
69
-	private $baseUrl = null;
70
-	private ?IAppManager $appManager = null;
71
-
72
-	public function __construct(IConfig $config,
73
-								IUserSession $userSession,
74
-								ICacheFactory $cacheFactory,
75
-								IRequest $request,
76
-								Router $router
77
-	) {
78
-		$this->config = $config;
79
-		$this->userSession = $userSession;
80
-		$this->cacheFactory = $cacheFactory;
81
-		$this->request = $request;
82
-		$this->router = $router;
83
-	}
84
-
85
-	private function getAppManager(): IAppManager {
86
-		if ($this->appManager !== null) {
87
-			return $this->appManager;
88
-		}
89
-		$this->appManager = \OCP\Server::get(IAppManager::class);
90
-		return $this->appManager;
91
-	}
92
-
93
-	/**
94
-	 * Creates an url using a defined route
95
-	 *
96
-	 * @param string $routeName
97
-	 * @param array $arguments args with param=>value, will be appended to the returned url
98
-	 * @return string the url
99
-	 *
100
-	 * Returns a url to the given route.
101
-	 */
102
-	public function linkToRoute(string $routeName, array $arguments = []): string {
103
-		return $this->router->generate($routeName, $arguments);
104
-	}
105
-
106
-	/**
107
-	 * Creates an absolute url using a defined route
108
-	 * @param string $routeName
109
-	 * @param array $arguments args with param=>value, will be appended to the returned url
110
-	 * @return string the url
111
-	 *
112
-	 * Returns an absolute url to the given route.
113
-	 */
114
-	public function linkToRouteAbsolute(string $routeName, array $arguments = []): string {
115
-		return $this->getAbsoluteURL($this->linkToRoute($routeName, $arguments));
116
-	}
117
-
118
-	public function linkToOCSRouteAbsolute(string $routeName, array $arguments = []): string {
119
-		$route = $this->router->generate('ocs.'.$routeName, $arguments, false);
120
-
121
-		$indexPhpPos = strpos($route, '/index.php/');
122
-		if ($indexPhpPos !== false) {
123
-			$route = substr($route, $indexPhpPos + 10);
124
-		}
125
-
126
-		$route = substr($route, 7);
127
-		$route = '/ocs/v2.php' . $route;
128
-
129
-		return $this->getAbsoluteURL($route);
130
-	}
131
-
132
-	/**
133
-	 * Creates an url
134
-	 *
135
-	 * @param string $appName app
136
-	 * @param string $file file
137
-	 * @param array $args array with param=>value, will be appended to the returned url
138
-	 *    The value of $args will be urlencoded
139
-	 * @return string the url
140
-	 *
141
-	 * Returns a url to the given app and file.
142
-	 */
143
-	public function linkTo(string $appName, string $file, array $args = []): string {
144
-		$frontControllerActive = ($this->config->getSystemValueBool('htaccess.IgnoreFrontController', false) || getenv('front_controller_active') === 'true');
145
-
146
-		if ($appName !== '') {
147
-			$app_path = $this->getAppManager()->getAppPath($appName);
148
-			// Check if the app is in the app folder
149
-			if (file_exists($app_path . '/' . $file)) {
150
-				if (substr($file, -3) === 'php') {
151
-					$urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $appName;
152
-					if ($frontControllerActive) {
153
-						$urlLinkTo = \OC::$WEBROOT . '/apps/' . $appName;
154
-					}
155
-					$urlLinkTo .= ($file !== 'index.php') ? '/' . $file : '';
156
-				} else {
157
-					$urlLinkTo = $this->getAppManager()->getAppWebPath($appName) . '/' . $file;
158
-				}
159
-			} else {
160
-				$urlLinkTo = \OC::$WEBROOT . '/' . $appName . '/' . $file;
161
-			}
162
-		} else {
163
-			if (file_exists(\OC::$SERVERROOT . '/core/' . $file)) {
164
-				$urlLinkTo = \OC::$WEBROOT . '/core/' . $file;
165
-			} else {
166
-				if ($frontControllerActive && $file === 'index.php') {
167
-					$urlLinkTo = \OC::$WEBROOT . '/';
168
-				} else {
169
-					$urlLinkTo = \OC::$WEBROOT . '/' . $file;
170
-				}
171
-			}
172
-		}
173
-
174
-		if ($args && $query = http_build_query($args, '', '&')) {
175
-			$urlLinkTo .= '?' . $query;
176
-		}
177
-
178
-		return $urlLinkTo;
179
-	}
180
-
181
-	/**
182
-	 * Creates path to an image
183
-	 *
184
-	 * @param string $appName app
185
-	 * @param string $file image name
186
-	 * @throws \RuntimeException If the image does not exist
187
-	 * @return string the url
188
-	 *
189
-	 * Returns the path to the image.
190
-	 */
191
-	public function imagePath(string $appName, string $file): string {
192
-		$cache = $this->cacheFactory->createDistributed('imagePath-'.md5($this->getBaseUrl()).'-');
193
-		$cacheKey = $appName.'-'.$file;
194
-		if ($key = $cache->get($cacheKey)) {
195
-			return $key;
196
-		}
197
-
198
-		// Read the selected theme from the config file
199
-		$theme = \OC_Util::getTheme();
200
-
201
-		//if a theme has a png but not an svg always use the png
202
-		$basename = substr(basename($file), 0, -4);
203
-
204
-		try {
205
-			$appPath = $this->getAppManager()->getAppPath($appName);
206
-		} catch (AppPathNotFoundException $e) {
207
-			if ($appName === 'core' || $appName === '') {
208
-				$appName = 'core';
209
-				$appPath = false;
210
-			} else {
211
-				throw new RuntimeException('image not found: image: ' . $file . ' webroot: ' . \OC::$WEBROOT . ' serverroot: ' . \OC::$SERVERROOT);
212
-			}
213
-		}
214
-
215
-		// Check if the app is in the app folder
216
-		$path = '';
217
-		$themingEnabled = $this->config->getSystemValueBool('installed', false) && $this->getAppManager()->isEnabledForUser('theming');
218
-		$themingImagePath = false;
219
-		if ($themingEnabled) {
220
-			$themingDefaults = \OC::$server->getThemingDefaults();
221
-			if ($themingDefaults instanceof ThemingDefaults) {
222
-				$themingImagePath = $themingDefaults->replaceImagePath($appName, $file);
223
-			}
224
-		}
225
-
226
-		if (file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$file")) {
227
-			$path = \OC::$WEBROOT . "/themes/$theme/apps/$appName/img/$file";
228
-		} elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$basename.svg")
229
-			&& file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$basename.png")) {
230
-			$path = \OC::$WEBROOT . "/themes/$theme/apps/$appName/img/$basename.png";
231
-		} elseif (!empty($appName) and file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$file")) {
232
-			$path = \OC::$WEBROOT . "/themes/$theme/$appName/img/$file";
233
-		} elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$basename.svg")
234
-			&& file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$basename.png"))) {
235
-			$path = \OC::$WEBROOT . "/themes/$theme/$appName/img/$basename.png";
236
-		} elseif (file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$file")) {
237
-			$path = \OC::$WEBROOT . "/themes/$theme/core/img/$file";
238
-		} elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.svg")
239
-			&& file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.png")) {
240
-			$path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png";
241
-		} elseif ($themingEnabled && $themingImagePath) {
242
-			$path = $themingImagePath;
243
-		} elseif ($appPath && file_exists($appPath . "/img/$file")) {
244
-			$path = $this->getAppManager()->getAppWebPath($appName) . "/img/$file";
245
-		} elseif ($appPath && !file_exists($appPath . "/img/$basename.svg")
246
-			&& file_exists($appPath . "/img/$basename.png")) {
247
-			$path = $this->getAppManager()->getAppWebPath($appName) . "/img/$basename.png";
248
-		} elseif (!empty($appName) and file_exists(\OC::$SERVERROOT . "/$appName/img/$file")) {
249
-			$path = \OC::$WEBROOT . "/$appName/img/$file";
250
-		} elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT . "/$appName/img/$basename.svg")
251
-				&& file_exists(\OC::$SERVERROOT . "/$appName/img/$basename.png"))) {
252
-			$path = \OC::$WEBROOT . "/$appName/img/$basename.png";
253
-		} elseif (file_exists(\OC::$SERVERROOT . "/core/img/$file")) {
254
-			$path = \OC::$WEBROOT . "/core/img/$file";
255
-		} elseif (!file_exists(\OC::$SERVERROOT . "/core/img/$basename.svg")
256
-			&& file_exists(\OC::$SERVERROOT . "/core/img/$basename.png")) {
257
-			$path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png";
258
-		}
259
-
260
-		if ($path !== '') {
261
-			$cache->set($cacheKey, $path);
262
-			return $path;
263
-		}
264
-
265
-		throw new RuntimeException('image not found: image:' . $file . ' webroot:' . \OC::$WEBROOT . ' serverroot:' . \OC::$SERVERROOT);
266
-	}
267
-
268
-
269
-	/**
270
-	 * Makes an URL absolute
271
-	 * @param string $url the url in the ownCloud host
272
-	 * @return string the absolute version of the url
273
-	 */
274
-	public function getAbsoluteURL(string $url): string {
275
-		$separator = str_starts_with($url, '/') ? '' : '/';
276
-
277
-		if (\OC::$CLI && !\defined('PHPUNIT_RUN')) {
278
-			return rtrim($this->config->getSystemValueString('overwrite.cli.url'), '/') . '/' . ltrim($url, '/');
279
-		}
280
-		// The ownCloud web root can already be prepended.
281
-		if (\OC::$WEBROOT !== '' && str_starts_with($url, \OC::$WEBROOT)) {
282
-			$url = substr($url, \strlen(\OC::$WEBROOT));
283
-		}
284
-
285
-		return $this->getBaseUrl() . $separator . $url;
286
-	}
287
-
288
-	/**
289
-	 * @param string $key
290
-	 * @return string url to the online documentation
291
-	 */
292
-	public function linkToDocs(string $key): string {
293
-		$theme = \OC::$server->getThemingDefaults();
294
-		return $theme->buildDocLinkToKey($key);
295
-	}
296
-
297
-	/**
298
-	 * Returns the URL of the default page based on the system configuration
299
-	 * and the apps visible for the current user
300
-	 * @return string
301
-	 */
302
-	public function linkToDefaultPageUrl(): string {
303
-		// Deny the redirect if the URL contains a @
304
-		// This prevents unvalidated redirects like ?redirect_url=:[email protected]
305
-		if (isset($_REQUEST['redirect_url']) && !str_contains($_REQUEST['redirect_url'], '@')) {
306
-			return $this->getAbsoluteURL(urldecode($_REQUEST['redirect_url']));
307
-		}
308
-
309
-		$defaultPage = $this->config->getAppValue('core', 'defaultpage');
310
-		if ($defaultPage) {
311
-			return $this->getAbsoluteURL($defaultPage);
312
-		}
313
-
314
-		$appId = $this->getAppManager()->getDefaultAppForUser();
315
-
316
-		if ($this->config->getSystemValueBool('htaccess.IgnoreFrontController', false)
317
-			|| getenv('front_controller_active') === 'true') {
318
-			return $this->getAbsoluteURL('/apps/' . $appId . '/');
319
-		}
320
-
321
-		return $this->getAbsoluteURL('/index.php/apps/' . $appId . '/');
322
-	}
323
-
324
-	/**
325
-	 * @return string base url of the current request
326
-	 */
327
-	public function getBaseUrl(): string {
328
-		// BaseUrl can be equal to 'http(s)://' during the first steps of the initial setup.
329
-		if ($this->baseUrl === null || $this->baseUrl === "http://" || $this->baseUrl === "https://") {
330
-			$this->baseUrl = $this->request->getServerProtocol() . '://' . $this->request->getServerHost() . \OC::$WEBROOT;
331
-		}
332
-		return $this->baseUrl;
333
-	}
334
-
335
-	/**
336
-	 * @return string webroot part of the base url
337
-	 */
338
-	public function getWebroot(): string {
339
-		return \OC::$WEBROOT;
340
-	}
58
+    /** @var IConfig */
59
+    private $config;
60
+    /** @var IUserSession */
61
+    public $userSession;
62
+    /** @var ICacheFactory */
63
+    private $cacheFactory;
64
+    /** @var IRequest */
65
+    private $request;
66
+    /** @var Router */
67
+    private $router;
68
+    /** @var null|string */
69
+    private $baseUrl = null;
70
+    private ?IAppManager $appManager = null;
71
+
72
+    public function __construct(IConfig $config,
73
+                                IUserSession $userSession,
74
+                                ICacheFactory $cacheFactory,
75
+                                IRequest $request,
76
+                                Router $router
77
+    ) {
78
+        $this->config = $config;
79
+        $this->userSession = $userSession;
80
+        $this->cacheFactory = $cacheFactory;
81
+        $this->request = $request;
82
+        $this->router = $router;
83
+    }
84
+
85
+    private function getAppManager(): IAppManager {
86
+        if ($this->appManager !== null) {
87
+            return $this->appManager;
88
+        }
89
+        $this->appManager = \OCP\Server::get(IAppManager::class);
90
+        return $this->appManager;
91
+    }
92
+
93
+    /**
94
+     * Creates an url using a defined route
95
+     *
96
+     * @param string $routeName
97
+     * @param array $arguments args with param=>value, will be appended to the returned url
98
+     * @return string the url
99
+     *
100
+     * Returns a url to the given route.
101
+     */
102
+    public function linkToRoute(string $routeName, array $arguments = []): string {
103
+        return $this->router->generate($routeName, $arguments);
104
+    }
105
+
106
+    /**
107
+     * Creates an absolute url using a defined route
108
+     * @param string $routeName
109
+     * @param array $arguments args with param=>value, will be appended to the returned url
110
+     * @return string the url
111
+     *
112
+     * Returns an absolute url to the given route.
113
+     */
114
+    public function linkToRouteAbsolute(string $routeName, array $arguments = []): string {
115
+        return $this->getAbsoluteURL($this->linkToRoute($routeName, $arguments));
116
+    }
117
+
118
+    public function linkToOCSRouteAbsolute(string $routeName, array $arguments = []): string {
119
+        $route = $this->router->generate('ocs.'.$routeName, $arguments, false);
120
+
121
+        $indexPhpPos = strpos($route, '/index.php/');
122
+        if ($indexPhpPos !== false) {
123
+            $route = substr($route, $indexPhpPos + 10);
124
+        }
125
+
126
+        $route = substr($route, 7);
127
+        $route = '/ocs/v2.php' . $route;
128
+
129
+        return $this->getAbsoluteURL($route);
130
+    }
131
+
132
+    /**
133
+     * Creates an url
134
+     *
135
+     * @param string $appName app
136
+     * @param string $file file
137
+     * @param array $args array with param=>value, will be appended to the returned url
138
+     *    The value of $args will be urlencoded
139
+     * @return string the url
140
+     *
141
+     * Returns a url to the given app and file.
142
+     */
143
+    public function linkTo(string $appName, string $file, array $args = []): string {
144
+        $frontControllerActive = ($this->config->getSystemValueBool('htaccess.IgnoreFrontController', false) || getenv('front_controller_active') === 'true');
145
+
146
+        if ($appName !== '') {
147
+            $app_path = $this->getAppManager()->getAppPath($appName);
148
+            // Check if the app is in the app folder
149
+            if (file_exists($app_path . '/' . $file)) {
150
+                if (substr($file, -3) === 'php') {
151
+                    $urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $appName;
152
+                    if ($frontControllerActive) {
153
+                        $urlLinkTo = \OC::$WEBROOT . '/apps/' . $appName;
154
+                    }
155
+                    $urlLinkTo .= ($file !== 'index.php') ? '/' . $file : '';
156
+                } else {
157
+                    $urlLinkTo = $this->getAppManager()->getAppWebPath($appName) . '/' . $file;
158
+                }
159
+            } else {
160
+                $urlLinkTo = \OC::$WEBROOT . '/' . $appName . '/' . $file;
161
+            }
162
+        } else {
163
+            if (file_exists(\OC::$SERVERROOT . '/core/' . $file)) {
164
+                $urlLinkTo = \OC::$WEBROOT . '/core/' . $file;
165
+            } else {
166
+                if ($frontControllerActive && $file === 'index.php') {
167
+                    $urlLinkTo = \OC::$WEBROOT . '/';
168
+                } else {
169
+                    $urlLinkTo = \OC::$WEBROOT . '/' . $file;
170
+                }
171
+            }
172
+        }
173
+
174
+        if ($args && $query = http_build_query($args, '', '&')) {
175
+            $urlLinkTo .= '?' . $query;
176
+        }
177
+
178
+        return $urlLinkTo;
179
+    }
180
+
181
+    /**
182
+     * Creates path to an image
183
+     *
184
+     * @param string $appName app
185
+     * @param string $file image name
186
+     * @throws \RuntimeException If the image does not exist
187
+     * @return string the url
188
+     *
189
+     * Returns the path to the image.
190
+     */
191
+    public function imagePath(string $appName, string $file): string {
192
+        $cache = $this->cacheFactory->createDistributed('imagePath-'.md5($this->getBaseUrl()).'-');
193
+        $cacheKey = $appName.'-'.$file;
194
+        if ($key = $cache->get($cacheKey)) {
195
+            return $key;
196
+        }
197
+
198
+        // Read the selected theme from the config file
199
+        $theme = \OC_Util::getTheme();
200
+
201
+        //if a theme has a png but not an svg always use the png
202
+        $basename = substr(basename($file), 0, -4);
203
+
204
+        try {
205
+            $appPath = $this->getAppManager()->getAppPath($appName);
206
+        } catch (AppPathNotFoundException $e) {
207
+            if ($appName === 'core' || $appName === '') {
208
+                $appName = 'core';
209
+                $appPath = false;
210
+            } else {
211
+                throw new RuntimeException('image not found: image: ' . $file . ' webroot: ' . \OC::$WEBROOT . ' serverroot: ' . \OC::$SERVERROOT);
212
+            }
213
+        }
214
+
215
+        // Check if the app is in the app folder
216
+        $path = '';
217
+        $themingEnabled = $this->config->getSystemValueBool('installed', false) && $this->getAppManager()->isEnabledForUser('theming');
218
+        $themingImagePath = false;
219
+        if ($themingEnabled) {
220
+            $themingDefaults = \OC::$server->getThemingDefaults();
221
+            if ($themingDefaults instanceof ThemingDefaults) {
222
+                $themingImagePath = $themingDefaults->replaceImagePath($appName, $file);
223
+            }
224
+        }
225
+
226
+        if (file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$file")) {
227
+            $path = \OC::$WEBROOT . "/themes/$theme/apps/$appName/img/$file";
228
+        } elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$basename.svg")
229
+            && file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$basename.png")) {
230
+            $path = \OC::$WEBROOT . "/themes/$theme/apps/$appName/img/$basename.png";
231
+        } elseif (!empty($appName) and file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$file")) {
232
+            $path = \OC::$WEBROOT . "/themes/$theme/$appName/img/$file";
233
+        } elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$basename.svg")
234
+            && file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$basename.png"))) {
235
+            $path = \OC::$WEBROOT . "/themes/$theme/$appName/img/$basename.png";
236
+        } elseif (file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$file")) {
237
+            $path = \OC::$WEBROOT . "/themes/$theme/core/img/$file";
238
+        } elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.svg")
239
+            && file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.png")) {
240
+            $path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png";
241
+        } elseif ($themingEnabled && $themingImagePath) {
242
+            $path = $themingImagePath;
243
+        } elseif ($appPath && file_exists($appPath . "/img/$file")) {
244
+            $path = $this->getAppManager()->getAppWebPath($appName) . "/img/$file";
245
+        } elseif ($appPath && !file_exists($appPath . "/img/$basename.svg")
246
+            && file_exists($appPath . "/img/$basename.png")) {
247
+            $path = $this->getAppManager()->getAppWebPath($appName) . "/img/$basename.png";
248
+        } elseif (!empty($appName) and file_exists(\OC::$SERVERROOT . "/$appName/img/$file")) {
249
+            $path = \OC::$WEBROOT . "/$appName/img/$file";
250
+        } elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT . "/$appName/img/$basename.svg")
251
+                && file_exists(\OC::$SERVERROOT . "/$appName/img/$basename.png"))) {
252
+            $path = \OC::$WEBROOT . "/$appName/img/$basename.png";
253
+        } elseif (file_exists(\OC::$SERVERROOT . "/core/img/$file")) {
254
+            $path = \OC::$WEBROOT . "/core/img/$file";
255
+        } elseif (!file_exists(\OC::$SERVERROOT . "/core/img/$basename.svg")
256
+            && file_exists(\OC::$SERVERROOT . "/core/img/$basename.png")) {
257
+            $path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png";
258
+        }
259
+
260
+        if ($path !== '') {
261
+            $cache->set($cacheKey, $path);
262
+            return $path;
263
+        }
264
+
265
+        throw new RuntimeException('image not found: image:' . $file . ' webroot:' . \OC::$WEBROOT . ' serverroot:' . \OC::$SERVERROOT);
266
+    }
267
+
268
+
269
+    /**
270
+     * Makes an URL absolute
271
+     * @param string $url the url in the ownCloud host
272
+     * @return string the absolute version of the url
273
+     */
274
+    public function getAbsoluteURL(string $url): string {
275
+        $separator = str_starts_with($url, '/') ? '' : '/';
276
+
277
+        if (\OC::$CLI && !\defined('PHPUNIT_RUN')) {
278
+            return rtrim($this->config->getSystemValueString('overwrite.cli.url'), '/') . '/' . ltrim($url, '/');
279
+        }
280
+        // The ownCloud web root can already be prepended.
281
+        if (\OC::$WEBROOT !== '' && str_starts_with($url, \OC::$WEBROOT)) {
282
+            $url = substr($url, \strlen(\OC::$WEBROOT));
283
+        }
284
+
285
+        return $this->getBaseUrl() . $separator . $url;
286
+    }
287
+
288
+    /**
289
+     * @param string $key
290
+     * @return string url to the online documentation
291
+     */
292
+    public function linkToDocs(string $key): string {
293
+        $theme = \OC::$server->getThemingDefaults();
294
+        return $theme->buildDocLinkToKey($key);
295
+    }
296
+
297
+    /**
298
+     * Returns the URL of the default page based on the system configuration
299
+     * and the apps visible for the current user
300
+     * @return string
301
+     */
302
+    public function linkToDefaultPageUrl(): string {
303
+        // Deny the redirect if the URL contains a @
304
+        // This prevents unvalidated redirects like ?redirect_url=:[email protected]
305
+        if (isset($_REQUEST['redirect_url']) && !str_contains($_REQUEST['redirect_url'], '@')) {
306
+            return $this->getAbsoluteURL(urldecode($_REQUEST['redirect_url']));
307
+        }
308
+
309
+        $defaultPage = $this->config->getAppValue('core', 'defaultpage');
310
+        if ($defaultPage) {
311
+            return $this->getAbsoluteURL($defaultPage);
312
+        }
313
+
314
+        $appId = $this->getAppManager()->getDefaultAppForUser();
315
+
316
+        if ($this->config->getSystemValueBool('htaccess.IgnoreFrontController', false)
317
+            || getenv('front_controller_active') === 'true') {
318
+            return $this->getAbsoluteURL('/apps/' . $appId . '/');
319
+        }
320
+
321
+        return $this->getAbsoluteURL('/index.php/apps/' . $appId . '/');
322
+    }
323
+
324
+    /**
325
+     * @return string base url of the current request
326
+     */
327
+    public function getBaseUrl(): string {
328
+        // BaseUrl can be equal to 'http(s)://' during the first steps of the initial setup.
329
+        if ($this->baseUrl === null || $this->baseUrl === "http://" || $this->baseUrl === "https://") {
330
+            $this->baseUrl = $this->request->getServerProtocol() . '://' . $this->request->getServerHost() . \OC::$WEBROOT;
331
+        }
332
+        return $this->baseUrl;
333
+    }
334
+
335
+    /**
336
+     * @return string webroot part of the base url
337
+     */
338
+    public function getWebroot(): string {
339
+        return \OC::$WEBROOT;
340
+    }
341 341
 }
Please login to merge, or discard this patch.
Spacing   +49 added lines, -49 removed lines patch added patch discarded remove patch
@@ -124,7 +124,7 @@  discard block
 block discarded – undo
124 124
 		}
125 125
 
126 126
 		$route = substr($route, 7);
127
-		$route = '/ocs/v2.php' . $route;
127
+		$route = '/ocs/v2.php'.$route;
128 128
 
129 129
 		return $this->getAbsoluteURL($route);
130 130
 	}
@@ -146,33 +146,33 @@  discard block
 block discarded – undo
146 146
 		if ($appName !== '') {
147 147
 			$app_path = $this->getAppManager()->getAppPath($appName);
148 148
 			// Check if the app is in the app folder
149
-			if (file_exists($app_path . '/' . $file)) {
149
+			if (file_exists($app_path.'/'.$file)) {
150 150
 				if (substr($file, -3) === 'php') {
151
-					$urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $appName;
151
+					$urlLinkTo = \OC::$WEBROOT.'/index.php/apps/'.$appName;
152 152
 					if ($frontControllerActive) {
153
-						$urlLinkTo = \OC::$WEBROOT . '/apps/' . $appName;
153
+						$urlLinkTo = \OC::$WEBROOT.'/apps/'.$appName;
154 154
 					}
155
-					$urlLinkTo .= ($file !== 'index.php') ? '/' . $file : '';
155
+					$urlLinkTo .= ($file !== 'index.php') ? '/'.$file : '';
156 156
 				} else {
157
-					$urlLinkTo = $this->getAppManager()->getAppWebPath($appName) . '/' . $file;
157
+					$urlLinkTo = $this->getAppManager()->getAppWebPath($appName).'/'.$file;
158 158
 				}
159 159
 			} else {
160
-				$urlLinkTo = \OC::$WEBROOT . '/' . $appName . '/' . $file;
160
+				$urlLinkTo = \OC::$WEBROOT.'/'.$appName.'/'.$file;
161 161
 			}
162 162
 		} else {
163
-			if (file_exists(\OC::$SERVERROOT . '/core/' . $file)) {
164
-				$urlLinkTo = \OC::$WEBROOT . '/core/' . $file;
163
+			if (file_exists(\OC::$SERVERROOT.'/core/'.$file)) {
164
+				$urlLinkTo = \OC::$WEBROOT.'/core/'.$file;
165 165
 			} else {
166 166
 				if ($frontControllerActive && $file === 'index.php') {
167
-					$urlLinkTo = \OC::$WEBROOT . '/';
167
+					$urlLinkTo = \OC::$WEBROOT.'/';
168 168
 				} else {
169
-					$urlLinkTo = \OC::$WEBROOT . '/' . $file;
169
+					$urlLinkTo = \OC::$WEBROOT.'/'.$file;
170 170
 				}
171 171
 			}
172 172
 		}
173 173
 
174 174
 		if ($args && $query = http_build_query($args, '', '&')) {
175
-			$urlLinkTo .= '?' . $query;
175
+			$urlLinkTo .= '?'.$query;
176 176
 		}
177 177
 
178 178
 		return $urlLinkTo;
@@ -208,7 +208,7 @@  discard block
 block discarded – undo
208 208
 				$appName = 'core';
209 209
 				$appPath = false;
210 210
 			} else {
211
-				throw new RuntimeException('image not found: image: ' . $file . ' webroot: ' . \OC::$WEBROOT . ' serverroot: ' . \OC::$SERVERROOT);
211
+				throw new RuntimeException('image not found: image: '.$file.' webroot: '.\OC::$WEBROOT.' serverroot: '.\OC::$SERVERROOT);
212 212
 			}
213 213
 		}
214 214
 
@@ -223,38 +223,38 @@  discard block
 block discarded – undo
223 223
 			}
224 224
 		}
225 225
 
226
-		if (file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$file")) {
227
-			$path = \OC::$WEBROOT . "/themes/$theme/apps/$appName/img/$file";
228
-		} elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$basename.svg")
229
-			&& file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$appName/img/$basename.png")) {
230
-			$path = \OC::$WEBROOT . "/themes/$theme/apps/$appName/img/$basename.png";
231
-		} elseif (!empty($appName) and file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$file")) {
232
-			$path = \OC::$WEBROOT . "/themes/$theme/$appName/img/$file";
233
-		} elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$basename.svg")
234
-			&& file_exists(\OC::$SERVERROOT . "/themes/$theme/$appName/img/$basename.png"))) {
235
-			$path = \OC::$WEBROOT . "/themes/$theme/$appName/img/$basename.png";
236
-		} elseif (file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$file")) {
237
-			$path = \OC::$WEBROOT . "/themes/$theme/core/img/$file";
238
-		} elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.svg")
239
-			&& file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.png")) {
240
-			$path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png";
226
+		if (file_exists(\OC::$SERVERROOT."/themes/$theme/apps/$appName/img/$file")) {
227
+			$path = \OC::$WEBROOT."/themes/$theme/apps/$appName/img/$file";
228
+		} elseif (!file_exists(\OC::$SERVERROOT."/themes/$theme/apps/$appName/img/$basename.svg")
229
+			&& file_exists(\OC::$SERVERROOT."/themes/$theme/apps/$appName/img/$basename.png")) {
230
+			$path = \OC::$WEBROOT."/themes/$theme/apps/$appName/img/$basename.png";
231
+		} elseif (!empty($appName) and file_exists(\OC::$SERVERROOT."/themes/$theme/$appName/img/$file")) {
232
+			$path = \OC::$WEBROOT."/themes/$theme/$appName/img/$file";
233
+		} elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT."/themes/$theme/$appName/img/$basename.svg")
234
+			&& file_exists(\OC::$SERVERROOT."/themes/$theme/$appName/img/$basename.png"))) {
235
+			$path = \OC::$WEBROOT."/themes/$theme/$appName/img/$basename.png";
236
+		} elseif (file_exists(\OC::$SERVERROOT."/themes/$theme/core/img/$file")) {
237
+			$path = \OC::$WEBROOT."/themes/$theme/core/img/$file";
238
+		} elseif (!file_exists(\OC::$SERVERROOT."/themes/$theme/core/img/$basename.svg")
239
+			&& file_exists(\OC::$SERVERROOT."/themes/$theme/core/img/$basename.png")) {
240
+			$path = \OC::$WEBROOT."/themes/$theme/core/img/$basename.png";
241 241
 		} elseif ($themingEnabled && $themingImagePath) {
242 242
 			$path = $themingImagePath;
243
-		} elseif ($appPath && file_exists($appPath . "/img/$file")) {
244
-			$path = $this->getAppManager()->getAppWebPath($appName) . "/img/$file";
245
-		} elseif ($appPath && !file_exists($appPath . "/img/$basename.svg")
246
-			&& file_exists($appPath . "/img/$basename.png")) {
247
-			$path = $this->getAppManager()->getAppWebPath($appName) . "/img/$basename.png";
248
-		} elseif (!empty($appName) and file_exists(\OC::$SERVERROOT . "/$appName/img/$file")) {
249
-			$path = \OC::$WEBROOT . "/$appName/img/$file";
250
-		} elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT . "/$appName/img/$basename.svg")
251
-				&& file_exists(\OC::$SERVERROOT . "/$appName/img/$basename.png"))) {
252
-			$path = \OC::$WEBROOT . "/$appName/img/$basename.png";
253
-		} elseif (file_exists(\OC::$SERVERROOT . "/core/img/$file")) {
254
-			$path = \OC::$WEBROOT . "/core/img/$file";
255
-		} elseif (!file_exists(\OC::$SERVERROOT . "/core/img/$basename.svg")
256
-			&& file_exists(\OC::$SERVERROOT . "/core/img/$basename.png")) {
257
-			$path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png";
243
+		} elseif ($appPath && file_exists($appPath."/img/$file")) {
244
+			$path = $this->getAppManager()->getAppWebPath($appName)."/img/$file";
245
+		} elseif ($appPath && !file_exists($appPath."/img/$basename.svg")
246
+			&& file_exists($appPath."/img/$basename.png")) {
247
+			$path = $this->getAppManager()->getAppWebPath($appName)."/img/$basename.png";
248
+		} elseif (!empty($appName) and file_exists(\OC::$SERVERROOT."/$appName/img/$file")) {
249
+			$path = \OC::$WEBROOT."/$appName/img/$file";
250
+		} elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT."/$appName/img/$basename.svg")
251
+				&& file_exists(\OC::$SERVERROOT."/$appName/img/$basename.png"))) {
252
+			$path = \OC::$WEBROOT."/$appName/img/$basename.png";
253
+		} elseif (file_exists(\OC::$SERVERROOT."/core/img/$file")) {
254
+			$path = \OC::$WEBROOT."/core/img/$file";
255
+		} elseif (!file_exists(\OC::$SERVERROOT."/core/img/$basename.svg")
256
+			&& file_exists(\OC::$SERVERROOT."/core/img/$basename.png")) {
257
+			$path = \OC::$WEBROOT."/themes/$theme/core/img/$basename.png";
258 258
 		}
259 259
 
260 260
 		if ($path !== '') {
@@ -262,7 +262,7 @@  discard block
 block discarded – undo
262 262
 			return $path;
263 263
 		}
264 264
 
265
-		throw new RuntimeException('image not found: image:' . $file . ' webroot:' . \OC::$WEBROOT . ' serverroot:' . \OC::$SERVERROOT);
265
+		throw new RuntimeException('image not found: image:'.$file.' webroot:'.\OC::$WEBROOT.' serverroot:'.\OC::$SERVERROOT);
266 266
 	}
267 267
 
268 268
 
@@ -275,14 +275,14 @@  discard block
 block discarded – undo
275 275
 		$separator = str_starts_with($url, '/') ? '' : '/';
276 276
 
277 277
 		if (\OC::$CLI && !\defined('PHPUNIT_RUN')) {
278
-			return rtrim($this->config->getSystemValueString('overwrite.cli.url'), '/') . '/' . ltrim($url, '/');
278
+			return rtrim($this->config->getSystemValueString('overwrite.cli.url'), '/').'/'.ltrim($url, '/');
279 279
 		}
280 280
 		// The ownCloud web root can already be prepended.
281 281
 		if (\OC::$WEBROOT !== '' && str_starts_with($url, \OC::$WEBROOT)) {
282 282
 			$url = substr($url, \strlen(\OC::$WEBROOT));
283 283
 		}
284 284
 
285
-		return $this->getBaseUrl() . $separator . $url;
285
+		return $this->getBaseUrl().$separator.$url;
286 286
 	}
287 287
 
288 288
 	/**
@@ -315,10 +315,10 @@  discard block
 block discarded – undo
315 315
 
316 316
 		if ($this->config->getSystemValueBool('htaccess.IgnoreFrontController', false)
317 317
 			|| getenv('front_controller_active') === 'true') {
318
-			return $this->getAbsoluteURL('/apps/' . $appId . '/');
318
+			return $this->getAbsoluteURL('/apps/'.$appId.'/');
319 319
 		}
320 320
 
321
-		return $this->getAbsoluteURL('/index.php/apps/' . $appId . '/');
321
+		return $this->getAbsoluteURL('/index.php/apps/'.$appId.'/');
322 322
 	}
323 323
 
324 324
 	/**
@@ -327,7 +327,7 @@  discard block
 block discarded – undo
327 327
 	public function getBaseUrl(): string {
328 328
 		// BaseUrl can be equal to 'http(s)://' during the first steps of the initial setup.
329 329
 		if ($this->baseUrl === null || $this->baseUrl === "http://" || $this->baseUrl === "https://") {
330
-			$this->baseUrl = $this->request->getServerProtocol() . '://' . $this->request->getServerHost() . \OC::$WEBROOT;
330
+			$this->baseUrl = $this->request->getServerProtocol().'://'.$this->request->getServerHost().\OC::$WEBROOT;
331 331
 		}
332 332
 		return $this->baseUrl;
333 333
 	}
Please login to merge, or discard this patch.
lib/private/Encryption/Util.php 1 patch
Indentation   +317 added lines, -317 removed lines patch added patch discarded remove patch
@@ -40,321 +40,321 @@
 block discarded – undo
40 40
 use OCP\IUserManager;
41 41
 
42 42
 class Util {
43
-	public const HEADER_START = 'HBEGIN';
44
-	public const HEADER_END = 'HEND';
45
-	public const HEADER_PADDING_CHAR = '-';
46
-
47
-	public const HEADER_ENCRYPTION_MODULE_KEY = 'oc_encryption_module';
48
-
49
-	/**
50
-	 * block size will always be 8192 for a PHP stream
51
-	 * @see https://bugs.php.net/bug.php?id=21641
52
-	 * @var integer
53
-	 */
54
-	protected $headerSize = 8192;
55
-
56
-	/**
57
-	 * block size will always be 8192 for a PHP stream
58
-	 * @see https://bugs.php.net/bug.php?id=21641
59
-	 * @var integer
60
-	 */
61
-	protected $blockSize = 8192;
62
-
63
-	/** @var View */
64
-	protected $rootView;
65
-
66
-	/** @var array */
67
-	protected $ocHeaderKeys;
68
-
69
-	/** @var IConfig */
70
-	protected $config;
71
-
72
-	/** @var array paths excluded from encryption */
73
-	protected array $excludedPaths = [];
74
-	protected IGroupManager $groupManager;
75
-	protected IUserManager $userManager;
76
-
77
-	/**
78
-	 *
79
-	 * @param View $rootView
80
-	 * @param IConfig $config
81
-	 */
82
-	public function __construct(
83
-		View $rootView,
84
-		IUserManager $userManager,
85
-		IGroupManager $groupManager,
86
-		IConfig $config) {
87
-		$this->ocHeaderKeys = [
88
-			self::HEADER_ENCRYPTION_MODULE_KEY
89
-		];
90
-
91
-		$this->rootView = $rootView;
92
-		$this->userManager = $userManager;
93
-		$this->groupManager = $groupManager;
94
-		$this->config = $config;
95
-
96
-		$this->excludedPaths[] = 'files_encryption';
97
-		$this->excludedPaths[] = 'appdata_' . $config->getSystemValueString('instanceid');
98
-		$this->excludedPaths[] = 'files_external';
99
-	}
100
-
101
-	/**
102
-	 * read encryption module ID from header
103
-	 *
104
-	 * @param array $header
105
-	 * @return string
106
-	 * @throws ModuleDoesNotExistsException
107
-	 */
108
-	public function getEncryptionModuleId(array $header = null) {
109
-		$id = '';
110
-		$encryptionModuleKey = self::HEADER_ENCRYPTION_MODULE_KEY;
111
-
112
-		if (isset($header[$encryptionModuleKey])) {
113
-			$id = $header[$encryptionModuleKey];
114
-		} elseif (isset($header['cipher'])) {
115
-			if (class_exists('\OCA\Encryption\Crypto\Encryption')) {
116
-				// fall back to default encryption if the user migrated from
117
-				// ownCloud <= 8.0 with the old encryption
118
-				$id = \OCA\Encryption\Crypto\Encryption::ID;
119
-			} else {
120
-				throw new ModuleDoesNotExistsException('Default encryption module missing');
121
-			}
122
-		}
123
-
124
-		return $id;
125
-	}
126
-
127
-	/**
128
-	 * create header for encrypted file
129
-	 *
130
-	 * @param array $headerData
131
-	 * @param IEncryptionModule $encryptionModule
132
-	 * @return string
133
-	 * @throws EncryptionHeaderToLargeException if header has to many arguments
134
-	 * @throws EncryptionHeaderKeyExistsException if header key is already in use
135
-	 */
136
-	public function createHeader(array $headerData, IEncryptionModule $encryptionModule) {
137
-		$header = self::HEADER_START . ':' . self::HEADER_ENCRYPTION_MODULE_KEY . ':' . $encryptionModule->getId() . ':';
138
-		foreach ($headerData as $key => $value) {
139
-			if (in_array($key, $this->ocHeaderKeys)) {
140
-				throw new EncryptionHeaderKeyExistsException($key);
141
-			}
142
-			$header .= $key . ':' . $value . ':';
143
-		}
144
-		$header .= self::HEADER_END;
145
-
146
-		if (strlen($header) > $this->getHeaderSize()) {
147
-			throw new EncryptionHeaderToLargeException();
148
-		}
149
-
150
-		$paddedHeader = str_pad($header, $this->headerSize, self::HEADER_PADDING_CHAR, STR_PAD_RIGHT);
151
-
152
-		return $paddedHeader;
153
-	}
154
-
155
-	/**
156
-	 * go recursively through a dir and collect all files and sub files.
157
-	 *
158
-	 * @param string $dir relative to the users files folder
159
-	 * @return array with list of files relative to the users files folder
160
-	 */
161
-	public function getAllFiles($dir) {
162
-		$result = [];
163
-		$dirList = [$dir];
164
-
165
-		while ($dirList) {
166
-			$dir = array_pop($dirList);
167
-			$content = $this->rootView->getDirectoryContent($dir);
168
-
169
-			foreach ($content as $c) {
170
-				if ($c->getType() === 'dir') {
171
-					$dirList[] = $c->getPath();
172
-				} else {
173
-					$result[] = $c->getPath();
174
-				}
175
-			}
176
-		}
177
-
178
-		return $result;
179
-	}
180
-
181
-	/**
182
-	 * check if it is a file uploaded by the user stored in data/user/files
183
-	 * or a metadata file
184
-	 *
185
-	 * @param string $path relative to the data/ folder
186
-	 * @return boolean
187
-	 */
188
-	public function isFile($path) {
189
-		$parts = explode('/', Filesystem::normalizePath($path), 4);
190
-		if (isset($parts[2]) && $parts[2] === 'files') {
191
-			return true;
192
-		}
193
-		return false;
194
-	}
195
-
196
-	/**
197
-	 * return size of encryption header
198
-	 *
199
-	 * @return integer
200
-	 */
201
-	public function getHeaderSize() {
202
-		return $this->headerSize;
203
-	}
204
-
205
-	/**
206
-	 * return size of block read by a PHP stream
207
-	 *
208
-	 * @return integer
209
-	 */
210
-	public function getBlockSize() {
211
-		return $this->blockSize;
212
-	}
213
-
214
-	/**
215
-	 * get the owner and the path for the file relative to the owners files folder
216
-	 *
217
-	 * @param string $path
218
-	 * @return array{0: string, 1: string}
219
-	 * @throws \BadMethodCallException
220
-	 */
221
-	public function getUidAndFilename($path) {
222
-		$parts = explode('/', $path);
223
-		$uid = '';
224
-		if (count($parts) > 2) {
225
-			$uid = $parts[1];
226
-		}
227
-		if (!$this->userManager->userExists($uid)) {
228
-			throw new \BadMethodCallException(
229
-				'path needs to be relative to the system wide data folder and point to a user specific file'
230
-			);
231
-		}
232
-
233
-		$ownerPath = implode('/', array_slice($parts, 2));
234
-
235
-		return [$uid, Filesystem::normalizePath($ownerPath)];
236
-	}
237
-
238
-	/**
239
-	 * Remove .path extension from a file path
240
-	 * @param string $path Path that may identify a .part file
241
-	 * @return string File path without .part extension
242
-	 * @note this is needed for reusing keys
243
-	 */
244
-	public function stripPartialFileExtension($path) {
245
-		$extension = pathinfo($path, PATHINFO_EXTENSION);
246
-
247
-		if ($extension === 'part') {
248
-			$newLength = strlen($path) - 5; // 5 = strlen(".part")
249
-			$fPath = substr($path, 0, $newLength);
250
-
251
-			// if path also contains a transaction id, we remove it too
252
-			$extension = pathinfo($fPath, PATHINFO_EXTENSION);
253
-			if (substr($extension, 0, 12) === 'ocTransferId') { // 12 = strlen("ocTransferId")
254
-				$newLength = strlen($fPath) - strlen($extension) - 1;
255
-				$fPath = substr($fPath, 0, $newLength);
256
-			}
257
-			return $fPath;
258
-		} else {
259
-			return $path;
260
-		}
261
-	}
262
-
263
-	public function getUserWithAccessToMountPoint($users, $groups) {
264
-		$result = [];
265
-		if ($users === [] && $groups === []) {
266
-			$users = $this->userManager->search('', null, null);
267
-			$result = array_map(function (IUser $user) {
268
-				return $user->getUID();
269
-			}, $users);
270
-		} else {
271
-			$result = array_merge($result, $users);
272
-
273
-			$groupManager = $this->groupManager;
274
-			foreach ($groups as $group) {
275
-				$groupObject = $groupManager->get($group);
276
-				if ($groupObject) {
277
-					$foundUsers = $groupObject->searchUsers('', -1, 0);
278
-					$userIds = [];
279
-					foreach ($foundUsers as $user) {
280
-						$userIds[] = $user->getUID();
281
-					}
282
-					$result = array_merge($result, $userIds);
283
-				}
284
-			}
285
-		}
286
-
287
-		return $result;
288
-	}
289
-
290
-	/**
291
-	 * check if the file is stored on a system wide mount point
292
-	 * @param string $path relative to /data/user with leading '/'
293
-	 * @param string $uid
294
-	 * @return boolean
295
-	 */
296
-	public function isSystemWideMountPoint(string $path, string $uid) {
297
-		$mount = Filesystem::getMountManager()->find('/' . $uid . $path);
298
-		return $mount instanceof ISystemMountPoint;
299
-	}
300
-
301
-	/**
302
-	 * check if it is a path which is excluded by ownCloud from encryption
303
-	 *
304
-	 * @param string $path
305
-	 * @return boolean
306
-	 */
307
-	public function isExcluded($path) {
308
-		$normalizedPath = Filesystem::normalizePath($path);
309
-		$root = explode('/', $normalizedPath, 4);
310
-		if (count($root) > 1) {
311
-			// detect alternative key storage root
312
-			$rootDir = $this->getKeyStorageRoot();
313
-			if ($rootDir !== '' &&
314
-				str_starts_with(Filesystem::normalizePath($path), Filesystem::normalizePath($rootDir))
315
-			) {
316
-				return true;
317
-			}
318
-
319
-
320
-			//detect system wide folders
321
-			if (in_array($root[1], $this->excludedPaths)) {
322
-				return true;
323
-			}
324
-
325
-			// detect user specific folders
326
-			if ($this->userManager->userExists($root[1])
327
-				&& in_array($root[2], $this->excludedPaths)) {
328
-				return true;
329
-			}
330
-		}
331
-		return false;
332
-	}
333
-
334
-	/**
335
-	 * Check if recovery key is enabled for user
336
-	 */
337
-	public function recoveryEnabled(string $uid): bool {
338
-		$enabled = $this->config->getUserValue($uid, 'encryption', 'recovery_enabled', '0');
339
-
340
-		return $enabled === '1';
341
-	}
342
-
343
-	/**
344
-	 * Set new key storage root
345
-	 *
346
-	 * @param string $root new key store root relative to the data folder
347
-	 */
348
-	public function setKeyStorageRoot(string $root): void {
349
-		$this->config->setAppValue('core', 'encryption_key_storage_root', $root);
350
-	}
351
-
352
-	/**
353
-	 * Get key storage root
354
-	 *
355
-	 * @return string key storage root
356
-	 */
357
-	public function getKeyStorageRoot(): string {
358
-		return $this->config->getAppValue('core', 'encryption_key_storage_root', '');
359
-	}
43
+    public const HEADER_START = 'HBEGIN';
44
+    public const HEADER_END = 'HEND';
45
+    public const HEADER_PADDING_CHAR = '-';
46
+
47
+    public const HEADER_ENCRYPTION_MODULE_KEY = 'oc_encryption_module';
48
+
49
+    /**
50
+     * block size will always be 8192 for a PHP stream
51
+     * @see https://bugs.php.net/bug.php?id=21641
52
+     * @var integer
53
+     */
54
+    protected $headerSize = 8192;
55
+
56
+    /**
57
+     * block size will always be 8192 for a PHP stream
58
+     * @see https://bugs.php.net/bug.php?id=21641
59
+     * @var integer
60
+     */
61
+    protected $blockSize = 8192;
62
+
63
+    /** @var View */
64
+    protected $rootView;
65
+
66
+    /** @var array */
67
+    protected $ocHeaderKeys;
68
+
69
+    /** @var IConfig */
70
+    protected $config;
71
+
72
+    /** @var array paths excluded from encryption */
73
+    protected array $excludedPaths = [];
74
+    protected IGroupManager $groupManager;
75
+    protected IUserManager $userManager;
76
+
77
+    /**
78
+     *
79
+     * @param View $rootView
80
+     * @param IConfig $config
81
+     */
82
+    public function __construct(
83
+        View $rootView,
84
+        IUserManager $userManager,
85
+        IGroupManager $groupManager,
86
+        IConfig $config) {
87
+        $this->ocHeaderKeys = [
88
+            self::HEADER_ENCRYPTION_MODULE_KEY
89
+        ];
90
+
91
+        $this->rootView = $rootView;
92
+        $this->userManager = $userManager;
93
+        $this->groupManager = $groupManager;
94
+        $this->config = $config;
95
+
96
+        $this->excludedPaths[] = 'files_encryption';
97
+        $this->excludedPaths[] = 'appdata_' . $config->getSystemValueString('instanceid');
98
+        $this->excludedPaths[] = 'files_external';
99
+    }
100
+
101
+    /**
102
+     * read encryption module ID from header
103
+     *
104
+     * @param array $header
105
+     * @return string
106
+     * @throws ModuleDoesNotExistsException
107
+     */
108
+    public function getEncryptionModuleId(array $header = null) {
109
+        $id = '';
110
+        $encryptionModuleKey = self::HEADER_ENCRYPTION_MODULE_KEY;
111
+
112
+        if (isset($header[$encryptionModuleKey])) {
113
+            $id = $header[$encryptionModuleKey];
114
+        } elseif (isset($header['cipher'])) {
115
+            if (class_exists('\OCA\Encryption\Crypto\Encryption')) {
116
+                // fall back to default encryption if the user migrated from
117
+                // ownCloud <= 8.0 with the old encryption
118
+                $id = \OCA\Encryption\Crypto\Encryption::ID;
119
+            } else {
120
+                throw new ModuleDoesNotExistsException('Default encryption module missing');
121
+            }
122
+        }
123
+
124
+        return $id;
125
+    }
126
+
127
+    /**
128
+     * create header for encrypted file
129
+     *
130
+     * @param array $headerData
131
+     * @param IEncryptionModule $encryptionModule
132
+     * @return string
133
+     * @throws EncryptionHeaderToLargeException if header has to many arguments
134
+     * @throws EncryptionHeaderKeyExistsException if header key is already in use
135
+     */
136
+    public function createHeader(array $headerData, IEncryptionModule $encryptionModule) {
137
+        $header = self::HEADER_START . ':' . self::HEADER_ENCRYPTION_MODULE_KEY . ':' . $encryptionModule->getId() . ':';
138
+        foreach ($headerData as $key => $value) {
139
+            if (in_array($key, $this->ocHeaderKeys)) {
140
+                throw new EncryptionHeaderKeyExistsException($key);
141
+            }
142
+            $header .= $key . ':' . $value . ':';
143
+        }
144
+        $header .= self::HEADER_END;
145
+
146
+        if (strlen($header) > $this->getHeaderSize()) {
147
+            throw new EncryptionHeaderToLargeException();
148
+        }
149
+
150
+        $paddedHeader = str_pad($header, $this->headerSize, self::HEADER_PADDING_CHAR, STR_PAD_RIGHT);
151
+
152
+        return $paddedHeader;
153
+    }
154
+
155
+    /**
156
+     * go recursively through a dir and collect all files and sub files.
157
+     *
158
+     * @param string $dir relative to the users files folder
159
+     * @return array with list of files relative to the users files folder
160
+     */
161
+    public function getAllFiles($dir) {
162
+        $result = [];
163
+        $dirList = [$dir];
164
+
165
+        while ($dirList) {
166
+            $dir = array_pop($dirList);
167
+            $content = $this->rootView->getDirectoryContent($dir);
168
+
169
+            foreach ($content as $c) {
170
+                if ($c->getType() === 'dir') {
171
+                    $dirList[] = $c->getPath();
172
+                } else {
173
+                    $result[] = $c->getPath();
174
+                }
175
+            }
176
+        }
177
+
178
+        return $result;
179
+    }
180
+
181
+    /**
182
+     * check if it is a file uploaded by the user stored in data/user/files
183
+     * or a metadata file
184
+     *
185
+     * @param string $path relative to the data/ folder
186
+     * @return boolean
187
+     */
188
+    public function isFile($path) {
189
+        $parts = explode('/', Filesystem::normalizePath($path), 4);
190
+        if (isset($parts[2]) && $parts[2] === 'files') {
191
+            return true;
192
+        }
193
+        return false;
194
+    }
195
+
196
+    /**
197
+     * return size of encryption header
198
+     *
199
+     * @return integer
200
+     */
201
+    public function getHeaderSize() {
202
+        return $this->headerSize;
203
+    }
204
+
205
+    /**
206
+     * return size of block read by a PHP stream
207
+     *
208
+     * @return integer
209
+     */
210
+    public function getBlockSize() {
211
+        return $this->blockSize;
212
+    }
213
+
214
+    /**
215
+     * get the owner and the path for the file relative to the owners files folder
216
+     *
217
+     * @param string $path
218
+     * @return array{0: string, 1: string}
219
+     * @throws \BadMethodCallException
220
+     */
221
+    public function getUidAndFilename($path) {
222
+        $parts = explode('/', $path);
223
+        $uid = '';
224
+        if (count($parts) > 2) {
225
+            $uid = $parts[1];
226
+        }
227
+        if (!$this->userManager->userExists($uid)) {
228
+            throw new \BadMethodCallException(
229
+                'path needs to be relative to the system wide data folder and point to a user specific file'
230
+            );
231
+        }
232
+
233
+        $ownerPath = implode('/', array_slice($parts, 2));
234
+
235
+        return [$uid, Filesystem::normalizePath($ownerPath)];
236
+    }
237
+
238
+    /**
239
+     * Remove .path extension from a file path
240
+     * @param string $path Path that may identify a .part file
241
+     * @return string File path without .part extension
242
+     * @note this is needed for reusing keys
243
+     */
244
+    public function stripPartialFileExtension($path) {
245
+        $extension = pathinfo($path, PATHINFO_EXTENSION);
246
+
247
+        if ($extension === 'part') {
248
+            $newLength = strlen($path) - 5; // 5 = strlen(".part")
249
+            $fPath = substr($path, 0, $newLength);
250
+
251
+            // if path also contains a transaction id, we remove it too
252
+            $extension = pathinfo($fPath, PATHINFO_EXTENSION);
253
+            if (substr($extension, 0, 12) === 'ocTransferId') { // 12 = strlen("ocTransferId")
254
+                $newLength = strlen($fPath) - strlen($extension) - 1;
255
+                $fPath = substr($fPath, 0, $newLength);
256
+            }
257
+            return $fPath;
258
+        } else {
259
+            return $path;
260
+        }
261
+    }
262
+
263
+    public function getUserWithAccessToMountPoint($users, $groups) {
264
+        $result = [];
265
+        if ($users === [] && $groups === []) {
266
+            $users = $this->userManager->search('', null, null);
267
+            $result = array_map(function (IUser $user) {
268
+                return $user->getUID();
269
+            }, $users);
270
+        } else {
271
+            $result = array_merge($result, $users);
272
+
273
+            $groupManager = $this->groupManager;
274
+            foreach ($groups as $group) {
275
+                $groupObject = $groupManager->get($group);
276
+                if ($groupObject) {
277
+                    $foundUsers = $groupObject->searchUsers('', -1, 0);
278
+                    $userIds = [];
279
+                    foreach ($foundUsers as $user) {
280
+                        $userIds[] = $user->getUID();
281
+                    }
282
+                    $result = array_merge($result, $userIds);
283
+                }
284
+            }
285
+        }
286
+
287
+        return $result;
288
+    }
289
+
290
+    /**
291
+     * check if the file is stored on a system wide mount point
292
+     * @param string $path relative to /data/user with leading '/'
293
+     * @param string $uid
294
+     * @return boolean
295
+     */
296
+    public function isSystemWideMountPoint(string $path, string $uid) {
297
+        $mount = Filesystem::getMountManager()->find('/' . $uid . $path);
298
+        return $mount instanceof ISystemMountPoint;
299
+    }
300
+
301
+    /**
302
+     * check if it is a path which is excluded by ownCloud from encryption
303
+     *
304
+     * @param string $path
305
+     * @return boolean
306
+     */
307
+    public function isExcluded($path) {
308
+        $normalizedPath = Filesystem::normalizePath($path);
309
+        $root = explode('/', $normalizedPath, 4);
310
+        if (count($root) > 1) {
311
+            // detect alternative key storage root
312
+            $rootDir = $this->getKeyStorageRoot();
313
+            if ($rootDir !== '' &&
314
+                str_starts_with(Filesystem::normalizePath($path), Filesystem::normalizePath($rootDir))
315
+            ) {
316
+                return true;
317
+            }
318
+
319
+
320
+            //detect system wide folders
321
+            if (in_array($root[1], $this->excludedPaths)) {
322
+                return true;
323
+            }
324
+
325
+            // detect user specific folders
326
+            if ($this->userManager->userExists($root[1])
327
+                && in_array($root[2], $this->excludedPaths)) {
328
+                return true;
329
+            }
330
+        }
331
+        return false;
332
+    }
333
+
334
+    /**
335
+     * Check if recovery key is enabled for user
336
+     */
337
+    public function recoveryEnabled(string $uid): bool {
338
+        $enabled = $this->config->getUserValue($uid, 'encryption', 'recovery_enabled', '0');
339
+
340
+        return $enabled === '1';
341
+    }
342
+
343
+    /**
344
+     * Set new key storage root
345
+     *
346
+     * @param string $root new key store root relative to the data folder
347
+     */
348
+    public function setKeyStorageRoot(string $root): void {
349
+        $this->config->setAppValue('core', 'encryption_key_storage_root', $root);
350
+    }
351
+
352
+    /**
353
+     * Get key storage root
354
+     *
355
+     * @return string key storage root
356
+     */
357
+    public function getKeyStorageRoot(): string {
358
+        return $this->config->getAppValue('core', 'encryption_key_storage_root', '');
359
+    }
360 360
 }
Please login to merge, or discard this patch.
lib/private/AppFramework/App.php 2 patches
Indentation   +209 added lines, -209 removed lines patch added patch discarded remove patch
@@ -52,218 +52,218 @@
 block discarded – undo
52 52
  * Handles all the dependency injection, controllers and output flow
53 53
  */
54 54
 class App {
55
-	/** @var string[] */
56
-	private static $nameSpaceCache = [];
57
-
58
-	/**
59
-	 * Turns an app id into a namespace by either reading the appinfo.xml's
60
-	 * namespace tag or uppercasing the appid's first letter
61
-	 * @param string $appId the app id
62
-	 * @param string $topNamespace the namespace which should be prepended to
63
-	 * the transformed app id, defaults to OCA\
64
-	 * @return string the starting namespace for the app
65
-	 */
66
-	public static function buildAppNamespace(string $appId, string $topNamespace = 'OCA\\'): string {
67
-		// Hit the cache!
68
-		if (isset(self::$nameSpaceCache[$appId])) {
69
-			return $topNamespace . self::$nameSpaceCache[$appId];
70
-		}
71
-
72
-		$appInfo = \OCP\Server::get(IAppManager::class)->getAppInfo($appId);
73
-		if (isset($appInfo['namespace'])) {
74
-			self::$nameSpaceCache[$appId] = trim($appInfo['namespace']);
75
-		} else {
76
-			if ($appId !== 'spreed') {
77
-				// if the tag is not found, fall back to uppercasing the first letter
78
-				self::$nameSpaceCache[$appId] = ucfirst($appId);
79
-			} else {
80
-				// For the Talk app (appid spreed) the above fallback doesn't work.
81
-				// This leads to a problem when trying to install it freshly,
82
-				// because the apps namespace is already registered before the
83
-				// app is downloaded from the appstore, because of the hackish
84
-				// global route index.php/call/{token} which is registered via
85
-				// the core/routes.php so it does not have the app namespace.
86
-				// @ref https://github.com/nextcloud/server/pull/19433
87
-				self::$nameSpaceCache[$appId] = 'Talk';
88
-			}
89
-		}
90
-
91
-		return $topNamespace . self::$nameSpaceCache[$appId];
92
-	}
93
-
94
-	public static function getAppIdForClass(string $className, string $topNamespace = 'OCA\\'): ?string {
95
-		if (!str_starts_with($className, $topNamespace)) {
96
-			return null;
97
-		}
98
-
99
-		foreach (self::$nameSpaceCache as $appId => $namespace) {
100
-			if (str_starts_with($className, $topNamespace . $namespace . '\\')) {
101
-				return $appId;
102
-			}
103
-		}
104
-
105
-		return null;
106
-	}
107
-
108
-
109
-	/**
110
-	 * Shortcut for calling a controller method and printing the result
111
-	 *
112
-	 * @param string $controllerName the name of the controller under which it is
113
-	 *                               stored in the DI container
114
-	 * @param string $methodName the method that you want to call
115
-	 * @param DIContainer $container an instance of a pimple container.
116
-	 * @param array $urlParams list of URL parameters (optional)
117
-	 * @throws HintException
118
-	 */
119
-	public static function main(string $controllerName, string $methodName, DIContainer $container, array $urlParams = null) {
120
-		/** @var IProfiler $profiler */
121
-		$profiler = $container->get(IProfiler::class);
122
-		$eventLogger = $container->get(IEventLogger::class);
123
-		// Disable profiler on the profiler UI
124
-		$profiler->setEnabled($profiler->isEnabled() && !is_null($urlParams) && isset($urlParams['_route']) && !str_starts_with($urlParams['_route'], 'profiler.'));
125
-		if ($profiler->isEnabled()) {
126
-			\OC::$server->get(IEventLogger::class)->activate();
127
-			$profiler->add(new RoutingDataCollector($container['AppName'], $controllerName, $methodName));
128
-		}
129
-
130
-		$eventLogger->start('app:controller:params', 'Gather controller parameters');
131
-
132
-		if (!is_null($urlParams)) {
133
-			/** @var Request $request */
134
-			$request = $container->get(IRequest::class);
135
-			$request->setUrlParameters($urlParams);
136
-		} elseif (isset($container['urlParams']) && !is_null($container['urlParams'])) {
137
-			/** @var Request $request */
138
-			$request = $container->get(IRequest::class);
139
-			$request->setUrlParameters($container['urlParams']);
140
-		}
141
-		$appName = $container['AppName'];
142
-
143
-		$eventLogger->end('app:controller:params');
144
-
145
-		$eventLogger->start('app:controller:load', 'Load app controller');
146
-
147
-		// first try $controllerName then go for \OCA\AppName\Controller\$controllerName
148
-		try {
149
-			$controller = $container->get($controllerName);
150
-		} catch (QueryException $e) {
151
-			if (str_contains($controllerName, '\\Controller\\')) {
152
-				// This is from a global registered app route that is not enabled.
153
-				[/*OC(A)*/, $app, /* Controller/Name*/] = explode('\\', $controllerName, 3);
154
-				throw new HintException('App ' . strtolower($app) . ' is not enabled');
155
-			}
156
-
157
-			if ($appName === 'core') {
158
-				$appNameSpace = 'OC\\Core';
159
-			} else {
160
-				$appNameSpace = self::buildAppNamespace($appName);
161
-			}
162
-			$controllerName = $appNameSpace . '\\Controller\\' . $controllerName;
163
-			$controller = $container->query($controllerName);
164
-		}
165
-
166
-		$eventLogger->end('app:controller:load');
167
-
168
-		$eventLogger->start('app:controller:dispatcher', 'Initialize dispatcher and pre-middleware');
169
-
170
-		// initialize the dispatcher and run all the middleware before the controller
171
-		/** @var Dispatcher $dispatcher */
172
-		$dispatcher = $container['Dispatcher'];
173
-
174
-		$eventLogger->end('app:controller:dispatcher');
175
-
176
-		$eventLogger->start('app:controller:run', 'Run app controller');
177
-
178
-		[
179
-			$httpHeaders,
180
-			$responseHeaders,
181
-			$responseCookies,
182
-			$output,
183
-			$response
184
-		] = $dispatcher->dispatch($controller, $methodName);
185
-
186
-		$eventLogger->end('app:controller:run');
187
-
188
-		$io = $container[IOutput::class];
189
-
190
-		if ($profiler->isEnabled()) {
191
-			$eventLogger->end('runtime');
192
-			$profile = $profiler->collect($container->get(IRequest::class), $response);
193
-			$profiler->saveProfile($profile);
194
-			$io->setHeader('X-Debug-Token:' . $profile->getToken());
195
-			$io->setHeader('Server-Timing: token;desc="' . $profile->getToken() . '"');
196
-		}
197
-
198
-		if (!is_null($httpHeaders)) {
199
-			$io->setHeader($httpHeaders);
200
-		}
201
-
202
-		foreach ($responseHeaders as $name => $value) {
203
-			$io->setHeader($name . ': ' . $value);
204
-		}
205
-
206
-		foreach ($responseCookies as $name => $value) {
207
-			$expireDate = null;
208
-			if ($value['expireDate'] instanceof \DateTime) {
209
-				$expireDate = $value['expireDate']->getTimestamp();
210
-			}
211
-			$sameSite = $value['sameSite'] ?? 'Lax';
212
-
213
-			$io->setCookie(
214
-				$name,
215
-				$value['value'],
216
-				$expireDate,
217
-				$container->getServer()->getWebRoot(),
218
-				null,
219
-				$container->getServer()->getRequest()->getServerProtocol() === 'https',
220
-				true,
221
-				$sameSite
222
-			);
223
-		}
224
-
225
-		/*
55
+    /** @var string[] */
56
+    private static $nameSpaceCache = [];
57
+
58
+    /**
59
+     * Turns an app id into a namespace by either reading the appinfo.xml's
60
+     * namespace tag or uppercasing the appid's first letter
61
+     * @param string $appId the app id
62
+     * @param string $topNamespace the namespace which should be prepended to
63
+     * the transformed app id, defaults to OCA\
64
+     * @return string the starting namespace for the app
65
+     */
66
+    public static function buildAppNamespace(string $appId, string $topNamespace = 'OCA\\'): string {
67
+        // Hit the cache!
68
+        if (isset(self::$nameSpaceCache[$appId])) {
69
+            return $topNamespace . self::$nameSpaceCache[$appId];
70
+        }
71
+
72
+        $appInfo = \OCP\Server::get(IAppManager::class)->getAppInfo($appId);
73
+        if (isset($appInfo['namespace'])) {
74
+            self::$nameSpaceCache[$appId] = trim($appInfo['namespace']);
75
+        } else {
76
+            if ($appId !== 'spreed') {
77
+                // if the tag is not found, fall back to uppercasing the first letter
78
+                self::$nameSpaceCache[$appId] = ucfirst($appId);
79
+            } else {
80
+                // For the Talk app (appid spreed) the above fallback doesn't work.
81
+                // This leads to a problem when trying to install it freshly,
82
+                // because the apps namespace is already registered before the
83
+                // app is downloaded from the appstore, because of the hackish
84
+                // global route index.php/call/{token} which is registered via
85
+                // the core/routes.php so it does not have the app namespace.
86
+                // @ref https://github.com/nextcloud/server/pull/19433
87
+                self::$nameSpaceCache[$appId] = 'Talk';
88
+            }
89
+        }
90
+
91
+        return $topNamespace . self::$nameSpaceCache[$appId];
92
+    }
93
+
94
+    public static function getAppIdForClass(string $className, string $topNamespace = 'OCA\\'): ?string {
95
+        if (!str_starts_with($className, $topNamespace)) {
96
+            return null;
97
+        }
98
+
99
+        foreach (self::$nameSpaceCache as $appId => $namespace) {
100
+            if (str_starts_with($className, $topNamespace . $namespace . '\\')) {
101
+                return $appId;
102
+            }
103
+        }
104
+
105
+        return null;
106
+    }
107
+
108
+
109
+    /**
110
+     * Shortcut for calling a controller method and printing the result
111
+     *
112
+     * @param string $controllerName the name of the controller under which it is
113
+     *                               stored in the DI container
114
+     * @param string $methodName the method that you want to call
115
+     * @param DIContainer $container an instance of a pimple container.
116
+     * @param array $urlParams list of URL parameters (optional)
117
+     * @throws HintException
118
+     */
119
+    public static function main(string $controllerName, string $methodName, DIContainer $container, array $urlParams = null) {
120
+        /** @var IProfiler $profiler */
121
+        $profiler = $container->get(IProfiler::class);
122
+        $eventLogger = $container->get(IEventLogger::class);
123
+        // Disable profiler on the profiler UI
124
+        $profiler->setEnabled($profiler->isEnabled() && !is_null($urlParams) && isset($urlParams['_route']) && !str_starts_with($urlParams['_route'], 'profiler.'));
125
+        if ($profiler->isEnabled()) {
126
+            \OC::$server->get(IEventLogger::class)->activate();
127
+            $profiler->add(new RoutingDataCollector($container['AppName'], $controllerName, $methodName));
128
+        }
129
+
130
+        $eventLogger->start('app:controller:params', 'Gather controller parameters');
131
+
132
+        if (!is_null($urlParams)) {
133
+            /** @var Request $request */
134
+            $request = $container->get(IRequest::class);
135
+            $request->setUrlParameters($urlParams);
136
+        } elseif (isset($container['urlParams']) && !is_null($container['urlParams'])) {
137
+            /** @var Request $request */
138
+            $request = $container->get(IRequest::class);
139
+            $request->setUrlParameters($container['urlParams']);
140
+        }
141
+        $appName = $container['AppName'];
142
+
143
+        $eventLogger->end('app:controller:params');
144
+
145
+        $eventLogger->start('app:controller:load', 'Load app controller');
146
+
147
+        // first try $controllerName then go for \OCA\AppName\Controller\$controllerName
148
+        try {
149
+            $controller = $container->get($controllerName);
150
+        } catch (QueryException $e) {
151
+            if (str_contains($controllerName, '\\Controller\\')) {
152
+                // This is from a global registered app route that is not enabled.
153
+                [/*OC(A)*/, $app, /* Controller/Name*/] = explode('\\', $controllerName, 3);
154
+                throw new HintException('App ' . strtolower($app) . ' is not enabled');
155
+            }
156
+
157
+            if ($appName === 'core') {
158
+                $appNameSpace = 'OC\\Core';
159
+            } else {
160
+                $appNameSpace = self::buildAppNamespace($appName);
161
+            }
162
+            $controllerName = $appNameSpace . '\\Controller\\' . $controllerName;
163
+            $controller = $container->query($controllerName);
164
+        }
165
+
166
+        $eventLogger->end('app:controller:load');
167
+
168
+        $eventLogger->start('app:controller:dispatcher', 'Initialize dispatcher and pre-middleware');
169
+
170
+        // initialize the dispatcher and run all the middleware before the controller
171
+        /** @var Dispatcher $dispatcher */
172
+        $dispatcher = $container['Dispatcher'];
173
+
174
+        $eventLogger->end('app:controller:dispatcher');
175
+
176
+        $eventLogger->start('app:controller:run', 'Run app controller');
177
+
178
+        [
179
+            $httpHeaders,
180
+            $responseHeaders,
181
+            $responseCookies,
182
+            $output,
183
+            $response
184
+        ] = $dispatcher->dispatch($controller, $methodName);
185
+
186
+        $eventLogger->end('app:controller:run');
187
+
188
+        $io = $container[IOutput::class];
189
+
190
+        if ($profiler->isEnabled()) {
191
+            $eventLogger->end('runtime');
192
+            $profile = $profiler->collect($container->get(IRequest::class), $response);
193
+            $profiler->saveProfile($profile);
194
+            $io->setHeader('X-Debug-Token:' . $profile->getToken());
195
+            $io->setHeader('Server-Timing: token;desc="' . $profile->getToken() . '"');
196
+        }
197
+
198
+        if (!is_null($httpHeaders)) {
199
+            $io->setHeader($httpHeaders);
200
+        }
201
+
202
+        foreach ($responseHeaders as $name => $value) {
203
+            $io->setHeader($name . ': ' . $value);
204
+        }
205
+
206
+        foreach ($responseCookies as $name => $value) {
207
+            $expireDate = null;
208
+            if ($value['expireDate'] instanceof \DateTime) {
209
+                $expireDate = $value['expireDate']->getTimestamp();
210
+            }
211
+            $sameSite = $value['sameSite'] ?? 'Lax';
212
+
213
+            $io->setCookie(
214
+                $name,
215
+                $value['value'],
216
+                $expireDate,
217
+                $container->getServer()->getWebRoot(),
218
+                null,
219
+                $container->getServer()->getRequest()->getServerProtocol() === 'https',
220
+                true,
221
+                $sameSite
222
+            );
223
+        }
224
+
225
+        /*
226 226
 		 * Status 204 does not have a body and no Content Length
227 227
 		 * Status 304 does not have a body and does not need a Content Length
228 228
 		 * https://tools.ietf.org/html/rfc7230#section-3.3
229 229
 		 * https://tools.ietf.org/html/rfc7230#section-3.3.2
230 230
 		 */
231
-		$emptyResponse = false;
232
-		if (preg_match('/^HTTP\/\d\.\d (\d{3}) .*$/', $httpHeaders, $matches)) {
233
-			$status = (int)$matches[1];
234
-			if ($status === Http::STATUS_NO_CONTENT || $status === Http::STATUS_NOT_MODIFIED) {
235
-				$emptyResponse = true;
236
-			}
237
-		}
238
-
239
-		if (!$emptyResponse) {
240
-			if ($response instanceof ICallbackResponse) {
241
-				$response->callback($io);
242
-			} elseif (!is_null($output)) {
243
-				$io->setHeader('Content-Length: ' . strlen($output));
244
-				$io->setOutput($output);
245
-			}
246
-		}
247
-	}
248
-
249
-	/**
250
-	 * Shortcut for calling a controller method and printing the result.
251
-	 * Similar to App:main except that no headers will be sent.
252
-	 *
253
-	 * @param string $controllerName the name of the controller under which it is
254
-	 *                               stored in the DI container
255
-	 * @param string $methodName the method that you want to call
256
-	 * @param array $urlParams an array with variables extracted from the routes
257
-	 * @param DIContainer $container an instance of a pimple container.
258
-	 */
259
-	public static function part(string $controllerName, string $methodName, array $urlParams,
260
-								DIContainer $container) {
261
-		$container['urlParams'] = $urlParams;
262
-		$controller = $container[$controllerName];
263
-
264
-		$dispatcher = $container['Dispatcher'];
265
-
266
-		[, , $output] = $dispatcher->dispatch($controller, $methodName);
267
-		return $output;
268
-	}
231
+        $emptyResponse = false;
232
+        if (preg_match('/^HTTP\/\d\.\d (\d{3}) .*$/', $httpHeaders, $matches)) {
233
+            $status = (int)$matches[1];
234
+            if ($status === Http::STATUS_NO_CONTENT || $status === Http::STATUS_NOT_MODIFIED) {
235
+                $emptyResponse = true;
236
+            }
237
+        }
238
+
239
+        if (!$emptyResponse) {
240
+            if ($response instanceof ICallbackResponse) {
241
+                $response->callback($io);
242
+            } elseif (!is_null($output)) {
243
+                $io->setHeader('Content-Length: ' . strlen($output));
244
+                $io->setOutput($output);
245
+            }
246
+        }
247
+    }
248
+
249
+    /**
250
+     * Shortcut for calling a controller method and printing the result.
251
+     * Similar to App:main except that no headers will be sent.
252
+     *
253
+     * @param string $controllerName the name of the controller under which it is
254
+     *                               stored in the DI container
255
+     * @param string $methodName the method that you want to call
256
+     * @param array $urlParams an array with variables extracted from the routes
257
+     * @param DIContainer $container an instance of a pimple container.
258
+     */
259
+    public static function part(string $controllerName, string $methodName, array $urlParams,
260
+                                DIContainer $container) {
261
+        $container['urlParams'] = $urlParams;
262
+        $controller = $container[$controllerName];
263
+
264
+        $dispatcher = $container['Dispatcher'];
265
+
266
+        [, , $output] = $dispatcher->dispatch($controller, $methodName);
267
+        return $output;
268
+    }
269 269
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -66,7 +66,7 @@  discard block
 block discarded – undo
66 66
 	public static function buildAppNamespace(string $appId, string $topNamespace = 'OCA\\'): string {
67 67
 		// Hit the cache!
68 68
 		if (isset(self::$nameSpaceCache[$appId])) {
69
-			return $topNamespace . self::$nameSpaceCache[$appId];
69
+			return $topNamespace.self::$nameSpaceCache[$appId];
70 70
 		}
71 71
 
72 72
 		$appInfo = \OCP\Server::get(IAppManager::class)->getAppInfo($appId);
@@ -88,7 +88,7 @@  discard block
 block discarded – undo
88 88
 			}
89 89
 		}
90 90
 
91
-		return $topNamespace . self::$nameSpaceCache[$appId];
91
+		return $topNamespace.self::$nameSpaceCache[$appId];
92 92
 	}
93 93
 
94 94
 	public static function getAppIdForClass(string $className, string $topNamespace = 'OCA\\'): ?string {
@@ -97,7 +97,7 @@  discard block
 block discarded – undo
97 97
 		}
98 98
 
99 99
 		foreach (self::$nameSpaceCache as $appId => $namespace) {
100
-			if (str_starts_with($className, $topNamespace . $namespace . '\\')) {
100
+			if (str_starts_with($className, $topNamespace.$namespace.'\\')) {
101 101
 				return $appId;
102 102
 			}
103 103
 		}
@@ -151,7 +151,7 @@  discard block
 block discarded – undo
151 151
 			if (str_contains($controllerName, '\\Controller\\')) {
152 152
 				// This is from a global registered app route that is not enabled.
153 153
 				[/*OC(A)*/, $app, /* Controller/Name*/] = explode('\\', $controllerName, 3);
154
-				throw new HintException('App ' . strtolower($app) . ' is not enabled');
154
+				throw new HintException('App '.strtolower($app).' is not enabled');
155 155
 			}
156 156
 
157 157
 			if ($appName === 'core') {
@@ -159,7 +159,7 @@  discard block
 block discarded – undo
159 159
 			} else {
160 160
 				$appNameSpace = self::buildAppNamespace($appName);
161 161
 			}
162
-			$controllerName = $appNameSpace . '\\Controller\\' . $controllerName;
162
+			$controllerName = $appNameSpace.'\\Controller\\'.$controllerName;
163 163
 			$controller = $container->query($controllerName);
164 164
 		}
165 165
 
@@ -191,8 +191,8 @@  discard block
 block discarded – undo
191 191
 			$eventLogger->end('runtime');
192 192
 			$profile = $profiler->collect($container->get(IRequest::class), $response);
193 193
 			$profiler->saveProfile($profile);
194
-			$io->setHeader('X-Debug-Token:' . $profile->getToken());
195
-			$io->setHeader('Server-Timing: token;desc="' . $profile->getToken() . '"');
194
+			$io->setHeader('X-Debug-Token:'.$profile->getToken());
195
+			$io->setHeader('Server-Timing: token;desc="'.$profile->getToken().'"');
196 196
 		}
197 197
 
198 198
 		if (!is_null($httpHeaders)) {
@@ -200,7 +200,7 @@  discard block
 block discarded – undo
200 200
 		}
201 201
 
202 202
 		foreach ($responseHeaders as $name => $value) {
203
-			$io->setHeader($name . ': ' . $value);
203
+			$io->setHeader($name.': '.$value);
204 204
 		}
205 205
 
206 206
 		foreach ($responseCookies as $name => $value) {
@@ -230,7 +230,7 @@  discard block
 block discarded – undo
230 230
 		 */
231 231
 		$emptyResponse = false;
232 232
 		if (preg_match('/^HTTP\/\d\.\d (\d{3}) .*$/', $httpHeaders, $matches)) {
233
-			$status = (int)$matches[1];
233
+			$status = (int) $matches[1];
234 234
 			if ($status === Http::STATUS_NO_CONTENT || $status === Http::STATUS_NOT_MODIFIED) {
235 235
 				$emptyResponse = true;
236 236
 			}
@@ -240,7 +240,7 @@  discard block
 block discarded – undo
240 240
 			if ($response instanceof ICallbackResponse) {
241 241
 				$response->callback($io);
242 242
 			} elseif (!is_null($output)) {
243
-				$io->setHeader('Content-Length: ' . strlen($output));
243
+				$io->setHeader('Content-Length: '.strlen($output));
244 244
 				$io->setOutput($output);
245 245
 			}
246 246
 		}
@@ -263,7 +263,7 @@  discard block
 block discarded – undo
263 263
 
264 264
 		$dispatcher = $container['Dispatcher'];
265 265
 
266
-		[, , $output] = $dispatcher->dispatch($controller, $methodName);
266
+		[,, $output] = $dispatcher->dispatch($controller, $methodName);
267 267
 		return $output;
268 268
 	}
269 269
 }
Please login to merge, or discard this patch.
lib/private/AppFramework/OCS/BaseResponse.php 1 patch
Indentation   +125 added lines, -125 removed lines patch added patch discarded remove patch
@@ -31,129 +31,129 @@
 block discarded – undo
31 31
 use OCP\AppFramework\Http\Response;
32 32
 
33 33
 abstract class BaseResponse extends Response {
34
-	/** @var array */
35
-	protected $data;
36
-
37
-	/** @var string */
38
-	protected $format;
39
-
40
-	/** @var ?string */
41
-	protected $statusMessage;
42
-
43
-	/** @var ?int */
44
-	protected $itemsCount;
45
-
46
-	/** @var ?int */
47
-	protected $itemsPerPage;
48
-
49
-	/**
50
-	 * BaseResponse constructor.
51
-	 *
52
-	 * @param DataResponse $dataResponse
53
-	 * @param string $format
54
-	 * @param string|null $statusMessage
55
-	 * @param int|null $itemsCount
56
-	 * @param int|null $itemsPerPage
57
-	 */
58
-	public function __construct(DataResponse $dataResponse,
59
-								$format = 'xml',
60
-								$statusMessage = null,
61
-								$itemsCount = null,
62
-								$itemsPerPage = null) {
63
-		parent::__construct();
64
-
65
-		$this->format = $format;
66
-		$this->statusMessage = $statusMessage;
67
-		$this->itemsCount = $itemsCount;
68
-		$this->itemsPerPage = $itemsPerPage;
69
-
70
-		$this->data = $dataResponse->getData();
71
-
72
-		$this->setHeaders($dataResponse->getHeaders());
73
-		$this->setStatus($dataResponse->getStatus());
74
-		$this->setETag($dataResponse->getETag());
75
-		$this->setLastModified($dataResponse->getLastModified());
76
-		$this->setCookies($dataResponse->getCookies());
77
-
78
-		if ($dataResponse->isThrottled()) {
79
-			$throttleMetadata = $dataResponse->getThrottleMetadata();
80
-			$this->throttle($throttleMetadata);
81
-		}
82
-
83
-		if ($format === 'json') {
84
-			$this->addHeader(
85
-				'Content-Type', 'application/json; charset=utf-8'
86
-			);
87
-		} else {
88
-			$this->addHeader(
89
-				'Content-Type', 'application/xml; charset=utf-8'
90
-			);
91
-		}
92
-	}
93
-
94
-	/**
95
-	 * @param array<string,string|int> $meta
96
-	 * @return string
97
-	 */
98
-	protected function renderResult(array $meta): string {
99
-		$status = $this->getStatus();
100
-		if ($status === Http::STATUS_NO_CONTENT ||
101
-			$status === Http::STATUS_NOT_MODIFIED ||
102
-			($status >= 100 && $status <= 199)) {
103
-			// Those status codes are not supposed to have a body:
104
-			// https://stackoverflow.com/q/8628725
105
-			return '';
106
-		}
107
-
108
-		$response = [
109
-			'ocs' => [
110
-				'meta' => $meta,
111
-				'data' => $this->data,
112
-			],
113
-		];
114
-
115
-		if ($this->format === 'json') {
116
-			return json_encode($response, JSON_HEX_TAG);
117
-		}
118
-
119
-		$writer = new \XMLWriter();
120
-		$writer->openMemory();
121
-		$writer->setIndent(true);
122
-		$writer->startDocument();
123
-		$this->toXML($response, $writer);
124
-		$writer->endDocument();
125
-		return $writer->outputMemory(true);
126
-	}
127
-
128
-	protected function toXML(array $array, \XMLWriter $writer): void {
129
-		foreach ($array as $k => $v) {
130
-			if ($k === '@attributes' && is_array($v)) {
131
-				foreach ($v as $k2 => $v2) {
132
-					$writer->writeAttribute($k2, $v2);
133
-				}
134
-				continue;
135
-			}
136
-
137
-			if (\is_string($k) && str_starts_with($k, '@')) {
138
-				$writer->writeAttribute(substr($k, 1), $v);
139
-				continue;
140
-			}
141
-
142
-			if (\is_numeric($k)) {
143
-				$k = 'element';
144
-			}
145
-
146
-			if (\is_array($v)) {
147
-				$writer->startElement($k);
148
-				$this->toXML($v, $writer);
149
-				$writer->endElement();
150
-			} else {
151
-				$writer->writeElement($k, $v);
152
-			}
153
-		}
154
-	}
155
-
156
-	public function getOCSStatus() {
157
-		return parent::getStatus();
158
-	}
34
+    /** @var array */
35
+    protected $data;
36
+
37
+    /** @var string */
38
+    protected $format;
39
+
40
+    /** @var ?string */
41
+    protected $statusMessage;
42
+
43
+    /** @var ?int */
44
+    protected $itemsCount;
45
+
46
+    /** @var ?int */
47
+    protected $itemsPerPage;
48
+
49
+    /**
50
+     * BaseResponse constructor.
51
+     *
52
+     * @param DataResponse $dataResponse
53
+     * @param string $format
54
+     * @param string|null $statusMessage
55
+     * @param int|null $itemsCount
56
+     * @param int|null $itemsPerPage
57
+     */
58
+    public function __construct(DataResponse $dataResponse,
59
+                                $format = 'xml',
60
+                                $statusMessage = null,
61
+                                $itemsCount = null,
62
+                                $itemsPerPage = null) {
63
+        parent::__construct();
64
+
65
+        $this->format = $format;
66
+        $this->statusMessage = $statusMessage;
67
+        $this->itemsCount = $itemsCount;
68
+        $this->itemsPerPage = $itemsPerPage;
69
+
70
+        $this->data = $dataResponse->getData();
71
+
72
+        $this->setHeaders($dataResponse->getHeaders());
73
+        $this->setStatus($dataResponse->getStatus());
74
+        $this->setETag($dataResponse->getETag());
75
+        $this->setLastModified($dataResponse->getLastModified());
76
+        $this->setCookies($dataResponse->getCookies());
77
+
78
+        if ($dataResponse->isThrottled()) {
79
+            $throttleMetadata = $dataResponse->getThrottleMetadata();
80
+            $this->throttle($throttleMetadata);
81
+        }
82
+
83
+        if ($format === 'json') {
84
+            $this->addHeader(
85
+                'Content-Type', 'application/json; charset=utf-8'
86
+            );
87
+        } else {
88
+            $this->addHeader(
89
+                'Content-Type', 'application/xml; charset=utf-8'
90
+            );
91
+        }
92
+    }
93
+
94
+    /**
95
+     * @param array<string,string|int> $meta
96
+     * @return string
97
+     */
98
+    protected function renderResult(array $meta): string {
99
+        $status = $this->getStatus();
100
+        if ($status === Http::STATUS_NO_CONTENT ||
101
+            $status === Http::STATUS_NOT_MODIFIED ||
102
+            ($status >= 100 && $status <= 199)) {
103
+            // Those status codes are not supposed to have a body:
104
+            // https://stackoverflow.com/q/8628725
105
+            return '';
106
+        }
107
+
108
+        $response = [
109
+            'ocs' => [
110
+                'meta' => $meta,
111
+                'data' => $this->data,
112
+            ],
113
+        ];
114
+
115
+        if ($this->format === 'json') {
116
+            return json_encode($response, JSON_HEX_TAG);
117
+        }
118
+
119
+        $writer = new \XMLWriter();
120
+        $writer->openMemory();
121
+        $writer->setIndent(true);
122
+        $writer->startDocument();
123
+        $this->toXML($response, $writer);
124
+        $writer->endDocument();
125
+        return $writer->outputMemory(true);
126
+    }
127
+
128
+    protected function toXML(array $array, \XMLWriter $writer): void {
129
+        foreach ($array as $k => $v) {
130
+            if ($k === '@attributes' && is_array($v)) {
131
+                foreach ($v as $k2 => $v2) {
132
+                    $writer->writeAttribute($k2, $v2);
133
+                }
134
+                continue;
135
+            }
136
+
137
+            if (\is_string($k) && str_starts_with($k, '@')) {
138
+                $writer->writeAttribute(substr($k, 1), $v);
139
+                continue;
140
+            }
141
+
142
+            if (\is_numeric($k)) {
143
+                $k = 'element';
144
+            }
145
+
146
+            if (\is_array($v)) {
147
+                $writer->startElement($k);
148
+                $this->toXML($v, $writer);
149
+                $writer->endElement();
150
+            } else {
151
+                $writer->writeElement($k, $v);
152
+            }
153
+        }
154
+    }
155
+
156
+    public function getOCSStatus() {
157
+        return parent::getStatus();
158
+    }
159 159
 }
Please login to merge, or discard this patch.
lib/private/AppFramework/Http/Dispatcher.php 1 patch
Indentation   +201 added lines, -201 removed lines patch added patch discarded remove patch
@@ -49,205 +49,205 @@
 block discarded – undo
49 49
  * Class to dispatch the request to the middleware dispatcher
50 50
  */
51 51
 class Dispatcher {
52
-	/** @var MiddlewareDispatcher */
53
-	private $middlewareDispatcher;
54
-
55
-	/** @var Http */
56
-	private $protocol;
57
-
58
-	/** @var ControllerMethodReflector */
59
-	private $reflector;
60
-
61
-	/** @var IRequest */
62
-	private $request;
63
-
64
-	/** @var IConfig */
65
-	private $config;
66
-
67
-	/** @var ConnectionAdapter */
68
-	private $connection;
69
-
70
-	/** @var LoggerInterface */
71
-	private $logger;
72
-
73
-	/** @var IEventLogger */
74
-	private $eventLogger;
75
-
76
-	private ContainerInterface $appContainer;
77
-
78
-	/**
79
-	 * @param Http $protocol the http protocol with contains all status headers
80
-	 * @param MiddlewareDispatcher $middlewareDispatcher the dispatcher which
81
-	 * runs the middleware
82
-	 * @param ControllerMethodReflector $reflector the reflector that is used to inject
83
-	 * the arguments for the controller
84
-	 * @param IRequest $request the incoming request
85
-	 * @param IConfig $config
86
-	 * @param ConnectionAdapter $connection
87
-	 * @param LoggerInterface $logger
88
-	 * @param IEventLogger $eventLogger
89
-	 */
90
-	public function __construct(Http $protocol,
91
-								MiddlewareDispatcher $middlewareDispatcher,
92
-								ControllerMethodReflector $reflector,
93
-								IRequest $request,
94
-								IConfig $config,
95
-								ConnectionAdapter $connection,
96
-								LoggerInterface $logger,
97
-								IEventLogger $eventLogger,
98
-								ContainerInterface $appContainer) {
99
-		$this->protocol = $protocol;
100
-		$this->middlewareDispatcher = $middlewareDispatcher;
101
-		$this->reflector = $reflector;
102
-		$this->request = $request;
103
-		$this->config = $config;
104
-		$this->connection = $connection;
105
-		$this->logger = $logger;
106
-		$this->eventLogger = $eventLogger;
107
-		$this->appContainer = $appContainer;
108
-	}
109
-
110
-
111
-	/**
112
-	 * Handles a request and calls the dispatcher on the controller
113
-	 * @param Controller $controller the controller which will be called
114
-	 * @param string $methodName the method name which will be called on
115
-	 * the controller
116
-	 * @return array $array[0] contains a string with the http main header,
117
-	 * $array[1] contains headers in the form: $key => value, $array[2] contains
118
-	 * the response output
119
-	 * @throws \Exception
120
-	 */
121
-	public function dispatch(Controller $controller, string $methodName): array {
122
-		$out = [null, [], null];
123
-
124
-		try {
125
-			// prefill reflector with everything that's needed for the
126
-			// middlewares
127
-			$this->reflector->reflect($controller, $methodName);
128
-
129
-			$this->middlewareDispatcher->beforeController($controller,
130
-				$methodName);
131
-
132
-			$databaseStatsBefore = [];
133
-			if ($this->config->getSystemValueBool('debug', false)) {
134
-				$databaseStatsBefore = $this->connection->getInner()->getStats();
135
-			}
136
-
137
-			$response = $this->executeController($controller, $methodName);
138
-
139
-			if (!empty($databaseStatsBefore)) {
140
-				$databaseStatsAfter = $this->connection->getInner()->getStats();
141
-				$numBuilt = $databaseStatsAfter['built'] - $databaseStatsBefore['built'];
142
-				$numExecuted = $databaseStatsAfter['executed'] - $databaseStatsBefore['executed'];
143
-
144
-				if ($numBuilt > 50) {
145
-					$this->logger->debug('Controller {class}::{method} created {count} QueryBuilder objects, please check if they are created inside a loop by accident.', [
146
-						'class' => get_class($controller),
147
-						'method' => $methodName,
148
-						'count' => $numBuilt,
149
-					]);
150
-				}
151
-
152
-				if ($numExecuted > 100) {
153
-					$this->logger->warning('Controller {class}::{method} executed {count} queries.', [
154
-						'class' => get_class($controller),
155
-						'method' => $methodName,
156
-						'count' => $numExecuted,
157
-					]);
158
-				}
159
-			}
160
-
161
-			// if an exception appears, the middleware checks if it can handle the
162
-			// exception and creates a response. If no response is created, it is
163
-			// assumed that there's no middleware who can handle it and the error is
164
-			// thrown again
165
-		} catch (\Exception $exception) {
166
-			$response = $this->middlewareDispatcher->afterException(
167
-				$controller, $methodName, $exception);
168
-		} catch (\Throwable $throwable) {
169
-			$exception = new \Exception($throwable->getMessage() . ' in file \'' . $throwable->getFile() . '\' line ' . $throwable->getLine(), $throwable->getCode(), $throwable);
170
-			$response = $this->middlewareDispatcher->afterException(
171
-				$controller, $methodName, $exception);
172
-		}
173
-
174
-		$response = $this->middlewareDispatcher->afterController(
175
-			$controller, $methodName, $response);
176
-
177
-		// depending on the cache object the headers need to be changed
178
-		$out[0] = $this->protocol->getStatusHeader($response->getStatus());
179
-		$out[1] = array_merge($response->getHeaders());
180
-		$out[2] = $response->getCookies();
181
-		$out[3] = $this->middlewareDispatcher->beforeOutput(
182
-			$controller, $methodName, $response->render()
183
-		);
184
-		$out[4] = $response;
185
-
186
-		return $out;
187
-	}
188
-
189
-
190
-	/**
191
-	 * Uses the reflected parameters, types and request parameters to execute
192
-	 * the controller
193
-	 * @param Controller $controller the controller to be executed
194
-	 * @param string $methodName the method on the controller that should be executed
195
-	 * @return Response
196
-	 */
197
-	private function executeController(Controller $controller, string $methodName): Response {
198
-		$arguments = [];
199
-
200
-		// valid types that will be casted
201
-		$types = ['int', 'integer', 'bool', 'boolean', 'float', 'double'];
202
-
203
-		foreach ($this->reflector->getParameters() as $param => $default) {
204
-			// try to get the parameter from the request object and cast
205
-			// it to the type annotated in the @param annotation
206
-			$value = $this->request->getParam($param, $default);
207
-			$type = $this->reflector->getType($param);
208
-
209
-			// if this is submitted using GET or a POST form, 'false' should be
210
-			// converted to false
211
-			if (($type === 'bool' || $type === 'boolean') &&
212
-				$value === 'false' &&
213
-				(
214
-					$this->request->method === 'GET' ||
215
-					str_contains($this->request->getHeader('Content-Type'),
216
-						'application/x-www-form-urlencoded')
217
-				)
218
-			) {
219
-				$value = false;
220
-			} elseif ($value !== null && \in_array($type, $types, true)) {
221
-				settype($value, $type);
222
-			} elseif ($value === null && $type !== null && $this->appContainer->has($type)) {
223
-				$value = $this->appContainer->get($type);
224
-			}
225
-
226
-			$arguments[] = $value;
227
-		}
228
-
229
-		$this->eventLogger->start('controller:' . get_class($controller) . '::' . $methodName, 'App framework controller execution');
230
-		$response = \call_user_func_array([$controller, $methodName], $arguments);
231
-		$this->eventLogger->end('controller:' . get_class($controller) . '::' . $methodName);
232
-
233
-		// format response
234
-		if ($response instanceof DataResponse || !($response instanceof Response)) {
235
-			// get format from the url format or request format parameter
236
-			$format = $this->request->getParam('format');
237
-
238
-			// if none is given try the first Accept header
239
-			if ($format === null) {
240
-				$headers = $this->request->getHeader('Accept');
241
-				$format = $controller->getResponderByHTTPHeader($headers, null);
242
-			}
243
-
244
-			if ($format !== null) {
245
-				$response = $controller->buildResponse($response, $format);
246
-			} else {
247
-				$response = $controller->buildResponse($response);
248
-			}
249
-		}
250
-
251
-		return $response;
252
-	}
52
+    /** @var MiddlewareDispatcher */
53
+    private $middlewareDispatcher;
54
+
55
+    /** @var Http */
56
+    private $protocol;
57
+
58
+    /** @var ControllerMethodReflector */
59
+    private $reflector;
60
+
61
+    /** @var IRequest */
62
+    private $request;
63
+
64
+    /** @var IConfig */
65
+    private $config;
66
+
67
+    /** @var ConnectionAdapter */
68
+    private $connection;
69
+
70
+    /** @var LoggerInterface */
71
+    private $logger;
72
+
73
+    /** @var IEventLogger */
74
+    private $eventLogger;
75
+
76
+    private ContainerInterface $appContainer;
77
+
78
+    /**
79
+     * @param Http $protocol the http protocol with contains all status headers
80
+     * @param MiddlewareDispatcher $middlewareDispatcher the dispatcher which
81
+     * runs the middleware
82
+     * @param ControllerMethodReflector $reflector the reflector that is used to inject
83
+     * the arguments for the controller
84
+     * @param IRequest $request the incoming request
85
+     * @param IConfig $config
86
+     * @param ConnectionAdapter $connection
87
+     * @param LoggerInterface $logger
88
+     * @param IEventLogger $eventLogger
89
+     */
90
+    public function __construct(Http $protocol,
91
+                                MiddlewareDispatcher $middlewareDispatcher,
92
+                                ControllerMethodReflector $reflector,
93
+                                IRequest $request,
94
+                                IConfig $config,
95
+                                ConnectionAdapter $connection,
96
+                                LoggerInterface $logger,
97
+                                IEventLogger $eventLogger,
98
+                                ContainerInterface $appContainer) {
99
+        $this->protocol = $protocol;
100
+        $this->middlewareDispatcher = $middlewareDispatcher;
101
+        $this->reflector = $reflector;
102
+        $this->request = $request;
103
+        $this->config = $config;
104
+        $this->connection = $connection;
105
+        $this->logger = $logger;
106
+        $this->eventLogger = $eventLogger;
107
+        $this->appContainer = $appContainer;
108
+    }
109
+
110
+
111
+    /**
112
+     * Handles a request and calls the dispatcher on the controller
113
+     * @param Controller $controller the controller which will be called
114
+     * @param string $methodName the method name which will be called on
115
+     * the controller
116
+     * @return array $array[0] contains a string with the http main header,
117
+     * $array[1] contains headers in the form: $key => value, $array[2] contains
118
+     * the response output
119
+     * @throws \Exception
120
+     */
121
+    public function dispatch(Controller $controller, string $methodName): array {
122
+        $out = [null, [], null];
123
+
124
+        try {
125
+            // prefill reflector with everything that's needed for the
126
+            // middlewares
127
+            $this->reflector->reflect($controller, $methodName);
128
+
129
+            $this->middlewareDispatcher->beforeController($controller,
130
+                $methodName);
131
+
132
+            $databaseStatsBefore = [];
133
+            if ($this->config->getSystemValueBool('debug', false)) {
134
+                $databaseStatsBefore = $this->connection->getInner()->getStats();
135
+            }
136
+
137
+            $response = $this->executeController($controller, $methodName);
138
+
139
+            if (!empty($databaseStatsBefore)) {
140
+                $databaseStatsAfter = $this->connection->getInner()->getStats();
141
+                $numBuilt = $databaseStatsAfter['built'] - $databaseStatsBefore['built'];
142
+                $numExecuted = $databaseStatsAfter['executed'] - $databaseStatsBefore['executed'];
143
+
144
+                if ($numBuilt > 50) {
145
+                    $this->logger->debug('Controller {class}::{method} created {count} QueryBuilder objects, please check if they are created inside a loop by accident.', [
146
+                        'class' => get_class($controller),
147
+                        'method' => $methodName,
148
+                        'count' => $numBuilt,
149
+                    ]);
150
+                }
151
+
152
+                if ($numExecuted > 100) {
153
+                    $this->logger->warning('Controller {class}::{method} executed {count} queries.', [
154
+                        'class' => get_class($controller),
155
+                        'method' => $methodName,
156
+                        'count' => $numExecuted,
157
+                    ]);
158
+                }
159
+            }
160
+
161
+            // if an exception appears, the middleware checks if it can handle the
162
+            // exception and creates a response. If no response is created, it is
163
+            // assumed that there's no middleware who can handle it and the error is
164
+            // thrown again
165
+        } catch (\Exception $exception) {
166
+            $response = $this->middlewareDispatcher->afterException(
167
+                $controller, $methodName, $exception);
168
+        } catch (\Throwable $throwable) {
169
+            $exception = new \Exception($throwable->getMessage() . ' in file \'' . $throwable->getFile() . '\' line ' . $throwable->getLine(), $throwable->getCode(), $throwable);
170
+            $response = $this->middlewareDispatcher->afterException(
171
+                $controller, $methodName, $exception);
172
+        }
173
+
174
+        $response = $this->middlewareDispatcher->afterController(
175
+            $controller, $methodName, $response);
176
+
177
+        // depending on the cache object the headers need to be changed
178
+        $out[0] = $this->protocol->getStatusHeader($response->getStatus());
179
+        $out[1] = array_merge($response->getHeaders());
180
+        $out[2] = $response->getCookies();
181
+        $out[3] = $this->middlewareDispatcher->beforeOutput(
182
+            $controller, $methodName, $response->render()
183
+        );
184
+        $out[4] = $response;
185
+
186
+        return $out;
187
+    }
188
+
189
+
190
+    /**
191
+     * Uses the reflected parameters, types and request parameters to execute
192
+     * the controller
193
+     * @param Controller $controller the controller to be executed
194
+     * @param string $methodName the method on the controller that should be executed
195
+     * @return Response
196
+     */
197
+    private function executeController(Controller $controller, string $methodName): Response {
198
+        $arguments = [];
199
+
200
+        // valid types that will be casted
201
+        $types = ['int', 'integer', 'bool', 'boolean', 'float', 'double'];
202
+
203
+        foreach ($this->reflector->getParameters() as $param => $default) {
204
+            // try to get the parameter from the request object and cast
205
+            // it to the type annotated in the @param annotation
206
+            $value = $this->request->getParam($param, $default);
207
+            $type = $this->reflector->getType($param);
208
+
209
+            // if this is submitted using GET or a POST form, 'false' should be
210
+            // converted to false
211
+            if (($type === 'bool' || $type === 'boolean') &&
212
+                $value === 'false' &&
213
+                (
214
+                    $this->request->method === 'GET' ||
215
+                    str_contains($this->request->getHeader('Content-Type'),
216
+                        'application/x-www-form-urlencoded')
217
+                )
218
+            ) {
219
+                $value = false;
220
+            } elseif ($value !== null && \in_array($type, $types, true)) {
221
+                settype($value, $type);
222
+            } elseif ($value === null && $type !== null && $this->appContainer->has($type)) {
223
+                $value = $this->appContainer->get($type);
224
+            }
225
+
226
+            $arguments[] = $value;
227
+        }
228
+
229
+        $this->eventLogger->start('controller:' . get_class($controller) . '::' . $methodName, 'App framework controller execution');
230
+        $response = \call_user_func_array([$controller, $methodName], $arguments);
231
+        $this->eventLogger->end('controller:' . get_class($controller) . '::' . $methodName);
232
+
233
+        // format response
234
+        if ($response instanceof DataResponse || !($response instanceof Response)) {
235
+            // get format from the url format or request format parameter
236
+            $format = $this->request->getParam('format');
237
+
238
+            // if none is given try the first Accept header
239
+            if ($format === null) {
240
+                $headers = $this->request->getHeader('Accept');
241
+                $format = $controller->getResponderByHTTPHeader($headers, null);
242
+            }
243
+
244
+            if ($format !== null) {
245
+                $response = $controller->buildResponse($response, $format);
246
+            } else {
247
+                $response = $controller->buildResponse($response);
248
+            }
249
+        }
250
+
251
+        return $response;
252
+    }
253 253
 }
Please login to merge, or discard this patch.
lib/private/AppFramework/Http/Request.php 2 patches
Indentation   +806 added lines, -806 removed lines patch added patch discarded remove patch
@@ -65,810 +65,810 @@
 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
-				if ($this->isPutStreamContent()) {
264
-					return $this->items['parameters'];
265
-				}
266
-				return $this->getContent();
267
-			default:
268
-				return isset($this[$name])
269
-					? $this[$name]
270
-					: null;
271
-		}
272
-	}
273
-
274
-	/**
275
-	 * @param string $name
276
-	 * @return bool
277
-	 */
278
-	public function __isset($name) {
279
-		if (\in_array($name, $this->allowedKeys, true)) {
280
-			return true;
281
-		}
282
-		return isset($this->items['parameters'][$name]);
283
-	}
284
-
285
-	/**
286
-	 * @param string $id
287
-	 */
288
-	public function __unset($id) {
289
-		throw new \RuntimeException('You cannot change the contents of the request object');
290
-	}
291
-
292
-	/**
293
-	 * Returns the value for a specific http header.
294
-	 *
295
-	 * This method returns an empty string if the header did not exist.
296
-	 *
297
-	 * @param string $name
298
-	 * @return string
299
-	 */
300
-	public function getHeader(string $name): string {
301
-		$name = strtoupper(str_replace('-', '_', $name));
302
-		if (isset($this->server['HTTP_' . $name])) {
303
-			return $this->server['HTTP_' . $name];
304
-		}
305
-
306
-		// There's a few headers that seem to end up in the top-level
307
-		// server array.
308
-		switch ($name) {
309
-			case 'CONTENT_TYPE':
310
-			case 'CONTENT_LENGTH':
311
-			case 'REMOTE_ADDR':
312
-				if (isset($this->server[$name])) {
313
-					return $this->server[$name];
314
-				}
315
-				break;
316
-		}
317
-
318
-		return '';
319
-	}
320
-
321
-	/**
322
-	 * Lets you access post and get parameters by the index
323
-	 * In case of json requests the encoded json body is accessed
324
-	 *
325
-	 * @param string $key the key which you want to access in the URL Parameter
326
-	 *                     placeholder, $_POST or $_GET array.
327
-	 *                     The priority how they're returned is the following:
328
-	 *                     1. URL parameters
329
-	 *                     2. POST parameters
330
-	 *                     3. GET parameters
331
-	 * @param mixed $default If the key is not found, this value will be returned
332
-	 * @return mixed the content of the array
333
-	 */
334
-	public function getParam(string $key, $default = null) {
335
-		return isset($this->parameters[$key])
336
-			? $this->parameters[$key]
337
-			: $default;
338
-	}
339
-
340
-	/**
341
-	 * Returns all params that were received, be it from the request
342
-	 * (as GET or POST) or through the URL by the route
343
-	 * @return array the array with all parameters
344
-	 */
345
-	public function getParams(): array {
346
-		return is_array($this->parameters) ? $this->parameters : [];
347
-	}
348
-
349
-	/**
350
-	 * Returns the method of the request
351
-	 * @return string the method of the request (POST, GET, etc)
352
-	 */
353
-	public function getMethod(): string {
354
-		return $this->method;
355
-	}
356
-
357
-	/**
358
-	 * Shortcut for accessing an uploaded file through the $_FILES array
359
-	 * @param string $key the key that will be taken from the $_FILES array
360
-	 * @return array the file in the $_FILES element
361
-	 */
362
-	public function getUploadedFile(string $key) {
363
-		return isset($this->files[$key]) ? $this->files[$key] : null;
364
-	}
365
-
366
-	/**
367
-	 * Shortcut for getting env variables
368
-	 * @param string $key the key that will be taken from the $_ENV array
369
-	 * @return array the value in the $_ENV element
370
-	 */
371
-	public function getEnv(string $key) {
372
-		return isset($this->env[$key]) ? $this->env[$key] : null;
373
-	}
374
-
375
-	/**
376
-	 * Shortcut for getting cookie variables
377
-	 * @param string $key the key that will be taken from the $_COOKIE array
378
-	 * @return string the value in the $_COOKIE element
379
-	 */
380
-	public function getCookie(string $key) {
381
-		return isset($this->cookies[$key]) ? $this->cookies[$key] : null;
382
-	}
383
-
384
-	/**
385
-	 * Returns the request body content.
386
-	 *
387
-	 * If the HTTP request method is PUT and the body
388
-	 * not application/x-www-form-urlencoded or application/json a stream
389
-	 * resource is returned, otherwise an array.
390
-	 *
391
-	 * @return array|string|resource The request body content or a resource to read the body stream.
392
-	 *
393
-	 * @throws \LogicException
394
-	 */
395
-	protected function getContent() {
396
-		// If the content can't be parsed into an array then return a stream resource.
397
-		if ($this->isPutStreamContent()) {
398
-			if ($this->content === false) {
399
-				throw new \LogicException(
400
-					'"put" can only be accessed once if not '
401
-					. 'application/x-www-form-urlencoded or application/json.'
402
-				);
403
-			}
404
-			$this->content = false;
405
-			return fopen($this->inputStream, 'rb');
406
-		} else {
407
-			$this->decodeContent();
408
-			return $this->items['parameters'];
409
-		}
410
-	}
411
-
412
-	private function isPutStreamContent(): bool {
413
-		return $this->method === 'PUT'
414
-			&& $this->getHeader('Content-Length') !== '0'
415
-			&& $this->getHeader('Content-Length') !== ''
416
-			&& !str_contains($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded')
417
-			&& !str_contains($this->getHeader('Content-Type'), 'application/json');
418
-	}
419
-
420
-	/**
421
-	 * Attempt to decode the content and populate parameters
422
-	 */
423
-	protected function decodeContent() {
424
-		if ($this->contentDecoded) {
425
-			return;
426
-		}
427
-		$params = [];
428
-
429
-		// 'application/json' and other JSON-related content types must be decoded manually.
430
-		if (preg_match(self::JSON_CONTENT_TYPE_REGEX, $this->getHeader('Content-Type')) === 1) {
431
-			$params = json_decode(file_get_contents($this->inputStream), true);
432
-			if (\is_array($params) && \count($params) > 0) {
433
-				$this->items['params'] = $params;
434
-				if ($this->method === 'POST') {
435
-					$this->items['post'] = $params;
436
-				}
437
-			}
438
-		// Handle application/x-www-form-urlencoded for methods other than GET
439
-		// or post correctly
440
-		} elseif ($this->method !== 'GET'
441
-				&& $this->method !== 'POST'
442
-				&& str_contains($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded')) {
443
-			parse_str(file_get_contents($this->inputStream), $params);
444
-			if (\is_array($params)) {
445
-				$this->items['params'] = $params;
446
-			}
447
-		}
448
-
449
-		if (\is_array($params)) {
450
-			$this->items['parameters'] = array_merge($this->items['parameters'], $params);
451
-		}
452
-		$this->contentDecoded = true;
453
-	}
454
-
455
-
456
-	/**
457
-	 * Checks if the CSRF check was correct
458
-	 * @return bool true if CSRF check passed
459
-	 */
460
-	public function passesCSRFCheck(): bool {
461
-		if ($this->csrfTokenManager === null) {
462
-			return false;
463
-		}
464
-
465
-		if (!$this->passesStrictCookieCheck()) {
466
-			return false;
467
-		}
468
-
469
-		if (isset($this->items['get']['requesttoken'])) {
470
-			$token = $this->items['get']['requesttoken'];
471
-		} elseif (isset($this->items['post']['requesttoken'])) {
472
-			$token = $this->items['post']['requesttoken'];
473
-		} elseif (isset($this->items['server']['HTTP_REQUESTTOKEN'])) {
474
-			$token = $this->items['server']['HTTP_REQUESTTOKEN'];
475
-		} else {
476
-			//no token found.
477
-			return false;
478
-		}
479
-		$token = new CsrfToken($token);
480
-
481
-		return $this->csrfTokenManager->isTokenValid($token);
482
-	}
483
-
484
-	/**
485
-	 * Whether the cookie checks are required
486
-	 *
487
-	 * @return bool
488
-	 */
489
-	private function cookieCheckRequired(): bool {
490
-		if ($this->getHeader('OCS-APIREQUEST')) {
491
-			return false;
492
-		}
493
-		if ($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
494
-			return false;
495
-		}
496
-
497
-		return true;
498
-	}
499
-
500
-	/**
501
-	 * Wrapper around session_get_cookie_params
502
-	 *
503
-	 * @return array
504
-	 */
505
-	public function getCookieParams(): array {
506
-		return session_get_cookie_params();
507
-	}
508
-
509
-	/**
510
-	 * Appends the __Host- prefix to the cookie if applicable
511
-	 *
512
-	 * @param string $name
513
-	 * @return string
514
-	 */
515
-	protected function getProtectedCookieName(string $name): string {
516
-		$cookieParams = $this->getCookieParams();
517
-		$prefix = '';
518
-		if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
519
-			$prefix = '__Host-';
520
-		}
521
-
522
-		return $prefix.$name;
523
-	}
524
-
525
-	/**
526
-	 * Checks if the strict cookie has been sent with the request if the request
527
-	 * is including any cookies.
528
-	 *
529
-	 * @return bool
530
-	 * @since 9.1.0
531
-	 */
532
-	public function passesStrictCookieCheck(): bool {
533
-		if (!$this->cookieCheckRequired()) {
534
-			return true;
535
-		}
536
-
537
-		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict');
538
-		if ($this->getCookie($cookieName) === 'true'
539
-			&& $this->passesLaxCookieCheck()) {
540
-			return true;
541
-		}
542
-		return false;
543
-	}
544
-
545
-	/**
546
-	 * Checks if the lax cookie has been sent with the request if the request
547
-	 * is including any cookies.
548
-	 *
549
-	 * @return bool
550
-	 * @since 9.1.0
551
-	 */
552
-	public function passesLaxCookieCheck(): bool {
553
-		if (!$this->cookieCheckRequired()) {
554
-			return true;
555
-		}
556
-
557
-		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax');
558
-		if ($this->getCookie($cookieName) === 'true') {
559
-			return true;
560
-		}
561
-		return false;
562
-	}
563
-
564
-
565
-	/**
566
-	 * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
567
-	 * If `mod_unique_id` is installed this value will be taken.
568
-	 * @return string
569
-	 */
570
-	public function getId(): string {
571
-		return $this->requestId->getId();
572
-	}
573
-
574
-	/**
575
-	 * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
576
-	 * For details regarding what "match" means, refer to `matchesTrustedProxy`.
577
-	 * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
578
-	 */
579
-	protected function isTrustedProxy($trustedProxies, $remoteAddress) {
580
-		return IpUtils::checkIp($remoteAddress, $trustedProxies);
581
-	}
582
-
583
-	/**
584
-	 * Returns the remote address, if the connection came from a trusted proxy
585
-	 * and `forwarded_for_headers` has been configured then the IP address
586
-	 * specified in this header will be returned instead.
587
-	 * Do always use this instead of $_SERVER['REMOTE_ADDR']
588
-	 * @return string IP address
589
-	 */
590
-	public function getRemoteAddress(): string {
591
-		$remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
592
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
593
-
594
-		if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
595
-			$forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
596
-				'HTTP_X_FORWARDED_FOR'
597
-				// only have one default, so we cannot ship an insecure product out of the box
598
-			]);
599
-
600
-			foreach ($forwardedForHeaders as $header) {
601
-				if (isset($this->server[$header])) {
602
-					foreach (explode(',', $this->server[$header]) as $IP) {
603
-						$IP = trim($IP);
604
-
605
-						// remove brackets from IPv6 addresses
606
-						if (str_starts_with($IP, '[') && str_ends_with($IP, ']')) {
607
-							$IP = substr($IP, 1, -1);
608
-						}
609
-
610
-						if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
611
-							return $IP;
612
-						}
613
-					}
614
-				}
615
-			}
616
-		}
617
-
618
-		return $remoteAddress;
619
-	}
620
-
621
-	/**
622
-	 * Check overwrite condition
623
-	 * @param string $type
624
-	 * @return bool
625
-	 */
626
-	private function isOverwriteCondition(string $type = ''): bool {
627
-		$regex = '/' . $this->config->getSystemValueString('overwritecondaddr', '')  . '/';
628
-		$remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
629
-		return $regex === '//' || preg_match($regex, $remoteAddr) === 1
630
-		|| $type !== 'protocol';
631
-	}
632
-
633
-	/**
634
-	 * Returns the server protocol. It respects one or more reverse proxies servers
635
-	 * and load balancers
636
-	 * @return string Server protocol (http or https)
637
-	 */
638
-	public function getServerProtocol(): string {
639
-		if ($this->config->getSystemValueString('overwriteprotocol') !== ''
640
-			&& $this->isOverwriteCondition('protocol')) {
641
-			return $this->config->getSystemValueString('overwriteprotocol');
642
-		}
643
-
644
-		if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
645
-			if (str_contains($this->server['HTTP_X_FORWARDED_PROTO'], ',')) {
646
-				$parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
647
-				$proto = strtolower(trim($parts[0]));
648
-			} else {
649
-				$proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']);
650
-			}
651
-
652
-			// Verify that the protocol is always HTTP or HTTPS
653
-			// default to http if an invalid value is provided
654
-			return $proto === 'https' ? 'https' : 'http';
655
-		}
656
-
657
-		if (isset($this->server['HTTPS'])
658
-			&& $this->server['HTTPS'] !== null
659
-			&& $this->server['HTTPS'] !== 'off'
660
-			&& $this->server['HTTPS'] !== '') {
661
-			return 'https';
662
-		}
663
-
664
-		return 'http';
665
-	}
666
-
667
-	/**
668
-	 * Returns the used HTTP protocol.
669
-	 *
670
-	 * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
671
-	 */
672
-	public function getHttpProtocol(): string {
673
-		$claimedProtocol = $this->server['SERVER_PROTOCOL'];
674
-
675
-		if (\is_string($claimedProtocol)) {
676
-			$claimedProtocol = strtoupper($claimedProtocol);
677
-		}
678
-
679
-		$validProtocols = [
680
-			'HTTP/1.0',
681
-			'HTTP/1.1',
682
-			'HTTP/2',
683
-		];
684
-
685
-		if (\in_array($claimedProtocol, $validProtocols, true)) {
686
-			return $claimedProtocol;
687
-		}
688
-
689
-		return 'HTTP/1.1';
690
-	}
691
-
692
-	/**
693
-	 * Returns the request uri, even if the website uses one or more
694
-	 * reverse proxies
695
-	 * @return string
696
-	 */
697
-	public function getRequestUri(): string {
698
-		$uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
699
-		if ($this->config->getSystemValueString('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
700
-			$uri = $this->getScriptName() . substr($uri, \strlen($this->server['SCRIPT_NAME']));
701
-		}
702
-		return $uri;
703
-	}
704
-
705
-	/**
706
-	 * Get raw PathInfo from request (not urldecoded)
707
-	 * @throws \Exception
708
-	 * @return string Path info
709
-	 */
710
-	public function getRawPathInfo(): string {
711
-		$requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
712
-		// remove too many slashes - can be caused by reverse proxy configuration
713
-		$requestUri = preg_replace('%/{2,}%', '/', $requestUri);
714
-
715
-		// Remove the query string from REQUEST_URI
716
-		if ($pos = strpos($requestUri, '?')) {
717
-			$requestUri = substr($requestUri, 0, $pos);
718
-		}
719
-
720
-		$scriptName = $this->server['SCRIPT_NAME'];
721
-		$pathInfo = $requestUri;
722
-
723
-		// strip off the script name's dir and file name
724
-		// FIXME: Sabre does not really belong here
725
-		[$path, $name] = \Sabre\Uri\split($scriptName);
726
-		if (!empty($path)) {
727
-			if ($path === $pathInfo || str_starts_with($pathInfo, $path . '/')) {
728
-				$pathInfo = substr($pathInfo, \strlen($path));
729
-			} else {
730
-				throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
731
-			}
732
-		}
733
-		if ($name === null) {
734
-			$name = '';
735
-		}
736
-
737
-		if (str_starts_with($pathInfo, '/' . $name)) {
738
-			$pathInfo = substr($pathInfo, \strlen($name) + 1);
739
-		}
740
-		if ($name !== '' && str_starts_with($pathInfo, $name)) {
741
-			$pathInfo = substr($pathInfo, \strlen($name));
742
-		}
743
-		if ($pathInfo === false || $pathInfo === '/') {
744
-			return '';
745
-		} else {
746
-			return $pathInfo;
747
-		}
748
-	}
749
-
750
-	/**
751
-	 * Get PathInfo from request
752
-	 * @throws \Exception
753
-	 * @return string|false Path info or false when not found
754
-	 */
755
-	public function getPathInfo() {
756
-		$pathInfo = $this->getRawPathInfo();
757
-		return \Sabre\HTTP\decodePath($pathInfo);
758
-	}
759
-
760
-	/**
761
-	 * Returns the script name, even if the website uses one or more
762
-	 * reverse proxies
763
-	 * @return string the script name
764
-	 */
765
-	public function getScriptName(): string {
766
-		$name = $this->server['SCRIPT_NAME'];
767
-		$overwriteWebRoot = $this->config->getSystemValueString('overwritewebroot');
768
-		if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) {
769
-			// FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
770
-			$serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -\strlen('lib/private/appframework/http/')));
771
-			$suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), \strlen($serverRoot)));
772
-			$name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
773
-		}
774
-		return $name;
775
-	}
776
-
777
-	/**
778
-	 * Checks whether the user agent matches a given regex
779
-	 * @param array $agent array of agent names
780
-	 * @return bool true if at least one of the given agent matches, false otherwise
781
-	 */
782
-	public function isUserAgent(array $agent): bool {
783
-		if (!isset($this->server['HTTP_USER_AGENT'])) {
784
-			return false;
785
-		}
786
-		foreach ($agent as $regex) {
787
-			if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) {
788
-				return true;
789
-			}
790
-		}
791
-		return false;
792
-	}
793
-
794
-	/**
795
-	 * Returns the unverified server host from the headers without checking
796
-	 * whether it is a trusted domain
797
-	 * @return string Server host
798
-	 */
799
-	public function getInsecureServerHost(): string {
800
-		if ($this->fromTrustedProxy() && $this->getOverwriteHost() !== null) {
801
-			return $this->getOverwriteHost();
802
-		}
803
-
804
-		$host = 'localhost';
805
-		if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_HOST'])) {
806
-			if (str_contains($this->server['HTTP_X_FORWARDED_HOST'], ',')) {
807
-				$parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']);
808
-				$host = trim(current($parts));
809
-			} else {
810
-				$host = $this->server['HTTP_X_FORWARDED_HOST'];
811
-			}
812
-		} else {
813
-			if (isset($this->server['HTTP_HOST'])) {
814
-				$host = $this->server['HTTP_HOST'];
815
-			} elseif (isset($this->server['SERVER_NAME'])) {
816
-				$host = $this->server['SERVER_NAME'];
817
-			}
818
-		}
819
-
820
-		return $host;
821
-	}
822
-
823
-
824
-	/**
825
-	 * Returns the server host from the headers, or the first configured
826
-	 * trusted domain if the host isn't in the trusted list
827
-	 * @return string Server host
828
-	 */
829
-	public function getServerHost(): string {
830
-		// overwritehost is always trusted
831
-		$host = $this->getOverwriteHost();
832
-		if ($host !== null) {
833
-			return $host;
834
-		}
835
-
836
-		// get the host from the headers
837
-		$host = $this->getInsecureServerHost();
838
-
839
-		// Verify that the host is a trusted domain if the trusted domains
840
-		// are defined
841
-		// If no trusted domain is provided the first trusted domain is returned
842
-		$trustedDomainHelper = new TrustedDomainHelper($this->config);
843
-		if ($trustedDomainHelper->isTrustedDomain($host)) {
844
-			return $host;
845
-		}
846
-
847
-		$trustedList = (array)$this->config->getSystemValue('trusted_domains', []);
848
-		if (count($trustedList) > 0) {
849
-			return reset($trustedList);
850
-		}
851
-
852
-		return '';
853
-	}
854
-
855
-	/**
856
-	 * Returns the overwritehost setting from the config if set and
857
-	 * if the overwrite condition is met
858
-	 * @return string|null overwritehost value or null if not defined or the defined condition
859
-	 * isn't met
860
-	 */
861
-	private function getOverwriteHost() {
862
-		if ($this->config->getSystemValueString('overwritehost') !== '' && $this->isOverwriteCondition()) {
863
-			return $this->config->getSystemValueString('overwritehost');
864
-		}
865
-		return null;
866
-	}
867
-
868
-	private function fromTrustedProxy(): bool {
869
-		$remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
870
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
871
-
872
-		return \is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress);
873
-	}
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
+                if ($this->isPutStreamContent()) {
264
+                    return $this->items['parameters'];
265
+                }
266
+                return $this->getContent();
267
+            default:
268
+                return isset($this[$name])
269
+                    ? $this[$name]
270
+                    : null;
271
+        }
272
+    }
273
+
274
+    /**
275
+     * @param string $name
276
+     * @return bool
277
+     */
278
+    public function __isset($name) {
279
+        if (\in_array($name, $this->allowedKeys, true)) {
280
+            return true;
281
+        }
282
+        return isset($this->items['parameters'][$name]);
283
+    }
284
+
285
+    /**
286
+     * @param string $id
287
+     */
288
+    public function __unset($id) {
289
+        throw new \RuntimeException('You cannot change the contents of the request object');
290
+    }
291
+
292
+    /**
293
+     * Returns the value for a specific http header.
294
+     *
295
+     * This method returns an empty string if the header did not exist.
296
+     *
297
+     * @param string $name
298
+     * @return string
299
+     */
300
+    public function getHeader(string $name): string {
301
+        $name = strtoupper(str_replace('-', '_', $name));
302
+        if (isset($this->server['HTTP_' . $name])) {
303
+            return $this->server['HTTP_' . $name];
304
+        }
305
+
306
+        // There's a few headers that seem to end up in the top-level
307
+        // server array.
308
+        switch ($name) {
309
+            case 'CONTENT_TYPE':
310
+            case 'CONTENT_LENGTH':
311
+            case 'REMOTE_ADDR':
312
+                if (isset($this->server[$name])) {
313
+                    return $this->server[$name];
314
+                }
315
+                break;
316
+        }
317
+
318
+        return '';
319
+    }
320
+
321
+    /**
322
+     * Lets you access post and get parameters by the index
323
+     * In case of json requests the encoded json body is accessed
324
+     *
325
+     * @param string $key the key which you want to access in the URL Parameter
326
+     *                     placeholder, $_POST or $_GET array.
327
+     *                     The priority how they're returned is the following:
328
+     *                     1. URL parameters
329
+     *                     2. POST parameters
330
+     *                     3. GET parameters
331
+     * @param mixed $default If the key is not found, this value will be returned
332
+     * @return mixed the content of the array
333
+     */
334
+    public function getParam(string $key, $default = null) {
335
+        return isset($this->parameters[$key])
336
+            ? $this->parameters[$key]
337
+            : $default;
338
+    }
339
+
340
+    /**
341
+     * Returns all params that were received, be it from the request
342
+     * (as GET or POST) or through the URL by the route
343
+     * @return array the array with all parameters
344
+     */
345
+    public function getParams(): array {
346
+        return is_array($this->parameters) ? $this->parameters : [];
347
+    }
348
+
349
+    /**
350
+     * Returns the method of the request
351
+     * @return string the method of the request (POST, GET, etc)
352
+     */
353
+    public function getMethod(): string {
354
+        return $this->method;
355
+    }
356
+
357
+    /**
358
+     * Shortcut for accessing an uploaded file through the $_FILES array
359
+     * @param string $key the key that will be taken from the $_FILES array
360
+     * @return array the file in the $_FILES element
361
+     */
362
+    public function getUploadedFile(string $key) {
363
+        return isset($this->files[$key]) ? $this->files[$key] : null;
364
+    }
365
+
366
+    /**
367
+     * Shortcut for getting env variables
368
+     * @param string $key the key that will be taken from the $_ENV array
369
+     * @return array the value in the $_ENV element
370
+     */
371
+    public function getEnv(string $key) {
372
+        return isset($this->env[$key]) ? $this->env[$key] : null;
373
+    }
374
+
375
+    /**
376
+     * Shortcut for getting cookie variables
377
+     * @param string $key the key that will be taken from the $_COOKIE array
378
+     * @return string the value in the $_COOKIE element
379
+     */
380
+    public function getCookie(string $key) {
381
+        return isset($this->cookies[$key]) ? $this->cookies[$key] : null;
382
+    }
383
+
384
+    /**
385
+     * Returns the request body content.
386
+     *
387
+     * If the HTTP request method is PUT and the body
388
+     * not application/x-www-form-urlencoded or application/json a stream
389
+     * resource is returned, otherwise an array.
390
+     *
391
+     * @return array|string|resource The request body content or a resource to read the body stream.
392
+     *
393
+     * @throws \LogicException
394
+     */
395
+    protected function getContent() {
396
+        // If the content can't be parsed into an array then return a stream resource.
397
+        if ($this->isPutStreamContent()) {
398
+            if ($this->content === false) {
399
+                throw new \LogicException(
400
+                    '"put" can only be accessed once if not '
401
+                    . 'application/x-www-form-urlencoded or application/json.'
402
+                );
403
+            }
404
+            $this->content = false;
405
+            return fopen($this->inputStream, 'rb');
406
+        } else {
407
+            $this->decodeContent();
408
+            return $this->items['parameters'];
409
+        }
410
+    }
411
+
412
+    private function isPutStreamContent(): bool {
413
+        return $this->method === 'PUT'
414
+            && $this->getHeader('Content-Length') !== '0'
415
+            && $this->getHeader('Content-Length') !== ''
416
+            && !str_contains($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded')
417
+            && !str_contains($this->getHeader('Content-Type'), 'application/json');
418
+    }
419
+
420
+    /**
421
+     * Attempt to decode the content and populate parameters
422
+     */
423
+    protected function decodeContent() {
424
+        if ($this->contentDecoded) {
425
+            return;
426
+        }
427
+        $params = [];
428
+
429
+        // 'application/json' and other JSON-related content types must be decoded manually.
430
+        if (preg_match(self::JSON_CONTENT_TYPE_REGEX, $this->getHeader('Content-Type')) === 1) {
431
+            $params = json_decode(file_get_contents($this->inputStream), true);
432
+            if (\is_array($params) && \count($params) > 0) {
433
+                $this->items['params'] = $params;
434
+                if ($this->method === 'POST') {
435
+                    $this->items['post'] = $params;
436
+                }
437
+            }
438
+        // Handle application/x-www-form-urlencoded for methods other than GET
439
+        // or post correctly
440
+        } elseif ($this->method !== 'GET'
441
+                && $this->method !== 'POST'
442
+                && str_contains($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded')) {
443
+            parse_str(file_get_contents($this->inputStream), $params);
444
+            if (\is_array($params)) {
445
+                $this->items['params'] = $params;
446
+            }
447
+        }
448
+
449
+        if (\is_array($params)) {
450
+            $this->items['parameters'] = array_merge($this->items['parameters'], $params);
451
+        }
452
+        $this->contentDecoded = true;
453
+    }
454
+
455
+
456
+    /**
457
+     * Checks if the CSRF check was correct
458
+     * @return bool true if CSRF check passed
459
+     */
460
+    public function passesCSRFCheck(): bool {
461
+        if ($this->csrfTokenManager === null) {
462
+            return false;
463
+        }
464
+
465
+        if (!$this->passesStrictCookieCheck()) {
466
+            return false;
467
+        }
468
+
469
+        if (isset($this->items['get']['requesttoken'])) {
470
+            $token = $this->items['get']['requesttoken'];
471
+        } elseif (isset($this->items['post']['requesttoken'])) {
472
+            $token = $this->items['post']['requesttoken'];
473
+        } elseif (isset($this->items['server']['HTTP_REQUESTTOKEN'])) {
474
+            $token = $this->items['server']['HTTP_REQUESTTOKEN'];
475
+        } else {
476
+            //no token found.
477
+            return false;
478
+        }
479
+        $token = new CsrfToken($token);
480
+
481
+        return $this->csrfTokenManager->isTokenValid($token);
482
+    }
483
+
484
+    /**
485
+     * Whether the cookie checks are required
486
+     *
487
+     * @return bool
488
+     */
489
+    private function cookieCheckRequired(): bool {
490
+        if ($this->getHeader('OCS-APIREQUEST')) {
491
+            return false;
492
+        }
493
+        if ($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
494
+            return false;
495
+        }
496
+
497
+        return true;
498
+    }
499
+
500
+    /**
501
+     * Wrapper around session_get_cookie_params
502
+     *
503
+     * @return array
504
+     */
505
+    public function getCookieParams(): array {
506
+        return session_get_cookie_params();
507
+    }
508
+
509
+    /**
510
+     * Appends the __Host- prefix to the cookie if applicable
511
+     *
512
+     * @param string $name
513
+     * @return string
514
+     */
515
+    protected function getProtectedCookieName(string $name): string {
516
+        $cookieParams = $this->getCookieParams();
517
+        $prefix = '';
518
+        if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
519
+            $prefix = '__Host-';
520
+        }
521
+
522
+        return $prefix.$name;
523
+    }
524
+
525
+    /**
526
+     * Checks if the strict cookie has been sent with the request if the request
527
+     * is including any cookies.
528
+     *
529
+     * @return bool
530
+     * @since 9.1.0
531
+     */
532
+    public function passesStrictCookieCheck(): bool {
533
+        if (!$this->cookieCheckRequired()) {
534
+            return true;
535
+        }
536
+
537
+        $cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict');
538
+        if ($this->getCookie($cookieName) === 'true'
539
+            && $this->passesLaxCookieCheck()) {
540
+            return true;
541
+        }
542
+        return false;
543
+    }
544
+
545
+    /**
546
+     * Checks if the lax cookie has been sent with the request if the request
547
+     * is including any cookies.
548
+     *
549
+     * @return bool
550
+     * @since 9.1.0
551
+     */
552
+    public function passesLaxCookieCheck(): bool {
553
+        if (!$this->cookieCheckRequired()) {
554
+            return true;
555
+        }
556
+
557
+        $cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax');
558
+        if ($this->getCookie($cookieName) === 'true') {
559
+            return true;
560
+        }
561
+        return false;
562
+    }
563
+
564
+
565
+    /**
566
+     * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
567
+     * If `mod_unique_id` is installed this value will be taken.
568
+     * @return string
569
+     */
570
+    public function getId(): string {
571
+        return $this->requestId->getId();
572
+    }
573
+
574
+    /**
575
+     * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
576
+     * For details regarding what "match" means, refer to `matchesTrustedProxy`.
577
+     * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
578
+     */
579
+    protected function isTrustedProxy($trustedProxies, $remoteAddress) {
580
+        return IpUtils::checkIp($remoteAddress, $trustedProxies);
581
+    }
582
+
583
+    /**
584
+     * Returns the remote address, if the connection came from a trusted proxy
585
+     * and `forwarded_for_headers` has been configured then the IP address
586
+     * specified in this header will be returned instead.
587
+     * Do always use this instead of $_SERVER['REMOTE_ADDR']
588
+     * @return string IP address
589
+     */
590
+    public function getRemoteAddress(): string {
591
+        $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
592
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
593
+
594
+        if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
595
+            $forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
596
+                'HTTP_X_FORWARDED_FOR'
597
+                // only have one default, so we cannot ship an insecure product out of the box
598
+            ]);
599
+
600
+            foreach ($forwardedForHeaders as $header) {
601
+                if (isset($this->server[$header])) {
602
+                    foreach (explode(',', $this->server[$header]) as $IP) {
603
+                        $IP = trim($IP);
604
+
605
+                        // remove brackets from IPv6 addresses
606
+                        if (str_starts_with($IP, '[') && str_ends_with($IP, ']')) {
607
+                            $IP = substr($IP, 1, -1);
608
+                        }
609
+
610
+                        if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
611
+                            return $IP;
612
+                        }
613
+                    }
614
+                }
615
+            }
616
+        }
617
+
618
+        return $remoteAddress;
619
+    }
620
+
621
+    /**
622
+     * Check overwrite condition
623
+     * @param string $type
624
+     * @return bool
625
+     */
626
+    private function isOverwriteCondition(string $type = ''): bool {
627
+        $regex = '/' . $this->config->getSystemValueString('overwritecondaddr', '')  . '/';
628
+        $remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
629
+        return $regex === '//' || preg_match($regex, $remoteAddr) === 1
630
+        || $type !== 'protocol';
631
+    }
632
+
633
+    /**
634
+     * Returns the server protocol. It respects one or more reverse proxies servers
635
+     * and load balancers
636
+     * @return string Server protocol (http or https)
637
+     */
638
+    public function getServerProtocol(): string {
639
+        if ($this->config->getSystemValueString('overwriteprotocol') !== ''
640
+            && $this->isOverwriteCondition('protocol')) {
641
+            return $this->config->getSystemValueString('overwriteprotocol');
642
+        }
643
+
644
+        if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
645
+            if (str_contains($this->server['HTTP_X_FORWARDED_PROTO'], ',')) {
646
+                $parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
647
+                $proto = strtolower(trim($parts[0]));
648
+            } else {
649
+                $proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']);
650
+            }
651
+
652
+            // Verify that the protocol is always HTTP or HTTPS
653
+            // default to http if an invalid value is provided
654
+            return $proto === 'https' ? 'https' : 'http';
655
+        }
656
+
657
+        if (isset($this->server['HTTPS'])
658
+            && $this->server['HTTPS'] !== null
659
+            && $this->server['HTTPS'] !== 'off'
660
+            && $this->server['HTTPS'] !== '') {
661
+            return 'https';
662
+        }
663
+
664
+        return 'http';
665
+    }
666
+
667
+    /**
668
+     * Returns the used HTTP protocol.
669
+     *
670
+     * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
671
+     */
672
+    public function getHttpProtocol(): string {
673
+        $claimedProtocol = $this->server['SERVER_PROTOCOL'];
674
+
675
+        if (\is_string($claimedProtocol)) {
676
+            $claimedProtocol = strtoupper($claimedProtocol);
677
+        }
678
+
679
+        $validProtocols = [
680
+            'HTTP/1.0',
681
+            'HTTP/1.1',
682
+            'HTTP/2',
683
+        ];
684
+
685
+        if (\in_array($claimedProtocol, $validProtocols, true)) {
686
+            return $claimedProtocol;
687
+        }
688
+
689
+        return 'HTTP/1.1';
690
+    }
691
+
692
+    /**
693
+     * Returns the request uri, even if the website uses one or more
694
+     * reverse proxies
695
+     * @return string
696
+     */
697
+    public function getRequestUri(): string {
698
+        $uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
699
+        if ($this->config->getSystemValueString('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
700
+            $uri = $this->getScriptName() . substr($uri, \strlen($this->server['SCRIPT_NAME']));
701
+        }
702
+        return $uri;
703
+    }
704
+
705
+    /**
706
+     * Get raw PathInfo from request (not urldecoded)
707
+     * @throws \Exception
708
+     * @return string Path info
709
+     */
710
+    public function getRawPathInfo(): string {
711
+        $requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
712
+        // remove too many slashes - can be caused by reverse proxy configuration
713
+        $requestUri = preg_replace('%/{2,}%', '/', $requestUri);
714
+
715
+        // Remove the query string from REQUEST_URI
716
+        if ($pos = strpos($requestUri, '?')) {
717
+            $requestUri = substr($requestUri, 0, $pos);
718
+        }
719
+
720
+        $scriptName = $this->server['SCRIPT_NAME'];
721
+        $pathInfo = $requestUri;
722
+
723
+        // strip off the script name's dir and file name
724
+        // FIXME: Sabre does not really belong here
725
+        [$path, $name] = \Sabre\Uri\split($scriptName);
726
+        if (!empty($path)) {
727
+            if ($path === $pathInfo || str_starts_with($pathInfo, $path . '/')) {
728
+                $pathInfo = substr($pathInfo, \strlen($path));
729
+            } else {
730
+                throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
731
+            }
732
+        }
733
+        if ($name === null) {
734
+            $name = '';
735
+        }
736
+
737
+        if (str_starts_with($pathInfo, '/' . $name)) {
738
+            $pathInfo = substr($pathInfo, \strlen($name) + 1);
739
+        }
740
+        if ($name !== '' && str_starts_with($pathInfo, $name)) {
741
+            $pathInfo = substr($pathInfo, \strlen($name));
742
+        }
743
+        if ($pathInfo === false || $pathInfo === '/') {
744
+            return '';
745
+        } else {
746
+            return $pathInfo;
747
+        }
748
+    }
749
+
750
+    /**
751
+     * Get PathInfo from request
752
+     * @throws \Exception
753
+     * @return string|false Path info or false when not found
754
+     */
755
+    public function getPathInfo() {
756
+        $pathInfo = $this->getRawPathInfo();
757
+        return \Sabre\HTTP\decodePath($pathInfo);
758
+    }
759
+
760
+    /**
761
+     * Returns the script name, even if the website uses one or more
762
+     * reverse proxies
763
+     * @return string the script name
764
+     */
765
+    public function getScriptName(): string {
766
+        $name = $this->server['SCRIPT_NAME'];
767
+        $overwriteWebRoot = $this->config->getSystemValueString('overwritewebroot');
768
+        if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) {
769
+            // FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
770
+            $serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -\strlen('lib/private/appframework/http/')));
771
+            $suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), \strlen($serverRoot)));
772
+            $name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
773
+        }
774
+        return $name;
775
+    }
776
+
777
+    /**
778
+     * Checks whether the user agent matches a given regex
779
+     * @param array $agent array of agent names
780
+     * @return bool true if at least one of the given agent matches, false otherwise
781
+     */
782
+    public function isUserAgent(array $agent): bool {
783
+        if (!isset($this->server['HTTP_USER_AGENT'])) {
784
+            return false;
785
+        }
786
+        foreach ($agent as $regex) {
787
+            if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) {
788
+                return true;
789
+            }
790
+        }
791
+        return false;
792
+    }
793
+
794
+    /**
795
+     * Returns the unverified server host from the headers without checking
796
+     * whether it is a trusted domain
797
+     * @return string Server host
798
+     */
799
+    public function getInsecureServerHost(): string {
800
+        if ($this->fromTrustedProxy() && $this->getOverwriteHost() !== null) {
801
+            return $this->getOverwriteHost();
802
+        }
803
+
804
+        $host = 'localhost';
805
+        if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_HOST'])) {
806
+            if (str_contains($this->server['HTTP_X_FORWARDED_HOST'], ',')) {
807
+                $parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']);
808
+                $host = trim(current($parts));
809
+            } else {
810
+                $host = $this->server['HTTP_X_FORWARDED_HOST'];
811
+            }
812
+        } else {
813
+            if (isset($this->server['HTTP_HOST'])) {
814
+                $host = $this->server['HTTP_HOST'];
815
+            } elseif (isset($this->server['SERVER_NAME'])) {
816
+                $host = $this->server['SERVER_NAME'];
817
+            }
818
+        }
819
+
820
+        return $host;
821
+    }
822
+
823
+
824
+    /**
825
+     * Returns the server host from the headers, or the first configured
826
+     * trusted domain if the host isn't in the trusted list
827
+     * @return string Server host
828
+     */
829
+    public function getServerHost(): string {
830
+        // overwritehost is always trusted
831
+        $host = $this->getOverwriteHost();
832
+        if ($host !== null) {
833
+            return $host;
834
+        }
835
+
836
+        // get the host from the headers
837
+        $host = $this->getInsecureServerHost();
838
+
839
+        // Verify that the host is a trusted domain if the trusted domains
840
+        // are defined
841
+        // If no trusted domain is provided the first trusted domain is returned
842
+        $trustedDomainHelper = new TrustedDomainHelper($this->config);
843
+        if ($trustedDomainHelper->isTrustedDomain($host)) {
844
+            return $host;
845
+        }
846
+
847
+        $trustedList = (array)$this->config->getSystemValue('trusted_domains', []);
848
+        if (count($trustedList) > 0) {
849
+            return reset($trustedList);
850
+        }
851
+
852
+        return '';
853
+    }
854
+
855
+    /**
856
+     * Returns the overwritehost setting from the config if set and
857
+     * if the overwrite condition is met
858
+     * @return string|null overwritehost value or null if not defined or the defined condition
859
+     * isn't met
860
+     */
861
+    private function getOverwriteHost() {
862
+        if ($this->config->getSystemValueString('overwritehost') !== '' && $this->isOverwriteCondition()) {
863
+            return $this->config->getSystemValueString('overwritehost');
864
+        }
865
+        return null;
866
+    }
867
+
868
+    private function fromTrustedProxy(): bool {
869
+        $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
870
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
871
+
872
+        return \is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress);
873
+    }
874 874
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -299,8 +299,8 @@  discard block
 block discarded – undo
299 299
 	 */
300 300
 	public function getHeader(string $name): string {
301 301
 		$name = strtoupper(str_replace('-', '_', $name));
302
-		if (isset($this->server['HTTP_' . $name])) {
303
-			return $this->server['HTTP_' . $name];
302
+		if (isset($this->server['HTTP_'.$name])) {
303
+			return $this->server['HTTP_'.$name];
304 304
 		}
305 305
 
306 306
 		// There's a few headers that seem to end up in the top-level
@@ -624,7 +624,7 @@  discard block
 block discarded – undo
624 624
 	 * @return bool
625 625
 	 */
626 626
 	private function isOverwriteCondition(string $type = ''): bool {
627
-		$regex = '/' . $this->config->getSystemValueString('overwritecondaddr', '')  . '/';
627
+		$regex = '/'.$this->config->getSystemValueString('overwritecondaddr', '').'/';
628 628
 		$remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
629 629
 		return $regex === '//' || preg_match($regex, $remoteAddr) === 1
630 630
 		|| $type !== 'protocol';
@@ -697,7 +697,7 @@  discard block
 block discarded – undo
697 697
 	public function getRequestUri(): string {
698 698
 		$uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
699 699
 		if ($this->config->getSystemValueString('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
700
-			$uri = $this->getScriptName() . substr($uri, \strlen($this->server['SCRIPT_NAME']));
700
+			$uri = $this->getScriptName().substr($uri, \strlen($this->server['SCRIPT_NAME']));
701 701
 		}
702 702
 		return $uri;
703 703
 	}
@@ -724,7 +724,7 @@  discard block
 block discarded – undo
724 724
 		// FIXME: Sabre does not really belong here
725 725
 		[$path, $name] = \Sabre\Uri\split($scriptName);
726 726
 		if (!empty($path)) {
727
-			if ($path === $pathInfo || str_starts_with($pathInfo, $path . '/')) {
727
+			if ($path === $pathInfo || str_starts_with($pathInfo, $path.'/')) {
728 728
 				$pathInfo = substr($pathInfo, \strlen($path));
729 729
 			} else {
730 730
 				throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
@@ -734,7 +734,7 @@  discard block
 block discarded – undo
734 734
 			$name = '';
735 735
 		}
736 736
 
737
-		if (str_starts_with($pathInfo, '/' . $name)) {
737
+		if (str_starts_with($pathInfo, '/'.$name)) {
738 738
 			$pathInfo = substr($pathInfo, \strlen($name) + 1);
739 739
 		}
740 740
 		if ($name !== '' && str_starts_with($pathInfo, $name)) {
@@ -769,7 +769,7 @@  discard block
 block discarded – undo
769 769
 			// FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
770 770
 			$serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -\strlen('lib/private/appframework/http/')));
771 771
 			$suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), \strlen($serverRoot)));
772
-			$name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
772
+			$name = '/'.ltrim($overwriteWebRoot.$suburi, '/');
773 773
 		}
774 774
 		return $name;
775 775
 	}
@@ -844,7 +844,7 @@  discard block
 block discarded – undo
844 844
 			return $host;
845 845
 		}
846 846
 
847
-		$trustedList = (array)$this->config->getSystemValue('trusted_domains', []);
847
+		$trustedList = (array) $this->config->getSystemValue('trusted_domains', []);
848 848
 		if (count($trustedList) > 0) {
849 849
 			return reset($trustedList);
850 850
 		}
Please login to merge, or discard this patch.
lib/private/AppFramework/Middleware/CompressionMiddleware.php 1 patch
Indentation   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -35,56 +35,56 @@
 block discarded – undo
35 35
 use OCP\IRequest;
36 36
 
37 37
 class CompressionMiddleware extends Middleware {
38
-	/** @var bool */
39
-	private $useGZip;
38
+    /** @var bool */
39
+    private $useGZip;
40 40
 
41
-	/** @var IRequest */
42
-	private $request;
41
+    /** @var IRequest */
42
+    private $request;
43 43
 
44
-	public function __construct(IRequest $request) {
45
-		$this->request = $request;
46
-		$this->useGZip = false;
47
-	}
44
+    public function __construct(IRequest $request) {
45
+        $this->request = $request;
46
+        $this->useGZip = false;
47
+    }
48 48
 
49
-	public function afterController($controller, $methodName, Response $response) {
50
-		// By default we do not gzip
51
-		$allowGzip = false;
49
+    public function afterController($controller, $methodName, Response $response) {
50
+        // By default we do not gzip
51
+        $allowGzip = false;
52 52
 
53
-		// Only return gzipped content for 200 responses
54
-		if ($response->getStatus() !== Http::STATUS_OK) {
55
-			return $response;
56
-		}
53
+        // Only return gzipped content for 200 responses
54
+        if ($response->getStatus() !== Http::STATUS_OK) {
55
+            return $response;
56
+        }
57 57
 
58
-		// Check if we are even asked for gzip
59
-		$header = $this->request->getHeader('Accept-Encoding');
60
-		if (!str_contains($header, 'gzip')) {
61
-			return $response;
62
-		}
58
+        // Check if we are even asked for gzip
59
+        $header = $this->request->getHeader('Accept-Encoding');
60
+        if (!str_contains($header, 'gzip')) {
61
+            return $response;
62
+        }
63 63
 
64
-		// We only allow gzip in some cases
65
-		if ($response instanceof BaseResponse) {
66
-			$allowGzip = true;
67
-		}
68
-		if ($response instanceof JSONResponse) {
69
-			$allowGzip = true;
70
-		}
71
-		if ($response instanceof TemplateResponse) {
72
-			$allowGzip = true;
73
-		}
64
+        // We only allow gzip in some cases
65
+        if ($response instanceof BaseResponse) {
66
+            $allowGzip = true;
67
+        }
68
+        if ($response instanceof JSONResponse) {
69
+            $allowGzip = true;
70
+        }
71
+        if ($response instanceof TemplateResponse) {
72
+            $allowGzip = true;
73
+        }
74 74
 
75
-		if ($allowGzip) {
76
-			$this->useGZip = true;
77
-			$response->addHeader('Content-Encoding', 'gzip');
78
-		}
75
+        if ($allowGzip) {
76
+            $this->useGZip = true;
77
+            $response->addHeader('Content-Encoding', 'gzip');
78
+        }
79 79
 
80
-		return $response;
81
-	}
80
+        return $response;
81
+    }
82 82
 
83
-	public function beforeOutput($controller, $methodName, $output) {
84
-		if (!$this->useGZip) {
85
-			return $output;
86
-		}
83
+    public function beforeOutput($controller, $methodName, $output) {
84
+        if (!$this->useGZip) {
85
+            return $output;
86
+        }
87 87
 
88
-		return gzencode($output);
89
-	}
88
+        return gzencode($output);
89
+    }
90 90
 }
Please login to merge, or discard this patch.
lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php 1 patch
Indentation   +231 added lines, -231 removed lines patch added patch discarded remove patch
@@ -76,138 +76,138 @@  discard block
 block discarded – undo
76 76
  * check fails
77 77
  */
78 78
 class SecurityMiddleware extends Middleware {
79
-	/** @var INavigationManager */
80
-	private $navigationManager;
81
-	/** @var IRequest */
82
-	private $request;
83
-	/** @var ControllerMethodReflector */
84
-	private $reflector;
85
-	/** @var string */
86
-	private $appName;
87
-	/** @var IURLGenerator */
88
-	private $urlGenerator;
89
-	/** @var LoggerInterface */
90
-	private $logger;
91
-	/** @var bool */
92
-	private $isLoggedIn;
93
-	/** @var bool */
94
-	private $isAdminUser;
95
-	/** @var bool */
96
-	private $isSubAdmin;
97
-	/** @var IAppManager */
98
-	private $appManager;
99
-	/** @var IL10N */
100
-	private $l10n;
101
-	/** @var AuthorizedGroupMapper */
102
-	private $groupAuthorizationMapper;
103
-	/** @var IUserSession */
104
-	private $userSession;
79
+    /** @var INavigationManager */
80
+    private $navigationManager;
81
+    /** @var IRequest */
82
+    private $request;
83
+    /** @var ControllerMethodReflector */
84
+    private $reflector;
85
+    /** @var string */
86
+    private $appName;
87
+    /** @var IURLGenerator */
88
+    private $urlGenerator;
89
+    /** @var LoggerInterface */
90
+    private $logger;
91
+    /** @var bool */
92
+    private $isLoggedIn;
93
+    /** @var bool */
94
+    private $isAdminUser;
95
+    /** @var bool */
96
+    private $isSubAdmin;
97
+    /** @var IAppManager */
98
+    private $appManager;
99
+    /** @var IL10N */
100
+    private $l10n;
101
+    /** @var AuthorizedGroupMapper */
102
+    private $groupAuthorizationMapper;
103
+    /** @var IUserSession */
104
+    private $userSession;
105 105
 
106
-	public function __construct(IRequest $request,
107
-								ControllerMethodReflector $reflector,
108
-								INavigationManager $navigationManager,
109
-								IURLGenerator $urlGenerator,
110
-								LoggerInterface $logger,
111
-								string $appName,
112
-								bool $isLoggedIn,
113
-								bool $isAdminUser,
114
-								bool $isSubAdmin,
115
-								IAppManager $appManager,
116
-								IL10N $l10n,
117
-								AuthorizedGroupMapper $mapper,
118
-								IUserSession $userSession
119
-	) {
120
-		$this->navigationManager = $navigationManager;
121
-		$this->request = $request;
122
-		$this->reflector = $reflector;
123
-		$this->appName = $appName;
124
-		$this->urlGenerator = $urlGenerator;
125
-		$this->logger = $logger;
126
-		$this->isLoggedIn = $isLoggedIn;
127
-		$this->isAdminUser = $isAdminUser;
128
-		$this->isSubAdmin = $isSubAdmin;
129
-		$this->appManager = $appManager;
130
-		$this->l10n = $l10n;
131
-		$this->groupAuthorizationMapper = $mapper;
132
-		$this->userSession = $userSession;
133
-	}
106
+    public function __construct(IRequest $request,
107
+                                ControllerMethodReflector $reflector,
108
+                                INavigationManager $navigationManager,
109
+                                IURLGenerator $urlGenerator,
110
+                                LoggerInterface $logger,
111
+                                string $appName,
112
+                                bool $isLoggedIn,
113
+                                bool $isAdminUser,
114
+                                bool $isSubAdmin,
115
+                                IAppManager $appManager,
116
+                                IL10N $l10n,
117
+                                AuthorizedGroupMapper $mapper,
118
+                                IUserSession $userSession
119
+    ) {
120
+        $this->navigationManager = $navigationManager;
121
+        $this->request = $request;
122
+        $this->reflector = $reflector;
123
+        $this->appName = $appName;
124
+        $this->urlGenerator = $urlGenerator;
125
+        $this->logger = $logger;
126
+        $this->isLoggedIn = $isLoggedIn;
127
+        $this->isAdminUser = $isAdminUser;
128
+        $this->isSubAdmin = $isSubAdmin;
129
+        $this->appManager = $appManager;
130
+        $this->l10n = $l10n;
131
+        $this->groupAuthorizationMapper = $mapper;
132
+        $this->userSession = $userSession;
133
+    }
134 134
 
135
-	/**
136
-	 * This runs all the security checks before a method call. The
137
-	 * security checks are determined by inspecting the controller method
138
-	 * annotations
139
-	 *
140
-	 * @param Controller $controller the controller
141
-	 * @param string $methodName the name of the method
142
-	 * @throws SecurityException when a security check fails
143
-	 *
144
-	 * @suppress PhanUndeclaredClassConstant
145
-	 */
146
-	public function beforeController($controller, $methodName) {
147
-		// this will set the current navigation entry of the app, use this only
148
-		// for normal HTML requests and not for AJAX requests
149
-		$this->navigationManager->setActiveEntry($this->appName);
135
+    /**
136
+     * This runs all the security checks before a method call. The
137
+     * security checks are determined by inspecting the controller method
138
+     * annotations
139
+     *
140
+     * @param Controller $controller the controller
141
+     * @param string $methodName the name of the method
142
+     * @throws SecurityException when a security check fails
143
+     *
144
+     * @suppress PhanUndeclaredClassConstant
145
+     */
146
+    public function beforeController($controller, $methodName) {
147
+        // this will set the current navigation entry of the app, use this only
148
+        // for normal HTML requests and not for AJAX requests
149
+        $this->navigationManager->setActiveEntry($this->appName);
150 150
 
151
-		if (get_class($controller) === \OCA\Talk\Controller\PageController::class && $methodName === 'showCall') {
152
-			$this->navigationManager->setActiveEntry('spreed');
153
-		}
151
+        if (get_class($controller) === \OCA\Talk\Controller\PageController::class && $methodName === 'showCall') {
152
+            $this->navigationManager->setActiveEntry('spreed');
153
+        }
154 154
 
155
-		$reflectionMethod = new ReflectionMethod($controller, $methodName);
155
+        $reflectionMethod = new ReflectionMethod($controller, $methodName);
156 156
 
157
-		// security checks
158
-		$isPublicPage = $this->hasAnnotationOrAttribute($reflectionMethod, 'PublicPage', PublicPage::class);
159
-		if (!$isPublicPage) {
160
-			if (!$this->isLoggedIn) {
161
-				throw new NotLoggedInException();
162
-			}
163
-			$authorized = false;
164
-			if ($this->hasAnnotationOrAttribute($reflectionMethod, 'AuthorizedAdminSetting', AuthorizedAdminSetting::class)) {
165
-				$authorized = $this->isAdminUser;
157
+        // security checks
158
+        $isPublicPage = $this->hasAnnotationOrAttribute($reflectionMethod, 'PublicPage', PublicPage::class);
159
+        if (!$isPublicPage) {
160
+            if (!$this->isLoggedIn) {
161
+                throw new NotLoggedInException();
162
+            }
163
+            $authorized = false;
164
+            if ($this->hasAnnotationOrAttribute($reflectionMethod, 'AuthorizedAdminSetting', AuthorizedAdminSetting::class)) {
165
+                $authorized = $this->isAdminUser;
166 166
 
167
-				if (!$authorized && $this->hasAnnotationOrAttribute($reflectionMethod, 'SubAdminRequired', SubAdminRequired::class)) {
168
-					$authorized = $this->isSubAdmin;
169
-				}
167
+                if (!$authorized && $this->hasAnnotationOrAttribute($reflectionMethod, 'SubAdminRequired', SubAdminRequired::class)) {
168
+                    $authorized = $this->isSubAdmin;
169
+                }
170 170
 
171
-				if (!$authorized) {
172
-					$settingClasses = $this->getAuthorizedAdminSettingClasses($reflectionMethod);
173
-					$authorizedClasses = $this->groupAuthorizationMapper->findAllClassesForUser($this->userSession->getUser());
174
-					foreach ($settingClasses as $settingClass) {
175
-						$authorized = in_array($settingClass, $authorizedClasses, true);
171
+                if (!$authorized) {
172
+                    $settingClasses = $this->getAuthorizedAdminSettingClasses($reflectionMethod);
173
+                    $authorizedClasses = $this->groupAuthorizationMapper->findAllClassesForUser($this->userSession->getUser());
174
+                    foreach ($settingClasses as $settingClass) {
175
+                        $authorized = in_array($settingClass, $authorizedClasses, true);
176 176
 
177
-						if ($authorized) {
178
-							break;
179
-						}
180
-					}
181
-				}
182
-				if (!$authorized) {
183
-					throw new NotAdminException($this->l10n->t('Logged in user must be an admin, a sub admin or gotten special right to access this setting'));
184
-				}
185
-			}
186
-			if ($this->hasAnnotationOrAttribute($reflectionMethod, 'SubAdminRequired', SubAdminRequired::class)
187
-				&& !$this->isSubAdmin
188
-				&& !$this->isAdminUser
189
-				&& !$authorized) {
190
-				throw new NotAdminException($this->l10n->t('Logged in user must be an admin or sub admin'));
191
-			}
192
-			if (!$this->hasAnnotationOrAttribute($reflectionMethod, 'SubAdminRequired', SubAdminRequired::class)
193
-				&& !$this->hasAnnotationOrAttribute($reflectionMethod, 'NoAdminRequired', NoAdminRequired::class)
194
-				&& !$this->isAdminUser
195
-				&& !$authorized) {
196
-				throw new NotAdminException($this->l10n->t('Logged in user must be an admin'));
197
-			}
198
-		}
177
+                        if ($authorized) {
178
+                            break;
179
+                        }
180
+                    }
181
+                }
182
+                if (!$authorized) {
183
+                    throw new NotAdminException($this->l10n->t('Logged in user must be an admin, a sub admin or gotten special right to access this setting'));
184
+                }
185
+            }
186
+            if ($this->hasAnnotationOrAttribute($reflectionMethod, 'SubAdminRequired', SubAdminRequired::class)
187
+                && !$this->isSubAdmin
188
+                && !$this->isAdminUser
189
+                && !$authorized) {
190
+                throw new NotAdminException($this->l10n->t('Logged in user must be an admin or sub admin'));
191
+            }
192
+            if (!$this->hasAnnotationOrAttribute($reflectionMethod, 'SubAdminRequired', SubAdminRequired::class)
193
+                && !$this->hasAnnotationOrAttribute($reflectionMethod, 'NoAdminRequired', NoAdminRequired::class)
194
+                && !$this->isAdminUser
195
+                && !$authorized) {
196
+                throw new NotAdminException($this->l10n->t('Logged in user must be an admin'));
197
+            }
198
+        }
199 199
 
200
-		// Check for strict cookie requirement
201
-		if ($this->hasAnnotationOrAttribute($reflectionMethod, 'StrictCookieRequired', StrictCookiesRequired::class) ||
202
-			!$this->hasAnnotationOrAttribute($reflectionMethod, 'NoCSRFRequired', NoCSRFRequired::class)) {
203
-			if (!$this->request->passesStrictCookieCheck()) {
204
-				throw new StrictCookieMissingException();
205
-			}
206
-		}
207
-		// CSRF check - also registers the CSRF token since the session may be closed later
208
-		Util::callRegister();
209
-		if (!$this->hasAnnotationOrAttribute($reflectionMethod, 'NoCSRFRequired', NoCSRFRequired::class)) {
210
-			/*
200
+        // Check for strict cookie requirement
201
+        if ($this->hasAnnotationOrAttribute($reflectionMethod, 'StrictCookieRequired', StrictCookiesRequired::class) ||
202
+            !$this->hasAnnotationOrAttribute($reflectionMethod, 'NoCSRFRequired', NoCSRFRequired::class)) {
203
+            if (!$this->request->passesStrictCookieCheck()) {
204
+                throw new StrictCookieMissingException();
205
+            }
206
+        }
207
+        // CSRF check - also registers the CSRF token since the session may be closed later
208
+        Util::callRegister();
209
+        if (!$this->hasAnnotationOrAttribute($reflectionMethod, 'NoCSRFRequired', NoCSRFRequired::class)) {
210
+            /*
211 211
 			 * Only allow the CSRF check to fail on OCS Requests. This kind of
212 212
 			 * hacks around that we have no full token auth in place yet and we
213 213
 			 * do want to offer CSRF checks for web requests.
@@ -215,123 +215,123 @@  discard block
 block discarded – undo
215 215
 			 * Additionally we allow Bearer authenticated requests to pass on OCS routes.
216 216
 			 * This allows oauth apps (e.g. moodle) to use the OCS endpoints
217 217
 			 */
218
-			if (!$this->request->passesCSRFCheck() && !(
219
-				$controller instanceof OCSController && (
220
-					$this->request->getHeader('OCS-APIREQUEST') === 'true' ||
221
-					str_starts_with($this->request->getHeader('Authorization'), 'Bearer ')
222
-				)
223
-			)) {
224
-				throw new CrossSiteRequestForgeryException();
225
-			}
226
-		}
218
+            if (!$this->request->passesCSRFCheck() && !(
219
+                $controller instanceof OCSController && (
220
+                    $this->request->getHeader('OCS-APIREQUEST') === 'true' ||
221
+                    str_starts_with($this->request->getHeader('Authorization'), 'Bearer ')
222
+                )
223
+            )) {
224
+                throw new CrossSiteRequestForgeryException();
225
+            }
226
+        }
227 227
 
228
-		/**
229
-		 * Checks if app is enabled (also includes a check whether user is allowed to access the resource)
230
-		 * The getAppPath() check is here since components such as settings also use the AppFramework and
231
-		 * therefore won't pass this check.
232
-		 * If page is public, app does not need to be enabled for current user/visitor
233
-		 */
234
-		try {
235
-			$appPath = $this->appManager->getAppPath($this->appName);
236
-		} catch (AppPathNotFoundException $e) {
237
-			$appPath = false;
238
-		}
228
+        /**
229
+         * Checks if app is enabled (also includes a check whether user is allowed to access the resource)
230
+         * The getAppPath() check is here since components such as settings also use the AppFramework and
231
+         * therefore won't pass this check.
232
+         * If page is public, app does not need to be enabled for current user/visitor
233
+         */
234
+        try {
235
+            $appPath = $this->appManager->getAppPath($this->appName);
236
+        } catch (AppPathNotFoundException $e) {
237
+            $appPath = false;
238
+        }
239 239
 
240
-		if ($appPath !== false && !$isPublicPage && !$this->appManager->isEnabledForUser($this->appName)) {
241
-			throw new AppNotEnabledException();
242
-		}
243
-	}
240
+        if ($appPath !== false && !$isPublicPage && !$this->appManager->isEnabledForUser($this->appName)) {
241
+            throw new AppNotEnabledException();
242
+        }
243
+    }
244 244
 
245
-	/**
246
-	 * @template T
247
-	 *
248
-	 * @param ReflectionMethod $reflectionMethod
249
-	 * @param string $annotationName
250
-	 * @param class-string<T> $attributeClass
251
-	 * @return boolean
252
-	 */
253
-	protected function hasAnnotationOrAttribute(ReflectionMethod $reflectionMethod, string $annotationName, string $attributeClass): bool {
254
-		if (!empty($reflectionMethod->getAttributes($attributeClass))) {
255
-			return true;
256
-		}
245
+    /**
246
+     * @template T
247
+     *
248
+     * @param ReflectionMethod $reflectionMethod
249
+     * @param string $annotationName
250
+     * @param class-string<T> $attributeClass
251
+     * @return boolean
252
+     */
253
+    protected function hasAnnotationOrAttribute(ReflectionMethod $reflectionMethod, string $annotationName, string $attributeClass): bool {
254
+        if (!empty($reflectionMethod->getAttributes($attributeClass))) {
255
+            return true;
256
+        }
257 257
 
258
-		if ($this->reflector->hasAnnotation($annotationName)) {
259
-			return true;
260
-		}
258
+        if ($this->reflector->hasAnnotation($annotationName)) {
259
+            return true;
260
+        }
261 261
 
262
-		return false;
263
-	}
262
+        return false;
263
+    }
264 264
 
265
-	/**
266
-	 * @param ReflectionMethod $reflectionMethod
267
-	 * @return string[]
268
-	 */
269
-	protected function getAuthorizedAdminSettingClasses(ReflectionMethod $reflectionMethod): array {
270
-		$classes = [];
271
-		if ($this->reflector->hasAnnotation('AuthorizedAdminSetting')) {
272
-			$classes = explode(';', $this->reflector->getAnnotationParameter('AuthorizedAdminSetting', 'settings'));
273
-		}
265
+    /**
266
+     * @param ReflectionMethod $reflectionMethod
267
+     * @return string[]
268
+     */
269
+    protected function getAuthorizedAdminSettingClasses(ReflectionMethod $reflectionMethod): array {
270
+        $classes = [];
271
+        if ($this->reflector->hasAnnotation('AuthorizedAdminSetting')) {
272
+            $classes = explode(';', $this->reflector->getAnnotationParameter('AuthorizedAdminSetting', 'settings'));
273
+        }
274 274
 
275
-		$attributes = $reflectionMethod->getAttributes(AuthorizedAdminSetting::class);
276
-		if (!empty($attributes)) {
277
-			foreach ($attributes as $attribute) {
278
-				/** @var AuthorizedAdminSetting $setting */
279
-				$setting = $attribute->newInstance();
280
-				$classes[] = $setting->getSettings();
281
-			}
282
-		}
275
+        $attributes = $reflectionMethod->getAttributes(AuthorizedAdminSetting::class);
276
+        if (!empty($attributes)) {
277
+            foreach ($attributes as $attribute) {
278
+                /** @var AuthorizedAdminSetting $setting */
279
+                $setting = $attribute->newInstance();
280
+                $classes[] = $setting->getSettings();
281
+            }
282
+        }
283 283
 
284
-		return $classes;
285
-	}
284
+        return $classes;
285
+    }
286 286
 
287
-	/**
288
-	 * If an SecurityException is being caught, ajax requests return a JSON error
289
-	 * response and non ajax requests redirect to the index
290
-	 *
291
-	 * @param Controller $controller the controller that is being called
292
-	 * @param string $methodName the name of the method that will be called on
293
-	 *                           the controller
294
-	 * @param \Exception $exception the thrown exception
295
-	 * @return Response a Response object or null in case that the exception could not be handled
296
-	 * @throws \Exception the passed in exception if it can't handle it
297
-	 */
298
-	public function afterException($controller, $methodName, \Exception $exception): Response {
299
-		if ($exception instanceof SecurityException) {
300
-			if ($exception instanceof StrictCookieMissingException) {
301
-				return new RedirectResponse(\OC::$WEBROOT . '/');
302
-			}
303
-			if (stripos($this->request->getHeader('Accept'), 'html') === false) {
304
-				$response = new JSONResponse(
305
-					['message' => $exception->getMessage()],
306
-					$exception->getCode()
307
-				);
308
-			} else {
309
-				if ($exception instanceof NotLoggedInException) {
310
-					$params = [];
311
-					if (isset($this->request->server['REQUEST_URI'])) {
312
-						$params['redirect_url'] = $this->request->server['REQUEST_URI'];
313
-					}
314
-					$usernamePrefill = $this->request->getParam('user', '');
315
-					if ($usernamePrefill !== '') {
316
-						$params['user'] = $usernamePrefill;
317
-					}
318
-					if ($this->request->getParam('direct')) {
319
-						$params['direct'] = 1;
320
-					}
321
-					$url = $this->urlGenerator->linkToRoute('core.login.showLoginForm', $params);
322
-					$response = new RedirectResponse($url);
323
-				} else {
324
-					$response = new TemplateResponse('core', '403', ['message' => $exception->getMessage()], 'guest');
325
-					$response->setStatus($exception->getCode());
326
-				}
327
-			}
287
+    /**
288
+     * If an SecurityException is being caught, ajax requests return a JSON error
289
+     * response and non ajax requests redirect to the index
290
+     *
291
+     * @param Controller $controller the controller that is being called
292
+     * @param string $methodName the name of the method that will be called on
293
+     *                           the controller
294
+     * @param \Exception $exception the thrown exception
295
+     * @return Response a Response object or null in case that the exception could not be handled
296
+     * @throws \Exception the passed in exception if it can't handle it
297
+     */
298
+    public function afterException($controller, $methodName, \Exception $exception): Response {
299
+        if ($exception instanceof SecurityException) {
300
+            if ($exception instanceof StrictCookieMissingException) {
301
+                return new RedirectResponse(\OC::$WEBROOT . '/');
302
+            }
303
+            if (stripos($this->request->getHeader('Accept'), 'html') === false) {
304
+                $response = new JSONResponse(
305
+                    ['message' => $exception->getMessage()],
306
+                    $exception->getCode()
307
+                );
308
+            } else {
309
+                if ($exception instanceof NotLoggedInException) {
310
+                    $params = [];
311
+                    if (isset($this->request->server['REQUEST_URI'])) {
312
+                        $params['redirect_url'] = $this->request->server['REQUEST_URI'];
313
+                    }
314
+                    $usernamePrefill = $this->request->getParam('user', '');
315
+                    if ($usernamePrefill !== '') {
316
+                        $params['user'] = $usernamePrefill;
317
+                    }
318
+                    if ($this->request->getParam('direct')) {
319
+                        $params['direct'] = 1;
320
+                    }
321
+                    $url = $this->urlGenerator->linkToRoute('core.login.showLoginForm', $params);
322
+                    $response = new RedirectResponse($url);
323
+                } else {
324
+                    $response = new TemplateResponse('core', '403', ['message' => $exception->getMessage()], 'guest');
325
+                    $response->setStatus($exception->getCode());
326
+                }
327
+            }
328 328
 
329
-			$this->logger->debug($exception->getMessage(), [
330
-				'exception' => $exception,
331
-			]);
332
-			return $response;
333
-		}
329
+            $this->logger->debug($exception->getMessage(), [
330
+                'exception' => $exception,
331
+            ]);
332
+            return $response;
333
+        }
334 334
 
335
-		throw $exception;
336
-	}
335
+        throw $exception;
336
+    }
337 337
 }
Please login to merge, or discard this patch.
lib/private/ServerContainer.php 1 patch
Indentation   +149 added lines, -149 removed lines patch added patch discarded remove patch
@@ -39,153 +39,153 @@
 block discarded – undo
39 39
  * @package OC
40 40
  */
41 41
 class ServerContainer extends SimpleContainer {
42
-	/** @var DIContainer[] */
43
-	protected $appContainers;
44
-
45
-	/** @var string[] */
46
-	protected $hasNoAppContainer;
47
-
48
-	/** @var string[] */
49
-	protected $namespaces;
50
-
51
-	/**
52
-	 * ServerContainer constructor.
53
-	 */
54
-	public function __construct() {
55
-		parent::__construct();
56
-		$this->appContainers = [];
57
-		$this->namespaces = [];
58
-		$this->hasNoAppContainer = [];
59
-	}
60
-
61
-	/**
62
-	 * @param string $appName
63
-	 * @param string $appNamespace
64
-	 */
65
-	public function registerNamespace(string $appName, string $appNamespace): void {
66
-		// Cut of OCA\ and lowercase
67
-		$appNamespace = strtolower(substr($appNamespace, strrpos($appNamespace, '\\') + 1));
68
-		$this->namespaces[$appNamespace] = $appName;
69
-	}
70
-
71
-	/**
72
-	 * @param string $appName
73
-	 * @param DIContainer $container
74
-	 */
75
-	public function registerAppContainer(string $appName, DIContainer $container): void {
76
-		$this->appContainers[strtolower(App::buildAppNamespace($appName, ''))] = $container;
77
-	}
78
-
79
-	/**
80
-	 * @param string $appName
81
-	 * @return DIContainer
82
-	 * @throws QueryException
83
-	 */
84
-	public function getRegisteredAppContainer(string $appName): DIContainer {
85
-		if (isset($this->appContainers[strtolower(App::buildAppNamespace($appName, ''))])) {
86
-			return $this->appContainers[strtolower(App::buildAppNamespace($appName, ''))];
87
-		}
88
-
89
-		throw new QueryException();
90
-	}
91
-
92
-	/**
93
-	 * @param string $namespace
94
-	 * @param string $sensitiveNamespace
95
-	 * @return DIContainer
96
-	 * @throws QueryException
97
-	 */
98
-	protected function getAppContainer(string $namespace, string $sensitiveNamespace): DIContainer {
99
-		if (isset($this->appContainers[$namespace])) {
100
-			return $this->appContainers[$namespace];
101
-		}
102
-
103
-		if (isset($this->namespaces[$namespace])) {
104
-			if (!isset($this->hasNoAppContainer[$namespace])) {
105
-				$applicationClassName = 'OCA\\' . $sensitiveNamespace . '\\AppInfo\\Application';
106
-				if (class_exists($applicationClassName)) {
107
-					$app = new $applicationClassName();
108
-					if (isset($this->appContainers[$namespace])) {
109
-						$this->appContainers[$namespace]->offsetSet($applicationClassName, $app);
110
-						return $this->appContainers[$namespace];
111
-					}
112
-				}
113
-				$this->hasNoAppContainer[$namespace] = true;
114
-			}
115
-
116
-			return new DIContainer($this->namespaces[$namespace]);
117
-		}
118
-		throw new QueryException();
119
-	}
120
-
121
-	public function has($id, bool $noRecursion = false): bool {
122
-		if (!$noRecursion && ($appContainer = $this->getAppContainerForService($id)) !== null) {
123
-			return $appContainer->has($id);
124
-		}
125
-
126
-		return parent::has($id);
127
-	}
128
-
129
-	/**
130
-	 * @template T
131
-	 * @param class-string<T>|string $name
132
-	 * @return T|mixed
133
-	 * @psalm-template S as class-string<T>|string
134
-	 * @psalm-param S $name
135
-	 * @psalm-return (S is class-string<T> ? T : mixed)
136
-	 * @throws QueryException
137
-	 * @deprecated 20.0.0 use \Psr\Container\ContainerInterface::get
138
-	 */
139
-	public function query(string $name, bool $autoload = true) {
140
-		$name = $this->sanitizeName($name);
141
-
142
-		if (str_starts_with($name, 'OCA\\')) {
143
-			// Skip server container query for app namespace classes
144
-			try {
145
-				return parent::query($name, false);
146
-			} catch (QueryException $e) {
147
-				// Continue with general autoloading then
148
-			}
149
-		}
150
-
151
-		// In case the service starts with OCA\ we try to find the service in
152
-		// the apps container first.
153
-		if (($appContainer = $this->getAppContainerForService($name)) !== null) {
154
-			try {
155
-				return $appContainer->queryNoFallback($name);
156
-			} catch (QueryException $e) {
157
-				// Didn't find the service or the respective app container,
158
-				// ignore it and fall back to the core container.
159
-			}
160
-		} elseif (str_starts_with($name, 'OC\\Settings\\') && substr_count($name, '\\') >= 3) {
161
-			$segments = explode('\\', $name);
162
-			try {
163
-				$appContainer = $this->getAppContainer(strtolower($segments[1]), $segments[1]);
164
-				return $appContainer->queryNoFallback($name);
165
-			} catch (QueryException $e) {
166
-				// Didn't find the service or the respective app container,
167
-				// ignore it and fall back to the core container.
168
-			}
169
-		}
170
-
171
-		return parent::query($name, $autoload);
172
-	}
173
-
174
-	/**
175
-	 * @internal
176
-	 * @param string $id
177
-	 * @return DIContainer|null
178
-	 */
179
-	public function getAppContainerForService(string $id): ?DIContainer {
180
-		if (!str_starts_with($id, 'OCA\\') || substr_count($id, '\\') < 2) {
181
-			return null;
182
-		}
183
-
184
-		try {
185
-			[,$namespace,] = explode('\\', $id);
186
-			return $this->getAppContainer(strtolower($namespace), $namespace);
187
-		} catch (QueryException $e) {
188
-			return null;
189
-		}
190
-	}
42
+    /** @var DIContainer[] */
43
+    protected $appContainers;
44
+
45
+    /** @var string[] */
46
+    protected $hasNoAppContainer;
47
+
48
+    /** @var string[] */
49
+    protected $namespaces;
50
+
51
+    /**
52
+     * ServerContainer constructor.
53
+     */
54
+    public function __construct() {
55
+        parent::__construct();
56
+        $this->appContainers = [];
57
+        $this->namespaces = [];
58
+        $this->hasNoAppContainer = [];
59
+    }
60
+
61
+    /**
62
+     * @param string $appName
63
+     * @param string $appNamespace
64
+     */
65
+    public function registerNamespace(string $appName, string $appNamespace): void {
66
+        // Cut of OCA\ and lowercase
67
+        $appNamespace = strtolower(substr($appNamespace, strrpos($appNamespace, '\\') + 1));
68
+        $this->namespaces[$appNamespace] = $appName;
69
+    }
70
+
71
+    /**
72
+     * @param string $appName
73
+     * @param DIContainer $container
74
+     */
75
+    public function registerAppContainer(string $appName, DIContainer $container): void {
76
+        $this->appContainers[strtolower(App::buildAppNamespace($appName, ''))] = $container;
77
+    }
78
+
79
+    /**
80
+     * @param string $appName
81
+     * @return DIContainer
82
+     * @throws QueryException
83
+     */
84
+    public function getRegisteredAppContainer(string $appName): DIContainer {
85
+        if (isset($this->appContainers[strtolower(App::buildAppNamespace($appName, ''))])) {
86
+            return $this->appContainers[strtolower(App::buildAppNamespace($appName, ''))];
87
+        }
88
+
89
+        throw new QueryException();
90
+    }
91
+
92
+    /**
93
+     * @param string $namespace
94
+     * @param string $sensitiveNamespace
95
+     * @return DIContainer
96
+     * @throws QueryException
97
+     */
98
+    protected function getAppContainer(string $namespace, string $sensitiveNamespace): DIContainer {
99
+        if (isset($this->appContainers[$namespace])) {
100
+            return $this->appContainers[$namespace];
101
+        }
102
+
103
+        if (isset($this->namespaces[$namespace])) {
104
+            if (!isset($this->hasNoAppContainer[$namespace])) {
105
+                $applicationClassName = 'OCA\\' . $sensitiveNamespace . '\\AppInfo\\Application';
106
+                if (class_exists($applicationClassName)) {
107
+                    $app = new $applicationClassName();
108
+                    if (isset($this->appContainers[$namespace])) {
109
+                        $this->appContainers[$namespace]->offsetSet($applicationClassName, $app);
110
+                        return $this->appContainers[$namespace];
111
+                    }
112
+                }
113
+                $this->hasNoAppContainer[$namespace] = true;
114
+            }
115
+
116
+            return new DIContainer($this->namespaces[$namespace]);
117
+        }
118
+        throw new QueryException();
119
+    }
120
+
121
+    public function has($id, bool $noRecursion = false): bool {
122
+        if (!$noRecursion && ($appContainer = $this->getAppContainerForService($id)) !== null) {
123
+            return $appContainer->has($id);
124
+        }
125
+
126
+        return parent::has($id);
127
+    }
128
+
129
+    /**
130
+     * @template T
131
+     * @param class-string<T>|string $name
132
+     * @return T|mixed
133
+     * @psalm-template S as class-string<T>|string
134
+     * @psalm-param S $name
135
+     * @psalm-return (S is class-string<T> ? T : mixed)
136
+     * @throws QueryException
137
+     * @deprecated 20.0.0 use \Psr\Container\ContainerInterface::get
138
+     */
139
+    public function query(string $name, bool $autoload = true) {
140
+        $name = $this->sanitizeName($name);
141
+
142
+        if (str_starts_with($name, 'OCA\\')) {
143
+            // Skip server container query for app namespace classes
144
+            try {
145
+                return parent::query($name, false);
146
+            } catch (QueryException $e) {
147
+                // Continue with general autoloading then
148
+            }
149
+        }
150
+
151
+        // In case the service starts with OCA\ we try to find the service in
152
+        // the apps container first.
153
+        if (($appContainer = $this->getAppContainerForService($name)) !== null) {
154
+            try {
155
+                return $appContainer->queryNoFallback($name);
156
+            } catch (QueryException $e) {
157
+                // Didn't find the service or the respective app container,
158
+                // ignore it and fall back to the core container.
159
+            }
160
+        } elseif (str_starts_with($name, 'OC\\Settings\\') && substr_count($name, '\\') >= 3) {
161
+            $segments = explode('\\', $name);
162
+            try {
163
+                $appContainer = $this->getAppContainer(strtolower($segments[1]), $segments[1]);
164
+                return $appContainer->queryNoFallback($name);
165
+            } catch (QueryException $e) {
166
+                // Didn't find the service or the respective app container,
167
+                // ignore it and fall back to the core container.
168
+            }
169
+        }
170
+
171
+        return parent::query($name, $autoload);
172
+    }
173
+
174
+    /**
175
+     * @internal
176
+     * @param string $id
177
+     * @return DIContainer|null
178
+     */
179
+    public function getAppContainerForService(string $id): ?DIContainer {
180
+        if (!str_starts_with($id, 'OCA\\') || substr_count($id, '\\') < 2) {
181
+            return null;
182
+        }
183
+
184
+        try {
185
+            [,$namespace,] = explode('\\', $id);
186
+            return $this->getAppContainer(strtolower($namespace), $namespace);
187
+        } catch (QueryException $e) {
188
+            return null;
189
+        }
190
+    }
191 191
 }
Please login to merge, or discard this patch.