Passed
Push — master ( bbb39c...5026d2 )
by Christoph
27:34 queued 12:27
created
lib/private/Files/Node/Folder.php 1 patch
Indentation   +530 added lines, -530 removed lines patch added patch discarded remove patch
@@ -43,541 +43,541 @@
 block discarded – undo
43 43
 use OCP\Files\Search\ISearchQuery;
44 44
 
45 45
 class Folder extends Node implements \OCP\Files\Folder {
46
-	/**
47
-	 * Creates a Folder that represents a non-existing path
48
-	 *
49
-	 * @param string $path path
50
-	 * @return string non-existing node class
51
-	 */
52
-	protected function createNonExistingNode($path) {
53
-		return new NonExistingFolder($this->root, $this->view, $path);
54
-	}
55
-
56
-	/**
57
-	 * @param string $path path relative to the folder
58
-	 * @return string
59
-	 * @throws \OCP\Files\NotPermittedException
60
-	 */
61
-	public function getFullPath($path) {
62
-		if (!$this->isValidPath($path)) {
63
-			throw new NotPermittedException('Invalid path');
64
-		}
65
-		return $this->path . $this->normalizePath($path);
66
-	}
67
-
68
-	/**
69
-	 * @param string $path
70
-	 * @return string
71
-	 */
72
-	public function getRelativePath($path) {
73
-		if ($this->path === '' or $this->path === '/') {
74
-			return $this->normalizePath($path);
75
-		}
76
-		if ($path === $this->path) {
77
-			return '/';
78
-		} elseif (strpos($path, $this->path . '/') !== 0) {
79
-			return null;
80
-		} else {
81
-			$path = substr($path, strlen($this->path));
82
-			return $this->normalizePath($path);
83
-		}
84
-	}
85
-
86
-	/**
87
-	 * check if a node is a (grand-)child of the folder
88
-	 *
89
-	 * @param \OC\Files\Node\Node $node
90
-	 * @return bool
91
-	 */
92
-	public function isSubNode($node) {
93
-		return strpos($node->getPath(), $this->path . '/') === 0;
94
-	}
95
-
96
-	/**
97
-	 * get the content of this directory
98
-	 *
99
-	 * @throws \OCP\Files\NotFoundException
100
-	 * @return Node[]
101
-	 */
102
-	public function getDirectoryListing() {
103
-		$folderContent = $this->view->getDirectoryContent($this->path);
104
-
105
-		return array_map(function (FileInfo $info) {
106
-			if ($info->getMimetype() === 'httpd/unix-directory') {
107
-				return new Folder($this->root, $this->view, $info->getPath(), $info);
108
-			} else {
109
-				return new File($this->root, $this->view, $info->getPath(), $info);
110
-			}
111
-		}, $folderContent);
112
-	}
113
-
114
-	/**
115
-	 * @param string $path
116
-	 * @param FileInfo $info
117
-	 * @return File|Folder
118
-	 */
119
-	protected function createNode($path, FileInfo $info = null) {
120
-		if (is_null($info)) {
121
-			$isDir = $this->view->is_dir($path);
122
-		} else {
123
-			$isDir = $info->getType() === FileInfo::TYPE_FOLDER;
124
-		}
125
-		if ($isDir) {
126
-			return new Folder($this->root, $this->view, $path, $info);
127
-		} else {
128
-			return new File($this->root, $this->view, $path, $info);
129
-		}
130
-	}
131
-
132
-	/**
133
-	 * Get the node at $path
134
-	 *
135
-	 * @param string $path
136
-	 * @return \OC\Files\Node\Node
137
-	 * @throws \OCP\Files\NotFoundException
138
-	 */
139
-	public function get($path) {
140
-		return $this->root->get($this->getFullPath($path));
141
-	}
142
-
143
-	/**
144
-	 * @param string $path
145
-	 * @return bool
146
-	 */
147
-	public function nodeExists($path) {
148
-		try {
149
-			$this->get($path);
150
-			return true;
151
-		} catch (NotFoundException $e) {
152
-			return false;
153
-		}
154
-	}
155
-
156
-	/**
157
-	 * @param string $path
158
-	 * @return \OC\Files\Node\Folder
159
-	 * @throws \OCP\Files\NotPermittedException
160
-	 */
161
-	public function newFolder($path) {
162
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
163
-			$fullPath = $this->getFullPath($path);
164
-			$nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath);
165
-			$this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
166
-			if (!$this->view->mkdir($fullPath)) {
167
-				throw new NotPermittedException('Could not create folder');
168
-			}
169
-			$node = new Folder($this->root, $this->view, $fullPath);
170
-			$this->sendHooks(['postWrite', 'postCreate'], [$node]);
171
-			return $node;
172
-		} else {
173
-			throw new NotPermittedException('No create permission for folder');
174
-		}
175
-	}
176
-
177
-	/**
178
-	 * @param string $path
179
-	 * @param string | resource | null $content
180
-	 * @return \OC\Files\Node\File
181
-	 * @throws \OCP\Files\NotPermittedException
182
-	 */
183
-	public function newFile($path, $content = null) {
184
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
185
-			$fullPath = $this->getFullPath($path);
186
-			$nonExisting = new NonExistingFile($this->root, $this->view, $fullPath);
187
-			$this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
188
-			if ($content !== null) {
189
-				$result = $this->view->file_put_contents($fullPath, $content);
190
-			} else {
191
-				$result = $this->view->touch($fullPath);
192
-			}
193
-			if ($result === false) {
194
-				throw new NotPermittedException('Could not create path');
195
-			}
196
-			$node = new File($this->root, $this->view, $fullPath);
197
-			$this->sendHooks(['postWrite', 'postCreate'], [$node]);
198
-			return $node;
199
-		}
200
-		throw new NotPermittedException('No create permission for path');
201
-	}
202
-
203
-	/**
204
-	 * search for files with the name matching $query
205
-	 *
206
-	 * @param string|ISearchQuery $query
207
-	 * @return \OC\Files\Node\Node[]
208
-	 */
209
-	public function search($query) {
210
-		if (is_string($query)) {
211
-			return $this->searchCommon('search', ['%' . $query . '%']);
212
-		} else {
213
-			return $this->searchCommon('searchQuery', [$query]);
214
-		}
215
-	}
216
-
217
-	/**
218
-	 * search for files by mimetype
219
-	 *
220
-	 * @param string $mimetype
221
-	 * @return Node[]
222
-	 */
223
-	public function searchByMime($mimetype) {
224
-		return $this->searchCommon('searchByMime', [$mimetype]);
225
-	}
226
-
227
-	/**
228
-	 * search for files by tag
229
-	 *
230
-	 * @param string|int $tag name or tag id
231
-	 * @param string $userId owner of the tags
232
-	 * @return Node[]
233
-	 */
234
-	public function searchByTag($tag, $userId) {
235
-		return $this->searchCommon('searchByTag', [$tag, $userId]);
236
-	}
237
-
238
-	/**
239
-	 * @param string $method cache method
240
-	 * @param array $args call args
241
-	 * @return \OC\Files\Node\Node[]
242
-	 */
243
-	private function searchCommon($method, $args) {
244
-		$limitToHome = ($method === 'searchQuery')? $args[0]->limitToHome(): false;
245
-		if ($limitToHome && count(explode('/', $this->path)) !== 3) {
246
-			throw new \InvalidArgumentException('searching by owner is only allows on the users home folder');
247
-		}
248
-
249
-		$files = [];
250
-		$rootLength = strlen($this->path);
251
-		$mount = $this->root->getMount($this->path);
252
-		$storage = $mount->getStorage();
253
-		$internalPath = $mount->getInternalPath($this->path);
254
-		$internalPath = rtrim($internalPath, '/');
255
-		if ($internalPath !== '') {
256
-			$internalPath = $internalPath . '/';
257
-		}
258
-		$internalRootLength = strlen($internalPath);
259
-
260
-		$cache = $storage->getCache('');
261
-
262
-		$results = call_user_func_array([$cache, $method], $args);
263
-		foreach ($results as $result) {
264
-			if ($internalRootLength === 0 or substr($result['path'], 0, $internalRootLength) === $internalPath) {
265
-				$result['internalPath'] = $result['path'];
266
-				$result['path'] = substr($result['path'], $internalRootLength);
267
-				$result['storage'] = $storage;
268
-				$files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
269
-			}
270
-		}
271
-
272
-		if (!$limitToHome) {
273
-			$mounts = $this->root->getMountsIn($this->path);
274
-			foreach ($mounts as $mount) {
275
-				$storage = $mount->getStorage();
276
-				if ($storage) {
277
-					$cache = $storage->getCache('');
278
-
279
-					$relativeMountPoint = ltrim(substr($mount->getMountPoint(), $rootLength), '/');
280
-					$results = call_user_func_array([$cache, $method], $args);
281
-					foreach ($results as $result) {
282
-						$result['internalPath'] = $result['path'];
283
-						$result['path'] = $relativeMountPoint . $result['path'];
284
-						$result['storage'] = $storage;
285
-						$files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage,
286
-							$result['internalPath'], $result, $mount);
287
-					}
288
-				}
289
-			}
290
-		}
291
-
292
-		return array_map(function (FileInfo $file) {
293
-			return $this->createNode($file->getPath(), $file);
294
-		}, $files);
295
-	}
296
-
297
-	/**
298
-	 * @param int $id
299
-	 * @return \OC\Files\Node\Node[]
300
-	 */
301
-	public function getById($id) {
302
-		$mountCache = $this->root->getUserMountCache();
303
-		if (strpos($this->getPath(), '/', 1) > 0) {
304
-			[, $user] = explode('/', $this->getPath());
305
-		} else {
306
-			$user = null;
307
-		}
308
-		$mountsContainingFile = $mountCache->getMountsForFileId((int)$id, $user);
309
-		$mounts = $this->root->getMountsIn($this->path);
310
-		$mounts[] = $this->root->getMount($this->path);
311
-		/** @var IMountPoint[] $folderMounts */
312
-		$folderMounts = array_combine(array_map(function (IMountPoint $mountPoint) {
313
-			return $mountPoint->getMountPoint();
314
-		}, $mounts), $mounts);
315
-
316
-		/** @var ICachedMountInfo[] $mountsContainingFile */
317
-		$mountsContainingFile = array_values(array_filter($mountsContainingFile, function (ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
318
-			return isset($folderMounts[$cachedMountInfo->getMountPoint()]);
319
-		}));
320
-
321
-		if (count($mountsContainingFile) === 0) {
322
-			if ($user === $this->getAppDataDirectoryName()) {
323
-				return $this->getByIdInRootMount((int) $id);
324
-			}
325
-			return [];
326
-		}
327
-
328
-		$nodes = array_map(function (ICachedMountInfo $cachedMountInfo) use ($folderMounts, $id) {
329
-			$mount = $folderMounts[$cachedMountInfo->getMountPoint()];
330
-			$cacheEntry = $mount->getStorage()->getCache()->get((int)$id);
331
-			if (!$cacheEntry) {
332
-				return null;
333
-			}
334
-
335
-			// cache jails will hide the "true" internal path
336
-			$internalPath = ltrim($cachedMountInfo->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/');
337
-			$pathRelativeToMount = substr($internalPath, strlen($cachedMountInfo->getRootInternalPath()));
338
-			$pathRelativeToMount = ltrim($pathRelativeToMount, '/');
339
-			$absolutePath = rtrim($cachedMountInfo->getMountPoint() . $pathRelativeToMount, '/');
340
-			return $this->root->createNode($absolutePath, new \OC\Files\FileInfo(
341
-				$absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
342
-				\OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
343
-			));
344
-		}, $mountsContainingFile);
345
-
346
-		$nodes = array_filter($nodes);
347
-
348
-		return array_filter($nodes, function (Node $node) {
349
-			return $this->getRelativePath($node->getPath());
350
-		});
351
-	}
352
-
353
-	protected function getAppDataDirectoryName(): string {
354
-		$instanceId = \OC::$server->getConfig()->getSystemValueString('instanceid');
355
-		return 'appdata_' . $instanceId;
356
-	}
357
-
358
-	/**
359
-	 * In case the path we are currently in is inside the appdata_* folder,
360
-	 * the original getById method does not work, because it can only look inside
361
-	 * the user's mount points. But the user has no mount point for the root storage.
362
-	 *
363
-	 * So in that case we directly check the mount of the root if it contains
364
-	 * the id. If it does we check if the path is inside the path we are working
365
-	 * in.
366
-	 *
367
-	 * @param int $id
368
-	 * @return array
369
-	 */
370
-	protected function getByIdInRootMount(int $id): array {
371
-		$mount = $this->root->getMount('');
372
-		$cacheEntry = $mount->getStorage()->getCache($this->path)->get($id);
373
-		if (!$cacheEntry) {
374
-			return [];
375
-		}
376
-
377
-		$absolutePath = '/' . ltrim($cacheEntry->getPath(), '/');
378
-		$currentPath = rtrim($this->path, '/') . '/';
379
-
380
-		if (strpos($absolutePath, $currentPath) !== 0) {
381
-			return [];
382
-		}
383
-
384
-		return [$this->root->createNode(
385
-			$absolutePath, new \OC\Files\FileInfo(
386
-				$absolutePath,
387
-				$mount->getStorage(),
388
-				$cacheEntry->getPath(),
389
-				$cacheEntry,
390
-				$mount
391
-		))];
392
-	}
393
-
394
-	public function getFreeSpace() {
395
-		return $this->view->free_space($this->path);
396
-	}
397
-
398
-	public function delete() {
399
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
400
-			$this->sendHooks(['preDelete']);
401
-			$fileInfo = $this->getFileInfo();
402
-			$this->view->rmdir($this->path);
403
-			$nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo);
404
-			$this->sendHooks(['postDelete'], [$nonExisting]);
405
-			$this->exists = false;
406
-		} else {
407
-			throw new NotPermittedException('No delete permission for path');
408
-		}
409
-	}
410
-
411
-	/**
412
-	 * Add a suffix to the name in case the file exists
413
-	 *
414
-	 * @param string $name
415
-	 * @return string
416
-	 * @throws NotPermittedException
417
-	 */
418
-	public function getNonExistingName($name) {
419
-		$uniqueName = \OC_Helper::buildNotExistingFileNameForView($this->getPath(), $name, $this->view);
420
-		return trim($this->getRelativePath($uniqueName), '/');
421
-	}
422
-
423
-	/**
424
-	 * @param int $limit
425
-	 * @param int $offset
426
-	 * @return \OCP\Files\Node[]
427
-	 */
428
-	public function getRecent($limit, $offset = 0) {
429
-		$mimetypeLoader = \OC::$server->getMimeTypeLoader();
430
-		$mounts = $this->root->getMountsIn($this->path);
431
-		$mounts[] = $this->getMountPoint();
432
-
433
-		$mounts = array_filter($mounts, function (IMountPoint $mount) {
434
-			return $mount->getStorage();
435
-		});
436
-		$storageIds = array_map(function (IMountPoint $mount) {
437
-			return $mount->getStorage()->getCache()->getNumericStorageId();
438
-		}, $mounts);
439
-		/** @var IMountPoint[] $mountMap */
440
-		$mountMap = array_combine($storageIds, $mounts);
441
-		$folderMimetype = $mimetypeLoader->getId(FileInfo::MIMETYPE_FOLDER);
442
-
443
-		/*
46
+    /**
47
+     * Creates a Folder that represents a non-existing path
48
+     *
49
+     * @param string $path path
50
+     * @return string non-existing node class
51
+     */
52
+    protected function createNonExistingNode($path) {
53
+        return new NonExistingFolder($this->root, $this->view, $path);
54
+    }
55
+
56
+    /**
57
+     * @param string $path path relative to the folder
58
+     * @return string
59
+     * @throws \OCP\Files\NotPermittedException
60
+     */
61
+    public function getFullPath($path) {
62
+        if (!$this->isValidPath($path)) {
63
+            throw new NotPermittedException('Invalid path');
64
+        }
65
+        return $this->path . $this->normalizePath($path);
66
+    }
67
+
68
+    /**
69
+     * @param string $path
70
+     * @return string
71
+     */
72
+    public function getRelativePath($path) {
73
+        if ($this->path === '' or $this->path === '/') {
74
+            return $this->normalizePath($path);
75
+        }
76
+        if ($path === $this->path) {
77
+            return '/';
78
+        } elseif (strpos($path, $this->path . '/') !== 0) {
79
+            return null;
80
+        } else {
81
+            $path = substr($path, strlen($this->path));
82
+            return $this->normalizePath($path);
83
+        }
84
+    }
85
+
86
+    /**
87
+     * check if a node is a (grand-)child of the folder
88
+     *
89
+     * @param \OC\Files\Node\Node $node
90
+     * @return bool
91
+     */
92
+    public function isSubNode($node) {
93
+        return strpos($node->getPath(), $this->path . '/') === 0;
94
+    }
95
+
96
+    /**
97
+     * get the content of this directory
98
+     *
99
+     * @throws \OCP\Files\NotFoundException
100
+     * @return Node[]
101
+     */
102
+    public function getDirectoryListing() {
103
+        $folderContent = $this->view->getDirectoryContent($this->path);
104
+
105
+        return array_map(function (FileInfo $info) {
106
+            if ($info->getMimetype() === 'httpd/unix-directory') {
107
+                return new Folder($this->root, $this->view, $info->getPath(), $info);
108
+            } else {
109
+                return new File($this->root, $this->view, $info->getPath(), $info);
110
+            }
111
+        }, $folderContent);
112
+    }
113
+
114
+    /**
115
+     * @param string $path
116
+     * @param FileInfo $info
117
+     * @return File|Folder
118
+     */
119
+    protected function createNode($path, FileInfo $info = null) {
120
+        if (is_null($info)) {
121
+            $isDir = $this->view->is_dir($path);
122
+        } else {
123
+            $isDir = $info->getType() === FileInfo::TYPE_FOLDER;
124
+        }
125
+        if ($isDir) {
126
+            return new Folder($this->root, $this->view, $path, $info);
127
+        } else {
128
+            return new File($this->root, $this->view, $path, $info);
129
+        }
130
+    }
131
+
132
+    /**
133
+     * Get the node at $path
134
+     *
135
+     * @param string $path
136
+     * @return \OC\Files\Node\Node
137
+     * @throws \OCP\Files\NotFoundException
138
+     */
139
+    public function get($path) {
140
+        return $this->root->get($this->getFullPath($path));
141
+    }
142
+
143
+    /**
144
+     * @param string $path
145
+     * @return bool
146
+     */
147
+    public function nodeExists($path) {
148
+        try {
149
+            $this->get($path);
150
+            return true;
151
+        } catch (NotFoundException $e) {
152
+            return false;
153
+        }
154
+    }
155
+
156
+    /**
157
+     * @param string $path
158
+     * @return \OC\Files\Node\Folder
159
+     * @throws \OCP\Files\NotPermittedException
160
+     */
161
+    public function newFolder($path) {
162
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
163
+            $fullPath = $this->getFullPath($path);
164
+            $nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath);
165
+            $this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
166
+            if (!$this->view->mkdir($fullPath)) {
167
+                throw new NotPermittedException('Could not create folder');
168
+            }
169
+            $node = new Folder($this->root, $this->view, $fullPath);
170
+            $this->sendHooks(['postWrite', 'postCreate'], [$node]);
171
+            return $node;
172
+        } else {
173
+            throw new NotPermittedException('No create permission for folder');
174
+        }
175
+    }
176
+
177
+    /**
178
+     * @param string $path
179
+     * @param string | resource | null $content
180
+     * @return \OC\Files\Node\File
181
+     * @throws \OCP\Files\NotPermittedException
182
+     */
183
+    public function newFile($path, $content = null) {
184
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
185
+            $fullPath = $this->getFullPath($path);
186
+            $nonExisting = new NonExistingFile($this->root, $this->view, $fullPath);
187
+            $this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
188
+            if ($content !== null) {
189
+                $result = $this->view->file_put_contents($fullPath, $content);
190
+            } else {
191
+                $result = $this->view->touch($fullPath);
192
+            }
193
+            if ($result === false) {
194
+                throw new NotPermittedException('Could not create path');
195
+            }
196
+            $node = new File($this->root, $this->view, $fullPath);
197
+            $this->sendHooks(['postWrite', 'postCreate'], [$node]);
198
+            return $node;
199
+        }
200
+        throw new NotPermittedException('No create permission for path');
201
+    }
202
+
203
+    /**
204
+     * search for files with the name matching $query
205
+     *
206
+     * @param string|ISearchQuery $query
207
+     * @return \OC\Files\Node\Node[]
208
+     */
209
+    public function search($query) {
210
+        if (is_string($query)) {
211
+            return $this->searchCommon('search', ['%' . $query . '%']);
212
+        } else {
213
+            return $this->searchCommon('searchQuery', [$query]);
214
+        }
215
+    }
216
+
217
+    /**
218
+     * search for files by mimetype
219
+     *
220
+     * @param string $mimetype
221
+     * @return Node[]
222
+     */
223
+    public function searchByMime($mimetype) {
224
+        return $this->searchCommon('searchByMime', [$mimetype]);
225
+    }
226
+
227
+    /**
228
+     * search for files by tag
229
+     *
230
+     * @param string|int $tag name or tag id
231
+     * @param string $userId owner of the tags
232
+     * @return Node[]
233
+     */
234
+    public function searchByTag($tag, $userId) {
235
+        return $this->searchCommon('searchByTag', [$tag, $userId]);
236
+    }
237
+
238
+    /**
239
+     * @param string $method cache method
240
+     * @param array $args call args
241
+     * @return \OC\Files\Node\Node[]
242
+     */
243
+    private function searchCommon($method, $args) {
244
+        $limitToHome = ($method === 'searchQuery')? $args[0]->limitToHome(): false;
245
+        if ($limitToHome && count(explode('/', $this->path)) !== 3) {
246
+            throw new \InvalidArgumentException('searching by owner is only allows on the users home folder');
247
+        }
248
+
249
+        $files = [];
250
+        $rootLength = strlen($this->path);
251
+        $mount = $this->root->getMount($this->path);
252
+        $storage = $mount->getStorage();
253
+        $internalPath = $mount->getInternalPath($this->path);
254
+        $internalPath = rtrim($internalPath, '/');
255
+        if ($internalPath !== '') {
256
+            $internalPath = $internalPath . '/';
257
+        }
258
+        $internalRootLength = strlen($internalPath);
259
+
260
+        $cache = $storage->getCache('');
261
+
262
+        $results = call_user_func_array([$cache, $method], $args);
263
+        foreach ($results as $result) {
264
+            if ($internalRootLength === 0 or substr($result['path'], 0, $internalRootLength) === $internalPath) {
265
+                $result['internalPath'] = $result['path'];
266
+                $result['path'] = substr($result['path'], $internalRootLength);
267
+                $result['storage'] = $storage;
268
+                $files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
269
+            }
270
+        }
271
+
272
+        if (!$limitToHome) {
273
+            $mounts = $this->root->getMountsIn($this->path);
274
+            foreach ($mounts as $mount) {
275
+                $storage = $mount->getStorage();
276
+                if ($storage) {
277
+                    $cache = $storage->getCache('');
278
+
279
+                    $relativeMountPoint = ltrim(substr($mount->getMountPoint(), $rootLength), '/');
280
+                    $results = call_user_func_array([$cache, $method], $args);
281
+                    foreach ($results as $result) {
282
+                        $result['internalPath'] = $result['path'];
283
+                        $result['path'] = $relativeMountPoint . $result['path'];
284
+                        $result['storage'] = $storage;
285
+                        $files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage,
286
+                            $result['internalPath'], $result, $mount);
287
+                    }
288
+                }
289
+            }
290
+        }
291
+
292
+        return array_map(function (FileInfo $file) {
293
+            return $this->createNode($file->getPath(), $file);
294
+        }, $files);
295
+    }
296
+
297
+    /**
298
+     * @param int $id
299
+     * @return \OC\Files\Node\Node[]
300
+     */
301
+    public function getById($id) {
302
+        $mountCache = $this->root->getUserMountCache();
303
+        if (strpos($this->getPath(), '/', 1) > 0) {
304
+            [, $user] = explode('/', $this->getPath());
305
+        } else {
306
+            $user = null;
307
+        }
308
+        $mountsContainingFile = $mountCache->getMountsForFileId((int)$id, $user);
309
+        $mounts = $this->root->getMountsIn($this->path);
310
+        $mounts[] = $this->root->getMount($this->path);
311
+        /** @var IMountPoint[] $folderMounts */
312
+        $folderMounts = array_combine(array_map(function (IMountPoint $mountPoint) {
313
+            return $mountPoint->getMountPoint();
314
+        }, $mounts), $mounts);
315
+
316
+        /** @var ICachedMountInfo[] $mountsContainingFile */
317
+        $mountsContainingFile = array_values(array_filter($mountsContainingFile, function (ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
318
+            return isset($folderMounts[$cachedMountInfo->getMountPoint()]);
319
+        }));
320
+
321
+        if (count($mountsContainingFile) === 0) {
322
+            if ($user === $this->getAppDataDirectoryName()) {
323
+                return $this->getByIdInRootMount((int) $id);
324
+            }
325
+            return [];
326
+        }
327
+
328
+        $nodes = array_map(function (ICachedMountInfo $cachedMountInfo) use ($folderMounts, $id) {
329
+            $mount = $folderMounts[$cachedMountInfo->getMountPoint()];
330
+            $cacheEntry = $mount->getStorage()->getCache()->get((int)$id);
331
+            if (!$cacheEntry) {
332
+                return null;
333
+            }
334
+
335
+            // cache jails will hide the "true" internal path
336
+            $internalPath = ltrim($cachedMountInfo->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/');
337
+            $pathRelativeToMount = substr($internalPath, strlen($cachedMountInfo->getRootInternalPath()));
338
+            $pathRelativeToMount = ltrim($pathRelativeToMount, '/');
339
+            $absolutePath = rtrim($cachedMountInfo->getMountPoint() . $pathRelativeToMount, '/');
340
+            return $this->root->createNode($absolutePath, new \OC\Files\FileInfo(
341
+                $absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
342
+                \OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
343
+            ));
344
+        }, $mountsContainingFile);
345
+
346
+        $nodes = array_filter($nodes);
347
+
348
+        return array_filter($nodes, function (Node $node) {
349
+            return $this->getRelativePath($node->getPath());
350
+        });
351
+    }
352
+
353
+    protected function getAppDataDirectoryName(): string {
354
+        $instanceId = \OC::$server->getConfig()->getSystemValueString('instanceid');
355
+        return 'appdata_' . $instanceId;
356
+    }
357
+
358
+    /**
359
+     * In case the path we are currently in is inside the appdata_* folder,
360
+     * the original getById method does not work, because it can only look inside
361
+     * the user's mount points. But the user has no mount point for the root storage.
362
+     *
363
+     * So in that case we directly check the mount of the root if it contains
364
+     * the id. If it does we check if the path is inside the path we are working
365
+     * in.
366
+     *
367
+     * @param int $id
368
+     * @return array
369
+     */
370
+    protected function getByIdInRootMount(int $id): array {
371
+        $mount = $this->root->getMount('');
372
+        $cacheEntry = $mount->getStorage()->getCache($this->path)->get($id);
373
+        if (!$cacheEntry) {
374
+            return [];
375
+        }
376
+
377
+        $absolutePath = '/' . ltrim($cacheEntry->getPath(), '/');
378
+        $currentPath = rtrim($this->path, '/') . '/';
379
+
380
+        if (strpos($absolutePath, $currentPath) !== 0) {
381
+            return [];
382
+        }
383
+
384
+        return [$this->root->createNode(
385
+            $absolutePath, new \OC\Files\FileInfo(
386
+                $absolutePath,
387
+                $mount->getStorage(),
388
+                $cacheEntry->getPath(),
389
+                $cacheEntry,
390
+                $mount
391
+        ))];
392
+    }
393
+
394
+    public function getFreeSpace() {
395
+        return $this->view->free_space($this->path);
396
+    }
397
+
398
+    public function delete() {
399
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
400
+            $this->sendHooks(['preDelete']);
401
+            $fileInfo = $this->getFileInfo();
402
+            $this->view->rmdir($this->path);
403
+            $nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo);
404
+            $this->sendHooks(['postDelete'], [$nonExisting]);
405
+            $this->exists = false;
406
+        } else {
407
+            throw new NotPermittedException('No delete permission for path');
408
+        }
409
+    }
410
+
411
+    /**
412
+     * Add a suffix to the name in case the file exists
413
+     *
414
+     * @param string $name
415
+     * @return string
416
+     * @throws NotPermittedException
417
+     */
418
+    public function getNonExistingName($name) {
419
+        $uniqueName = \OC_Helper::buildNotExistingFileNameForView($this->getPath(), $name, $this->view);
420
+        return trim($this->getRelativePath($uniqueName), '/');
421
+    }
422
+
423
+    /**
424
+     * @param int $limit
425
+     * @param int $offset
426
+     * @return \OCP\Files\Node[]
427
+     */
428
+    public function getRecent($limit, $offset = 0) {
429
+        $mimetypeLoader = \OC::$server->getMimeTypeLoader();
430
+        $mounts = $this->root->getMountsIn($this->path);
431
+        $mounts[] = $this->getMountPoint();
432
+
433
+        $mounts = array_filter($mounts, function (IMountPoint $mount) {
434
+            return $mount->getStorage();
435
+        });
436
+        $storageIds = array_map(function (IMountPoint $mount) {
437
+            return $mount->getStorage()->getCache()->getNumericStorageId();
438
+        }, $mounts);
439
+        /** @var IMountPoint[] $mountMap */
440
+        $mountMap = array_combine($storageIds, $mounts);
441
+        $folderMimetype = $mimetypeLoader->getId(FileInfo::MIMETYPE_FOLDER);
442
+
443
+        /*
444 444
 		 * Construct an array of the storage id with their prefix path
445 445
 		 * This helps us to filter in the final query
446 446
 		 */
447
-		$filters = array_map(function (IMountPoint $mount) {
448
-			$storage = $mount->getStorage();
449
-
450
-			$storageId = $storage->getCache()->getNumericStorageId();
451
-			$prefix = '';
452
-
453
-			if ($storage->instanceOfStorage(Jail::class)) {
454
-				$prefix = $storage->getUnJailedPath('');
455
-			}
456
-
457
-			return [
458
-				'storageId' => $storageId,
459
-				'pathPrefix' => $prefix,
460
-			];
461
-		}, $mounts);
462
-
463
-		// Search in batches of 500 entries
464
-		$searchLimit = 500;
465
-		$results = [];
466
-		$searchResultCount = 0;
467
-		$count = 0;
468
-		do {
469
-			$searchResult = $this->recentSearch($searchLimit, $offset, $folderMimetype, $filters);
470
-
471
-			// Exit condition if there are no more results
472
-			if (count($searchResult) === 0) {
473
-				break;
474
-			}
475
-
476
-			$searchResultCount += count($searchResult);
477
-
478
-			$parseResult = $this->recentParse($searchResult, $mountMap, $mimetypeLoader);
479
-
480
-			foreach ($parseResult as $result) {
481
-				$results[] = $result;
482
-			}
483
-
484
-			$offset += $searchLimit;
485
-			$count++;
486
-		} while (count($results) < $limit && ($searchResultCount < (3 * $limit) || $count < 5));
487
-
488
-		return array_slice($results, 0, $limit);
489
-	}
490
-
491
-	private function recentSearch($limit, $offset, $folderMimetype, $filters) {
492
-		$dbconn = \OC::$server->getDatabaseConnection();
493
-		$builder = $dbconn->getQueryBuilder();
494
-		$query = $builder
495
-			->select('f.*')
496
-			->from('filecache', 'f');
497
-
498
-		/*
447
+        $filters = array_map(function (IMountPoint $mount) {
448
+            $storage = $mount->getStorage();
449
+
450
+            $storageId = $storage->getCache()->getNumericStorageId();
451
+            $prefix = '';
452
+
453
+            if ($storage->instanceOfStorage(Jail::class)) {
454
+                $prefix = $storage->getUnJailedPath('');
455
+            }
456
+
457
+            return [
458
+                'storageId' => $storageId,
459
+                'pathPrefix' => $prefix,
460
+            ];
461
+        }, $mounts);
462
+
463
+        // Search in batches of 500 entries
464
+        $searchLimit = 500;
465
+        $results = [];
466
+        $searchResultCount = 0;
467
+        $count = 0;
468
+        do {
469
+            $searchResult = $this->recentSearch($searchLimit, $offset, $folderMimetype, $filters);
470
+
471
+            // Exit condition if there are no more results
472
+            if (count($searchResult) === 0) {
473
+                break;
474
+            }
475
+
476
+            $searchResultCount += count($searchResult);
477
+
478
+            $parseResult = $this->recentParse($searchResult, $mountMap, $mimetypeLoader);
479
+
480
+            foreach ($parseResult as $result) {
481
+                $results[] = $result;
482
+            }
483
+
484
+            $offset += $searchLimit;
485
+            $count++;
486
+        } while (count($results) < $limit && ($searchResultCount < (3 * $limit) || $count < 5));
487
+
488
+        return array_slice($results, 0, $limit);
489
+    }
490
+
491
+    private function recentSearch($limit, $offset, $folderMimetype, $filters) {
492
+        $dbconn = \OC::$server->getDatabaseConnection();
493
+        $builder = $dbconn->getQueryBuilder();
494
+        $query = $builder
495
+            ->select('f.*')
496
+            ->from('filecache', 'f');
497
+
498
+        /*
499 499
 		 * Here is where we construct the filtering.
500 500
 		 * Note that this is expensive filtering as it is a lot of like queries.
501 501
 		 * However the alternative is we do this filtering and parsing later in php with the risk of looping endlessly
502 502
 		 */
503
-		$storageFilters = $builder->expr()->orX();
504
-		foreach ($filters as $filter) {
505
-			$storageFilter = $builder->expr()->andX(
506
-				$builder->expr()->eq('f.storage', $builder->createNamedParameter($filter['storageId']))
507
-			);
508
-
509
-			if ($filter['pathPrefix'] !== '') {
510
-				$storageFilter->add(
511
-					$builder->expr()->like('f.path', $builder->createNamedParameter($dbconn->escapeLikeParameter($filter['pathPrefix']) . '/%'))
512
-				);
513
-			}
514
-
515
-			$storageFilters->add($storageFilter);
516
-		}
517
-
518
-		$query->andWhere($storageFilters);
519
-
520
-		$query->andWhere($builder->expr()->orX(
521
-			// handle non empty folders separate
522
-				$builder->expr()->neq('f.mimetype', $builder->createNamedParameter($folderMimetype, IQueryBuilder::PARAM_INT)),
523
-				$builder->expr()->eq('f.size', new Literal(0))
524
-			))
525
-			->andWhere($builder->expr()->notLike('f.path', $builder->createNamedParameter('files_versions/%')))
526
-			->andWhere($builder->expr()->notLike('f.path', $builder->createNamedParameter('files_trashbin/%')))
527
-			->orderBy('f.mtime', 'DESC')
528
-			->setMaxResults($limit)
529
-			->setFirstResult($offset);
530
-
531
-		$result = $query->execute();
532
-		$rows = $result->fetchAll();
533
-		$result->closeCursor();
534
-
535
-		return $rows;
536
-	}
537
-
538
-	private function recentParse($result, $mountMap, $mimetypeLoader) {
539
-		$files = array_filter(array_map(function (array $entry) use ($mountMap, $mimetypeLoader) {
540
-			$mount = $mountMap[$entry['storage']];
541
-			$entry['internalPath'] = $entry['path'];
542
-			$entry['mimetype'] = $mimetypeLoader->getMimetypeById($entry['mimetype']);
543
-			$entry['mimepart'] = $mimetypeLoader->getMimetypeById($entry['mimepart']);
544
-			$path = $this->getAbsolutePath($mount, $entry['path']);
545
-			if (is_null($path)) {
546
-				return null;
547
-			}
548
-			$fileInfo = new \OC\Files\FileInfo($path, $mount->getStorage(), $entry['internalPath'], $entry, $mount);
549
-			return $this->root->createNode($fileInfo->getPath(), $fileInfo);
550
-		}, $result));
551
-
552
-		return array_values(array_filter($files, function (Node $node) {
553
-			$cacheEntry = $node->getMountPoint()->getStorage()->getCache()->get($node->getId());
554
-			if (!$cacheEntry) {
555
-				return false;
556
-			}
557
-			$relative = $this->getRelativePath($node->getPath());
558
-			return $relative !== null && $relative !== '/'
559
-				&& ($cacheEntry->getPermissions() & \OCP\Constants::PERMISSION_READ) === \OCP\Constants::PERMISSION_READ;
560
-		}));
561
-	}
562
-
563
-	private function getAbsolutePath(IMountPoint $mount, $path) {
564
-		$storage = $mount->getStorage();
565
-		if ($storage->instanceOfStorage('\OC\Files\Storage\Wrapper\Jail')) {
566
-			if ($storage->instanceOfStorage(SharedStorage::class)) {
567
-				$storage->getSourceStorage();
568
-			}
569
-			/** @var \OC\Files\Storage\Wrapper\Jail $storage */
570
-			$jailRoot = $storage->getUnjailedPath('');
571
-			$rootLength = strlen($jailRoot) + 1;
572
-			if ($path === $jailRoot) {
573
-				return $mount->getMountPoint();
574
-			} elseif (substr($path, 0, $rootLength) === $jailRoot . '/') {
575
-				return $mount->getMountPoint() . substr($path, $rootLength);
576
-			} else {
577
-				return null;
578
-			}
579
-		} else {
580
-			return $mount->getMountPoint() . $path;
581
-		}
582
-	}
503
+        $storageFilters = $builder->expr()->orX();
504
+        foreach ($filters as $filter) {
505
+            $storageFilter = $builder->expr()->andX(
506
+                $builder->expr()->eq('f.storage', $builder->createNamedParameter($filter['storageId']))
507
+            );
508
+
509
+            if ($filter['pathPrefix'] !== '') {
510
+                $storageFilter->add(
511
+                    $builder->expr()->like('f.path', $builder->createNamedParameter($dbconn->escapeLikeParameter($filter['pathPrefix']) . '/%'))
512
+                );
513
+            }
514
+
515
+            $storageFilters->add($storageFilter);
516
+        }
517
+
518
+        $query->andWhere($storageFilters);
519
+
520
+        $query->andWhere($builder->expr()->orX(
521
+            // handle non empty folders separate
522
+                $builder->expr()->neq('f.mimetype', $builder->createNamedParameter($folderMimetype, IQueryBuilder::PARAM_INT)),
523
+                $builder->expr()->eq('f.size', new Literal(0))
524
+            ))
525
+            ->andWhere($builder->expr()->notLike('f.path', $builder->createNamedParameter('files_versions/%')))
526
+            ->andWhere($builder->expr()->notLike('f.path', $builder->createNamedParameter('files_trashbin/%')))
527
+            ->orderBy('f.mtime', 'DESC')
528
+            ->setMaxResults($limit)
529
+            ->setFirstResult($offset);
530
+
531
+        $result = $query->execute();
532
+        $rows = $result->fetchAll();
533
+        $result->closeCursor();
534
+
535
+        return $rows;
536
+    }
537
+
538
+    private function recentParse($result, $mountMap, $mimetypeLoader) {
539
+        $files = array_filter(array_map(function (array $entry) use ($mountMap, $mimetypeLoader) {
540
+            $mount = $mountMap[$entry['storage']];
541
+            $entry['internalPath'] = $entry['path'];
542
+            $entry['mimetype'] = $mimetypeLoader->getMimetypeById($entry['mimetype']);
543
+            $entry['mimepart'] = $mimetypeLoader->getMimetypeById($entry['mimepart']);
544
+            $path = $this->getAbsolutePath($mount, $entry['path']);
545
+            if (is_null($path)) {
546
+                return null;
547
+            }
548
+            $fileInfo = new \OC\Files\FileInfo($path, $mount->getStorage(), $entry['internalPath'], $entry, $mount);
549
+            return $this->root->createNode($fileInfo->getPath(), $fileInfo);
550
+        }, $result));
551
+
552
+        return array_values(array_filter($files, function (Node $node) {
553
+            $cacheEntry = $node->getMountPoint()->getStorage()->getCache()->get($node->getId());
554
+            if (!$cacheEntry) {
555
+                return false;
556
+            }
557
+            $relative = $this->getRelativePath($node->getPath());
558
+            return $relative !== null && $relative !== '/'
559
+                && ($cacheEntry->getPermissions() & \OCP\Constants::PERMISSION_READ) === \OCP\Constants::PERMISSION_READ;
560
+        }));
561
+    }
562
+
563
+    private function getAbsolutePath(IMountPoint $mount, $path) {
564
+        $storage = $mount->getStorage();
565
+        if ($storage->instanceOfStorage('\OC\Files\Storage\Wrapper\Jail')) {
566
+            if ($storage->instanceOfStorage(SharedStorage::class)) {
567
+                $storage->getSourceStorage();
568
+            }
569
+            /** @var \OC\Files\Storage\Wrapper\Jail $storage */
570
+            $jailRoot = $storage->getUnjailedPath('');
571
+            $rootLength = strlen($jailRoot) + 1;
572
+            if ($path === $jailRoot) {
573
+                return $mount->getMountPoint();
574
+            } elseif (substr($path, 0, $rootLength) === $jailRoot . '/') {
575
+                return $mount->getMountPoint() . substr($path, $rootLength);
576
+            } else {
577
+                return null;
578
+            }
579
+        } else {
580
+            return $mount->getMountPoint() . $path;
581
+        }
582
+    }
583 583
 }
Please login to merge, or discard this patch.
lib/private/Files/Node/Node.php 2 patches
Indentation   +424 added lines, -424 removed lines patch added patch discarded remove patch
@@ -41,428 +41,428 @@
 block discarded – undo
41 41
 
42 42
 // FIXME: this class really should be abstract
43 43
 class Node implements \OCP\Files\Node {
44
-	/**
45
-	 * @var \OC\Files\View $view
46
-	 */
47
-	protected $view;
48
-
49
-	/**
50
-	 * @var \OC\Files\Node\Root $root
51
-	 */
52
-	protected $root;
53
-
54
-	/**
55
-	 * @var string $path
56
-	 */
57
-	protected $path;
58
-
59
-	/**
60
-	 * @var \OCP\Files\FileInfo
61
-	 */
62
-	protected $fileInfo;
63
-
64
-	/**
65
-	 * @param \OC\Files\View $view
66
-	 * @param \OCP\Files\IRootFolder $root
67
-	 * @param string $path
68
-	 * @param FileInfo $fileInfo
69
-	 */
70
-	public function __construct($root, $view, $path, $fileInfo = null) {
71
-		$this->view = $view;
72
-		$this->root = $root;
73
-		$this->path = $path;
74
-		$this->fileInfo = $fileInfo;
75
-	}
76
-
77
-	/**
78
-	 * Creates a Node of the same type that represents a non-existing path
79
-	 *
80
-	 * @param string $path path
81
-	 * @return string non-existing node class
82
-	 * @throws \Exception
83
-	 */
84
-	protected function createNonExistingNode($path) {
85
-		throw new \Exception('Must be implemented by subclasses');
86
-	}
87
-
88
-	/**
89
-	 * Returns the matching file info
90
-	 *
91
-	 * @return FileInfo
92
-	 * @throws InvalidPathException
93
-	 * @throws NotFoundException
94
-	 */
95
-	public function getFileInfo() {
96
-		if (!Filesystem::isValidPath($this->path)) {
97
-			throw new InvalidPathException();
98
-		}
99
-		if (!$this->fileInfo) {
100
-			$fileInfo = $this->view->getFileInfo($this->path);
101
-			if ($fileInfo instanceof FileInfo) {
102
-				$this->fileInfo = $fileInfo;
103
-			} else {
104
-				throw new NotFoundException();
105
-			}
106
-		}
107
-		return $this->fileInfo;
108
-	}
109
-
110
-	/**
111
-	 * @param string[] $hooks
112
-	 */
113
-	protected function sendHooks($hooks, array $args = null) {
114
-		$args = !empty($args) ? $args : [$this];
115
-		$dispatcher = \OC::$server->getEventDispatcher();
116
-		foreach ($hooks as $hook) {
117
-			$this->root->emit('\OC\Files', $hook, $args);
118
-			$dispatcher->dispatch('\OCP\Files::' . $hook, new GenericEvent($args));
119
-		}
120
-	}
121
-
122
-	/**
123
-	 * @param int $permissions
124
-	 * @return bool
125
-	 * @throws InvalidPathException
126
-	 * @throws NotFoundException
127
-	 */
128
-	protected function checkPermissions($permissions) {
129
-		return ($this->getPermissions() & $permissions) === $permissions;
130
-	}
131
-
132
-	public function delete() {
133
-	}
134
-
135
-	/**
136
-	 * @param int $mtime
137
-	 * @throws InvalidPathException
138
-	 * @throws NotFoundException
139
-	 * @throws NotPermittedException
140
-	 */
141
-	public function touch($mtime = null) {
142
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) {
143
-			$this->sendHooks(['preTouch']);
144
-			$this->view->touch($this->path, $mtime);
145
-			$this->sendHooks(['postTouch']);
146
-			if ($this->fileInfo) {
147
-				if (is_null($mtime)) {
148
-					$mtime = time();
149
-				}
150
-				$this->fileInfo['mtime'] = $mtime;
151
-			}
152
-		} else {
153
-			throw new NotPermittedException();
154
-		}
155
-	}
156
-
157
-	/**
158
-	 * @return \OC\Files\Storage\Storage
159
-	 * @throws \OCP\Files\NotFoundException
160
-	 */
161
-	public function getStorage() {
162
-		[$storage,] = $this->view->resolvePath($this->path);
163
-		return $storage;
164
-	}
165
-
166
-	/**
167
-	 * @return string
168
-	 */
169
-	public function getPath() {
170
-		return $this->path;
171
-	}
172
-
173
-	/**
174
-	 * @return string
175
-	 */
176
-	public function getInternalPath() {
177
-		[, $internalPath] = $this->view->resolvePath($this->path);
178
-		return $internalPath;
179
-	}
180
-
181
-	/**
182
-	 * @return int
183
-	 * @throws InvalidPathException
184
-	 * @throws NotFoundException
185
-	 */
186
-	public function getId() {
187
-		return $this->getFileInfo()->getId();
188
-	}
189
-
190
-	/**
191
-	 * @return array
192
-	 */
193
-	public function stat() {
194
-		return $this->view->stat($this->path);
195
-	}
196
-
197
-	/**
198
-	 * @return int
199
-	 * @throws InvalidPathException
200
-	 * @throws NotFoundException
201
-	 */
202
-	public function getMTime() {
203
-		return $this->getFileInfo()->getMTime();
204
-	}
205
-
206
-	/**
207
-	 * @param bool $includeMounts
208
-	 * @return int
209
-	 * @throws InvalidPathException
210
-	 * @throws NotFoundException
211
-	 */
212
-	public function getSize($includeMounts = true) {
213
-		return $this->getFileInfo()->getSize($includeMounts);
214
-	}
215
-
216
-	/**
217
-	 * @return string
218
-	 * @throws InvalidPathException
219
-	 * @throws NotFoundException
220
-	 */
221
-	public function getEtag() {
222
-		return $this->getFileInfo()->getEtag();
223
-	}
224
-
225
-	/**
226
-	 * @return int
227
-	 * @throws InvalidPathException
228
-	 * @throws NotFoundException
229
-	 */
230
-	public function getPermissions() {
231
-		return $this->getFileInfo()->getPermissions();
232
-	}
233
-
234
-	/**
235
-	 * @return bool
236
-	 * @throws InvalidPathException
237
-	 * @throws NotFoundException
238
-	 */
239
-	public function isReadable() {
240
-		return $this->getFileInfo()->isReadable();
241
-	}
242
-
243
-	/**
244
-	 * @return bool
245
-	 * @throws InvalidPathException
246
-	 * @throws NotFoundException
247
-	 */
248
-	public function isUpdateable() {
249
-		return $this->getFileInfo()->isUpdateable();
250
-	}
251
-
252
-	/**
253
-	 * @return bool
254
-	 * @throws InvalidPathException
255
-	 * @throws NotFoundException
256
-	 */
257
-	public function isDeletable() {
258
-		return $this->getFileInfo()->isDeletable();
259
-	}
260
-
261
-	/**
262
-	 * @return bool
263
-	 * @throws InvalidPathException
264
-	 * @throws NotFoundException
265
-	 */
266
-	public function isShareable() {
267
-		return $this->getFileInfo()->isShareable();
268
-	}
269
-
270
-	/**
271
-	 * @return bool
272
-	 * @throws InvalidPathException
273
-	 * @throws NotFoundException
274
-	 */
275
-	public function isCreatable() {
276
-		return $this->getFileInfo()->isCreatable();
277
-	}
278
-
279
-	/**
280
-	 * @return Node
281
-	 */
282
-	public function getParent() {
283
-		$newPath = dirname($this->path);
284
-		if ($newPath === '' || $newPath === '.' || $newPath === '/') {
285
-			return $this->root;
286
-		}
287
-		return $this->root->get($newPath);
288
-	}
289
-
290
-	/**
291
-	 * @return string
292
-	 */
293
-	public function getName() {
294
-		return basename($this->path);
295
-	}
296
-
297
-	/**
298
-	 * @param string $path
299
-	 * @return string
300
-	 */
301
-	protected function normalizePath($path) {
302
-		if ($path === '' or $path === '/') {
303
-			return '/';
304
-		}
305
-		//no windows style slashes
306
-		$path = str_replace('\\', '/', $path);
307
-		//add leading slash
308
-		if ($path[0] !== '/') {
309
-			$path = '/' . $path;
310
-		}
311
-		//remove duplicate slashes
312
-		while (strpos($path, '//') !== false) {
313
-			$path = str_replace('//', '/', $path);
314
-		}
315
-		//remove trailing slash
316
-		$path = rtrim($path, '/');
317
-
318
-		return $path;
319
-	}
320
-
321
-	/**
322
-	 * check if the requested path is valid
323
-	 *
324
-	 * @param string $path
325
-	 * @return bool
326
-	 */
327
-	public function isValidPath($path) {
328
-		if (!$path || $path[0] !== '/') {
329
-			$path = '/' . $path;
330
-		}
331
-		if (strstr($path, '/../') || strrchr($path, '/') === '/..') {
332
-			return false;
333
-		}
334
-		return true;
335
-	}
336
-
337
-	public function isMounted() {
338
-		return $this->getFileInfo()->isMounted();
339
-	}
340
-
341
-	public function isShared() {
342
-		return $this->getFileInfo()->isShared();
343
-	}
344
-
345
-	public function getMimeType() {
346
-		return $this->getFileInfo()->getMimetype();
347
-	}
348
-
349
-	public function getMimePart() {
350
-		return $this->getFileInfo()->getMimePart();
351
-	}
352
-
353
-	public function getType() {
354
-		return $this->getFileInfo()->getType();
355
-	}
356
-
357
-	public function isEncrypted() {
358
-		return $this->getFileInfo()->isEncrypted();
359
-	}
360
-
361
-	public function getMountPoint() {
362
-		return $this->getFileInfo()->getMountPoint();
363
-	}
364
-
365
-	public function getOwner() {
366
-		return $this->getFileInfo()->getOwner();
367
-	}
368
-
369
-	public function getChecksum() {
370
-	}
371
-
372
-	public function getExtension(): string {
373
-		return $this->getFileInfo()->getExtension();
374
-	}
375
-
376
-	/**
377
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
378
-	 * @throws LockedException
379
-	 */
380
-	public function lock($type) {
381
-		$this->view->lockFile($this->path, $type);
382
-	}
383
-
384
-	/**
385
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
386
-	 * @throws LockedException
387
-	 */
388
-	public function changeLock($type) {
389
-		$this->view->changeLock($this->path, $type);
390
-	}
391
-
392
-	/**
393
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
394
-	 * @throws LockedException
395
-	 */
396
-	public function unlock($type) {
397
-		$this->view->unlockFile($this->path, $type);
398
-	}
399
-
400
-	/**
401
-	 * @param string $targetPath
402
-	 * @return \OC\Files\Node\Node
403
-	 * @throws InvalidPathException
404
-	 * @throws NotFoundException
405
-	 * @throws NotPermittedException if copy not allowed or failed
406
-	 */
407
-	public function copy($targetPath) {
408
-		$targetPath = $this->normalizePath($targetPath);
409
-		$parent = $this->root->get(dirname($targetPath));
410
-		if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) {
411
-			$nonExisting = $this->createNonExistingNode($targetPath);
412
-			$this->sendHooks(['preCopy'], [$this, $nonExisting]);
413
-			$this->sendHooks(['preWrite'], [$nonExisting]);
414
-			if (!$this->view->copy($this->path, $targetPath)) {
415
-				throw new NotPermittedException('Could not copy ' . $this->path . ' to ' . $targetPath);
416
-			}
417
-			$targetNode = $this->root->get($targetPath);
418
-			$this->sendHooks(['postCopy'], [$this, $targetNode]);
419
-			$this->sendHooks(['postWrite'], [$targetNode]);
420
-			return $targetNode;
421
-		} else {
422
-			throw new NotPermittedException('No permission to copy to path ' . $targetPath);
423
-		}
424
-	}
425
-
426
-	/**
427
-	 * @param string $targetPath
428
-	 * @return \OC\Files\Node\Node
429
-	 * @throws InvalidPathException
430
-	 * @throws NotFoundException
431
-	 * @throws NotPermittedException if move not allowed or failed
432
-	 * @throws LockedException
433
-	 */
434
-	public function move($targetPath) {
435
-		$targetPath = $this->normalizePath($targetPath);
436
-		$parent = $this->root->get(dirname($targetPath));
437
-		if (
438
-			$parent instanceof Folder and
439
-			$this->isValidPath($targetPath) and
440
-			(
441
-				$parent->isCreatable() ||
442
-				($parent->getInternalPath() === '' && $parent->getMountPoint() instanceof MoveableMount)
443
-			)
444
-		) {
445
-			$nonExisting = $this->createNonExistingNode($targetPath);
446
-			$this->sendHooks(['preRename'], [$this, $nonExisting]);
447
-			$this->sendHooks(['preWrite'], [$nonExisting]);
448
-			if (!$this->view->rename($this->path, $targetPath)) {
449
-				throw new NotPermittedException('Could not move ' . $this->path . ' to ' . $targetPath);
450
-			}
451
-			$targetNode = $this->root->get($targetPath);
452
-			$this->sendHooks(['postRename'], [$this, $targetNode]);
453
-			$this->sendHooks(['postWrite'], [$targetNode]);
454
-			$this->path = $targetPath;
455
-			return $targetNode;
456
-		} else {
457
-			throw new NotPermittedException('No permission to move to path ' . $targetPath);
458
-		}
459
-	}
460
-
461
-	public function getCreationTime(): int {
462
-		return $this->getFileInfo()->getCreationTime();
463
-	}
464
-
465
-	public function getUploadTime(): int {
466
-		return $this->getFileInfo()->getUploadTime();
467
-	}
44
+    /**
45
+     * @var \OC\Files\View $view
46
+     */
47
+    protected $view;
48
+
49
+    /**
50
+     * @var \OC\Files\Node\Root $root
51
+     */
52
+    protected $root;
53
+
54
+    /**
55
+     * @var string $path
56
+     */
57
+    protected $path;
58
+
59
+    /**
60
+     * @var \OCP\Files\FileInfo
61
+     */
62
+    protected $fileInfo;
63
+
64
+    /**
65
+     * @param \OC\Files\View $view
66
+     * @param \OCP\Files\IRootFolder $root
67
+     * @param string $path
68
+     * @param FileInfo $fileInfo
69
+     */
70
+    public function __construct($root, $view, $path, $fileInfo = null) {
71
+        $this->view = $view;
72
+        $this->root = $root;
73
+        $this->path = $path;
74
+        $this->fileInfo = $fileInfo;
75
+    }
76
+
77
+    /**
78
+     * Creates a Node of the same type that represents a non-existing path
79
+     *
80
+     * @param string $path path
81
+     * @return string non-existing node class
82
+     * @throws \Exception
83
+     */
84
+    protected function createNonExistingNode($path) {
85
+        throw new \Exception('Must be implemented by subclasses');
86
+    }
87
+
88
+    /**
89
+     * Returns the matching file info
90
+     *
91
+     * @return FileInfo
92
+     * @throws InvalidPathException
93
+     * @throws NotFoundException
94
+     */
95
+    public function getFileInfo() {
96
+        if (!Filesystem::isValidPath($this->path)) {
97
+            throw new InvalidPathException();
98
+        }
99
+        if (!$this->fileInfo) {
100
+            $fileInfo = $this->view->getFileInfo($this->path);
101
+            if ($fileInfo instanceof FileInfo) {
102
+                $this->fileInfo = $fileInfo;
103
+            } else {
104
+                throw new NotFoundException();
105
+            }
106
+        }
107
+        return $this->fileInfo;
108
+    }
109
+
110
+    /**
111
+     * @param string[] $hooks
112
+     */
113
+    protected function sendHooks($hooks, array $args = null) {
114
+        $args = !empty($args) ? $args : [$this];
115
+        $dispatcher = \OC::$server->getEventDispatcher();
116
+        foreach ($hooks as $hook) {
117
+            $this->root->emit('\OC\Files', $hook, $args);
118
+            $dispatcher->dispatch('\OCP\Files::' . $hook, new GenericEvent($args));
119
+        }
120
+    }
121
+
122
+    /**
123
+     * @param int $permissions
124
+     * @return bool
125
+     * @throws InvalidPathException
126
+     * @throws NotFoundException
127
+     */
128
+    protected function checkPermissions($permissions) {
129
+        return ($this->getPermissions() & $permissions) === $permissions;
130
+    }
131
+
132
+    public function delete() {
133
+    }
134
+
135
+    /**
136
+     * @param int $mtime
137
+     * @throws InvalidPathException
138
+     * @throws NotFoundException
139
+     * @throws NotPermittedException
140
+     */
141
+    public function touch($mtime = null) {
142
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) {
143
+            $this->sendHooks(['preTouch']);
144
+            $this->view->touch($this->path, $mtime);
145
+            $this->sendHooks(['postTouch']);
146
+            if ($this->fileInfo) {
147
+                if (is_null($mtime)) {
148
+                    $mtime = time();
149
+                }
150
+                $this->fileInfo['mtime'] = $mtime;
151
+            }
152
+        } else {
153
+            throw new NotPermittedException();
154
+        }
155
+    }
156
+
157
+    /**
158
+     * @return \OC\Files\Storage\Storage
159
+     * @throws \OCP\Files\NotFoundException
160
+     */
161
+    public function getStorage() {
162
+        [$storage,] = $this->view->resolvePath($this->path);
163
+        return $storage;
164
+    }
165
+
166
+    /**
167
+     * @return string
168
+     */
169
+    public function getPath() {
170
+        return $this->path;
171
+    }
172
+
173
+    /**
174
+     * @return string
175
+     */
176
+    public function getInternalPath() {
177
+        [, $internalPath] = $this->view->resolvePath($this->path);
178
+        return $internalPath;
179
+    }
180
+
181
+    /**
182
+     * @return int
183
+     * @throws InvalidPathException
184
+     * @throws NotFoundException
185
+     */
186
+    public function getId() {
187
+        return $this->getFileInfo()->getId();
188
+    }
189
+
190
+    /**
191
+     * @return array
192
+     */
193
+    public function stat() {
194
+        return $this->view->stat($this->path);
195
+    }
196
+
197
+    /**
198
+     * @return int
199
+     * @throws InvalidPathException
200
+     * @throws NotFoundException
201
+     */
202
+    public function getMTime() {
203
+        return $this->getFileInfo()->getMTime();
204
+    }
205
+
206
+    /**
207
+     * @param bool $includeMounts
208
+     * @return int
209
+     * @throws InvalidPathException
210
+     * @throws NotFoundException
211
+     */
212
+    public function getSize($includeMounts = true) {
213
+        return $this->getFileInfo()->getSize($includeMounts);
214
+    }
215
+
216
+    /**
217
+     * @return string
218
+     * @throws InvalidPathException
219
+     * @throws NotFoundException
220
+     */
221
+    public function getEtag() {
222
+        return $this->getFileInfo()->getEtag();
223
+    }
224
+
225
+    /**
226
+     * @return int
227
+     * @throws InvalidPathException
228
+     * @throws NotFoundException
229
+     */
230
+    public function getPermissions() {
231
+        return $this->getFileInfo()->getPermissions();
232
+    }
233
+
234
+    /**
235
+     * @return bool
236
+     * @throws InvalidPathException
237
+     * @throws NotFoundException
238
+     */
239
+    public function isReadable() {
240
+        return $this->getFileInfo()->isReadable();
241
+    }
242
+
243
+    /**
244
+     * @return bool
245
+     * @throws InvalidPathException
246
+     * @throws NotFoundException
247
+     */
248
+    public function isUpdateable() {
249
+        return $this->getFileInfo()->isUpdateable();
250
+    }
251
+
252
+    /**
253
+     * @return bool
254
+     * @throws InvalidPathException
255
+     * @throws NotFoundException
256
+     */
257
+    public function isDeletable() {
258
+        return $this->getFileInfo()->isDeletable();
259
+    }
260
+
261
+    /**
262
+     * @return bool
263
+     * @throws InvalidPathException
264
+     * @throws NotFoundException
265
+     */
266
+    public function isShareable() {
267
+        return $this->getFileInfo()->isShareable();
268
+    }
269
+
270
+    /**
271
+     * @return bool
272
+     * @throws InvalidPathException
273
+     * @throws NotFoundException
274
+     */
275
+    public function isCreatable() {
276
+        return $this->getFileInfo()->isCreatable();
277
+    }
278
+
279
+    /**
280
+     * @return Node
281
+     */
282
+    public function getParent() {
283
+        $newPath = dirname($this->path);
284
+        if ($newPath === '' || $newPath === '.' || $newPath === '/') {
285
+            return $this->root;
286
+        }
287
+        return $this->root->get($newPath);
288
+    }
289
+
290
+    /**
291
+     * @return string
292
+     */
293
+    public function getName() {
294
+        return basename($this->path);
295
+    }
296
+
297
+    /**
298
+     * @param string $path
299
+     * @return string
300
+     */
301
+    protected function normalizePath($path) {
302
+        if ($path === '' or $path === '/') {
303
+            return '/';
304
+        }
305
+        //no windows style slashes
306
+        $path = str_replace('\\', '/', $path);
307
+        //add leading slash
308
+        if ($path[0] !== '/') {
309
+            $path = '/' . $path;
310
+        }
311
+        //remove duplicate slashes
312
+        while (strpos($path, '//') !== false) {
313
+            $path = str_replace('//', '/', $path);
314
+        }
315
+        //remove trailing slash
316
+        $path = rtrim($path, '/');
317
+
318
+        return $path;
319
+    }
320
+
321
+    /**
322
+     * check if the requested path is valid
323
+     *
324
+     * @param string $path
325
+     * @return bool
326
+     */
327
+    public function isValidPath($path) {
328
+        if (!$path || $path[0] !== '/') {
329
+            $path = '/' . $path;
330
+        }
331
+        if (strstr($path, '/../') || strrchr($path, '/') === '/..') {
332
+            return false;
333
+        }
334
+        return true;
335
+    }
336
+
337
+    public function isMounted() {
338
+        return $this->getFileInfo()->isMounted();
339
+    }
340
+
341
+    public function isShared() {
342
+        return $this->getFileInfo()->isShared();
343
+    }
344
+
345
+    public function getMimeType() {
346
+        return $this->getFileInfo()->getMimetype();
347
+    }
348
+
349
+    public function getMimePart() {
350
+        return $this->getFileInfo()->getMimePart();
351
+    }
352
+
353
+    public function getType() {
354
+        return $this->getFileInfo()->getType();
355
+    }
356
+
357
+    public function isEncrypted() {
358
+        return $this->getFileInfo()->isEncrypted();
359
+    }
360
+
361
+    public function getMountPoint() {
362
+        return $this->getFileInfo()->getMountPoint();
363
+    }
364
+
365
+    public function getOwner() {
366
+        return $this->getFileInfo()->getOwner();
367
+    }
368
+
369
+    public function getChecksum() {
370
+    }
371
+
372
+    public function getExtension(): string {
373
+        return $this->getFileInfo()->getExtension();
374
+    }
375
+
376
+    /**
377
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
378
+     * @throws LockedException
379
+     */
380
+    public function lock($type) {
381
+        $this->view->lockFile($this->path, $type);
382
+    }
383
+
384
+    /**
385
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
386
+     * @throws LockedException
387
+     */
388
+    public function changeLock($type) {
389
+        $this->view->changeLock($this->path, $type);
390
+    }
391
+
392
+    /**
393
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
394
+     * @throws LockedException
395
+     */
396
+    public function unlock($type) {
397
+        $this->view->unlockFile($this->path, $type);
398
+    }
399
+
400
+    /**
401
+     * @param string $targetPath
402
+     * @return \OC\Files\Node\Node
403
+     * @throws InvalidPathException
404
+     * @throws NotFoundException
405
+     * @throws NotPermittedException if copy not allowed or failed
406
+     */
407
+    public function copy($targetPath) {
408
+        $targetPath = $this->normalizePath($targetPath);
409
+        $parent = $this->root->get(dirname($targetPath));
410
+        if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) {
411
+            $nonExisting = $this->createNonExistingNode($targetPath);
412
+            $this->sendHooks(['preCopy'], [$this, $nonExisting]);
413
+            $this->sendHooks(['preWrite'], [$nonExisting]);
414
+            if (!$this->view->copy($this->path, $targetPath)) {
415
+                throw new NotPermittedException('Could not copy ' . $this->path . ' to ' . $targetPath);
416
+            }
417
+            $targetNode = $this->root->get($targetPath);
418
+            $this->sendHooks(['postCopy'], [$this, $targetNode]);
419
+            $this->sendHooks(['postWrite'], [$targetNode]);
420
+            return $targetNode;
421
+        } else {
422
+            throw new NotPermittedException('No permission to copy to path ' . $targetPath);
423
+        }
424
+    }
425
+
426
+    /**
427
+     * @param string $targetPath
428
+     * @return \OC\Files\Node\Node
429
+     * @throws InvalidPathException
430
+     * @throws NotFoundException
431
+     * @throws NotPermittedException if move not allowed or failed
432
+     * @throws LockedException
433
+     */
434
+    public function move($targetPath) {
435
+        $targetPath = $this->normalizePath($targetPath);
436
+        $parent = $this->root->get(dirname($targetPath));
437
+        if (
438
+            $parent instanceof Folder and
439
+            $this->isValidPath($targetPath) and
440
+            (
441
+                $parent->isCreatable() ||
442
+                ($parent->getInternalPath() === '' && $parent->getMountPoint() instanceof MoveableMount)
443
+            )
444
+        ) {
445
+            $nonExisting = $this->createNonExistingNode($targetPath);
446
+            $this->sendHooks(['preRename'], [$this, $nonExisting]);
447
+            $this->sendHooks(['preWrite'], [$nonExisting]);
448
+            if (!$this->view->rename($this->path, $targetPath)) {
449
+                throw new NotPermittedException('Could not move ' . $this->path . ' to ' . $targetPath);
450
+            }
451
+            $targetNode = $this->root->get($targetPath);
452
+            $this->sendHooks(['postRename'], [$this, $targetNode]);
453
+            $this->sendHooks(['postWrite'], [$targetNode]);
454
+            $this->path = $targetPath;
455
+            return $targetNode;
456
+        } else {
457
+            throw new NotPermittedException('No permission to move to path ' . $targetPath);
458
+        }
459
+    }
460
+
461
+    public function getCreationTime(): int {
462
+        return $this->getFileInfo()->getCreationTime();
463
+    }
464
+
465
+    public function getUploadTime(): int {
466
+        return $this->getFileInfo()->getUploadTime();
467
+    }
468 468
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -115,7 +115,7 @@  discard block
 block discarded – undo
115 115
 		$dispatcher = \OC::$server->getEventDispatcher();
116 116
 		foreach ($hooks as $hook) {
117 117
 			$this->root->emit('\OC\Files', $hook, $args);
118
-			$dispatcher->dispatch('\OCP\Files::' . $hook, new GenericEvent($args));
118
+			$dispatcher->dispatch('\OCP\Files::'.$hook, new GenericEvent($args));
119 119
 		}
120 120
 	}
121 121
 
@@ -159,7 +159,7 @@  discard block
 block discarded – undo
159 159
 	 * @throws \OCP\Files\NotFoundException
160 160
 	 */
161 161
 	public function getStorage() {
162
-		[$storage,] = $this->view->resolvePath($this->path);
162
+		[$storage, ] = $this->view->resolvePath($this->path);
163 163
 		return $storage;
164 164
 	}
165 165
 
@@ -306,7 +306,7 @@  discard block
 block discarded – undo
306 306
 		$path = str_replace('\\', '/', $path);
307 307
 		//add leading slash
308 308
 		if ($path[0] !== '/') {
309
-			$path = '/' . $path;
309
+			$path = '/'.$path;
310 310
 		}
311 311
 		//remove duplicate slashes
312 312
 		while (strpos($path, '//') !== false) {
@@ -326,7 +326,7 @@  discard block
 block discarded – undo
326 326
 	 */
327 327
 	public function isValidPath($path) {
328 328
 		if (!$path || $path[0] !== '/') {
329
-			$path = '/' . $path;
329
+			$path = '/'.$path;
330 330
 		}
331 331
 		if (strstr($path, '/../') || strrchr($path, '/') === '/..') {
332 332
 			return false;
@@ -412,14 +412,14 @@  discard block
 block discarded – undo
412 412
 			$this->sendHooks(['preCopy'], [$this, $nonExisting]);
413 413
 			$this->sendHooks(['preWrite'], [$nonExisting]);
414 414
 			if (!$this->view->copy($this->path, $targetPath)) {
415
-				throw new NotPermittedException('Could not copy ' . $this->path . ' to ' . $targetPath);
415
+				throw new NotPermittedException('Could not copy '.$this->path.' to '.$targetPath);
416 416
 			}
417 417
 			$targetNode = $this->root->get($targetPath);
418 418
 			$this->sendHooks(['postCopy'], [$this, $targetNode]);
419 419
 			$this->sendHooks(['postWrite'], [$targetNode]);
420 420
 			return $targetNode;
421 421
 		} else {
422
-			throw new NotPermittedException('No permission to copy to path ' . $targetPath);
422
+			throw new NotPermittedException('No permission to copy to path '.$targetPath);
423 423
 		}
424 424
 	}
425 425
 
@@ -446,7 +446,7 @@  discard block
 block discarded – undo
446 446
 			$this->sendHooks(['preRename'], [$this, $nonExisting]);
447 447
 			$this->sendHooks(['preWrite'], [$nonExisting]);
448 448
 			if (!$this->view->rename($this->path, $targetPath)) {
449
-				throw new NotPermittedException('Could not move ' . $this->path . ' to ' . $targetPath);
449
+				throw new NotPermittedException('Could not move '.$this->path.' to '.$targetPath);
450 450
 			}
451 451
 			$targetNode = $this->root->get($targetPath);
452 452
 			$this->sendHooks(['postRename'], [$this, $targetNode]);
@@ -454,7 +454,7 @@  discard block
 block discarded – undo
454 454
 			$this->path = $targetPath;
455 455
 			return $targetNode;
456 456
 		} else {
457
-			throw new NotPermittedException('No permission to move to path ' . $targetPath);
457
+			throw new NotPermittedException('No permission to move to path '.$targetPath);
458 458
 		}
459 459
 	}
460 460
 
Please login to merge, or discard this patch.
lib/private/Setup/AbstractDatabase.php 1 patch
Indentation   +104 added lines, -104 removed lines patch added patch discarded remove patch
@@ -39,118 +39,118 @@
 block discarded – undo
39 39
 
40 40
 abstract class AbstractDatabase {
41 41
 
42
-	/** @var IL10N */
43
-	protected $trans;
44
-	/** @var string */
45
-	protected $dbUser;
46
-	/** @var string */
47
-	protected $dbPassword;
48
-	/** @var string */
49
-	protected $dbName;
50
-	/** @var string */
51
-	protected $dbHost;
52
-	/** @var string */
53
-	protected $dbPort;
54
-	/** @var string */
55
-	protected $tablePrefix;
56
-	/** @var SystemConfig */
57
-	protected $config;
58
-	/** @var ILogger */
59
-	protected $logger;
60
-	/** @var ISecureRandom */
61
-	protected $random;
42
+    /** @var IL10N */
43
+    protected $trans;
44
+    /** @var string */
45
+    protected $dbUser;
46
+    /** @var string */
47
+    protected $dbPassword;
48
+    /** @var string */
49
+    protected $dbName;
50
+    /** @var string */
51
+    protected $dbHost;
52
+    /** @var string */
53
+    protected $dbPort;
54
+    /** @var string */
55
+    protected $tablePrefix;
56
+    /** @var SystemConfig */
57
+    protected $config;
58
+    /** @var ILogger */
59
+    protected $logger;
60
+    /** @var ISecureRandom */
61
+    protected $random;
62 62
 
63
-	public function __construct(IL10N $trans, SystemConfig $config, ILogger $logger, ISecureRandom $random) {
64
-		$this->trans = $trans;
65
-		$this->config = $config;
66
-		$this->logger = $logger;
67
-		$this->random = $random;
68
-	}
63
+    public function __construct(IL10N $trans, SystemConfig $config, ILogger $logger, ISecureRandom $random) {
64
+        $this->trans = $trans;
65
+        $this->config = $config;
66
+        $this->logger = $logger;
67
+        $this->random = $random;
68
+    }
69 69
 
70
-	public function validate($config) {
71
-		$errors = [];
72
-		if (empty($config['dbuser']) && empty($config['dbname'])) {
73
-			$errors[] = $this->trans->t("%s enter the database username and name.", [$this->dbprettyname]);
74
-		} elseif (empty($config['dbuser'])) {
75
-			$errors[] = $this->trans->t("%s enter the database username.", [$this->dbprettyname]);
76
-		} elseif (empty($config['dbname'])) {
77
-			$errors[] = $this->trans->t("%s enter the database name.", [$this->dbprettyname]);
78
-		}
79
-		if (substr_count($config['dbname'], '.') >= 1) {
80
-			$errors[] = $this->trans->t("%s you may not use dots in the database name", [$this->dbprettyname]);
81
-		}
82
-		return $errors;
83
-	}
70
+    public function validate($config) {
71
+        $errors = [];
72
+        if (empty($config['dbuser']) && empty($config['dbname'])) {
73
+            $errors[] = $this->trans->t("%s enter the database username and name.", [$this->dbprettyname]);
74
+        } elseif (empty($config['dbuser'])) {
75
+            $errors[] = $this->trans->t("%s enter the database username.", [$this->dbprettyname]);
76
+        } elseif (empty($config['dbname'])) {
77
+            $errors[] = $this->trans->t("%s enter the database name.", [$this->dbprettyname]);
78
+        }
79
+        if (substr_count($config['dbname'], '.') >= 1) {
80
+            $errors[] = $this->trans->t("%s you may not use dots in the database name", [$this->dbprettyname]);
81
+        }
82
+        return $errors;
83
+    }
84 84
 
85
-	public function initialize($config) {
86
-		$dbUser = $config['dbuser'];
87
-		$dbPass = $config['dbpass'];
88
-		$dbName = $config['dbname'];
89
-		$dbHost = !empty($config['dbhost']) ? $config['dbhost'] : 'localhost';
90
-		$dbPort = !empty($config['dbport']) ? $config['dbport'] : '';
91
-		$dbTablePrefix = isset($config['dbtableprefix']) ? $config['dbtableprefix'] : 'oc_';
85
+    public function initialize($config) {
86
+        $dbUser = $config['dbuser'];
87
+        $dbPass = $config['dbpass'];
88
+        $dbName = $config['dbname'];
89
+        $dbHost = !empty($config['dbhost']) ? $config['dbhost'] : 'localhost';
90
+        $dbPort = !empty($config['dbport']) ? $config['dbport'] : '';
91
+        $dbTablePrefix = isset($config['dbtableprefix']) ? $config['dbtableprefix'] : 'oc_';
92 92
 
93
-		$this->config->setValues([
94
-			'dbname' => $dbName,
95
-			'dbhost' => $dbHost,
96
-			'dbport' => $dbPort,
97
-			'dbtableprefix' => $dbTablePrefix,
98
-		]);
93
+        $this->config->setValues([
94
+            'dbname' => $dbName,
95
+            'dbhost' => $dbHost,
96
+            'dbport' => $dbPort,
97
+            'dbtableprefix' => $dbTablePrefix,
98
+        ]);
99 99
 
100
-		$this->dbUser = $dbUser;
101
-		$this->dbPassword = $dbPass;
102
-		$this->dbName = $dbName;
103
-		$this->dbHost = $dbHost;
104
-		$this->dbPort = $dbPort;
105
-		$this->tablePrefix = $dbTablePrefix;
106
-	}
100
+        $this->dbUser = $dbUser;
101
+        $this->dbPassword = $dbPass;
102
+        $this->dbName = $dbName;
103
+        $this->dbHost = $dbHost;
104
+        $this->dbPort = $dbPort;
105
+        $this->tablePrefix = $dbTablePrefix;
106
+    }
107 107
 
108
-	/**
109
-	 * @param array $configOverwrite
110
-	 * @return \OC\DB\Connection
111
-	 */
112
-	protected function connect(array $configOverwrite = []): Connection {
113
-		$connectionParams = [
114
-			'host' => $this->dbHost,
115
-			'user' => $this->dbUser,
116
-			'password' => $this->dbPassword,
117
-			'tablePrefix' => $this->tablePrefix,
118
-			'dbname' => $this->dbName
119
-		];
108
+    /**
109
+     * @param array $configOverwrite
110
+     * @return \OC\DB\Connection
111
+     */
112
+    protected function connect(array $configOverwrite = []): Connection {
113
+        $connectionParams = [
114
+            'host' => $this->dbHost,
115
+            'user' => $this->dbUser,
116
+            'password' => $this->dbPassword,
117
+            'tablePrefix' => $this->tablePrefix,
118
+            'dbname' => $this->dbName
119
+        ];
120 120
 
121
-		// adding port support through installer
122
-		if (!empty($this->dbPort)) {
123
-			if (ctype_digit($this->dbPort)) {
124
-				$connectionParams['port'] = $this->dbPort;
125
-			} else {
126
-				$connectionParams['unix_socket'] = $this->dbPort;
127
-			}
128
-		} elseif (strpos($this->dbHost, ':')) {
129
-			// Host variable may carry a port or socket.
130
-			[$host, $portOrSocket] = explode(':', $this->dbHost, 2);
131
-			if (ctype_digit($portOrSocket)) {
132
-				$connectionParams['port'] = $portOrSocket;
133
-			} else {
134
-				$connectionParams['unix_socket'] = $portOrSocket;
135
-			}
136
-			$connectionParams['host'] = $host;
137
-		}
121
+        // adding port support through installer
122
+        if (!empty($this->dbPort)) {
123
+            if (ctype_digit($this->dbPort)) {
124
+                $connectionParams['port'] = $this->dbPort;
125
+            } else {
126
+                $connectionParams['unix_socket'] = $this->dbPort;
127
+            }
128
+        } elseif (strpos($this->dbHost, ':')) {
129
+            // Host variable may carry a port or socket.
130
+            [$host, $portOrSocket] = explode(':', $this->dbHost, 2);
131
+            if (ctype_digit($portOrSocket)) {
132
+                $connectionParams['port'] = $portOrSocket;
133
+            } else {
134
+                $connectionParams['unix_socket'] = $portOrSocket;
135
+            }
136
+            $connectionParams['host'] = $host;
137
+        }
138 138
 
139
-		$connectionParams = array_merge($connectionParams, $configOverwrite);
140
-		$cf = new ConnectionFactory($this->config);
141
-		return $cf->getConnection($this->config->getValue('dbtype', 'sqlite'), $connectionParams);
142
-	}
139
+        $connectionParams = array_merge($connectionParams, $configOverwrite);
140
+        $cf = new ConnectionFactory($this->config);
141
+        return $cf->getConnection($this->config->getValue('dbtype', 'sqlite'), $connectionParams);
142
+    }
143 143
 
144
-	/**
145
-	 * @param string $userName
146
-	 */
147
-	abstract public function setupDatabase($userName);
144
+    /**
145
+     * @param string $userName
146
+     */
147
+    abstract public function setupDatabase($userName);
148 148
 
149
-	public function runMigrations() {
150
-		if (!is_dir(\OC::$SERVERROOT."/core/Migrations")) {
151
-			return;
152
-		}
153
-		$ms = new MigrationService('core', \OC::$server->get(Connection::class));
154
-		$ms->migrate('latest', true);
155
-	}
149
+    public function runMigrations() {
150
+        if (!is_dir(\OC::$SERVERROOT."/core/Migrations")) {
151
+            return;
152
+        }
153
+        $ms = new MigrationService('core', \OC::$server->get(Connection::class));
154
+        $ms->migrate('latest', true);
155
+    }
156 156
 }
Please login to merge, or discard this patch.
lib/private/Route/Router.php 2 patches
Indentation   +399 added lines, -399 removed lines patch added patch discarded remove patch
@@ -46,403 +46,403 @@
 block discarded – undo
46 46
 use Symfony\Component\Routing\RouteCollection;
47 47
 
48 48
 class Router implements IRouter {
49
-	/** @var RouteCollection[] */
50
-	protected $collections = [];
51
-	/** @var null|RouteCollection */
52
-	protected $collection = null;
53
-	/** @var null|string */
54
-	protected $collectionName = null;
55
-	/** @var null|RouteCollection */
56
-	protected $root = null;
57
-	/** @var null|UrlGenerator */
58
-	protected $generator = null;
59
-	/** @var string[] */
60
-	protected $routingFiles;
61
-	/** @var bool */
62
-	protected $loaded = false;
63
-	/** @var array */
64
-	protected $loadedApps = [];
65
-	/** @var ILogger */
66
-	protected $logger;
67
-	/** @var RequestContext */
68
-	protected $context;
69
-
70
-	/**
71
-	 * @param ILogger $logger
72
-	 */
73
-	public function __construct(ILogger $logger) {
74
-		$this->logger = $logger;
75
-		$baseUrl = \OC::$WEBROOT;
76
-		if (!(\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true')) {
77
-			$baseUrl .= '/index.php';
78
-		}
79
-		if (!\OC::$CLI && isset($_SERVER['REQUEST_METHOD'])) {
80
-			$method = $_SERVER['REQUEST_METHOD'];
81
-		} else {
82
-			$method = 'GET';
83
-		}
84
-		$request = \OC::$server->getRequest();
85
-		$host = $request->getServerHost();
86
-		$schema = $request->getServerProtocol();
87
-		$this->context = new RequestContext($baseUrl, $method, $host, $schema);
88
-		// TODO cache
89
-		$this->root = $this->getCollection('root');
90
-	}
91
-
92
-	/**
93
-	 * Get the files to load the routes from
94
-	 *
95
-	 * @return string[]
96
-	 */
97
-	public function getRoutingFiles() {
98
-		if (!isset($this->routingFiles)) {
99
-			$this->routingFiles = [];
100
-			foreach (\OC_APP::getEnabledApps() as $app) {
101
-				$appPath = \OC_App::getAppPath($app);
102
-				if ($appPath !== false) {
103
-					$file = $appPath . '/appinfo/routes.php';
104
-					if (file_exists($file)) {
105
-						$this->routingFiles[$app] = $file;
106
-					}
107
-				}
108
-			}
109
-		}
110
-		return $this->routingFiles;
111
-	}
112
-
113
-	/**
114
-	 * Loads the routes
115
-	 *
116
-	 * @param null|string $app
117
-	 */
118
-	public function loadRoutes($app = null) {
119
-		if (is_string($app)) {
120
-			$app = \OC_App::cleanAppId($app);
121
-		}
122
-
123
-		$requestedApp = $app;
124
-		if ($this->loaded) {
125
-			return;
126
-		}
127
-		if (is_null($app)) {
128
-			$this->loaded = true;
129
-			$routingFiles = $this->getRoutingFiles();
130
-		} else {
131
-			if (isset($this->loadedApps[$app])) {
132
-				return;
133
-			}
134
-			$file = \OC_App::getAppPath($app) . '/appinfo/routes.php';
135
-			if ($file !== false && file_exists($file)) {
136
-				$routingFiles = [$app => $file];
137
-			} else {
138
-				$routingFiles = [];
139
-			}
140
-		}
141
-		\OC::$server->getEventLogger()->start('loadroutes' . $requestedApp, 'Loading Routes');
142
-		foreach ($routingFiles as $app => $file) {
143
-			if (!isset($this->loadedApps[$app])) {
144
-				if (!\OC_App::isAppLoaded($app)) {
145
-					// app MUST be loaded before app routes
146
-					// try again next time loadRoutes() is called
147
-					$this->loaded = false;
148
-					continue;
149
-				}
150
-				$this->loadedApps[$app] = true;
151
-				$this->useCollection($app);
152
-				$this->requireRouteFile($file, $app);
153
-				$collection = $this->getCollection($app);
154
-				$this->root->addCollection($collection);
155
-
156
-				// Also add the OCS collection
157
-				$collection = $this->getCollection($app.'.ocs');
158
-				$collection->addPrefix('/ocsapp');
159
-				$this->root->addCollection($collection);
160
-			}
161
-		}
162
-		if (!isset($this->loadedApps['core'])) {
163
-			$this->loadedApps['core'] = true;
164
-			$this->useCollection('root');
165
-			require_once __DIR__ . '/../../../core/routes.php';
166
-
167
-			// Also add the OCS collection
168
-			$collection = $this->getCollection('root.ocs');
169
-			$collection->addPrefix('/ocsapp');
170
-			$this->root->addCollection($collection);
171
-		}
172
-		if ($this->loaded) {
173
-			$collection = $this->getCollection('ocs');
174
-			$collection->addPrefix('/ocs');
175
-			$this->root->addCollection($collection);
176
-		}
177
-		\OC::$server->getEventLogger()->end('loadroutes' . $requestedApp);
178
-	}
179
-
180
-	/**
181
-	 * @param string $name
182
-	 * @return \Symfony\Component\Routing\RouteCollection
183
-	 */
184
-	protected function getCollection($name) {
185
-		if (!isset($this->collections[$name])) {
186
-			$this->collections[$name] = new RouteCollection();
187
-		}
188
-		return $this->collections[$name];
189
-	}
190
-
191
-	/**
192
-	 * Sets the collection to use for adding routes
193
-	 *
194
-	 * @param string $name Name of the collection to use.
195
-	 * @return void
196
-	 */
197
-	public function useCollection($name) {
198
-		$this->collection = $this->getCollection($name);
199
-		$this->collectionName = $name;
200
-	}
201
-
202
-	/**
203
-	 * returns the current collection name in use for adding routes
204
-	 *
205
-	 * @return string the collection name
206
-	 */
207
-	public function getCurrentCollection() {
208
-		return $this->collectionName;
209
-	}
210
-
211
-
212
-	/**
213
-	 * Create a \OC\Route\Route.
214
-	 *
215
-	 * @param string $name Name of the route to create.
216
-	 * @param string $pattern The pattern to match
217
-	 * @param array $defaults An array of default parameter values
218
-	 * @param array $requirements An array of requirements for parameters (regexes)
219
-	 * @return \OC\Route\Route
220
-	 */
221
-	public function create($name,
222
-						   $pattern,
223
-						   array $defaults = [],
224
-						   array $requirements = []) {
225
-		$route = new Route($pattern, $defaults, $requirements);
226
-		$this->collection->add($name, $route);
227
-		return $route;
228
-	}
229
-
230
-	/**
231
-	 * Find the route matching $url
232
-	 *
233
-	 * @param string $url The url to find
234
-	 * @throws \Exception
235
-	 * @return array
236
-	 */
237
-	public function findMatchingRoute(string $url): array {
238
-		if (substr($url, 0, 6) === '/apps/') {
239
-			// empty string / 'apps' / $app / rest of the route
240
-			[, , $app,] = explode('/', $url, 4);
241
-
242
-			$app = \OC_App::cleanAppId($app);
243
-			\OC::$REQUESTEDAPP = $app;
244
-			$this->loadRoutes($app);
245
-		} elseif (substr($url, 0, 13) === '/ocsapp/apps/') {
246
-			// empty string / 'ocsapp' / 'apps' / $app / rest of the route
247
-			[, , , $app,] = explode('/', $url, 5);
248
-
249
-			$app = \OC_App::cleanAppId($app);
250
-			\OC::$REQUESTEDAPP = $app;
251
-			$this->loadRoutes($app);
252
-		} elseif (substr($url, 0, 10) === '/settings/') {
253
-			$this->loadRoutes('settings');
254
-		} elseif (substr($url, 0, 6) === '/core/') {
255
-			\OC::$REQUESTEDAPP = $url;
256
-			if (!\OC::$server->getConfig()->getSystemValueBool('maintenance') && !Util::needUpgrade()) {
257
-				\OC_App::loadApps();
258
-			}
259
-			$this->loadRoutes('core');
260
-		} else {
261
-			$this->loadRoutes();
262
-		}
263
-
264
-		$matcher = new UrlMatcher($this->root, $this->context);
265
-		try {
266
-			$parameters = $matcher->match($url);
267
-		} catch (ResourceNotFoundException $e) {
268
-			if (substr($url, -1) !== '/') {
269
-				// We allow links to apps/files? for backwards compatibility reasons
270
-				// However, since Symfony does not allow empty route names, the route
271
-				// we need to match is '/', so we need to append the '/' here.
272
-				try {
273
-					$parameters = $matcher->match($url . '/');
274
-				} catch (ResourceNotFoundException $newException) {
275
-					// If we still didn't match a route, we throw the original exception
276
-					throw $e;
277
-				}
278
-			} else {
279
-				throw $e;
280
-			}
281
-		}
282
-
283
-		return $parameters;
284
-	}
285
-
286
-	/**
287
-	 * Find and execute the route matching $url
288
-	 *
289
-	 * @param string $url The url to find
290
-	 * @throws \Exception
291
-	 * @return void
292
-	 */
293
-	public function match($url) {
294
-		$parameters = $this->findMatchingRoute($url);
295
-
296
-		\OC::$server->getEventLogger()->start('run_route', 'Run route');
297
-		if (isset($parameters['caller'])) {
298
-			$caller = $parameters['caller'];
299
-			unset($parameters['caller']);
300
-			unset($parameters['action']);
301
-			$application = $this->getApplicationClass($caller[0]);
302
-			\OC\AppFramework\App::main($caller[1], $caller[2], $application->getContainer(), $parameters);
303
-		} elseif (isset($parameters['action'])) {
304
-			$action = $parameters['action'];
305
-			if (!is_callable($action)) {
306
-				throw new \Exception('not a callable action');
307
-			}
308
-			unset($parameters['action']);
309
-			unset($parameters['caller']);
310
-			call_user_func($action, $parameters);
311
-		} elseif (isset($parameters['file'])) {
312
-			include $parameters['file'];
313
-		} else {
314
-			throw new \Exception('no action available');
315
-		}
316
-		\OC::$server->getEventLogger()->end('run_route');
317
-	}
318
-
319
-	/**
320
-	 * Get the url generator
321
-	 *
322
-	 * @return \Symfony\Component\Routing\Generator\UrlGenerator
323
-	 *
324
-	 */
325
-	public function getGenerator() {
326
-		if (null !== $this->generator) {
327
-			return $this->generator;
328
-		}
329
-
330
-		return $this->generator = new UrlGenerator($this->root, $this->context);
331
-	}
332
-
333
-	/**
334
-	 * Generate url based on $name and $parameters
335
-	 *
336
-	 * @param string $name Name of the route to use.
337
-	 * @param array $parameters Parameters for the route
338
-	 * @param bool $absolute
339
-	 * @return string
340
-	 */
341
-	public function generate($name,
342
-							 $parameters = [],
343
-							 $absolute = false) {
344
-		$referenceType = UrlGenerator::ABSOLUTE_URL;
345
-		if ($absolute === false) {
346
-			$referenceType = UrlGenerator::ABSOLUTE_PATH;
347
-		}
348
-		$name = $this->fixLegacyRootName($name);
349
-		if (strpos($name, '.') !== false) {
350
-			[$appName, $other] = explode('.', $name, 3);
351
-			// OCS routes are prefixed with "ocs."
352
-			if ($appName === 'ocs') {
353
-				$appName = $other;
354
-			}
355
-			$this->loadRoutes($appName);
356
-			try {
357
-				return $this->getGenerator()->generate($name, $parameters, $referenceType);
358
-			} catch (RouteNotFoundException $e) {
359
-			}
360
-		}
361
-
362
-		// Fallback load all routes
363
-		$this->loadRoutes();
364
-		try {
365
-			return $this->getGenerator()->generate($name, $parameters, $referenceType);
366
-		} catch (RouteNotFoundException $e) {
367
-			$this->logger->logException($e, ['level' => ILogger::INFO]);
368
-			return '';
369
-		}
370
-	}
371
-
372
-	protected function fixLegacyRootName(string $routeName): string {
373
-		if ($routeName === 'files.viewcontroller.showFile') {
374
-			return 'files.View.showFile';
375
-		}
376
-		if ($routeName === 'files_sharing.sharecontroller.showShare') {
377
-			return 'files_sharing.Share.showShare';
378
-		}
379
-		if ($routeName === 'files_sharing.sharecontroller.showAuthenticate') {
380
-			return 'files_sharing.Share.showAuthenticate';
381
-		}
382
-		if ($routeName === 'files_sharing.sharecontroller.authenticate') {
383
-			return 'files_sharing.Share.authenticate';
384
-		}
385
-		if ($routeName === 'files_sharing.sharecontroller.downloadShare') {
386
-			return 'files_sharing.Share.downloadShare';
387
-		}
388
-		if ($routeName === 'files_sharing.publicpreview.directLink') {
389
-			return 'files_sharing.PublicPreview.directLink';
390
-		}
391
-		if ($routeName === 'cloud_federation_api.requesthandlercontroller.addShare') {
392
-			return 'cloud_federation_api.RequestHandler.addShare';
393
-		}
394
-		if ($routeName === 'cloud_federation_api.requesthandlercontroller.receiveNotification') {
395
-			return 'cloud_federation_api.RequestHandler.receiveNotification';
396
-		}
397
-		return $routeName;
398
-	}
399
-
400
-	/**
401
-	 * To isolate the variable scope used inside the $file it is required in it's own method
402
-	 *
403
-	 * @param string $file the route file location to include
404
-	 * @param string $appName
405
-	 */
406
-	private function requireRouteFile($file, $appName) {
407
-		$this->setupRoutes(include_once $file, $appName);
408
-	}
409
-
410
-
411
-	/**
412
-	 * If a routes.php file returns an array, try to set up the application and
413
-	 * register the routes for the app. The application class will be chosen by
414
-	 * camelcasing the appname, e.g.: my_app will be turned into
415
-	 * \OCA\MyApp\AppInfo\Application. If that class does not exist, a default
416
-	 * App will be intialized. This makes it optional to ship an
417
-	 * appinfo/application.php by using the built in query resolver
418
-	 *
419
-	 * @param array $routes the application routes
420
-	 * @param string $appName the name of the app.
421
-	 */
422
-	private function setupRoutes($routes, $appName) {
423
-		if (is_array($routes)) {
424
-			$routeParser = new RouteParser();
425
-
426
-			$defaultRoutes = $routeParser->parseDefaultRoutes($routes, $appName);
427
-			$ocsRoutes = $routeParser->parseOCSRoutes($routes, $appName);
428
-
429
-			$this->root->addCollection($defaultRoutes);
430
-			$ocsRoutes->addPrefix('/ocsapp');
431
-			$this->root->addCollection($ocsRoutes);
432
-		}
433
-	}
434
-
435
-	private function getApplicationClass(string $appName) {
436
-		$appNameSpace = App::buildAppNamespace($appName);
437
-
438
-		$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
439
-
440
-		if (class_exists($applicationClassName)) {
441
-			$application = \OC::$server->query($applicationClassName);
442
-		} else {
443
-			$application = new App($appName);
444
-		}
445
-
446
-		return $application;
447
-	}
49
+    /** @var RouteCollection[] */
50
+    protected $collections = [];
51
+    /** @var null|RouteCollection */
52
+    protected $collection = null;
53
+    /** @var null|string */
54
+    protected $collectionName = null;
55
+    /** @var null|RouteCollection */
56
+    protected $root = null;
57
+    /** @var null|UrlGenerator */
58
+    protected $generator = null;
59
+    /** @var string[] */
60
+    protected $routingFiles;
61
+    /** @var bool */
62
+    protected $loaded = false;
63
+    /** @var array */
64
+    protected $loadedApps = [];
65
+    /** @var ILogger */
66
+    protected $logger;
67
+    /** @var RequestContext */
68
+    protected $context;
69
+
70
+    /**
71
+     * @param ILogger $logger
72
+     */
73
+    public function __construct(ILogger $logger) {
74
+        $this->logger = $logger;
75
+        $baseUrl = \OC::$WEBROOT;
76
+        if (!(\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true')) {
77
+            $baseUrl .= '/index.php';
78
+        }
79
+        if (!\OC::$CLI && isset($_SERVER['REQUEST_METHOD'])) {
80
+            $method = $_SERVER['REQUEST_METHOD'];
81
+        } else {
82
+            $method = 'GET';
83
+        }
84
+        $request = \OC::$server->getRequest();
85
+        $host = $request->getServerHost();
86
+        $schema = $request->getServerProtocol();
87
+        $this->context = new RequestContext($baseUrl, $method, $host, $schema);
88
+        // TODO cache
89
+        $this->root = $this->getCollection('root');
90
+    }
91
+
92
+    /**
93
+     * Get the files to load the routes from
94
+     *
95
+     * @return string[]
96
+     */
97
+    public function getRoutingFiles() {
98
+        if (!isset($this->routingFiles)) {
99
+            $this->routingFiles = [];
100
+            foreach (\OC_APP::getEnabledApps() as $app) {
101
+                $appPath = \OC_App::getAppPath($app);
102
+                if ($appPath !== false) {
103
+                    $file = $appPath . '/appinfo/routes.php';
104
+                    if (file_exists($file)) {
105
+                        $this->routingFiles[$app] = $file;
106
+                    }
107
+                }
108
+            }
109
+        }
110
+        return $this->routingFiles;
111
+    }
112
+
113
+    /**
114
+     * Loads the routes
115
+     *
116
+     * @param null|string $app
117
+     */
118
+    public function loadRoutes($app = null) {
119
+        if (is_string($app)) {
120
+            $app = \OC_App::cleanAppId($app);
121
+        }
122
+
123
+        $requestedApp = $app;
124
+        if ($this->loaded) {
125
+            return;
126
+        }
127
+        if (is_null($app)) {
128
+            $this->loaded = true;
129
+            $routingFiles = $this->getRoutingFiles();
130
+        } else {
131
+            if (isset($this->loadedApps[$app])) {
132
+                return;
133
+            }
134
+            $file = \OC_App::getAppPath($app) . '/appinfo/routes.php';
135
+            if ($file !== false && file_exists($file)) {
136
+                $routingFiles = [$app => $file];
137
+            } else {
138
+                $routingFiles = [];
139
+            }
140
+        }
141
+        \OC::$server->getEventLogger()->start('loadroutes' . $requestedApp, 'Loading Routes');
142
+        foreach ($routingFiles as $app => $file) {
143
+            if (!isset($this->loadedApps[$app])) {
144
+                if (!\OC_App::isAppLoaded($app)) {
145
+                    // app MUST be loaded before app routes
146
+                    // try again next time loadRoutes() is called
147
+                    $this->loaded = false;
148
+                    continue;
149
+                }
150
+                $this->loadedApps[$app] = true;
151
+                $this->useCollection($app);
152
+                $this->requireRouteFile($file, $app);
153
+                $collection = $this->getCollection($app);
154
+                $this->root->addCollection($collection);
155
+
156
+                // Also add the OCS collection
157
+                $collection = $this->getCollection($app.'.ocs');
158
+                $collection->addPrefix('/ocsapp');
159
+                $this->root->addCollection($collection);
160
+            }
161
+        }
162
+        if (!isset($this->loadedApps['core'])) {
163
+            $this->loadedApps['core'] = true;
164
+            $this->useCollection('root');
165
+            require_once __DIR__ . '/../../../core/routes.php';
166
+
167
+            // Also add the OCS collection
168
+            $collection = $this->getCollection('root.ocs');
169
+            $collection->addPrefix('/ocsapp');
170
+            $this->root->addCollection($collection);
171
+        }
172
+        if ($this->loaded) {
173
+            $collection = $this->getCollection('ocs');
174
+            $collection->addPrefix('/ocs');
175
+            $this->root->addCollection($collection);
176
+        }
177
+        \OC::$server->getEventLogger()->end('loadroutes' . $requestedApp);
178
+    }
179
+
180
+    /**
181
+     * @param string $name
182
+     * @return \Symfony\Component\Routing\RouteCollection
183
+     */
184
+    protected function getCollection($name) {
185
+        if (!isset($this->collections[$name])) {
186
+            $this->collections[$name] = new RouteCollection();
187
+        }
188
+        return $this->collections[$name];
189
+    }
190
+
191
+    /**
192
+     * Sets the collection to use for adding routes
193
+     *
194
+     * @param string $name Name of the collection to use.
195
+     * @return void
196
+     */
197
+    public function useCollection($name) {
198
+        $this->collection = $this->getCollection($name);
199
+        $this->collectionName = $name;
200
+    }
201
+
202
+    /**
203
+     * returns the current collection name in use for adding routes
204
+     *
205
+     * @return string the collection name
206
+     */
207
+    public function getCurrentCollection() {
208
+        return $this->collectionName;
209
+    }
210
+
211
+
212
+    /**
213
+     * Create a \OC\Route\Route.
214
+     *
215
+     * @param string $name Name of the route to create.
216
+     * @param string $pattern The pattern to match
217
+     * @param array $defaults An array of default parameter values
218
+     * @param array $requirements An array of requirements for parameters (regexes)
219
+     * @return \OC\Route\Route
220
+     */
221
+    public function create($name,
222
+                            $pattern,
223
+                            array $defaults = [],
224
+                            array $requirements = []) {
225
+        $route = new Route($pattern, $defaults, $requirements);
226
+        $this->collection->add($name, $route);
227
+        return $route;
228
+    }
229
+
230
+    /**
231
+     * Find the route matching $url
232
+     *
233
+     * @param string $url The url to find
234
+     * @throws \Exception
235
+     * @return array
236
+     */
237
+    public function findMatchingRoute(string $url): array {
238
+        if (substr($url, 0, 6) === '/apps/') {
239
+            // empty string / 'apps' / $app / rest of the route
240
+            [, , $app,] = explode('/', $url, 4);
241
+
242
+            $app = \OC_App::cleanAppId($app);
243
+            \OC::$REQUESTEDAPP = $app;
244
+            $this->loadRoutes($app);
245
+        } elseif (substr($url, 0, 13) === '/ocsapp/apps/') {
246
+            // empty string / 'ocsapp' / 'apps' / $app / rest of the route
247
+            [, , , $app,] = explode('/', $url, 5);
248
+
249
+            $app = \OC_App::cleanAppId($app);
250
+            \OC::$REQUESTEDAPP = $app;
251
+            $this->loadRoutes($app);
252
+        } elseif (substr($url, 0, 10) === '/settings/') {
253
+            $this->loadRoutes('settings');
254
+        } elseif (substr($url, 0, 6) === '/core/') {
255
+            \OC::$REQUESTEDAPP = $url;
256
+            if (!\OC::$server->getConfig()->getSystemValueBool('maintenance') && !Util::needUpgrade()) {
257
+                \OC_App::loadApps();
258
+            }
259
+            $this->loadRoutes('core');
260
+        } else {
261
+            $this->loadRoutes();
262
+        }
263
+
264
+        $matcher = new UrlMatcher($this->root, $this->context);
265
+        try {
266
+            $parameters = $matcher->match($url);
267
+        } catch (ResourceNotFoundException $e) {
268
+            if (substr($url, -1) !== '/') {
269
+                // We allow links to apps/files? for backwards compatibility reasons
270
+                // However, since Symfony does not allow empty route names, the route
271
+                // we need to match is '/', so we need to append the '/' here.
272
+                try {
273
+                    $parameters = $matcher->match($url . '/');
274
+                } catch (ResourceNotFoundException $newException) {
275
+                    // If we still didn't match a route, we throw the original exception
276
+                    throw $e;
277
+                }
278
+            } else {
279
+                throw $e;
280
+            }
281
+        }
282
+
283
+        return $parameters;
284
+    }
285
+
286
+    /**
287
+     * Find and execute the route matching $url
288
+     *
289
+     * @param string $url The url to find
290
+     * @throws \Exception
291
+     * @return void
292
+     */
293
+    public function match($url) {
294
+        $parameters = $this->findMatchingRoute($url);
295
+
296
+        \OC::$server->getEventLogger()->start('run_route', 'Run route');
297
+        if (isset($parameters['caller'])) {
298
+            $caller = $parameters['caller'];
299
+            unset($parameters['caller']);
300
+            unset($parameters['action']);
301
+            $application = $this->getApplicationClass($caller[0]);
302
+            \OC\AppFramework\App::main($caller[1], $caller[2], $application->getContainer(), $parameters);
303
+        } elseif (isset($parameters['action'])) {
304
+            $action = $parameters['action'];
305
+            if (!is_callable($action)) {
306
+                throw new \Exception('not a callable action');
307
+            }
308
+            unset($parameters['action']);
309
+            unset($parameters['caller']);
310
+            call_user_func($action, $parameters);
311
+        } elseif (isset($parameters['file'])) {
312
+            include $parameters['file'];
313
+        } else {
314
+            throw new \Exception('no action available');
315
+        }
316
+        \OC::$server->getEventLogger()->end('run_route');
317
+    }
318
+
319
+    /**
320
+     * Get the url generator
321
+     *
322
+     * @return \Symfony\Component\Routing\Generator\UrlGenerator
323
+     *
324
+     */
325
+    public function getGenerator() {
326
+        if (null !== $this->generator) {
327
+            return $this->generator;
328
+        }
329
+
330
+        return $this->generator = new UrlGenerator($this->root, $this->context);
331
+    }
332
+
333
+    /**
334
+     * Generate url based on $name and $parameters
335
+     *
336
+     * @param string $name Name of the route to use.
337
+     * @param array $parameters Parameters for the route
338
+     * @param bool $absolute
339
+     * @return string
340
+     */
341
+    public function generate($name,
342
+                                $parameters = [],
343
+                                $absolute = false) {
344
+        $referenceType = UrlGenerator::ABSOLUTE_URL;
345
+        if ($absolute === false) {
346
+            $referenceType = UrlGenerator::ABSOLUTE_PATH;
347
+        }
348
+        $name = $this->fixLegacyRootName($name);
349
+        if (strpos($name, '.') !== false) {
350
+            [$appName, $other] = explode('.', $name, 3);
351
+            // OCS routes are prefixed with "ocs."
352
+            if ($appName === 'ocs') {
353
+                $appName = $other;
354
+            }
355
+            $this->loadRoutes($appName);
356
+            try {
357
+                return $this->getGenerator()->generate($name, $parameters, $referenceType);
358
+            } catch (RouteNotFoundException $e) {
359
+            }
360
+        }
361
+
362
+        // Fallback load all routes
363
+        $this->loadRoutes();
364
+        try {
365
+            return $this->getGenerator()->generate($name, $parameters, $referenceType);
366
+        } catch (RouteNotFoundException $e) {
367
+            $this->logger->logException($e, ['level' => ILogger::INFO]);
368
+            return '';
369
+        }
370
+    }
371
+
372
+    protected function fixLegacyRootName(string $routeName): string {
373
+        if ($routeName === 'files.viewcontroller.showFile') {
374
+            return 'files.View.showFile';
375
+        }
376
+        if ($routeName === 'files_sharing.sharecontroller.showShare') {
377
+            return 'files_sharing.Share.showShare';
378
+        }
379
+        if ($routeName === 'files_sharing.sharecontroller.showAuthenticate') {
380
+            return 'files_sharing.Share.showAuthenticate';
381
+        }
382
+        if ($routeName === 'files_sharing.sharecontroller.authenticate') {
383
+            return 'files_sharing.Share.authenticate';
384
+        }
385
+        if ($routeName === 'files_sharing.sharecontroller.downloadShare') {
386
+            return 'files_sharing.Share.downloadShare';
387
+        }
388
+        if ($routeName === 'files_sharing.publicpreview.directLink') {
389
+            return 'files_sharing.PublicPreview.directLink';
390
+        }
391
+        if ($routeName === 'cloud_federation_api.requesthandlercontroller.addShare') {
392
+            return 'cloud_federation_api.RequestHandler.addShare';
393
+        }
394
+        if ($routeName === 'cloud_federation_api.requesthandlercontroller.receiveNotification') {
395
+            return 'cloud_federation_api.RequestHandler.receiveNotification';
396
+        }
397
+        return $routeName;
398
+    }
399
+
400
+    /**
401
+     * To isolate the variable scope used inside the $file it is required in it's own method
402
+     *
403
+     * @param string $file the route file location to include
404
+     * @param string $appName
405
+     */
406
+    private function requireRouteFile($file, $appName) {
407
+        $this->setupRoutes(include_once $file, $appName);
408
+    }
409
+
410
+
411
+    /**
412
+     * If a routes.php file returns an array, try to set up the application and
413
+     * register the routes for the app. The application class will be chosen by
414
+     * camelcasing the appname, e.g.: my_app will be turned into
415
+     * \OCA\MyApp\AppInfo\Application. If that class does not exist, a default
416
+     * App will be intialized. This makes it optional to ship an
417
+     * appinfo/application.php by using the built in query resolver
418
+     *
419
+     * @param array $routes the application routes
420
+     * @param string $appName the name of the app.
421
+     */
422
+    private function setupRoutes($routes, $appName) {
423
+        if (is_array($routes)) {
424
+            $routeParser = new RouteParser();
425
+
426
+            $defaultRoutes = $routeParser->parseDefaultRoutes($routes, $appName);
427
+            $ocsRoutes = $routeParser->parseOCSRoutes($routes, $appName);
428
+
429
+            $this->root->addCollection($defaultRoutes);
430
+            $ocsRoutes->addPrefix('/ocsapp');
431
+            $this->root->addCollection($ocsRoutes);
432
+        }
433
+    }
434
+
435
+    private function getApplicationClass(string $appName) {
436
+        $appNameSpace = App::buildAppNamespace($appName);
437
+
438
+        $applicationClassName = $appNameSpace . '\\AppInfo\\Application';
439
+
440
+        if (class_exists($applicationClassName)) {
441
+            $application = \OC::$server->query($applicationClassName);
442
+        } else {
443
+            $application = new App($appName);
444
+        }
445
+
446
+        return $application;
447
+    }
448 448
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -100,7 +100,7 @@  discard block
 block discarded – undo
100 100
 			foreach (\OC_APP::getEnabledApps() as $app) {
101 101
 				$appPath = \OC_App::getAppPath($app);
102 102
 				if ($appPath !== false) {
103
-					$file = $appPath . '/appinfo/routes.php';
103
+					$file = $appPath.'/appinfo/routes.php';
104 104
 					if (file_exists($file)) {
105 105
 						$this->routingFiles[$app] = $file;
106 106
 					}
@@ -131,14 +131,14 @@  discard block
 block discarded – undo
131 131
 			if (isset($this->loadedApps[$app])) {
132 132
 				return;
133 133
 			}
134
-			$file = \OC_App::getAppPath($app) . '/appinfo/routes.php';
134
+			$file = \OC_App::getAppPath($app).'/appinfo/routes.php';
135 135
 			if ($file !== false && file_exists($file)) {
136 136
 				$routingFiles = [$app => $file];
137 137
 			} else {
138 138
 				$routingFiles = [];
139 139
 			}
140 140
 		}
141
-		\OC::$server->getEventLogger()->start('loadroutes' . $requestedApp, 'Loading Routes');
141
+		\OC::$server->getEventLogger()->start('loadroutes'.$requestedApp, 'Loading Routes');
142 142
 		foreach ($routingFiles as $app => $file) {
143 143
 			if (!isset($this->loadedApps[$app])) {
144 144
 				if (!\OC_App::isAppLoaded($app)) {
@@ -162,7 +162,7 @@  discard block
 block discarded – undo
162 162
 		if (!isset($this->loadedApps['core'])) {
163 163
 			$this->loadedApps['core'] = true;
164 164
 			$this->useCollection('root');
165
-			require_once __DIR__ . '/../../../core/routes.php';
165
+			require_once __DIR__.'/../../../core/routes.php';
166 166
 
167 167
 			// Also add the OCS collection
168 168
 			$collection = $this->getCollection('root.ocs');
@@ -174,7 +174,7 @@  discard block
 block discarded – undo
174 174
 			$collection->addPrefix('/ocs');
175 175
 			$this->root->addCollection($collection);
176 176
 		}
177
-		\OC::$server->getEventLogger()->end('loadroutes' . $requestedApp);
177
+		\OC::$server->getEventLogger()->end('loadroutes'.$requestedApp);
178 178
 	}
179 179
 
180 180
 	/**
@@ -237,14 +237,14 @@  discard block
 block discarded – undo
237 237
 	public function findMatchingRoute(string $url): array {
238 238
 		if (substr($url, 0, 6) === '/apps/') {
239 239
 			// empty string / 'apps' / $app / rest of the route
240
-			[, , $app,] = explode('/', $url, 4);
240
+			[,, $app, ] = explode('/', $url, 4);
241 241
 
242 242
 			$app = \OC_App::cleanAppId($app);
243 243
 			\OC::$REQUESTEDAPP = $app;
244 244
 			$this->loadRoutes($app);
245 245
 		} elseif (substr($url, 0, 13) === '/ocsapp/apps/') {
246 246
 			// empty string / 'ocsapp' / 'apps' / $app / rest of the route
247
-			[, , , $app,] = explode('/', $url, 5);
247
+			[,,, $app, ] = explode('/', $url, 5);
248 248
 
249 249
 			$app = \OC_App::cleanAppId($app);
250 250
 			\OC::$REQUESTEDAPP = $app;
@@ -270,7 +270,7 @@  discard block
 block discarded – undo
270 270
 				// However, since Symfony does not allow empty route names, the route
271 271
 				// we need to match is '/', so we need to append the '/' here.
272 272
 				try {
273
-					$parameters = $matcher->match($url . '/');
273
+					$parameters = $matcher->match($url.'/');
274 274
 				} catch (ResourceNotFoundException $newException) {
275 275
 					// If we still didn't match a route, we throw the original exception
276 276
 					throw $e;
@@ -435,7 +435,7 @@  discard block
 block discarded – undo
435 435
 	private function getApplicationClass(string $appName) {
436 436
 		$appNameSpace = App::buildAppNamespace($appName);
437 437
 
438
-		$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
438
+		$applicationClassName = $appNameSpace.'\\AppInfo\\Application';
439 439
 
440 440
 		if (class_exists($applicationClassName)) {
441 441
 			$application = \OC::$server->query($applicationClassName);
Please login to merge, or discard this patch.
lib/private/AppFramework/Routing/RouteConfig.php 1 patch
Indentation   +252 added lines, -252 removed lines patch added patch discarded remove patch
@@ -39,258 +39,258 @@
 block discarded – undo
39 39
  * @package OC\AppFramework\routing
40 40
  */
41 41
 class RouteConfig {
42
-	/** @var DIContainer */
43
-	private $container;
44
-
45
-	/** @var Router */
46
-	private $router;
47
-
48
-	/** @var array */
49
-	private $routes;
50
-
51
-	/** @var string */
52
-	private $appName;
53
-
54
-	/** @var string[] */
55
-	private $controllerNameCache = [];
56
-
57
-	protected $rootUrlApps = [
58
-		'cloud_federation_api',
59
-		'core',
60
-		'files_sharing',
61
-		'files',
62
-		'settings',
63
-		'spreed',
64
-	];
65
-
66
-	/**
67
-	 * @param \OC\AppFramework\DependencyInjection\DIContainer $container
68
-	 * @param \OC\Route\Router $router
69
-	 * @param array $routes
70
-	 * @internal param $appName
71
-	 */
72
-	public function __construct(DIContainer $container, Router $router, $routes) {
73
-		$this->routes = $routes;
74
-		$this->container = $container;
75
-		$this->router = $router;
76
-		$this->appName = $container['AppName'];
77
-	}
78
-
79
-	/**
80
-	 * The routes and resource will be registered to the \OCP\Route\IRouter
81
-	 */
82
-	public function register() {
83
-
84
-		// parse simple
85
-		$this->processIndexRoutes($this->routes);
86
-
87
-		// parse resources
88
-		$this->processIndexResources($this->routes);
89
-
90
-		/*
42
+    /** @var DIContainer */
43
+    private $container;
44
+
45
+    /** @var Router */
46
+    private $router;
47
+
48
+    /** @var array */
49
+    private $routes;
50
+
51
+    /** @var string */
52
+    private $appName;
53
+
54
+    /** @var string[] */
55
+    private $controllerNameCache = [];
56
+
57
+    protected $rootUrlApps = [
58
+        'cloud_federation_api',
59
+        'core',
60
+        'files_sharing',
61
+        'files',
62
+        'settings',
63
+        'spreed',
64
+    ];
65
+
66
+    /**
67
+     * @param \OC\AppFramework\DependencyInjection\DIContainer $container
68
+     * @param \OC\Route\Router $router
69
+     * @param array $routes
70
+     * @internal param $appName
71
+     */
72
+    public function __construct(DIContainer $container, Router $router, $routes) {
73
+        $this->routes = $routes;
74
+        $this->container = $container;
75
+        $this->router = $router;
76
+        $this->appName = $container['AppName'];
77
+    }
78
+
79
+    /**
80
+     * The routes and resource will be registered to the \OCP\Route\IRouter
81
+     */
82
+    public function register() {
83
+
84
+        // parse simple
85
+        $this->processIndexRoutes($this->routes);
86
+
87
+        // parse resources
88
+        $this->processIndexResources($this->routes);
89
+
90
+        /*
91 91
 		 * OCS routes go into a different collection
92 92
 		 */
93
-		$oldCollection = $this->router->getCurrentCollection();
94
-		$this->router->useCollection($oldCollection . '.ocs');
95
-
96
-		// parse ocs simple routes
97
-		$this->processOCS($this->routes);
98
-
99
-		// parse ocs simple routes
100
-		$this->processOCSResources($this->routes);
101
-
102
-		$this->router->useCollection($oldCollection);
103
-	}
104
-
105
-	private function processOCS(array $routes): void {
106
-		$ocsRoutes = $routes['ocs'] ?? [];
107
-		foreach ($ocsRoutes as $ocsRoute) {
108
-			$this->processRoute($ocsRoute, 'ocs.');
109
-		}
110
-	}
111
-
112
-	/**
113
-	 * Creates one route base on the give configuration
114
-	 * @param array $routes
115
-	 * @throws \UnexpectedValueException
116
-	 */
117
-	private function processIndexRoutes(array $routes): void {
118
-		$simpleRoutes = $routes['routes'] ?? [];
119
-		foreach ($simpleRoutes as $simpleRoute) {
120
-			$this->processRoute($simpleRoute);
121
-		}
122
-	}
123
-
124
-	protected function processRoute(array $route, string $routeNamePrefix = ''): void {
125
-		$name = $route['name'];
126
-		$postfix = $route['postfix'] ?? '';
127
-		$root = $this->buildRootPrefix($route, $routeNamePrefix);
128
-
129
-		$url = $root . '/' . ltrim($route['url'], '/');
130
-		$verb = strtoupper($route['verb'] ?? 'GET');
131
-
132
-		$split = explode('#', $name, 2);
133
-		if (count($split) !== 2) {
134
-			throw new \UnexpectedValueException('Invalid route name');
135
-		}
136
-		[$controller, $action] = $split;
137
-
138
-		$controllerName = $this->buildControllerName($controller);
139
-		$actionName = $this->buildActionName($action);
140
-
141
-		$routeName = $routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix;
142
-
143
-		$router = $this->router->create($routeName, $url)
144
-			->method($verb);
145
-
146
-		// optionally register requirements for route. This is used to
147
-		// tell the route parser how url parameters should be matched
148
-		if (array_key_exists('requirements', $route)) {
149
-			$router->requirements($route['requirements']);
150
-		}
151
-
152
-		// optionally register defaults for route. This is used to
153
-		// tell the route parser how url parameters should be default valued
154
-		$defaults = [];
155
-		if (array_key_exists('defaults', $route)) {
156
-			$defaults = $route['defaults'];
157
-		}
158
-
159
-		$defaults['caller'] = [$this->appName, $controllerName, $actionName];
160
-		$router->defaults($defaults);
161
-	}
162
-
163
-	/**
164
-	 * For a given name and url restful OCS routes are created:
165
-	 *  - index
166
-	 *  - show
167
-	 *  - create
168
-	 *  - update
169
-	 *  - destroy
170
-	 *
171
-	 * @param array $routes
172
-	 */
173
-	private function processOCSResources(array $routes): void {
174
-		$this->processResources($routes['ocs-resources'] ?? [], 'ocs.');
175
-	}
176
-
177
-	/**
178
-	 * For a given name and url restful routes are created:
179
-	 *  - index
180
-	 *  - show
181
-	 *  - create
182
-	 *  - update
183
-	 *  - destroy
184
-	 *
185
-	 * @param array $routes
186
-	 */
187
-	private function processIndexResources(array $routes): void {
188
-		$this->processResources($routes['resources'] ?? []);
189
-	}
190
-
191
-	/**
192
-	 * For a given name and url restful routes are created:
193
-	 *  - index
194
-	 *  - show
195
-	 *  - create
196
-	 *  - update
197
-	 *  - destroy
198
-	 *
199
-	 * @param array $resources
200
-	 * @param string $routeNamePrefix
201
-	 */
202
-	protected function processResources(array $resources, string $routeNamePrefix = ''): void {
203
-		// declaration of all restful actions
204
-		$actions = [
205
-			['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
206
-			['name' => 'show', 'verb' => 'GET'],
207
-			['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
208
-			['name' => 'update', 'verb' => 'PUT'],
209
-			['name' => 'destroy', 'verb' => 'DELETE'],
210
-		];
211
-
212
-		foreach ($resources as $resource => $config) {
213
-			$root = $this->buildRootPrefix($config, $routeNamePrefix);
214
-
215
-			// the url parameter used as id to the resource
216
-			foreach ($actions as $action) {
217
-				$url = $root . '/' . ltrim($config['url'], '/');
218
-				$method = $action['name'];
219
-
220
-				$verb = strtoupper($action['verb'] ?? 'GET');
221
-				$collectionAction = $action['on-collection'] ?? false;
222
-				if (!$collectionAction) {
223
-					$url .= '/{id}';
224
-				}
225
-				if (isset($action['url-postfix'])) {
226
-					$url .= '/' . $action['url-postfix'];
227
-				}
228
-
229
-				$controller = $resource;
230
-
231
-				$controllerName = $this->buildControllerName($controller);
232
-				$actionName = $this->buildActionName($method);
233
-
234
-				$routeName = $routeNamePrefix . $this->appName . '.' . strtolower($resource) . '.' . $method;
235
-
236
-				$route = $this->router->create($routeName, $url)
237
-					->method($verb);
238
-
239
-				$route->defaults(['caller' => [$this->appName, $controllerName, $actionName]]);
240
-			}
241
-		}
242
-	}
243
-
244
-	private function buildRootPrefix(array $route, string $routeNamePrefix): string {
245
-		$defaultRoot = $this->appName === 'core' ? '' : '/apps/' . $this->appName;
246
-		$root = $route['root'] ?? $defaultRoot;
247
-
248
-		if ($routeNamePrefix !== '') {
249
-			// In OCS all apps are whitelisted
250
-			return $root;
251
-		}
252
-
253
-		if (!\in_array($this->appName, $this->rootUrlApps, true)) {
254
-			// Only allow root URLS for some apps
255
-			return  $defaultRoot;
256
-		}
257
-
258
-		return $root;
259
-	}
260
-
261
-	/**
262
-	 * Based on a given route name the controller name is generated
263
-	 * @param string $controller
264
-	 * @return string
265
-	 */
266
-	private function buildControllerName(string $controller): string {
267
-		if (!isset($this->controllerNameCache[$controller])) {
268
-			$this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
269
-		}
270
-		return $this->controllerNameCache[$controller];
271
-	}
272
-
273
-	/**
274
-	 * Based on the action part of the route name the controller method name is generated
275
-	 * @param string $action
276
-	 * @return string
277
-	 */
278
-	private function buildActionName(string $action): string {
279
-		return $this->underScoreToCamelCase($action);
280
-	}
281
-
282
-	/**
283
-	 * Underscored strings are converted to camel case strings
284
-	 * @param string $str
285
-	 * @return string
286
-	 */
287
-	private function underScoreToCamelCase(string $str): string {
288
-		$pattern = '/_[a-z]?/';
289
-		return preg_replace_callback(
290
-			$pattern,
291
-			function ($matches) {
292
-				return strtoupper(ltrim($matches[0], '_'));
293
-			},
294
-			$str);
295
-	}
93
+        $oldCollection = $this->router->getCurrentCollection();
94
+        $this->router->useCollection($oldCollection . '.ocs');
95
+
96
+        // parse ocs simple routes
97
+        $this->processOCS($this->routes);
98
+
99
+        // parse ocs simple routes
100
+        $this->processOCSResources($this->routes);
101
+
102
+        $this->router->useCollection($oldCollection);
103
+    }
104
+
105
+    private function processOCS(array $routes): void {
106
+        $ocsRoutes = $routes['ocs'] ?? [];
107
+        foreach ($ocsRoutes as $ocsRoute) {
108
+            $this->processRoute($ocsRoute, 'ocs.');
109
+        }
110
+    }
111
+
112
+    /**
113
+     * Creates one route base on the give configuration
114
+     * @param array $routes
115
+     * @throws \UnexpectedValueException
116
+     */
117
+    private function processIndexRoutes(array $routes): void {
118
+        $simpleRoutes = $routes['routes'] ?? [];
119
+        foreach ($simpleRoutes as $simpleRoute) {
120
+            $this->processRoute($simpleRoute);
121
+        }
122
+    }
123
+
124
+    protected function processRoute(array $route, string $routeNamePrefix = ''): void {
125
+        $name = $route['name'];
126
+        $postfix = $route['postfix'] ?? '';
127
+        $root = $this->buildRootPrefix($route, $routeNamePrefix);
128
+
129
+        $url = $root . '/' . ltrim($route['url'], '/');
130
+        $verb = strtoupper($route['verb'] ?? 'GET');
131
+
132
+        $split = explode('#', $name, 2);
133
+        if (count($split) !== 2) {
134
+            throw new \UnexpectedValueException('Invalid route name');
135
+        }
136
+        [$controller, $action] = $split;
137
+
138
+        $controllerName = $this->buildControllerName($controller);
139
+        $actionName = $this->buildActionName($action);
140
+
141
+        $routeName = $routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix;
142
+
143
+        $router = $this->router->create($routeName, $url)
144
+            ->method($verb);
145
+
146
+        // optionally register requirements for route. This is used to
147
+        // tell the route parser how url parameters should be matched
148
+        if (array_key_exists('requirements', $route)) {
149
+            $router->requirements($route['requirements']);
150
+        }
151
+
152
+        // optionally register defaults for route. This is used to
153
+        // tell the route parser how url parameters should be default valued
154
+        $defaults = [];
155
+        if (array_key_exists('defaults', $route)) {
156
+            $defaults = $route['defaults'];
157
+        }
158
+
159
+        $defaults['caller'] = [$this->appName, $controllerName, $actionName];
160
+        $router->defaults($defaults);
161
+    }
162
+
163
+    /**
164
+     * For a given name and url restful OCS routes are created:
165
+     *  - index
166
+     *  - show
167
+     *  - create
168
+     *  - update
169
+     *  - destroy
170
+     *
171
+     * @param array $routes
172
+     */
173
+    private function processOCSResources(array $routes): void {
174
+        $this->processResources($routes['ocs-resources'] ?? [], 'ocs.');
175
+    }
176
+
177
+    /**
178
+     * For a given name and url restful routes are created:
179
+     *  - index
180
+     *  - show
181
+     *  - create
182
+     *  - update
183
+     *  - destroy
184
+     *
185
+     * @param array $routes
186
+     */
187
+    private function processIndexResources(array $routes): void {
188
+        $this->processResources($routes['resources'] ?? []);
189
+    }
190
+
191
+    /**
192
+     * For a given name and url restful routes are created:
193
+     *  - index
194
+     *  - show
195
+     *  - create
196
+     *  - update
197
+     *  - destroy
198
+     *
199
+     * @param array $resources
200
+     * @param string $routeNamePrefix
201
+     */
202
+    protected function processResources(array $resources, string $routeNamePrefix = ''): void {
203
+        // declaration of all restful actions
204
+        $actions = [
205
+            ['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
206
+            ['name' => 'show', 'verb' => 'GET'],
207
+            ['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
208
+            ['name' => 'update', 'verb' => 'PUT'],
209
+            ['name' => 'destroy', 'verb' => 'DELETE'],
210
+        ];
211
+
212
+        foreach ($resources as $resource => $config) {
213
+            $root = $this->buildRootPrefix($config, $routeNamePrefix);
214
+
215
+            // the url parameter used as id to the resource
216
+            foreach ($actions as $action) {
217
+                $url = $root . '/' . ltrim($config['url'], '/');
218
+                $method = $action['name'];
219
+
220
+                $verb = strtoupper($action['verb'] ?? 'GET');
221
+                $collectionAction = $action['on-collection'] ?? false;
222
+                if (!$collectionAction) {
223
+                    $url .= '/{id}';
224
+                }
225
+                if (isset($action['url-postfix'])) {
226
+                    $url .= '/' . $action['url-postfix'];
227
+                }
228
+
229
+                $controller = $resource;
230
+
231
+                $controllerName = $this->buildControllerName($controller);
232
+                $actionName = $this->buildActionName($method);
233
+
234
+                $routeName = $routeNamePrefix . $this->appName . '.' . strtolower($resource) . '.' . $method;
235
+
236
+                $route = $this->router->create($routeName, $url)
237
+                    ->method($verb);
238
+
239
+                $route->defaults(['caller' => [$this->appName, $controllerName, $actionName]]);
240
+            }
241
+        }
242
+    }
243
+
244
+    private function buildRootPrefix(array $route, string $routeNamePrefix): string {
245
+        $defaultRoot = $this->appName === 'core' ? '' : '/apps/' . $this->appName;
246
+        $root = $route['root'] ?? $defaultRoot;
247
+
248
+        if ($routeNamePrefix !== '') {
249
+            // In OCS all apps are whitelisted
250
+            return $root;
251
+        }
252
+
253
+        if (!\in_array($this->appName, $this->rootUrlApps, true)) {
254
+            // Only allow root URLS for some apps
255
+            return  $defaultRoot;
256
+        }
257
+
258
+        return $root;
259
+    }
260
+
261
+    /**
262
+     * Based on a given route name the controller name is generated
263
+     * @param string $controller
264
+     * @return string
265
+     */
266
+    private function buildControllerName(string $controller): string {
267
+        if (!isset($this->controllerNameCache[$controller])) {
268
+            $this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
269
+        }
270
+        return $this->controllerNameCache[$controller];
271
+    }
272
+
273
+    /**
274
+     * Based on the action part of the route name the controller method name is generated
275
+     * @param string $action
276
+     * @return string
277
+     */
278
+    private function buildActionName(string $action): string {
279
+        return $this->underScoreToCamelCase($action);
280
+    }
281
+
282
+    /**
283
+     * Underscored strings are converted to camel case strings
284
+     * @param string $str
285
+     * @return string
286
+     */
287
+    private function underScoreToCamelCase(string $str): string {
288
+        $pattern = '/_[a-z]?/';
289
+        return preg_replace_callback(
290
+            $pattern,
291
+            function ($matches) {
292
+                return strtoupper(ltrim($matches[0], '_'));
293
+            },
294
+            $str);
295
+    }
296 296
 }
Please login to merge, or discard this patch.
lib/private/AppFramework/Routing/RouteParser.php 1 patch
Indentation   +231 added lines, -231 removed lines patch added patch discarded remove patch
@@ -29,235 +29,235 @@
 block discarded – undo
29 29
 use Symfony\Component\Routing\RouteCollection;
30 30
 
31 31
 class RouteParser {
32
-	/** @var string[] */
33
-	private $controllerNameCache = [];
34
-
35
-	private const rootUrlApps = [
36
-		'cloud_federation_api',
37
-		'core',
38
-		'files_sharing',
39
-		'files',
40
-		'settings',
41
-		'spreed',
42
-	];
43
-
44
-	public function parseDefaultRoutes(array $routes, string $appName): RouteCollection {
45
-		$collection = $this->processIndexRoutes($routes, $appName);
46
-		$collection->addCollection($this->processIndexResources($routes, $appName));
47
-
48
-		return $collection;
49
-	}
50
-
51
-	public function parseOCSRoutes(array $routes, string $appName): RouteCollection {
52
-		$collection = $this->processOCS($routes, $appName);
53
-		$collection->addCollection($this->processOCSResources($routes, $appName));
54
-
55
-		return $collection;
56
-	}
57
-
58
-	private function processOCS(array $routes, string $appName): RouteCollection {
59
-		$collection = new RouteCollection();
60
-		$ocsRoutes = $routes['ocs'] ?? [];
61
-		foreach ($ocsRoutes as $ocsRoute) {
62
-			$result = $this->processRoute($ocsRoute, $appName, 'ocs.');
63
-
64
-			$collection->add($result[0], $result[1]);
65
-		}
66
-
67
-		return $collection;
68
-	}
69
-
70
-	/**
71
-	 * Creates one route base on the give configuration
72
-	 * @param array $routes
73
-	 * @throws \UnexpectedValueException
74
-	 */
75
-	private function processIndexRoutes(array $routes, string $appName): RouteCollection {
76
-		$collection = new RouteCollection();
77
-		$simpleRoutes = $routes['routes'] ?? [];
78
-		foreach ($simpleRoutes as $simpleRoute) {
79
-			$result = $this->processRoute($simpleRoute, $appName);
80
-
81
-			$collection->add($result[0], $result[1]);
82
-		}
83
-
84
-		return $collection;
85
-	}
86
-
87
-	private function processRoute(array $route, string $appName, string $routeNamePrefix = ''): array {
88
-		$name = $route['name'];
89
-		$postfix = $route['postfix'] ?? '';
90
-		$root = $this->buildRootPrefix($route, $appName, $routeNamePrefix);
91
-
92
-		$url = $root . '/' . ltrim($route['url'], '/');
93
-		$verb = strtoupper($route['verb'] ?? 'GET');
94
-
95
-		$split = explode('#', $name, 2);
96
-		if (count($split) !== 2) {
97
-			throw new \UnexpectedValueException('Invalid route name');
98
-		}
99
-		[$controller, $action] = $split;
100
-
101
-		$controllerName = $this->buildControllerName($controller);
102
-		$actionName = $this->buildActionName($action);
103
-
104
-		$routeName = $routeNamePrefix . $appName . '.' . $controller . '.' . $action . $postfix;
105
-
106
-		$routeObject = new Route($url);
107
-		$routeObject->method($verb);
108
-
109
-		// optionally register requirements for route. This is used to
110
-		// tell the route parser how url parameters should be matched
111
-		if (array_key_exists('requirements', $route)) {
112
-			$routeObject->requirements($route['requirements']);
113
-		}
114
-
115
-		// optionally register defaults for route. This is used to
116
-		// tell the route parser how url parameters should be default valued
117
-		$defaults = [];
118
-		if (array_key_exists('defaults', $route)) {
119
-			$defaults = $route['defaults'];
120
-		}
121
-
122
-		$defaults['caller'] = [$appName, $controllerName, $actionName];
123
-		$routeObject->defaults($defaults);
124
-
125
-		return [$routeName, $routeObject];
126
-	}
127
-
128
-	/**
129
-	 * For a given name and url restful OCS routes are created:
130
-	 *  - index
131
-	 *  - show
132
-	 *  - create
133
-	 *  - update
134
-	 *  - destroy
135
-	 *
136
-	 * @param array $routes
137
-	 */
138
-	private function processOCSResources(array $routes, string $appName): RouteCollection {
139
-		return $this->processResources($routes['ocs-resources'] ?? [], $appName, 'ocs.');
140
-	}
141
-
142
-	/**
143
-	 * For a given name and url restful routes are created:
144
-	 *  - index
145
-	 *  - show
146
-	 *  - create
147
-	 *  - update
148
-	 *  - destroy
149
-	 *
150
-	 * @param array $routes
151
-	 */
152
-	private function processIndexResources(array $routes, string $appName): RouteCollection {
153
-		return $this->processResources($routes['resources'] ?? [], $appName);
154
-	}
155
-
156
-	/**
157
-	 * For a given name and url restful routes are created:
158
-	 *  - index
159
-	 *  - show
160
-	 *  - create
161
-	 *  - update
162
-	 *  - destroy
163
-	 *
164
-	 * @param array $resources
165
-	 * @param string $routeNamePrefix
166
-	 */
167
-	private function processResources(array $resources, string $appName, string $routeNamePrefix = ''): RouteCollection {
168
-		// declaration of all restful actions
169
-		$actions = [
170
-			['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
171
-			['name' => 'show', 'verb' => 'GET'],
172
-			['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
173
-			['name' => 'update', 'verb' => 'PUT'],
174
-			['name' => 'destroy', 'verb' => 'DELETE'],
175
-		];
176
-
177
-		$collection = new RouteCollection();
178
-		foreach ($resources as $resource => $config) {
179
-			$root = $this->buildRootPrefix($config, $appName, $routeNamePrefix);
180
-
181
-			// the url parameter used as id to the resource
182
-			foreach ($actions as $action) {
183
-				$url = $root . '/' . ltrim($config['url'], '/');
184
-				$method = $action['name'];
185
-
186
-				$verb = strtoupper($action['verb'] ?? 'GET');
187
-				$collectionAction = $action['on-collection'] ?? false;
188
-				if (!$collectionAction) {
189
-					$url .= '/{id}';
190
-				}
191
-
192
-				$controller = $resource;
193
-
194
-				$controllerName = $this->buildControllerName($controller);
195
-				$actionName = $this->buildActionName($method);
196
-
197
-				$routeName = $routeNamePrefix . $appName . '.' . strtolower($resource) . '.' . $method;
198
-
199
-				$route = new Route($url);
200
-				$route->method($verb);
201
-
202
-				$route->defaults(['caller' => [$appName, $controllerName, $actionName]]);
203
-
204
-				$collection->add($routeName, $route);
205
-			}
206
-		}
207
-
208
-		return $collection;
209
-	}
210
-
211
-	private function buildRootPrefix(array $route, string $appName, string $routeNamePrefix): string {
212
-		$defaultRoot = $appName === 'core' ? '' : '/apps/' . $appName;
213
-		$root = $route['root'] ?? $defaultRoot;
214
-
215
-		if ($routeNamePrefix !== '') {
216
-			// In OCS all apps are whitelisted
217
-			return $root;
218
-		}
219
-
220
-		if (!\in_array($appName, self::rootUrlApps, true)) {
221
-			// Only allow root URLS for some apps
222
-			return  $defaultRoot;
223
-		}
224
-
225
-		return $root;
226
-	}
227
-
228
-	/**
229
-	 * Based on a given route name the controller name is generated
230
-	 * @param string $controller
231
-	 * @return string
232
-	 */
233
-	private function buildControllerName(string $controller): string {
234
-		if (!isset($this->controllerNameCache[$controller])) {
235
-			$this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
236
-		}
237
-		return $this->controllerNameCache[$controller];
238
-	}
239
-
240
-	/**
241
-	 * Based on the action part of the route name the controller method name is generated
242
-	 * @param string $action
243
-	 * @return string
244
-	 */
245
-	private function buildActionName(string $action): string {
246
-		return $this->underScoreToCamelCase($action);
247
-	}
248
-
249
-	/**
250
-	 * Underscored strings are converted to camel case strings
251
-	 * @param string $str
252
-	 * @return string
253
-	 */
254
-	private function underScoreToCamelCase(string $str): string {
255
-		$pattern = '/_[a-z]?/';
256
-		return preg_replace_callback(
257
-			$pattern,
258
-			function ($matches) {
259
-				return strtoupper(ltrim($matches[0], '_'));
260
-			},
261
-			$str);
262
-	}
32
+    /** @var string[] */
33
+    private $controllerNameCache = [];
34
+
35
+    private const rootUrlApps = [
36
+        'cloud_federation_api',
37
+        'core',
38
+        'files_sharing',
39
+        'files',
40
+        'settings',
41
+        'spreed',
42
+    ];
43
+
44
+    public function parseDefaultRoutes(array $routes, string $appName): RouteCollection {
45
+        $collection = $this->processIndexRoutes($routes, $appName);
46
+        $collection->addCollection($this->processIndexResources($routes, $appName));
47
+
48
+        return $collection;
49
+    }
50
+
51
+    public function parseOCSRoutes(array $routes, string $appName): RouteCollection {
52
+        $collection = $this->processOCS($routes, $appName);
53
+        $collection->addCollection($this->processOCSResources($routes, $appName));
54
+
55
+        return $collection;
56
+    }
57
+
58
+    private function processOCS(array $routes, string $appName): RouteCollection {
59
+        $collection = new RouteCollection();
60
+        $ocsRoutes = $routes['ocs'] ?? [];
61
+        foreach ($ocsRoutes as $ocsRoute) {
62
+            $result = $this->processRoute($ocsRoute, $appName, 'ocs.');
63
+
64
+            $collection->add($result[0], $result[1]);
65
+        }
66
+
67
+        return $collection;
68
+    }
69
+
70
+    /**
71
+     * Creates one route base on the give configuration
72
+     * @param array $routes
73
+     * @throws \UnexpectedValueException
74
+     */
75
+    private function processIndexRoutes(array $routes, string $appName): RouteCollection {
76
+        $collection = new RouteCollection();
77
+        $simpleRoutes = $routes['routes'] ?? [];
78
+        foreach ($simpleRoutes as $simpleRoute) {
79
+            $result = $this->processRoute($simpleRoute, $appName);
80
+
81
+            $collection->add($result[0], $result[1]);
82
+        }
83
+
84
+        return $collection;
85
+    }
86
+
87
+    private function processRoute(array $route, string $appName, string $routeNamePrefix = ''): array {
88
+        $name = $route['name'];
89
+        $postfix = $route['postfix'] ?? '';
90
+        $root = $this->buildRootPrefix($route, $appName, $routeNamePrefix);
91
+
92
+        $url = $root . '/' . ltrim($route['url'], '/');
93
+        $verb = strtoupper($route['verb'] ?? 'GET');
94
+
95
+        $split = explode('#', $name, 2);
96
+        if (count($split) !== 2) {
97
+            throw new \UnexpectedValueException('Invalid route name');
98
+        }
99
+        [$controller, $action] = $split;
100
+
101
+        $controllerName = $this->buildControllerName($controller);
102
+        $actionName = $this->buildActionName($action);
103
+
104
+        $routeName = $routeNamePrefix . $appName . '.' . $controller . '.' . $action . $postfix;
105
+
106
+        $routeObject = new Route($url);
107
+        $routeObject->method($verb);
108
+
109
+        // optionally register requirements for route. This is used to
110
+        // tell the route parser how url parameters should be matched
111
+        if (array_key_exists('requirements', $route)) {
112
+            $routeObject->requirements($route['requirements']);
113
+        }
114
+
115
+        // optionally register defaults for route. This is used to
116
+        // tell the route parser how url parameters should be default valued
117
+        $defaults = [];
118
+        if (array_key_exists('defaults', $route)) {
119
+            $defaults = $route['defaults'];
120
+        }
121
+
122
+        $defaults['caller'] = [$appName, $controllerName, $actionName];
123
+        $routeObject->defaults($defaults);
124
+
125
+        return [$routeName, $routeObject];
126
+    }
127
+
128
+    /**
129
+     * For a given name and url restful OCS routes are created:
130
+     *  - index
131
+     *  - show
132
+     *  - create
133
+     *  - update
134
+     *  - destroy
135
+     *
136
+     * @param array $routes
137
+     */
138
+    private function processOCSResources(array $routes, string $appName): RouteCollection {
139
+        return $this->processResources($routes['ocs-resources'] ?? [], $appName, 'ocs.');
140
+    }
141
+
142
+    /**
143
+     * For a given name and url restful routes are created:
144
+     *  - index
145
+     *  - show
146
+     *  - create
147
+     *  - update
148
+     *  - destroy
149
+     *
150
+     * @param array $routes
151
+     */
152
+    private function processIndexResources(array $routes, string $appName): RouteCollection {
153
+        return $this->processResources($routes['resources'] ?? [], $appName);
154
+    }
155
+
156
+    /**
157
+     * For a given name and url restful routes are created:
158
+     *  - index
159
+     *  - show
160
+     *  - create
161
+     *  - update
162
+     *  - destroy
163
+     *
164
+     * @param array $resources
165
+     * @param string $routeNamePrefix
166
+     */
167
+    private function processResources(array $resources, string $appName, string $routeNamePrefix = ''): RouteCollection {
168
+        // declaration of all restful actions
169
+        $actions = [
170
+            ['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
171
+            ['name' => 'show', 'verb' => 'GET'],
172
+            ['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
173
+            ['name' => 'update', 'verb' => 'PUT'],
174
+            ['name' => 'destroy', 'verb' => 'DELETE'],
175
+        ];
176
+
177
+        $collection = new RouteCollection();
178
+        foreach ($resources as $resource => $config) {
179
+            $root = $this->buildRootPrefix($config, $appName, $routeNamePrefix);
180
+
181
+            // the url parameter used as id to the resource
182
+            foreach ($actions as $action) {
183
+                $url = $root . '/' . ltrim($config['url'], '/');
184
+                $method = $action['name'];
185
+
186
+                $verb = strtoupper($action['verb'] ?? 'GET');
187
+                $collectionAction = $action['on-collection'] ?? false;
188
+                if (!$collectionAction) {
189
+                    $url .= '/{id}';
190
+                }
191
+
192
+                $controller = $resource;
193
+
194
+                $controllerName = $this->buildControllerName($controller);
195
+                $actionName = $this->buildActionName($method);
196
+
197
+                $routeName = $routeNamePrefix . $appName . '.' . strtolower($resource) . '.' . $method;
198
+
199
+                $route = new Route($url);
200
+                $route->method($verb);
201
+
202
+                $route->defaults(['caller' => [$appName, $controllerName, $actionName]]);
203
+
204
+                $collection->add($routeName, $route);
205
+            }
206
+        }
207
+
208
+        return $collection;
209
+    }
210
+
211
+    private function buildRootPrefix(array $route, string $appName, string $routeNamePrefix): string {
212
+        $defaultRoot = $appName === 'core' ? '' : '/apps/' . $appName;
213
+        $root = $route['root'] ?? $defaultRoot;
214
+
215
+        if ($routeNamePrefix !== '') {
216
+            // In OCS all apps are whitelisted
217
+            return $root;
218
+        }
219
+
220
+        if (!\in_array($appName, self::rootUrlApps, true)) {
221
+            // Only allow root URLS for some apps
222
+            return  $defaultRoot;
223
+        }
224
+
225
+        return $root;
226
+    }
227
+
228
+    /**
229
+     * Based on a given route name the controller name is generated
230
+     * @param string $controller
231
+     * @return string
232
+     */
233
+    private function buildControllerName(string $controller): string {
234
+        if (!isset($this->controllerNameCache[$controller])) {
235
+            $this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
236
+        }
237
+        return $this->controllerNameCache[$controller];
238
+    }
239
+
240
+    /**
241
+     * Based on the action part of the route name the controller method name is generated
242
+     * @param string $action
243
+     * @return string
244
+     */
245
+    private function buildActionName(string $action): string {
246
+        return $this->underScoreToCamelCase($action);
247
+    }
248
+
249
+    /**
250
+     * Underscored strings are converted to camel case strings
251
+     * @param string $str
252
+     * @return string
253
+     */
254
+    private function underScoreToCamelCase(string $str): string {
255
+        $pattern = '/_[a-z]?/';
256
+        return preg_replace_callback(
257
+            $pattern,
258
+            function ($matches) {
259
+                return strtoupper(ltrim($matches[0], '_'));
260
+            },
261
+            $str);
262
+    }
263 263
 }
Please login to merge, or discard this patch.
lib/private/AppFramework/Utility/ControllerMethodReflector.php 1 patch
Indentation   +99 added lines, -99 removed lines patch added patch discarded remove patch
@@ -40,103 +40,103 @@
 block discarded – undo
40 40
  * Reads and parses annotations from doc comments
41 41
  */
42 42
 class ControllerMethodReflector implements IControllerMethodReflector {
43
-	public $annotations = [];
44
-	private $types = [];
45
-	private $parameters = [];
46
-
47
-	/**
48
-	 * @param object $object an object or classname
49
-	 * @param string $method the method which we want to inspect
50
-	 */
51
-	public function reflect($object, string $method) {
52
-		$reflection = new \ReflectionMethod($object, $method);
53
-		$docs = $reflection->getDocComment();
54
-
55
-		if ($docs !== false) {
56
-			// extract everything prefixed by @ and first letter uppercase
57
-			preg_match_all('/^\h+\*\h+@(?P<annotation>[A-Z]\w+)((?P<parameter>.*))?$/m', $docs, $matches);
58
-			foreach ($matches['annotation'] as $key => $annontation) {
59
-				$annontation = strtolower($annontation);
60
-				$annotationValue = $matches['parameter'][$key];
61
-				if (isset($annotationValue[0]) && $annotationValue[0] === '(' && $annotationValue[\strlen($annotationValue) - 1] === ')') {
62
-					$cutString = substr($annotationValue, 1, -1);
63
-					$cutString = str_replace(' ', '', $cutString);
64
-					$splittedArray = explode(',', $cutString);
65
-					foreach ($splittedArray as $annotationValues) {
66
-						[$key, $value] = explode('=', $annotationValues);
67
-						$this->annotations[$annontation][$key] = $value;
68
-					}
69
-					continue;
70
-				}
71
-
72
-				$this->annotations[$annontation] = [$annotationValue];
73
-			}
74
-
75
-			// extract type parameter information
76
-			preg_match_all('/@param\h+(?P<type>\w+)\h+\$(?P<var>\w+)/', $docs, $matches);
77
-			$this->types = array_combine($matches['var'], $matches['type']);
78
-		}
79
-
80
-		foreach ($reflection->getParameters() as $param) {
81
-			// extract type information from PHP 7 scalar types and prefer them over phpdoc annotations
82
-			$type = $param->getType();
83
-			if ($type instanceof \ReflectionNamedType) {
84
-				$this->types[$param->getName()] = $type->getName();
85
-			}
86
-
87
-			$default = null;
88
-			if ($param->isOptional()) {
89
-				$default = $param->getDefaultValue();
90
-			}
91
-			$this->parameters[$param->name] = $default;
92
-		}
93
-	}
94
-
95
-	/**
96
-	 * Inspects the PHPDoc parameters for types
97
-	 * @param string $parameter the parameter whose type comments should be
98
-	 * parsed
99
-	 * @return string|null type in the type parameters (@param int $something)
100
-	 * would return int or null if not existing
101
-	 */
102
-	public function getType(string $parameter) {
103
-		if (array_key_exists($parameter, $this->types)) {
104
-			return $this->types[$parameter];
105
-		}
106
-
107
-		return null;
108
-	}
109
-
110
-	/**
111
-	 * @return array the arguments of the method with key => default value
112
-	 */
113
-	public function getParameters(): array {
114
-		return $this->parameters;
115
-	}
116
-
117
-	/**
118
-	 * Check if a method contains an annotation
119
-	 * @param string $name the name of the annotation
120
-	 * @return bool true if the annotation is found
121
-	 */
122
-	public function hasAnnotation(string $name): bool {
123
-		$name = strtolower($name);
124
-		return array_key_exists($name, $this->annotations);
125
-	}
126
-
127
-	/**
128
-	 * Get optional annotation parameter by key
129
-	 *
130
-	 * @param string $name the name of the annotation
131
-	 * @param string $key the string of the annotation
132
-	 * @return string
133
-	 */
134
-	public function getAnnotationParameter(string $name, string $key): string {
135
-		$name = strtolower($name);
136
-		if (isset($this->annotations[$name][$key])) {
137
-			return $this->annotations[$name][$key];
138
-		}
139
-
140
-		return '';
141
-	}
43
+    public $annotations = [];
44
+    private $types = [];
45
+    private $parameters = [];
46
+
47
+    /**
48
+     * @param object $object an object or classname
49
+     * @param string $method the method which we want to inspect
50
+     */
51
+    public function reflect($object, string $method) {
52
+        $reflection = new \ReflectionMethod($object, $method);
53
+        $docs = $reflection->getDocComment();
54
+
55
+        if ($docs !== false) {
56
+            // extract everything prefixed by @ and first letter uppercase
57
+            preg_match_all('/^\h+\*\h+@(?P<annotation>[A-Z]\w+)((?P<parameter>.*))?$/m', $docs, $matches);
58
+            foreach ($matches['annotation'] as $key => $annontation) {
59
+                $annontation = strtolower($annontation);
60
+                $annotationValue = $matches['parameter'][$key];
61
+                if (isset($annotationValue[0]) && $annotationValue[0] === '(' && $annotationValue[\strlen($annotationValue) - 1] === ')') {
62
+                    $cutString = substr($annotationValue, 1, -1);
63
+                    $cutString = str_replace(' ', '', $cutString);
64
+                    $splittedArray = explode(',', $cutString);
65
+                    foreach ($splittedArray as $annotationValues) {
66
+                        [$key, $value] = explode('=', $annotationValues);
67
+                        $this->annotations[$annontation][$key] = $value;
68
+                    }
69
+                    continue;
70
+                }
71
+
72
+                $this->annotations[$annontation] = [$annotationValue];
73
+            }
74
+
75
+            // extract type parameter information
76
+            preg_match_all('/@param\h+(?P<type>\w+)\h+\$(?P<var>\w+)/', $docs, $matches);
77
+            $this->types = array_combine($matches['var'], $matches['type']);
78
+        }
79
+
80
+        foreach ($reflection->getParameters() as $param) {
81
+            // extract type information from PHP 7 scalar types and prefer them over phpdoc annotations
82
+            $type = $param->getType();
83
+            if ($type instanceof \ReflectionNamedType) {
84
+                $this->types[$param->getName()] = $type->getName();
85
+            }
86
+
87
+            $default = null;
88
+            if ($param->isOptional()) {
89
+                $default = $param->getDefaultValue();
90
+            }
91
+            $this->parameters[$param->name] = $default;
92
+        }
93
+    }
94
+
95
+    /**
96
+     * Inspects the PHPDoc parameters for types
97
+     * @param string $parameter the parameter whose type comments should be
98
+     * parsed
99
+     * @return string|null type in the type parameters (@param int $something)
100
+     * would return int or null if not existing
101
+     */
102
+    public function getType(string $parameter) {
103
+        if (array_key_exists($parameter, $this->types)) {
104
+            return $this->types[$parameter];
105
+        }
106
+
107
+        return null;
108
+    }
109
+
110
+    /**
111
+     * @return array the arguments of the method with key => default value
112
+     */
113
+    public function getParameters(): array {
114
+        return $this->parameters;
115
+    }
116
+
117
+    /**
118
+     * Check if a method contains an annotation
119
+     * @param string $name the name of the annotation
120
+     * @return bool true if the annotation is found
121
+     */
122
+    public function hasAnnotation(string $name): bool {
123
+        $name = strtolower($name);
124
+        return array_key_exists($name, $this->annotations);
125
+    }
126
+
127
+    /**
128
+     * Get optional annotation parameter by key
129
+     *
130
+     * @param string $name the name of the annotation
131
+     * @param string $key the string of the annotation
132
+     * @return string
133
+     */
134
+    public function getAnnotationParameter(string $name, string $key): string {
135
+        $name = strtolower($name);
136
+        if (isset($this->annotations[$name][$key])) {
137
+            return $this->annotations[$name][$key];
138
+        }
139
+
140
+        return '';
141
+    }
142 142
 }
Please login to merge, or discard this patch.
lib/private/AppFramework/Http/Request.php 1 patch
Indentation   +871 added lines, -871 removed lines patch added patch discarded remove patch
@@ -64,875 +64,875 @@
 block discarded – undo
64 64
  * @property mixed[] server
65 65
  */
66 66
 class Request implements \ArrayAccess, \Countable, IRequest {
67
-	public const USER_AGENT_IE = '/(MSIE)|(Trident)/';
68
-	// Microsoft Edge User Agent from https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
69
-	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.]+$/';
70
-	// Firefox User Agent from https://developer.mozilla.org/en-US/docs/Web/HTTP/Gecko_user_agent_string_reference
71
-	public const USER_AGENT_FIREFOX = '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/[0-9.]+$/';
72
-	// Chrome User Agent from https://developer.chrome.com/multidevice/user-agent
73
-	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.]+|)$/';
74
-	// Safari User Agent from http://www.useragentstring.com/pages/Safari/
75
-	public const USER_AGENT_SAFARI = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/[0-9.]+ Safari\/[0-9.A-Z]+$/';
76
-	// Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent
77
-	public const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#';
78
-	public const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#';
79
-	public const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost|\[::1\])$/';
80
-
81
-	/**
82
-	 * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_IOS instead
83
-	 */
84
-	public const USER_AGENT_OWNCLOUD_IOS = '/^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)\-iOS.*$/';
85
-	/**
86
-	 * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_ANDROID instead
87
-	 */
88
-	public const USER_AGENT_OWNCLOUD_ANDROID = '/^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/';
89
-	/**
90
-	 * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_DESKTOP instead
91
-	 */
92
-	public const USER_AGENT_OWNCLOUD_DESKTOP = '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/';
93
-
94
-	protected $inputStream;
95
-	protected $content;
96
-	protected $items = [];
97
-	protected $allowedKeys = [
98
-		'get',
99
-		'post',
100
-		'files',
101
-		'server',
102
-		'env',
103
-		'cookies',
104
-		'urlParams',
105
-		'parameters',
106
-		'method',
107
-		'requesttoken',
108
-	];
109
-	/** @var ISecureRandom */
110
-	protected $secureRandom;
111
-	/** @var IConfig */
112
-	protected $config;
113
-	/** @var string */
114
-	protected $requestId = '';
115
-	/** @var ICrypto */
116
-	protected $crypto;
117
-	/** @var CsrfTokenManager|null */
118
-	protected $csrfTokenManager;
119
-
120
-	/** @var bool */
121
-	protected $contentDecoded = false;
122
-
123
-	/**
124
-	 * @param array $vars An associative array with the following optional values:
125
-	 *        - array 'urlParams' the parameters which were matched from the URL
126
-	 *        - array 'get' the $_GET array
127
-	 *        - array|string 'post' the $_POST array or JSON string
128
-	 *        - array 'files' the $_FILES array
129
-	 *        - array 'server' the $_SERVER array
130
-	 *        - array 'env' the $_ENV array
131
-	 *        - array 'cookies' the $_COOKIE array
132
-	 *        - string 'method' the request method (GET, POST etc)
133
-	 *        - string|false 'requesttoken' the requesttoken or false when not available
134
-	 * @param ISecureRandom $secureRandom
135
-	 * @param IConfig $config
136
-	 * @param CsrfTokenManager|null $csrfTokenManager
137
-	 * @param string $stream
138
-	 * @see https://www.php.net/manual/en/reserved.variables.php
139
-	 */
140
-	public function __construct(array $vars,
141
-								ISecureRandom $secureRandom,
142
-								IConfig $config,
143
-								CsrfTokenManager $csrfTokenManager = null,
144
-								string $stream = 'php://input') {
145
-		$this->inputStream = $stream;
146
-		$this->items['params'] = [];
147
-		$this->secureRandom = $secureRandom;
148
-		$this->config = $config;
149
-		$this->csrfTokenManager = $csrfTokenManager;
150
-
151
-		if (!array_key_exists('method', $vars)) {
152
-			$vars['method'] = 'GET';
153
-		}
154
-
155
-		foreach ($this->allowedKeys as $name) {
156
-			$this->items[$name] = isset($vars[$name])
157
-				? $vars[$name]
158
-				: [];
159
-		}
160
-
161
-		$this->items['parameters'] = array_merge(
162
-			$this->items['get'],
163
-			$this->items['post'],
164
-			$this->items['urlParams'],
165
-			$this->items['params']
166
-		);
167
-	}
168
-	/**
169
-	 * @param array $parameters
170
-	 */
171
-	public function setUrlParameters(array $parameters) {
172
-		$this->items['urlParams'] = $parameters;
173
-		$this->items['parameters'] = array_merge(
174
-			$this->items['parameters'],
175
-			$this->items['urlParams']
176
-		);
177
-	}
178
-
179
-	/**
180
-	 * Countable method
181
-	 * @return int
182
-	 */
183
-	public function count(): int {
184
-		return \count($this->items['parameters']);
185
-	}
186
-
187
-	/**
188
-	 * ArrayAccess methods
189
-	 *
190
-	 * Gives access to the combined GET, POST and urlParams arrays
191
-	 *
192
-	 * Examples:
193
-	 *
194
-	 * $var = $request['myvar'];
195
-	 *
196
-	 * or
197
-	 *
198
-	 * if(!isset($request['myvar']) {
199
-	 * 	// Do something
200
-	 * }
201
-	 *
202
-	 * $request['myvar'] = 'something'; // This throws an exception.
203
-	 *
204
-	 * @param string $offset The key to lookup
205
-	 * @return boolean
206
-	 */
207
-	public function offsetExists($offset): bool {
208
-		return isset($this->items['parameters'][$offset]);
209
-	}
210
-
211
-	/**
212
-	 * @see offsetExists
213
-	 * @param string $offset
214
-	 * @return mixed
215
-	 */
216
-	public function offsetGet($offset) {
217
-		return isset($this->items['parameters'][$offset])
218
-			? $this->items['parameters'][$offset]
219
-			: null;
220
-	}
221
-
222
-	/**
223
-	 * @see offsetExists
224
-	 * @param string $offset
225
-	 * @param mixed $value
226
-	 */
227
-	public function offsetSet($offset, $value) {
228
-		throw new \RuntimeException('You cannot change the contents of the request object');
229
-	}
230
-
231
-	/**
232
-	 * @see offsetExists
233
-	 * @param string $offset
234
-	 */
235
-	public function offsetUnset($offset) {
236
-		throw new \RuntimeException('You cannot change the contents of the request object');
237
-	}
238
-
239
-	/**
240
-	 * Magic property accessors
241
-	 * @param string $name
242
-	 * @param mixed $value
243
-	 */
244
-	public function __set($name, $value) {
245
-		throw new \RuntimeException('You cannot change the contents of the request object');
246
-	}
247
-
248
-	/**
249
-	 * Access request variables by method and name.
250
-	 * Examples:
251
-	 *
252
-	 * $request->post['myvar']; // Only look for POST variables
253
-	 * $request->myvar; or $request->{'myvar'}; or $request->{$myvar}
254
-	 * Looks in the combined GET, POST and urlParams array.
255
-	 *
256
-	 * If you access e.g. ->post but the current HTTP request method
257
-	 * is GET a \LogicException will be thrown.
258
-	 *
259
-	 * @param string $name The key to look for.
260
-	 * @throws \LogicException
261
-	 * @return mixed|null
262
-	 */
263
-	public function __get($name) {
264
-		switch ($name) {
265
-			case 'put':
266
-			case 'patch':
267
-			case 'get':
268
-			case 'post':
269
-				if ($this->method !== strtoupper($name)) {
270
-					throw new \LogicException(sprintf('%s cannot be accessed in a %s request.', $name, $this->method));
271
-				}
272
-				return $this->getContent();
273
-			case 'files':
274
-			case 'server':
275
-			case 'env':
276
-			case 'cookies':
277
-			case 'urlParams':
278
-			case 'method':
279
-				return isset($this->items[$name])
280
-					? $this->items[$name]
281
-					: null;
282
-			case 'parameters':
283
-			case 'params':
284
-				return $this->getContent();
285
-			default:
286
-				return isset($this[$name])
287
-					? $this[$name]
288
-					: null;
289
-		}
290
-	}
291
-
292
-	/**
293
-	 * @param string $name
294
-	 * @return bool
295
-	 */
296
-	public function __isset($name) {
297
-		if (\in_array($name, $this->allowedKeys, true)) {
298
-			return true;
299
-		}
300
-		return isset($this->items['parameters'][$name]);
301
-	}
302
-
303
-	/**
304
-	 * @param string $id
305
-	 */
306
-	public function __unset($id) {
307
-		throw new \RuntimeException('You cannot change the contents of the request object');
308
-	}
309
-
310
-	/**
311
-	 * Returns the value for a specific http header.
312
-	 *
313
-	 * This method returns an empty string if the header did not exist.
314
-	 *
315
-	 * @param string $name
316
-	 * @return string
317
-	 */
318
-	public function getHeader(string $name): string {
319
-		$name = strtoupper(str_replace('-', '_',$name));
320
-		if (isset($this->server['HTTP_' . $name])) {
321
-			return $this->server['HTTP_' . $name];
322
-		}
323
-
324
-		// There's a few headers that seem to end up in the top-level
325
-		// server array.
326
-		switch ($name) {
327
-			case 'CONTENT_TYPE':
328
-			case 'CONTENT_LENGTH':
329
-			case 'REMOTE_ADDR':
330
-				if (isset($this->server[$name])) {
331
-					return $this->server[$name];
332
-				}
333
-				break;
334
-		}
335
-
336
-		return '';
337
-	}
338
-
339
-	/**
340
-	 * Lets you access post and get parameters by the index
341
-	 * In case of json requests the encoded json body is accessed
342
-	 *
343
-	 * @param string $key the key which you want to access in the URL Parameter
344
-	 *                     placeholder, $_POST or $_GET array.
345
-	 *                     The priority how they're returned is the following:
346
-	 *                     1. URL parameters
347
-	 *                     2. POST parameters
348
-	 *                     3. GET parameters
349
-	 * @param mixed $default If the key is not found, this value will be returned
350
-	 * @return mixed the content of the array
351
-	 */
352
-	public function getParam(string $key, $default = null) {
353
-		return isset($this->parameters[$key])
354
-			? $this->parameters[$key]
355
-			: $default;
356
-	}
357
-
358
-	/**
359
-	 * Returns all params that were received, be it from the request
360
-	 * (as GET or POST) or throuh the URL by the route
361
-	 * @return array the array with all parameters
362
-	 */
363
-	public function getParams(): array {
364
-		return is_array($this->parameters) ? $this->parameters : [];
365
-	}
366
-
367
-	/**
368
-	 * Returns the method of the request
369
-	 * @return string the method of the request (POST, GET, etc)
370
-	 */
371
-	public function getMethod(): string {
372
-		return $this->method;
373
-	}
374
-
375
-	/**
376
-	 * Shortcut for accessing an uploaded file through the $_FILES array
377
-	 * @param string $key the key that will be taken from the $_FILES array
378
-	 * @return array the file in the $_FILES element
379
-	 */
380
-	public function getUploadedFile(string $key) {
381
-		return isset($this->files[$key]) ? $this->files[$key] : null;
382
-	}
383
-
384
-	/**
385
-	 * Shortcut for getting env variables
386
-	 * @param string $key the key that will be taken from the $_ENV array
387
-	 * @return array the value in the $_ENV element
388
-	 */
389
-	public function getEnv(string $key) {
390
-		return isset($this->env[$key]) ? $this->env[$key] : null;
391
-	}
392
-
393
-	/**
394
-	 * Shortcut for getting cookie variables
395
-	 * @param string $key the key that will be taken from the $_COOKIE array
396
-	 * @return string the value in the $_COOKIE element
397
-	 */
398
-	public function getCookie(string $key) {
399
-		return isset($this->cookies[$key]) ? $this->cookies[$key] : null;
400
-	}
401
-
402
-	/**
403
-	 * Returns the request body content.
404
-	 *
405
-	 * If the HTTP request method is PUT and the body
406
-	 * not application/x-www-form-urlencoded or application/json a stream
407
-	 * resource is returned, otherwise an array.
408
-	 *
409
-	 * @return array|string|resource The request body content or a resource to read the body stream.
410
-	 *
411
-	 * @throws \LogicException
412
-	 */
413
-	protected function getContent() {
414
-		// If the content can't be parsed into an array then return a stream resource.
415
-		if ($this->method === 'PUT'
416
-			&& $this->getHeader('Content-Length') !== '0'
417
-			&& $this->getHeader('Content-Length') !== ''
418
-			&& strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false
419
-			&& strpos($this->getHeader('Content-Type'), 'application/json') === false
420
-		) {
421
-			if ($this->content === false) {
422
-				throw new \LogicException(
423
-					'"put" can only be accessed once if not '
424
-					. 'application/x-www-form-urlencoded or application/json.'
425
-				);
426
-			}
427
-			$this->content = false;
428
-			return fopen($this->inputStream, 'rb');
429
-		} else {
430
-			$this->decodeContent();
431
-			return $this->items['parameters'];
432
-		}
433
-	}
434
-
435
-	/**
436
-	 * Attempt to decode the content and populate parameters
437
-	 */
438
-	protected function decodeContent() {
439
-		if ($this->contentDecoded) {
440
-			return;
441
-		}
442
-		$params = [];
443
-
444
-		// 'application/json' must be decoded manually.
445
-		if (strpos($this->getHeader('Content-Type'), 'application/json') !== false) {
446
-			$params = json_decode(file_get_contents($this->inputStream), true);
447
-			if ($params !== null && \count($params) > 0) {
448
-				$this->items['params'] = $params;
449
-				if ($this->method === 'POST') {
450
-					$this->items['post'] = $params;
451
-				}
452
-			}
453
-
454
-			// Handle application/x-www-form-urlencoded for methods other than GET
455
-		// or post correctly
456
-		} elseif ($this->method !== 'GET'
457
-				&& $this->method !== 'POST'
458
-				&& strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) {
459
-			parse_str(file_get_contents($this->inputStream), $params);
460
-			if (\is_array($params)) {
461
-				$this->items['params'] = $params;
462
-			}
463
-		}
464
-
465
-		if (\is_array($params)) {
466
-			$this->items['parameters'] = array_merge($this->items['parameters'], $params);
467
-		}
468
-		$this->contentDecoded = true;
469
-	}
470
-
471
-
472
-	/**
473
-	 * Checks if the CSRF check was correct
474
-	 * @return bool true if CSRF check passed
475
-	 */
476
-	public function passesCSRFCheck(): bool {
477
-		if ($this->csrfTokenManager === null) {
478
-			return false;
479
-		}
480
-
481
-		if (!$this->passesStrictCookieCheck()) {
482
-			return false;
483
-		}
484
-
485
-		if (isset($this->items['get']['requesttoken'])) {
486
-			$token = $this->items['get']['requesttoken'];
487
-		} elseif (isset($this->items['post']['requesttoken'])) {
488
-			$token = $this->items['post']['requesttoken'];
489
-		} elseif (isset($this->items['server']['HTTP_REQUESTTOKEN'])) {
490
-			$token = $this->items['server']['HTTP_REQUESTTOKEN'];
491
-		} else {
492
-			//no token found.
493
-			return false;
494
-		}
495
-		$token = new CsrfToken($token);
496
-
497
-		return $this->csrfTokenManager->isTokenValid($token);
498
-	}
499
-
500
-	/**
501
-	 * Whether the cookie checks are required
502
-	 *
503
-	 * @return bool
504
-	 */
505
-	private function cookieCheckRequired(): bool {
506
-		if ($this->getHeader('OCS-APIREQUEST')) {
507
-			return false;
508
-		}
509
-		if ($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
510
-			return false;
511
-		}
512
-
513
-		return true;
514
-	}
515
-
516
-	/**
517
-	 * Wrapper around session_get_cookie_params
518
-	 *
519
-	 * @return array
520
-	 */
521
-	public function getCookieParams(): array {
522
-		return session_get_cookie_params();
523
-	}
524
-
525
-	/**
526
-	 * Appends the __Host- prefix to the cookie if applicable
527
-	 *
528
-	 * @param string $name
529
-	 * @return string
530
-	 */
531
-	protected function getProtectedCookieName(string $name): string {
532
-		$cookieParams = $this->getCookieParams();
533
-		$prefix = '';
534
-		if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
535
-			$prefix = '__Host-';
536
-		}
537
-
538
-		return $prefix.$name;
539
-	}
540
-
541
-	/**
542
-	 * Checks if the strict cookie has been sent with the request if the request
543
-	 * is including any cookies.
544
-	 *
545
-	 * @return bool
546
-	 * @since 9.1.0
547
-	 */
548
-	public function passesStrictCookieCheck(): bool {
549
-		if (!$this->cookieCheckRequired()) {
550
-			return true;
551
-		}
552
-
553
-		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict');
554
-		if ($this->getCookie($cookieName) === 'true'
555
-			&& $this->passesLaxCookieCheck()) {
556
-			return true;
557
-		}
558
-		return false;
559
-	}
560
-
561
-	/**
562
-	 * Checks if the lax cookie has been sent with the request if the request
563
-	 * is including any cookies.
564
-	 *
565
-	 * @return bool
566
-	 * @since 9.1.0
567
-	 */
568
-	public function passesLaxCookieCheck(): bool {
569
-		if (!$this->cookieCheckRequired()) {
570
-			return true;
571
-		}
572
-
573
-		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax');
574
-		if ($this->getCookie($cookieName) === 'true') {
575
-			return true;
576
-		}
577
-		return false;
578
-	}
579
-
580
-
581
-	/**
582
-	 * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
583
-	 * If `mod_unique_id` is installed this value will be taken.
584
-	 * @return string
585
-	 */
586
-	public function getId(): string {
587
-		if (isset($this->server['UNIQUE_ID'])) {
588
-			return $this->server['UNIQUE_ID'];
589
-		}
590
-
591
-		if (empty($this->requestId)) {
592
-			$validChars = ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS;
593
-			$this->requestId = $this->secureRandom->generate(20, $validChars);
594
-		}
595
-
596
-		return $this->requestId;
597
-	}
598
-
599
-	/**
600
-	 * Checks if given $remoteAddress matches given $trustedProxy.
601
-	 * If $trustedProxy is an IPv4 IP range given in CIDR notation, true will be returned if
602
-	 * $remoteAddress is an IPv4 address within that IP range.
603
-	 * Otherwise $remoteAddress will be compared to $trustedProxy literally and the result
604
-	 * will be returned.
605
-	 * @return boolean true if $remoteAddress matches $trustedProxy, false otherwise
606
-	 */
607
-	protected function matchesTrustedProxy($trustedProxy, $remoteAddress) {
608
-		$cidrre = '/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/([0-9]{1,2})$/';
609
-
610
-		if (preg_match($cidrre, $trustedProxy, $match)) {
611
-			$net = $match[1];
612
-			$shiftbits = min(32, max(0, 32 - intval($match[2])));
613
-			$netnum = ip2long($net) >> $shiftbits;
614
-			$ipnum = ip2long($remoteAddress) >> $shiftbits;
615
-
616
-			return $ipnum === $netnum;
617
-		}
618
-
619
-		return $trustedProxy === $remoteAddress;
620
-	}
621
-
622
-	/**
623
-	 * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
624
-	 * For details regarding what "match" means, refer to `matchesTrustedProxy`.
625
-	 * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
626
-	 */
627
-	protected function isTrustedProxy($trustedProxies, $remoteAddress) {
628
-		foreach ($trustedProxies as $tp) {
629
-			if ($this->matchesTrustedProxy($tp, $remoteAddress)) {
630
-				return true;
631
-			}
632
-		}
633
-
634
-		return false;
635
-	}
636
-
637
-	/**
638
-	 * Returns the remote address, if the connection came from a trusted proxy
639
-	 * and `forwarded_for_headers` has been configured then the IP address
640
-	 * specified in this header will be returned instead.
641
-	 * Do always use this instead of $_SERVER['REMOTE_ADDR']
642
-	 * @return string IP address
643
-	 */
644
-	public function getRemoteAddress(): string {
645
-		$remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
646
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
647
-
648
-		if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
649
-			$forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
650
-				'HTTP_X_FORWARDED_FOR'
651
-				// only have one default, so we cannot ship an insecure product out of the box
652
-			]);
653
-
654
-			foreach ($forwardedForHeaders as $header) {
655
-				if (isset($this->server[$header])) {
656
-					foreach (explode(',', $this->server[$header]) as $IP) {
657
-						$IP = trim($IP);
658
-
659
-						// remove brackets from IPv6 addresses
660
-						if (strpos($IP, '[') === 0 && substr($IP, -1) === ']') {
661
-							$IP = substr($IP, 1, -1);
662
-						}
663
-
664
-						if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
665
-							return $IP;
666
-						}
667
-					}
668
-				}
669
-			}
670
-		}
671
-
672
-		return $remoteAddress;
673
-	}
674
-
675
-	/**
676
-	 * Check overwrite condition
677
-	 * @param string $type
678
-	 * @return bool
679
-	 */
680
-	private function isOverwriteCondition(string $type = ''): bool {
681
-		$regex = '/' . $this->config->getSystemValue('overwritecondaddr', '')  . '/';
682
-		$remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
683
-		return $regex === '//' || preg_match($regex, $remoteAddr) === 1
684
-		|| $type !== 'protocol';
685
-	}
686
-
687
-	/**
688
-	 * Returns the server protocol. It respects one or more reverse proxies servers
689
-	 * and load balancers
690
-	 * @return string Server protocol (http or https)
691
-	 */
692
-	public function getServerProtocol(): string {
693
-		if ($this->config->getSystemValue('overwriteprotocol') !== ''
694
-			&& $this->isOverwriteCondition('protocol')) {
695
-			return $this->config->getSystemValue('overwriteprotocol');
696
-		}
697
-
698
-		if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
699
-			if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) {
700
-				$parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
701
-				$proto = strtolower(trim($parts[0]));
702
-			} else {
703
-				$proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']);
704
-			}
705
-
706
-			// Verify that the protocol is always HTTP or HTTPS
707
-			// default to http if an invalid value is provided
708
-			return $proto === 'https' ? 'https' : 'http';
709
-		}
710
-
711
-		if (isset($this->server['HTTPS'])
712
-			&& $this->server['HTTPS'] !== null
713
-			&& $this->server['HTTPS'] !== 'off'
714
-			&& $this->server['HTTPS'] !== '') {
715
-			return 'https';
716
-		}
717
-
718
-		return 'http';
719
-	}
720
-
721
-	/**
722
-	 * Returns the used HTTP protocol.
723
-	 *
724
-	 * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
725
-	 */
726
-	public function getHttpProtocol(): string {
727
-		$claimedProtocol = $this->server['SERVER_PROTOCOL'];
728
-
729
-		if (\is_string($claimedProtocol)) {
730
-			$claimedProtocol = strtoupper($claimedProtocol);
731
-		}
732
-
733
-		$validProtocols = [
734
-			'HTTP/1.0',
735
-			'HTTP/1.1',
736
-			'HTTP/2',
737
-		];
738
-
739
-		if (\in_array($claimedProtocol, $validProtocols, true)) {
740
-			return $claimedProtocol;
741
-		}
742
-
743
-		return 'HTTP/1.1';
744
-	}
745
-
746
-	/**
747
-	 * Returns the request uri, even if the website uses one or more
748
-	 * reverse proxies
749
-	 * @return string
750
-	 */
751
-	public function getRequestUri(): string {
752
-		$uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
753
-		if ($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
754
-			$uri = $this->getScriptName() . substr($uri, \strlen($this->server['SCRIPT_NAME']));
755
-		}
756
-		return $uri;
757
-	}
758
-
759
-	/**
760
-	 * Get raw PathInfo from request (not urldecoded)
761
-	 * @throws \Exception
762
-	 * @return string Path info
763
-	 */
764
-	public function getRawPathInfo(): string {
765
-		$requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
766
-		// remove too many slashes - can be caused by reverse proxy configuration
767
-		$requestUri = preg_replace('%/{2,}%', '/', $requestUri);
768
-
769
-		// Remove the query string from REQUEST_URI
770
-		if ($pos = strpos($requestUri, '?')) {
771
-			$requestUri = substr($requestUri, 0, $pos);
772
-		}
773
-
774
-		$scriptName = $this->server['SCRIPT_NAME'];
775
-		$pathInfo = $requestUri;
776
-
777
-		// strip off the script name's dir and file name
778
-		// FIXME: Sabre does not really belong here
779
-		[$path, $name] = \Sabre\Uri\split($scriptName);
780
-		if (!empty($path)) {
781
-			if ($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) {
782
-				$pathInfo = substr($pathInfo, \strlen($path));
783
-			} else {
784
-				throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
785
-			}
786
-		}
787
-		if ($name === null) {
788
-			$name = '';
789
-		}
790
-
791
-		if (strpos($pathInfo, '/'.$name) === 0) {
792
-			$pathInfo = substr($pathInfo, \strlen($name) + 1);
793
-		}
794
-		if ($name !== '' && strpos($pathInfo, $name) === 0) {
795
-			$pathInfo = substr($pathInfo, \strlen($name));
796
-		}
797
-		if ($pathInfo === false || $pathInfo === '/') {
798
-			return '';
799
-		} else {
800
-			return $pathInfo;
801
-		}
802
-	}
803
-
804
-	/**
805
-	 * Get PathInfo from request
806
-	 * @throws \Exception
807
-	 * @return string|false Path info or false when not found
808
-	 */
809
-	public function getPathInfo() {
810
-		$pathInfo = $this->getRawPathInfo();
811
-		// following is taken from \Sabre\HTTP\URLUtil::decodePathSegment
812
-		$pathInfo = rawurldecode($pathInfo);
813
-		$encoding = mb_detect_encoding($pathInfo, ['UTF-8', 'ISO-8859-1']);
814
-
815
-		switch ($encoding) {
816
-			case 'ISO-8859-1':
817
-				$pathInfo = utf8_encode($pathInfo);
818
-		}
819
-		// end copy
820
-
821
-		return $pathInfo;
822
-	}
823
-
824
-	/**
825
-	 * Returns the script name, even if the website uses one or more
826
-	 * reverse proxies
827
-	 * @return string the script name
828
-	 */
829
-	public function getScriptName(): string {
830
-		$name = $this->server['SCRIPT_NAME'];
831
-		$overwriteWebRoot = $this->config->getSystemValue('overwritewebroot');
832
-		if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) {
833
-			// FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
834
-			$serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -\strlen('lib/private/appframework/http/')));
835
-			$suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), \strlen($serverRoot)));
836
-			$name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
837
-		}
838
-		return $name;
839
-	}
840
-
841
-	/**
842
-	 * Checks whether the user agent matches a given regex
843
-	 * @param array $agent array of agent names
844
-	 * @return bool true if at least one of the given agent matches, false otherwise
845
-	 */
846
-	public function isUserAgent(array $agent): bool {
847
-		if (!isset($this->server['HTTP_USER_AGENT'])) {
848
-			return false;
849
-		}
850
-		foreach ($agent as $regex) {
851
-			if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) {
852
-				return true;
853
-			}
854
-		}
855
-		return false;
856
-	}
857
-
858
-	/**
859
-	 * Returns the unverified server host from the headers without checking
860
-	 * whether it is a trusted domain
861
-	 * @return string Server host
862
-	 */
863
-	public function getInsecureServerHost(): string {
864
-		if ($this->fromTrustedProxy() && $this->getOverwriteHost() !== null) {
865
-			return $this->getOverwriteHost();
866
-		}
867
-
868
-		$host = 'localhost';
869
-		if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_HOST'])) {
870
-			if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) {
871
-				$parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']);
872
-				$host = trim(current($parts));
873
-			} else {
874
-				$host = $this->server['HTTP_X_FORWARDED_HOST'];
875
-			}
876
-		} else {
877
-			if (isset($this->server['HTTP_HOST'])) {
878
-				$host = $this->server['HTTP_HOST'];
879
-			} elseif (isset($this->server['SERVER_NAME'])) {
880
-				$host = $this->server['SERVER_NAME'];
881
-			}
882
-		}
883
-
884
-		return $host;
885
-	}
886
-
887
-
888
-	/**
889
-	 * Returns the server host from the headers, or the first configured
890
-	 * trusted domain if the host isn't in the trusted list
891
-	 * @return string Server host
892
-	 */
893
-	public function getServerHost(): string {
894
-		// overwritehost is always trusted
895
-		$host = $this->getOverwriteHost();
896
-		if ($host !== null) {
897
-			return $host;
898
-		}
899
-
900
-		// get the host from the headers
901
-		$host = $this->getInsecureServerHost();
902
-
903
-		// Verify that the host is a trusted domain if the trusted domains
904
-		// are defined
905
-		// If no trusted domain is provided the first trusted domain is returned
906
-		$trustedDomainHelper = new TrustedDomainHelper($this->config);
907
-		if ($trustedDomainHelper->isTrustedDomain($host)) {
908
-			return $host;
909
-		}
910
-
911
-		$trustedList = (array)$this->config->getSystemValue('trusted_domains', []);
912
-		if (count($trustedList) > 0) {
913
-			return reset($trustedList);
914
-		}
915
-
916
-		return '';
917
-	}
918
-
919
-	/**
920
-	 * Returns the overwritehost setting from the config if set and
921
-	 * if the overwrite condition is met
922
-	 * @return string|null overwritehost value or null if not defined or the defined condition
923
-	 * isn't met
924
-	 */
925
-	private function getOverwriteHost() {
926
-		if ($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) {
927
-			return $this->config->getSystemValue('overwritehost');
928
-		}
929
-		return null;
930
-	}
931
-
932
-	private function fromTrustedProxy(): bool {
933
-		$remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
934
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
935
-
936
-		return \is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress);
937
-	}
67
+    public const USER_AGENT_IE = '/(MSIE)|(Trident)/';
68
+    // Microsoft Edge User Agent from https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
69
+    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.]+$/';
70
+    // Firefox User Agent from https://developer.mozilla.org/en-US/docs/Web/HTTP/Gecko_user_agent_string_reference
71
+    public const USER_AGENT_FIREFOX = '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/[0-9.]+$/';
72
+    // Chrome User Agent from https://developer.chrome.com/multidevice/user-agent
73
+    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.]+|)$/';
74
+    // Safari User Agent from http://www.useragentstring.com/pages/Safari/
75
+    public const USER_AGENT_SAFARI = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/[0-9.]+ Safari\/[0-9.A-Z]+$/';
76
+    // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent
77
+    public const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#';
78
+    public const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#';
79
+    public const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost|\[::1\])$/';
80
+
81
+    /**
82
+     * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_IOS instead
83
+     */
84
+    public const USER_AGENT_OWNCLOUD_IOS = '/^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)\-iOS.*$/';
85
+    /**
86
+     * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_ANDROID instead
87
+     */
88
+    public const USER_AGENT_OWNCLOUD_ANDROID = '/^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/';
89
+    /**
90
+     * @deprecated use \OCP\IRequest::USER_AGENT_CLIENT_DESKTOP instead
91
+     */
92
+    public const USER_AGENT_OWNCLOUD_DESKTOP = '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/';
93
+
94
+    protected $inputStream;
95
+    protected $content;
96
+    protected $items = [];
97
+    protected $allowedKeys = [
98
+        'get',
99
+        'post',
100
+        'files',
101
+        'server',
102
+        'env',
103
+        'cookies',
104
+        'urlParams',
105
+        'parameters',
106
+        'method',
107
+        'requesttoken',
108
+    ];
109
+    /** @var ISecureRandom */
110
+    protected $secureRandom;
111
+    /** @var IConfig */
112
+    protected $config;
113
+    /** @var string */
114
+    protected $requestId = '';
115
+    /** @var ICrypto */
116
+    protected $crypto;
117
+    /** @var CsrfTokenManager|null */
118
+    protected $csrfTokenManager;
119
+
120
+    /** @var bool */
121
+    protected $contentDecoded = false;
122
+
123
+    /**
124
+     * @param array $vars An associative array with the following optional values:
125
+     *        - array 'urlParams' the parameters which were matched from the URL
126
+     *        - array 'get' the $_GET array
127
+     *        - array|string 'post' the $_POST array or JSON string
128
+     *        - array 'files' the $_FILES array
129
+     *        - array 'server' the $_SERVER array
130
+     *        - array 'env' the $_ENV array
131
+     *        - array 'cookies' the $_COOKIE array
132
+     *        - string 'method' the request method (GET, POST etc)
133
+     *        - string|false 'requesttoken' the requesttoken or false when not available
134
+     * @param ISecureRandom $secureRandom
135
+     * @param IConfig $config
136
+     * @param CsrfTokenManager|null $csrfTokenManager
137
+     * @param string $stream
138
+     * @see https://www.php.net/manual/en/reserved.variables.php
139
+     */
140
+    public function __construct(array $vars,
141
+                                ISecureRandom $secureRandom,
142
+                                IConfig $config,
143
+                                CsrfTokenManager $csrfTokenManager = null,
144
+                                string $stream = 'php://input') {
145
+        $this->inputStream = $stream;
146
+        $this->items['params'] = [];
147
+        $this->secureRandom = $secureRandom;
148
+        $this->config = $config;
149
+        $this->csrfTokenManager = $csrfTokenManager;
150
+
151
+        if (!array_key_exists('method', $vars)) {
152
+            $vars['method'] = 'GET';
153
+        }
154
+
155
+        foreach ($this->allowedKeys as $name) {
156
+            $this->items[$name] = isset($vars[$name])
157
+                ? $vars[$name]
158
+                : [];
159
+        }
160
+
161
+        $this->items['parameters'] = array_merge(
162
+            $this->items['get'],
163
+            $this->items['post'],
164
+            $this->items['urlParams'],
165
+            $this->items['params']
166
+        );
167
+    }
168
+    /**
169
+     * @param array $parameters
170
+     */
171
+    public function setUrlParameters(array $parameters) {
172
+        $this->items['urlParams'] = $parameters;
173
+        $this->items['parameters'] = array_merge(
174
+            $this->items['parameters'],
175
+            $this->items['urlParams']
176
+        );
177
+    }
178
+
179
+    /**
180
+     * Countable method
181
+     * @return int
182
+     */
183
+    public function count(): int {
184
+        return \count($this->items['parameters']);
185
+    }
186
+
187
+    /**
188
+     * ArrayAccess methods
189
+     *
190
+     * Gives access to the combined GET, POST and urlParams arrays
191
+     *
192
+     * Examples:
193
+     *
194
+     * $var = $request['myvar'];
195
+     *
196
+     * or
197
+     *
198
+     * if(!isset($request['myvar']) {
199
+     * 	// Do something
200
+     * }
201
+     *
202
+     * $request['myvar'] = 'something'; // This throws an exception.
203
+     *
204
+     * @param string $offset The key to lookup
205
+     * @return boolean
206
+     */
207
+    public function offsetExists($offset): bool {
208
+        return isset($this->items['parameters'][$offset]);
209
+    }
210
+
211
+    /**
212
+     * @see offsetExists
213
+     * @param string $offset
214
+     * @return mixed
215
+     */
216
+    public function offsetGet($offset) {
217
+        return isset($this->items['parameters'][$offset])
218
+            ? $this->items['parameters'][$offset]
219
+            : null;
220
+    }
221
+
222
+    /**
223
+     * @see offsetExists
224
+     * @param string $offset
225
+     * @param mixed $value
226
+     */
227
+    public function offsetSet($offset, $value) {
228
+        throw new \RuntimeException('You cannot change the contents of the request object');
229
+    }
230
+
231
+    /**
232
+     * @see offsetExists
233
+     * @param string $offset
234
+     */
235
+    public function offsetUnset($offset) {
236
+        throw new \RuntimeException('You cannot change the contents of the request object');
237
+    }
238
+
239
+    /**
240
+     * Magic property accessors
241
+     * @param string $name
242
+     * @param mixed $value
243
+     */
244
+    public function __set($name, $value) {
245
+        throw new \RuntimeException('You cannot change the contents of the request object');
246
+    }
247
+
248
+    /**
249
+     * Access request variables by method and name.
250
+     * Examples:
251
+     *
252
+     * $request->post['myvar']; // Only look for POST variables
253
+     * $request->myvar; or $request->{'myvar'}; or $request->{$myvar}
254
+     * Looks in the combined GET, POST and urlParams array.
255
+     *
256
+     * If you access e.g. ->post but the current HTTP request method
257
+     * is GET a \LogicException will be thrown.
258
+     *
259
+     * @param string $name The key to look for.
260
+     * @throws \LogicException
261
+     * @return mixed|null
262
+     */
263
+    public function __get($name) {
264
+        switch ($name) {
265
+            case 'put':
266
+            case 'patch':
267
+            case 'get':
268
+            case 'post':
269
+                if ($this->method !== strtoupper($name)) {
270
+                    throw new \LogicException(sprintf('%s cannot be accessed in a %s request.', $name, $this->method));
271
+                }
272
+                return $this->getContent();
273
+            case 'files':
274
+            case 'server':
275
+            case 'env':
276
+            case 'cookies':
277
+            case 'urlParams':
278
+            case 'method':
279
+                return isset($this->items[$name])
280
+                    ? $this->items[$name]
281
+                    : null;
282
+            case 'parameters':
283
+            case 'params':
284
+                return $this->getContent();
285
+            default:
286
+                return isset($this[$name])
287
+                    ? $this[$name]
288
+                    : null;
289
+        }
290
+    }
291
+
292
+    /**
293
+     * @param string $name
294
+     * @return bool
295
+     */
296
+    public function __isset($name) {
297
+        if (\in_array($name, $this->allowedKeys, true)) {
298
+            return true;
299
+        }
300
+        return isset($this->items['parameters'][$name]);
301
+    }
302
+
303
+    /**
304
+     * @param string $id
305
+     */
306
+    public function __unset($id) {
307
+        throw new \RuntimeException('You cannot change the contents of the request object');
308
+    }
309
+
310
+    /**
311
+     * Returns the value for a specific http header.
312
+     *
313
+     * This method returns an empty string if the header did not exist.
314
+     *
315
+     * @param string $name
316
+     * @return string
317
+     */
318
+    public function getHeader(string $name): string {
319
+        $name = strtoupper(str_replace('-', '_',$name));
320
+        if (isset($this->server['HTTP_' . $name])) {
321
+            return $this->server['HTTP_' . $name];
322
+        }
323
+
324
+        // There's a few headers that seem to end up in the top-level
325
+        // server array.
326
+        switch ($name) {
327
+            case 'CONTENT_TYPE':
328
+            case 'CONTENT_LENGTH':
329
+            case 'REMOTE_ADDR':
330
+                if (isset($this->server[$name])) {
331
+                    return $this->server[$name];
332
+                }
333
+                break;
334
+        }
335
+
336
+        return '';
337
+    }
338
+
339
+    /**
340
+     * Lets you access post and get parameters by the index
341
+     * In case of json requests the encoded json body is accessed
342
+     *
343
+     * @param string $key the key which you want to access in the URL Parameter
344
+     *                     placeholder, $_POST or $_GET array.
345
+     *                     The priority how they're returned is the following:
346
+     *                     1. URL parameters
347
+     *                     2. POST parameters
348
+     *                     3. GET parameters
349
+     * @param mixed $default If the key is not found, this value will be returned
350
+     * @return mixed the content of the array
351
+     */
352
+    public function getParam(string $key, $default = null) {
353
+        return isset($this->parameters[$key])
354
+            ? $this->parameters[$key]
355
+            : $default;
356
+    }
357
+
358
+    /**
359
+     * Returns all params that were received, be it from the request
360
+     * (as GET or POST) or throuh the URL by the route
361
+     * @return array the array with all parameters
362
+     */
363
+    public function getParams(): array {
364
+        return is_array($this->parameters) ? $this->parameters : [];
365
+    }
366
+
367
+    /**
368
+     * Returns the method of the request
369
+     * @return string the method of the request (POST, GET, etc)
370
+     */
371
+    public function getMethod(): string {
372
+        return $this->method;
373
+    }
374
+
375
+    /**
376
+     * Shortcut for accessing an uploaded file through the $_FILES array
377
+     * @param string $key the key that will be taken from the $_FILES array
378
+     * @return array the file in the $_FILES element
379
+     */
380
+    public function getUploadedFile(string $key) {
381
+        return isset($this->files[$key]) ? $this->files[$key] : null;
382
+    }
383
+
384
+    /**
385
+     * Shortcut for getting env variables
386
+     * @param string $key the key that will be taken from the $_ENV array
387
+     * @return array the value in the $_ENV element
388
+     */
389
+    public function getEnv(string $key) {
390
+        return isset($this->env[$key]) ? $this->env[$key] : null;
391
+    }
392
+
393
+    /**
394
+     * Shortcut for getting cookie variables
395
+     * @param string $key the key that will be taken from the $_COOKIE array
396
+     * @return string the value in the $_COOKIE element
397
+     */
398
+    public function getCookie(string $key) {
399
+        return isset($this->cookies[$key]) ? $this->cookies[$key] : null;
400
+    }
401
+
402
+    /**
403
+     * Returns the request body content.
404
+     *
405
+     * If the HTTP request method is PUT and the body
406
+     * not application/x-www-form-urlencoded or application/json a stream
407
+     * resource is returned, otherwise an array.
408
+     *
409
+     * @return array|string|resource The request body content or a resource to read the body stream.
410
+     *
411
+     * @throws \LogicException
412
+     */
413
+    protected function getContent() {
414
+        // If the content can't be parsed into an array then return a stream resource.
415
+        if ($this->method === 'PUT'
416
+            && $this->getHeader('Content-Length') !== '0'
417
+            && $this->getHeader('Content-Length') !== ''
418
+            && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false
419
+            && strpos($this->getHeader('Content-Type'), 'application/json') === false
420
+        ) {
421
+            if ($this->content === false) {
422
+                throw new \LogicException(
423
+                    '"put" can only be accessed once if not '
424
+                    . 'application/x-www-form-urlencoded or application/json.'
425
+                );
426
+            }
427
+            $this->content = false;
428
+            return fopen($this->inputStream, 'rb');
429
+        } else {
430
+            $this->decodeContent();
431
+            return $this->items['parameters'];
432
+        }
433
+    }
434
+
435
+    /**
436
+     * Attempt to decode the content and populate parameters
437
+     */
438
+    protected function decodeContent() {
439
+        if ($this->contentDecoded) {
440
+            return;
441
+        }
442
+        $params = [];
443
+
444
+        // 'application/json' must be decoded manually.
445
+        if (strpos($this->getHeader('Content-Type'), 'application/json') !== false) {
446
+            $params = json_decode(file_get_contents($this->inputStream), true);
447
+            if ($params !== null && \count($params) > 0) {
448
+                $this->items['params'] = $params;
449
+                if ($this->method === 'POST') {
450
+                    $this->items['post'] = $params;
451
+                }
452
+            }
453
+
454
+            // Handle application/x-www-form-urlencoded for methods other than GET
455
+        // or post correctly
456
+        } elseif ($this->method !== 'GET'
457
+                && $this->method !== 'POST'
458
+                && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) {
459
+            parse_str(file_get_contents($this->inputStream), $params);
460
+            if (\is_array($params)) {
461
+                $this->items['params'] = $params;
462
+            }
463
+        }
464
+
465
+        if (\is_array($params)) {
466
+            $this->items['parameters'] = array_merge($this->items['parameters'], $params);
467
+        }
468
+        $this->contentDecoded = true;
469
+    }
470
+
471
+
472
+    /**
473
+     * Checks if the CSRF check was correct
474
+     * @return bool true if CSRF check passed
475
+     */
476
+    public function passesCSRFCheck(): bool {
477
+        if ($this->csrfTokenManager === null) {
478
+            return false;
479
+        }
480
+
481
+        if (!$this->passesStrictCookieCheck()) {
482
+            return false;
483
+        }
484
+
485
+        if (isset($this->items['get']['requesttoken'])) {
486
+            $token = $this->items['get']['requesttoken'];
487
+        } elseif (isset($this->items['post']['requesttoken'])) {
488
+            $token = $this->items['post']['requesttoken'];
489
+        } elseif (isset($this->items['server']['HTTP_REQUESTTOKEN'])) {
490
+            $token = $this->items['server']['HTTP_REQUESTTOKEN'];
491
+        } else {
492
+            //no token found.
493
+            return false;
494
+        }
495
+        $token = new CsrfToken($token);
496
+
497
+        return $this->csrfTokenManager->isTokenValid($token);
498
+    }
499
+
500
+    /**
501
+     * Whether the cookie checks are required
502
+     *
503
+     * @return bool
504
+     */
505
+    private function cookieCheckRequired(): bool {
506
+        if ($this->getHeader('OCS-APIREQUEST')) {
507
+            return false;
508
+        }
509
+        if ($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
510
+            return false;
511
+        }
512
+
513
+        return true;
514
+    }
515
+
516
+    /**
517
+     * Wrapper around session_get_cookie_params
518
+     *
519
+     * @return array
520
+     */
521
+    public function getCookieParams(): array {
522
+        return session_get_cookie_params();
523
+    }
524
+
525
+    /**
526
+     * Appends the __Host- prefix to the cookie if applicable
527
+     *
528
+     * @param string $name
529
+     * @return string
530
+     */
531
+    protected function getProtectedCookieName(string $name): string {
532
+        $cookieParams = $this->getCookieParams();
533
+        $prefix = '';
534
+        if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
535
+            $prefix = '__Host-';
536
+        }
537
+
538
+        return $prefix.$name;
539
+    }
540
+
541
+    /**
542
+     * Checks if the strict cookie has been sent with the request if the request
543
+     * is including any cookies.
544
+     *
545
+     * @return bool
546
+     * @since 9.1.0
547
+     */
548
+    public function passesStrictCookieCheck(): bool {
549
+        if (!$this->cookieCheckRequired()) {
550
+            return true;
551
+        }
552
+
553
+        $cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict');
554
+        if ($this->getCookie($cookieName) === 'true'
555
+            && $this->passesLaxCookieCheck()) {
556
+            return true;
557
+        }
558
+        return false;
559
+    }
560
+
561
+    /**
562
+     * Checks if the lax cookie has been sent with the request if the request
563
+     * is including any cookies.
564
+     *
565
+     * @return bool
566
+     * @since 9.1.0
567
+     */
568
+    public function passesLaxCookieCheck(): bool {
569
+        if (!$this->cookieCheckRequired()) {
570
+            return true;
571
+        }
572
+
573
+        $cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax');
574
+        if ($this->getCookie($cookieName) === 'true') {
575
+            return true;
576
+        }
577
+        return false;
578
+    }
579
+
580
+
581
+    /**
582
+     * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
583
+     * If `mod_unique_id` is installed this value will be taken.
584
+     * @return string
585
+     */
586
+    public function getId(): string {
587
+        if (isset($this->server['UNIQUE_ID'])) {
588
+            return $this->server['UNIQUE_ID'];
589
+        }
590
+
591
+        if (empty($this->requestId)) {
592
+            $validChars = ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS;
593
+            $this->requestId = $this->secureRandom->generate(20, $validChars);
594
+        }
595
+
596
+        return $this->requestId;
597
+    }
598
+
599
+    /**
600
+     * Checks if given $remoteAddress matches given $trustedProxy.
601
+     * If $trustedProxy is an IPv4 IP range given in CIDR notation, true will be returned if
602
+     * $remoteAddress is an IPv4 address within that IP range.
603
+     * Otherwise $remoteAddress will be compared to $trustedProxy literally and the result
604
+     * will be returned.
605
+     * @return boolean true if $remoteAddress matches $trustedProxy, false otherwise
606
+     */
607
+    protected function matchesTrustedProxy($trustedProxy, $remoteAddress) {
608
+        $cidrre = '/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/([0-9]{1,2})$/';
609
+
610
+        if (preg_match($cidrre, $trustedProxy, $match)) {
611
+            $net = $match[1];
612
+            $shiftbits = min(32, max(0, 32 - intval($match[2])));
613
+            $netnum = ip2long($net) >> $shiftbits;
614
+            $ipnum = ip2long($remoteAddress) >> $shiftbits;
615
+
616
+            return $ipnum === $netnum;
617
+        }
618
+
619
+        return $trustedProxy === $remoteAddress;
620
+    }
621
+
622
+    /**
623
+     * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
624
+     * For details regarding what "match" means, refer to `matchesTrustedProxy`.
625
+     * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
626
+     */
627
+    protected function isTrustedProxy($trustedProxies, $remoteAddress) {
628
+        foreach ($trustedProxies as $tp) {
629
+            if ($this->matchesTrustedProxy($tp, $remoteAddress)) {
630
+                return true;
631
+            }
632
+        }
633
+
634
+        return false;
635
+    }
636
+
637
+    /**
638
+     * Returns the remote address, if the connection came from a trusted proxy
639
+     * and `forwarded_for_headers` has been configured then the IP address
640
+     * specified in this header will be returned instead.
641
+     * Do always use this instead of $_SERVER['REMOTE_ADDR']
642
+     * @return string IP address
643
+     */
644
+    public function getRemoteAddress(): string {
645
+        $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
646
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
647
+
648
+        if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
649
+            $forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
650
+                'HTTP_X_FORWARDED_FOR'
651
+                // only have one default, so we cannot ship an insecure product out of the box
652
+            ]);
653
+
654
+            foreach ($forwardedForHeaders as $header) {
655
+                if (isset($this->server[$header])) {
656
+                    foreach (explode(',', $this->server[$header]) as $IP) {
657
+                        $IP = trim($IP);
658
+
659
+                        // remove brackets from IPv6 addresses
660
+                        if (strpos($IP, '[') === 0 && substr($IP, -1) === ']') {
661
+                            $IP = substr($IP, 1, -1);
662
+                        }
663
+
664
+                        if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
665
+                            return $IP;
666
+                        }
667
+                    }
668
+                }
669
+            }
670
+        }
671
+
672
+        return $remoteAddress;
673
+    }
674
+
675
+    /**
676
+     * Check overwrite condition
677
+     * @param string $type
678
+     * @return bool
679
+     */
680
+    private function isOverwriteCondition(string $type = ''): bool {
681
+        $regex = '/' . $this->config->getSystemValue('overwritecondaddr', '')  . '/';
682
+        $remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
683
+        return $regex === '//' || preg_match($regex, $remoteAddr) === 1
684
+        || $type !== 'protocol';
685
+    }
686
+
687
+    /**
688
+     * Returns the server protocol. It respects one or more reverse proxies servers
689
+     * and load balancers
690
+     * @return string Server protocol (http or https)
691
+     */
692
+    public function getServerProtocol(): string {
693
+        if ($this->config->getSystemValue('overwriteprotocol') !== ''
694
+            && $this->isOverwriteCondition('protocol')) {
695
+            return $this->config->getSystemValue('overwriteprotocol');
696
+        }
697
+
698
+        if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
699
+            if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) {
700
+                $parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
701
+                $proto = strtolower(trim($parts[0]));
702
+            } else {
703
+                $proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']);
704
+            }
705
+
706
+            // Verify that the protocol is always HTTP or HTTPS
707
+            // default to http if an invalid value is provided
708
+            return $proto === 'https' ? 'https' : 'http';
709
+        }
710
+
711
+        if (isset($this->server['HTTPS'])
712
+            && $this->server['HTTPS'] !== null
713
+            && $this->server['HTTPS'] !== 'off'
714
+            && $this->server['HTTPS'] !== '') {
715
+            return 'https';
716
+        }
717
+
718
+        return 'http';
719
+    }
720
+
721
+    /**
722
+     * Returns the used HTTP protocol.
723
+     *
724
+     * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
725
+     */
726
+    public function getHttpProtocol(): string {
727
+        $claimedProtocol = $this->server['SERVER_PROTOCOL'];
728
+
729
+        if (\is_string($claimedProtocol)) {
730
+            $claimedProtocol = strtoupper($claimedProtocol);
731
+        }
732
+
733
+        $validProtocols = [
734
+            'HTTP/1.0',
735
+            'HTTP/1.1',
736
+            'HTTP/2',
737
+        ];
738
+
739
+        if (\in_array($claimedProtocol, $validProtocols, true)) {
740
+            return $claimedProtocol;
741
+        }
742
+
743
+        return 'HTTP/1.1';
744
+    }
745
+
746
+    /**
747
+     * Returns the request uri, even if the website uses one or more
748
+     * reverse proxies
749
+     * @return string
750
+     */
751
+    public function getRequestUri(): string {
752
+        $uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
753
+        if ($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
754
+            $uri = $this->getScriptName() . substr($uri, \strlen($this->server['SCRIPT_NAME']));
755
+        }
756
+        return $uri;
757
+    }
758
+
759
+    /**
760
+     * Get raw PathInfo from request (not urldecoded)
761
+     * @throws \Exception
762
+     * @return string Path info
763
+     */
764
+    public function getRawPathInfo(): string {
765
+        $requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
766
+        // remove too many slashes - can be caused by reverse proxy configuration
767
+        $requestUri = preg_replace('%/{2,}%', '/', $requestUri);
768
+
769
+        // Remove the query string from REQUEST_URI
770
+        if ($pos = strpos($requestUri, '?')) {
771
+            $requestUri = substr($requestUri, 0, $pos);
772
+        }
773
+
774
+        $scriptName = $this->server['SCRIPT_NAME'];
775
+        $pathInfo = $requestUri;
776
+
777
+        // strip off the script name's dir and file name
778
+        // FIXME: Sabre does not really belong here
779
+        [$path, $name] = \Sabre\Uri\split($scriptName);
780
+        if (!empty($path)) {
781
+            if ($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) {
782
+                $pathInfo = substr($pathInfo, \strlen($path));
783
+            } else {
784
+                throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
785
+            }
786
+        }
787
+        if ($name === null) {
788
+            $name = '';
789
+        }
790
+
791
+        if (strpos($pathInfo, '/'.$name) === 0) {
792
+            $pathInfo = substr($pathInfo, \strlen($name) + 1);
793
+        }
794
+        if ($name !== '' && strpos($pathInfo, $name) === 0) {
795
+            $pathInfo = substr($pathInfo, \strlen($name));
796
+        }
797
+        if ($pathInfo === false || $pathInfo === '/') {
798
+            return '';
799
+        } else {
800
+            return $pathInfo;
801
+        }
802
+    }
803
+
804
+    /**
805
+     * Get PathInfo from request
806
+     * @throws \Exception
807
+     * @return string|false Path info or false when not found
808
+     */
809
+    public function getPathInfo() {
810
+        $pathInfo = $this->getRawPathInfo();
811
+        // following is taken from \Sabre\HTTP\URLUtil::decodePathSegment
812
+        $pathInfo = rawurldecode($pathInfo);
813
+        $encoding = mb_detect_encoding($pathInfo, ['UTF-8', 'ISO-8859-1']);
814
+
815
+        switch ($encoding) {
816
+            case 'ISO-8859-1':
817
+                $pathInfo = utf8_encode($pathInfo);
818
+        }
819
+        // end copy
820
+
821
+        return $pathInfo;
822
+    }
823
+
824
+    /**
825
+     * Returns the script name, even if the website uses one or more
826
+     * reverse proxies
827
+     * @return string the script name
828
+     */
829
+    public function getScriptName(): string {
830
+        $name = $this->server['SCRIPT_NAME'];
831
+        $overwriteWebRoot = $this->config->getSystemValue('overwritewebroot');
832
+        if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) {
833
+            // FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
834
+            $serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -\strlen('lib/private/appframework/http/')));
835
+            $suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), \strlen($serverRoot)));
836
+            $name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
837
+        }
838
+        return $name;
839
+    }
840
+
841
+    /**
842
+     * Checks whether the user agent matches a given regex
843
+     * @param array $agent array of agent names
844
+     * @return bool true if at least one of the given agent matches, false otherwise
845
+     */
846
+    public function isUserAgent(array $agent): bool {
847
+        if (!isset($this->server['HTTP_USER_AGENT'])) {
848
+            return false;
849
+        }
850
+        foreach ($agent as $regex) {
851
+            if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) {
852
+                return true;
853
+            }
854
+        }
855
+        return false;
856
+    }
857
+
858
+    /**
859
+     * Returns the unverified server host from the headers without checking
860
+     * whether it is a trusted domain
861
+     * @return string Server host
862
+     */
863
+    public function getInsecureServerHost(): string {
864
+        if ($this->fromTrustedProxy() && $this->getOverwriteHost() !== null) {
865
+            return $this->getOverwriteHost();
866
+        }
867
+
868
+        $host = 'localhost';
869
+        if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_HOST'])) {
870
+            if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) {
871
+                $parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']);
872
+                $host = trim(current($parts));
873
+            } else {
874
+                $host = $this->server['HTTP_X_FORWARDED_HOST'];
875
+            }
876
+        } else {
877
+            if (isset($this->server['HTTP_HOST'])) {
878
+                $host = $this->server['HTTP_HOST'];
879
+            } elseif (isset($this->server['SERVER_NAME'])) {
880
+                $host = $this->server['SERVER_NAME'];
881
+            }
882
+        }
883
+
884
+        return $host;
885
+    }
886
+
887
+
888
+    /**
889
+     * Returns the server host from the headers, or the first configured
890
+     * trusted domain if the host isn't in the trusted list
891
+     * @return string Server host
892
+     */
893
+    public function getServerHost(): string {
894
+        // overwritehost is always trusted
895
+        $host = $this->getOverwriteHost();
896
+        if ($host !== null) {
897
+            return $host;
898
+        }
899
+
900
+        // get the host from the headers
901
+        $host = $this->getInsecureServerHost();
902
+
903
+        // Verify that the host is a trusted domain if the trusted domains
904
+        // are defined
905
+        // If no trusted domain is provided the first trusted domain is returned
906
+        $trustedDomainHelper = new TrustedDomainHelper($this->config);
907
+        if ($trustedDomainHelper->isTrustedDomain($host)) {
908
+            return $host;
909
+        }
910
+
911
+        $trustedList = (array)$this->config->getSystemValue('trusted_domains', []);
912
+        if (count($trustedList) > 0) {
913
+            return reset($trustedList);
914
+        }
915
+
916
+        return '';
917
+    }
918
+
919
+    /**
920
+     * Returns the overwritehost setting from the config if set and
921
+     * if the overwrite condition is met
922
+     * @return string|null overwritehost value or null if not defined or the defined condition
923
+     * isn't met
924
+     */
925
+    private function getOverwriteHost() {
926
+        if ($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) {
927
+            return $this->config->getSystemValue('overwritehost');
928
+        }
929
+        return null;
930
+    }
931
+
932
+    private function fromTrustedProxy(): bool {
933
+        $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
934
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
935
+
936
+        return \is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress);
937
+    }
938 938
 }
Please login to merge, or discard this patch.
lib/private/AppFramework/App.php 2 patches
Indentation   +176 added lines, -176 removed lines patch added patch discarded remove patch
@@ -50,185 +50,185 @@
 block discarded – undo
50 50
  */
51 51
 class App {
52 52
 
53
-	/** @var string[] */
54
-	private static $nameSpaceCache = [];
55
-
56
-	/**
57
-	 * Turns an app id into a namespace by either reading the appinfo.xml's
58
-	 * namespace tag or uppercasing the appid's first letter
59
-	 * @param string $appId the app id
60
-	 * @param string $topNamespace the namespace which should be prepended to
61
-	 * the transformed app id, defaults to OCA\
62
-	 * @return string the starting namespace for the app
63
-	 */
64
-	public static function buildAppNamespace(string $appId, string $topNamespace = 'OCA\\'): string {
65
-		// Hit the cache!
66
-		if (isset(self::$nameSpaceCache[$appId])) {
67
-			return $topNamespace . self::$nameSpaceCache[$appId];
68
-		}
69
-
70
-		$appInfo = \OC_App::getAppInfo($appId);
71
-		if (isset($appInfo['namespace'])) {
72
-			self::$nameSpaceCache[$appId] = trim($appInfo['namespace']);
73
-		} else {
74
-			if ($appId !== 'spreed') {
75
-				// if the tag is not found, fall back to uppercasing the first letter
76
-				self::$nameSpaceCache[$appId] = ucfirst($appId);
77
-			} else {
78
-				// For the Talk app (appid spreed) the above fallback doesn't work.
79
-				// This leads to a problem when trying to install it freshly,
80
-				// because the apps namespace is already registered before the
81
-				// app is downloaded from the appstore, because of the hackish
82
-				// global route index.php/call/{token} which is registered via
83
-				// the core/routes.php so it does not have the app namespace.
84
-				// @ref https://github.com/nextcloud/server/pull/19433
85
-				self::$nameSpaceCache[$appId] = 'Talk';
86
-			}
87
-		}
88
-
89
-		return $topNamespace . self::$nameSpaceCache[$appId];
90
-	}
91
-
92
-	public static function getAppIdForClass(string $className, string $topNamespace = 'OCA\\'): ?string {
93
-		if (strpos($className, $topNamespace) !== 0) {
94
-			return null;
95
-		}
96
-
97
-		foreach (self::$nameSpaceCache as $appId => $namespace) {
98
-			if (strpos($className, $topNamespace . $namespace . '\\') === 0) {
99
-				return $appId;
100
-			}
101
-		}
102
-
103
-		return null;
104
-	}
105
-
106
-
107
-	/**
108
-	 * Shortcut for calling a controller method and printing the result
109
-	 * @param string $controllerName the name of the controller under which it is
110
-	 *                               stored in the DI container
111
-	 * @param string $methodName the method that you want to call
112
-	 * @param DIContainer $container an instance of a pimple container.
113
-	 * @param array $urlParams list of URL parameters (optional)
114
-	 * @throws HintException
115
-	 */
116
-	public static function main(string $controllerName, string $methodName, DIContainer $container, array $urlParams = null) {
117
-		if (!is_null($urlParams)) {
118
-			/** @var Request $request */
119
-			$request = $container->query(IRequest::class);
120
-			$request->setUrlParameters($urlParams);
121
-		} elseif (isset($container['urlParams']) && !is_null($container['urlParams'])) {
122
-			/** @var Request $request */
123
-			$request = $container->query(IRequest::class);
124
-			$request->setUrlParameters($container['urlParams']);
125
-		}
126
-		$appName = $container['AppName'];
127
-
128
-		// first try $controllerName then go for \OCA\AppName\Controller\$controllerName
129
-		try {
130
-			$controller = $container->query($controllerName);
131
-		} catch (QueryException $e) {
132
-			if (strpos($controllerName, '\\Controller\\') !== false) {
133
-				// This is from a global registered app route that is not enabled.
134
-				[/*OC(A)*/, $app, /* Controller/Name*/] = explode('\\', $controllerName, 3);
135
-				throw new HintException('App ' . strtolower($app) . ' is not enabled');
136
-			}
137
-
138
-			if ($appName === 'core') {
139
-				$appNameSpace = 'OC\\Core';
140
-			} else {
141
-				$appNameSpace = self::buildAppNamespace($appName);
142
-			}
143
-			$controllerName = $appNameSpace . '\\Controller\\' . $controllerName;
144
-			$controller = $container->query($controllerName);
145
-		}
146
-
147
-		// initialize the dispatcher and run all the middleware before the controller
148
-		/** @var Dispatcher $dispatcher */
149
-		$dispatcher = $container['Dispatcher'];
150
-
151
-		[
152
-			$httpHeaders,
153
-			$responseHeaders,
154
-			$responseCookies,
155
-			$output,
156
-			$response
157
-		] = $dispatcher->dispatch($controller, $methodName);
158
-
159
-		$io = $container[IOutput::class];
160
-
161
-		if (!is_null($httpHeaders)) {
162
-			$io->setHeader($httpHeaders);
163
-		}
164
-
165
-		foreach ($responseHeaders as $name => $value) {
166
-			$io->setHeader($name . ': ' . $value);
167
-		}
168
-
169
-		foreach ($responseCookies as $name => $value) {
170
-			$expireDate = null;
171
-			if ($value['expireDate'] instanceof \DateTime) {
172
-				$expireDate = $value['expireDate']->getTimestamp();
173
-			}
174
-			$sameSite = $value['sameSite'] ?? 'Lax';
175
-
176
-			$io->setCookie(
177
-				$name,
178
-				$value['value'],
179
-				$expireDate,
180
-				$container->getServer()->getWebRoot(),
181
-				null,
182
-				$container->getServer()->getRequest()->getServerProtocol() === 'https',
183
-				true,
184
-				$sameSite
185
-			);
186
-		}
187
-
188
-		/*
53
+    /** @var string[] */
54
+    private static $nameSpaceCache = [];
55
+
56
+    /**
57
+     * Turns an app id into a namespace by either reading the appinfo.xml's
58
+     * namespace tag or uppercasing the appid's first letter
59
+     * @param string $appId the app id
60
+     * @param string $topNamespace the namespace which should be prepended to
61
+     * the transformed app id, defaults to OCA\
62
+     * @return string the starting namespace for the app
63
+     */
64
+    public static function buildAppNamespace(string $appId, string $topNamespace = 'OCA\\'): string {
65
+        // Hit the cache!
66
+        if (isset(self::$nameSpaceCache[$appId])) {
67
+            return $topNamespace . self::$nameSpaceCache[$appId];
68
+        }
69
+
70
+        $appInfo = \OC_App::getAppInfo($appId);
71
+        if (isset($appInfo['namespace'])) {
72
+            self::$nameSpaceCache[$appId] = trim($appInfo['namespace']);
73
+        } else {
74
+            if ($appId !== 'spreed') {
75
+                // if the tag is not found, fall back to uppercasing the first letter
76
+                self::$nameSpaceCache[$appId] = ucfirst($appId);
77
+            } else {
78
+                // For the Talk app (appid spreed) the above fallback doesn't work.
79
+                // This leads to a problem when trying to install it freshly,
80
+                // because the apps namespace is already registered before the
81
+                // app is downloaded from the appstore, because of the hackish
82
+                // global route index.php/call/{token} which is registered via
83
+                // the core/routes.php so it does not have the app namespace.
84
+                // @ref https://github.com/nextcloud/server/pull/19433
85
+                self::$nameSpaceCache[$appId] = 'Talk';
86
+            }
87
+        }
88
+
89
+        return $topNamespace . self::$nameSpaceCache[$appId];
90
+    }
91
+
92
+    public static function getAppIdForClass(string $className, string $topNamespace = 'OCA\\'): ?string {
93
+        if (strpos($className, $topNamespace) !== 0) {
94
+            return null;
95
+        }
96
+
97
+        foreach (self::$nameSpaceCache as $appId => $namespace) {
98
+            if (strpos($className, $topNamespace . $namespace . '\\') === 0) {
99
+                return $appId;
100
+            }
101
+        }
102
+
103
+        return null;
104
+    }
105
+
106
+
107
+    /**
108
+     * Shortcut for calling a controller method and printing the result
109
+     * @param string $controllerName the name of the controller under which it is
110
+     *                               stored in the DI container
111
+     * @param string $methodName the method that you want to call
112
+     * @param DIContainer $container an instance of a pimple container.
113
+     * @param array $urlParams list of URL parameters (optional)
114
+     * @throws HintException
115
+     */
116
+    public static function main(string $controllerName, string $methodName, DIContainer $container, array $urlParams = null) {
117
+        if (!is_null($urlParams)) {
118
+            /** @var Request $request */
119
+            $request = $container->query(IRequest::class);
120
+            $request->setUrlParameters($urlParams);
121
+        } elseif (isset($container['urlParams']) && !is_null($container['urlParams'])) {
122
+            /** @var Request $request */
123
+            $request = $container->query(IRequest::class);
124
+            $request->setUrlParameters($container['urlParams']);
125
+        }
126
+        $appName = $container['AppName'];
127
+
128
+        // first try $controllerName then go for \OCA\AppName\Controller\$controllerName
129
+        try {
130
+            $controller = $container->query($controllerName);
131
+        } catch (QueryException $e) {
132
+            if (strpos($controllerName, '\\Controller\\') !== false) {
133
+                // This is from a global registered app route that is not enabled.
134
+                [/*OC(A)*/, $app, /* Controller/Name*/] = explode('\\', $controllerName, 3);
135
+                throw new HintException('App ' . strtolower($app) . ' is not enabled');
136
+            }
137
+
138
+            if ($appName === 'core') {
139
+                $appNameSpace = 'OC\\Core';
140
+            } else {
141
+                $appNameSpace = self::buildAppNamespace($appName);
142
+            }
143
+            $controllerName = $appNameSpace . '\\Controller\\' . $controllerName;
144
+            $controller = $container->query($controllerName);
145
+        }
146
+
147
+        // initialize the dispatcher and run all the middleware before the controller
148
+        /** @var Dispatcher $dispatcher */
149
+        $dispatcher = $container['Dispatcher'];
150
+
151
+        [
152
+            $httpHeaders,
153
+            $responseHeaders,
154
+            $responseCookies,
155
+            $output,
156
+            $response
157
+        ] = $dispatcher->dispatch($controller, $methodName);
158
+
159
+        $io = $container[IOutput::class];
160
+
161
+        if (!is_null($httpHeaders)) {
162
+            $io->setHeader($httpHeaders);
163
+        }
164
+
165
+        foreach ($responseHeaders as $name => $value) {
166
+            $io->setHeader($name . ': ' . $value);
167
+        }
168
+
169
+        foreach ($responseCookies as $name => $value) {
170
+            $expireDate = null;
171
+            if ($value['expireDate'] instanceof \DateTime) {
172
+                $expireDate = $value['expireDate']->getTimestamp();
173
+            }
174
+            $sameSite = $value['sameSite'] ?? 'Lax';
175
+
176
+            $io->setCookie(
177
+                $name,
178
+                $value['value'],
179
+                $expireDate,
180
+                $container->getServer()->getWebRoot(),
181
+                null,
182
+                $container->getServer()->getRequest()->getServerProtocol() === 'https',
183
+                true,
184
+                $sameSite
185
+            );
186
+        }
187
+
188
+        /*
189 189
 		 * Status 204 does not have a body and no Content Length
190 190
 		 * Status 304 does not have a body and does not need a Content Length
191 191
 		 * https://tools.ietf.org/html/rfc7230#section-3.3
192 192
 		 * https://tools.ietf.org/html/rfc7230#section-3.3.2
193 193
 		 */
194
-		$emptyResponse = false;
195
-		if (preg_match('/^HTTP\/\d\.\d (\d{3}) .*$/', $httpHeaders, $matches)) {
196
-			$status = (int)$matches[1];
197
-			if ($status === Http::STATUS_NO_CONTENT || $status === Http::STATUS_NOT_MODIFIED) {
198
-				$emptyResponse = true;
199
-			}
200
-		}
201
-
202
-		if (!$emptyResponse) {
203
-			if ($response instanceof ICallbackResponse) {
204
-				$response->callback($io);
205
-			} elseif (!is_null($output)) {
206
-				$io->setHeader('Content-Length: ' . strlen($output));
207
-				$io->setOutput($output);
208
-			}
209
-		}
210
-	}
211
-
212
-	/**
213
-	 * Shortcut for calling a controller method and printing the result.
214
-	 * Similar to App:main except that no headers will be sent.
215
-	 * This should be used for example when registering sections via
216
-	 * \OC\AppFramework\Core\API::registerAdmin()
217
-	 *
218
-	 * @param string $controllerName the name of the controller under which it is
219
-	 *                               stored in the DI container
220
-	 * @param string $methodName the method that you want to call
221
-	 * @param array $urlParams an array with variables extracted from the routes
222
-	 * @param DIContainer $container an instance of a pimple container.
223
-	 */
224
-	public static function part(string $controllerName, string $methodName, array $urlParams,
225
-								DIContainer $container) {
226
-		$container['urlParams'] = $urlParams;
227
-		$controller = $container[$controllerName];
228
-
229
-		$dispatcher = $container['Dispatcher'];
230
-
231
-		[, , $output] = $dispatcher->dispatch($controller, $methodName);
232
-		return $output;
233
-	}
194
+        $emptyResponse = false;
195
+        if (preg_match('/^HTTP\/\d\.\d (\d{3}) .*$/', $httpHeaders, $matches)) {
196
+            $status = (int)$matches[1];
197
+            if ($status === Http::STATUS_NO_CONTENT || $status === Http::STATUS_NOT_MODIFIED) {
198
+                $emptyResponse = true;
199
+            }
200
+        }
201
+
202
+        if (!$emptyResponse) {
203
+            if ($response instanceof ICallbackResponse) {
204
+                $response->callback($io);
205
+            } elseif (!is_null($output)) {
206
+                $io->setHeader('Content-Length: ' . strlen($output));
207
+                $io->setOutput($output);
208
+            }
209
+        }
210
+    }
211
+
212
+    /**
213
+     * Shortcut for calling a controller method and printing the result.
214
+     * Similar to App:main except that no headers will be sent.
215
+     * This should be used for example when registering sections via
216
+     * \OC\AppFramework\Core\API::registerAdmin()
217
+     *
218
+     * @param string $controllerName the name of the controller under which it is
219
+     *                               stored in the DI container
220
+     * @param string $methodName the method that you want to call
221
+     * @param array $urlParams an array with variables extracted from the routes
222
+     * @param DIContainer $container an instance of a pimple container.
223
+     */
224
+    public static function part(string $controllerName, string $methodName, array $urlParams,
225
+                                DIContainer $container) {
226
+        $container['urlParams'] = $urlParams;
227
+        $controller = $container[$controllerName];
228
+
229
+        $dispatcher = $container['Dispatcher'];
230
+
231
+        [, , $output] = $dispatcher->dispatch($controller, $methodName);
232
+        return $output;
233
+    }
234 234
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -64,7 +64,7 @@  discard block
 block discarded – undo
64 64
 	public static function buildAppNamespace(string $appId, string $topNamespace = 'OCA\\'): string {
65 65
 		// Hit the cache!
66 66
 		if (isset(self::$nameSpaceCache[$appId])) {
67
-			return $topNamespace . self::$nameSpaceCache[$appId];
67
+			return $topNamespace.self::$nameSpaceCache[$appId];
68 68
 		}
69 69
 
70 70
 		$appInfo = \OC_App::getAppInfo($appId);
@@ -86,7 +86,7 @@  discard block
 block discarded – undo
86 86
 			}
87 87
 		}
88 88
 
89
-		return $topNamespace . self::$nameSpaceCache[$appId];
89
+		return $topNamespace.self::$nameSpaceCache[$appId];
90 90
 	}
91 91
 
92 92
 	public static function getAppIdForClass(string $className, string $topNamespace = 'OCA\\'): ?string {
@@ -95,7 +95,7 @@  discard block
 block discarded – undo
95 95
 		}
96 96
 
97 97
 		foreach (self::$nameSpaceCache as $appId => $namespace) {
98
-			if (strpos($className, $topNamespace . $namespace . '\\') === 0) {
98
+			if (strpos($className, $topNamespace.$namespace.'\\') === 0) {
99 99
 				return $appId;
100 100
 			}
101 101
 		}
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
 			if (strpos($controllerName, '\\Controller\\') !== false) {
133 133
 				// This is from a global registered app route that is not enabled.
134 134
 				[/*OC(A)*/, $app, /* Controller/Name*/] = explode('\\', $controllerName, 3);
135
-				throw new HintException('App ' . strtolower($app) . ' is not enabled');
135
+				throw new HintException('App '.strtolower($app).' is not enabled');
136 136
 			}
137 137
 
138 138
 			if ($appName === 'core') {
@@ -140,7 +140,7 @@  discard block
 block discarded – undo
140 140
 			} else {
141 141
 				$appNameSpace = self::buildAppNamespace($appName);
142 142
 			}
143
-			$controllerName = $appNameSpace . '\\Controller\\' . $controllerName;
143
+			$controllerName = $appNameSpace.'\\Controller\\'.$controllerName;
144 144
 			$controller = $container->query($controllerName);
145 145
 		}
146 146
 
@@ -163,7 +163,7 @@  discard block
 block discarded – undo
163 163
 		}
164 164
 
165 165
 		foreach ($responseHeaders as $name => $value) {
166
-			$io->setHeader($name . ': ' . $value);
166
+			$io->setHeader($name.': '.$value);
167 167
 		}
168 168
 
169 169
 		foreach ($responseCookies as $name => $value) {
@@ -193,7 +193,7 @@  discard block
 block discarded – undo
193 193
 		 */
194 194
 		$emptyResponse = false;
195 195
 		if (preg_match('/^HTTP\/\d\.\d (\d{3}) .*$/', $httpHeaders, $matches)) {
196
-			$status = (int)$matches[1];
196
+			$status = (int) $matches[1];
197 197
 			if ($status === Http::STATUS_NO_CONTENT || $status === Http::STATUS_NOT_MODIFIED) {
198 198
 				$emptyResponse = true;
199 199
 			}
@@ -203,7 +203,7 @@  discard block
 block discarded – undo
203 203
 			if ($response instanceof ICallbackResponse) {
204 204
 				$response->callback($io);
205 205
 			} elseif (!is_null($output)) {
206
-				$io->setHeader('Content-Length: ' . strlen($output));
206
+				$io->setHeader('Content-Length: '.strlen($output));
207 207
 				$io->setOutput($output);
208 208
 			}
209 209
 		}
@@ -228,7 +228,7 @@  discard block
 block discarded – undo
228 228
 
229 229
 		$dispatcher = $container['Dispatcher'];
230 230
 
231
-		[, , $output] = $dispatcher->dispatch($controller, $methodName);
231
+		[,, $output] = $dispatcher->dispatch($controller, $methodName);
232 232
 		return $output;
233 233
 	}
234 234
 }
Please login to merge, or discard this patch.