Passed
Push — master ( 898529...45cfee )
by Roeland
14:50 queued 10s
created
lib/private/Cache/File.php 1 patch
Indentation   +157 added lines, -157 removed lines patch added patch discarded remove patch
@@ -38,170 +38,170 @@
 block discarded – undo
38 38
 
39 39
 class File implements ICache {
40 40
 
41
-	/** @var View */
42
-	protected $storage;
41
+    /** @var View */
42
+    protected $storage;
43 43
 
44
-	/**
45
-	 * Returns the cache storage for the logged in user
46
-	 *
47
-	 * @return \OC\Files\View cache storage
48
-	 * @throws \OC\ForbiddenException
49
-	 * @throws \OC\User\NoUserException
50
-	 */
51
-	protected function getStorage() {
52
-		if ($this->storage !== null) {
53
-			return $this->storage;
54
-		}
55
-		if (\OC::$server->getUserSession()->isLoggedIn()) {
56
-			$rootView = new View();
57
-			$user = \OC::$server->getUserSession()->getUser();
58
-			Filesystem::initMountPoints($user->getUID());
59
-			if (!$rootView->file_exists('/' . $user->getUID() . '/cache')) {
60
-				$rootView->mkdir('/' . $user->getUID() . '/cache');
61
-			}
62
-			$this->storage = new View('/' . $user->getUID() . '/cache');
63
-			return $this->storage;
64
-		} else {
65
-			\OCP\Util::writeLog('core', 'Can\'t get cache storage, user not logged in', ILogger::ERROR);
66
-			throw new \OC\ForbiddenException('Can\t get cache storage, user not logged in');
67
-		}
68
-	}
44
+    /**
45
+     * Returns the cache storage for the logged in user
46
+     *
47
+     * @return \OC\Files\View cache storage
48
+     * @throws \OC\ForbiddenException
49
+     * @throws \OC\User\NoUserException
50
+     */
51
+    protected function getStorage() {
52
+        if ($this->storage !== null) {
53
+            return $this->storage;
54
+        }
55
+        if (\OC::$server->getUserSession()->isLoggedIn()) {
56
+            $rootView = new View();
57
+            $user = \OC::$server->getUserSession()->getUser();
58
+            Filesystem::initMountPoints($user->getUID());
59
+            if (!$rootView->file_exists('/' . $user->getUID() . '/cache')) {
60
+                $rootView->mkdir('/' . $user->getUID() . '/cache');
61
+            }
62
+            $this->storage = new View('/' . $user->getUID() . '/cache');
63
+            return $this->storage;
64
+        } else {
65
+            \OCP\Util::writeLog('core', 'Can\'t get cache storage, user not logged in', ILogger::ERROR);
66
+            throw new \OC\ForbiddenException('Can\t get cache storage, user not logged in');
67
+        }
68
+    }
69 69
 
70
-	/**
71
-	 * @param string $key
72
-	 * @return mixed|null
73
-	 * @throws \OC\ForbiddenException
74
-	 */
75
-	public function get($key) {
76
-		$result = null;
77
-		if ($this->hasKey($key)) {
78
-			$storage = $this->getStorage();
79
-			$result = $storage->file_get_contents($key);
80
-		}
81
-		return $result;
82
-	}
70
+    /**
71
+     * @param string $key
72
+     * @return mixed|null
73
+     * @throws \OC\ForbiddenException
74
+     */
75
+    public function get($key) {
76
+        $result = null;
77
+        if ($this->hasKey($key)) {
78
+            $storage = $this->getStorage();
79
+            $result = $storage->file_get_contents($key);
80
+        }
81
+        return $result;
82
+    }
83 83
 
84
-	/**
85
-	 * Returns the size of the stored/cached data
86
-	 *
87
-	 * @param string $key
88
-	 * @return int
89
-	 */
90
-	public function size($key) {
91
-		$result = 0;
92
-		if ($this->hasKey($key)) {
93
-			$storage = $this->getStorage();
94
-			$result = $storage->filesize($key);
95
-		}
96
-		return $result;
97
-	}
84
+    /**
85
+     * Returns the size of the stored/cached data
86
+     *
87
+     * @param string $key
88
+     * @return int
89
+     */
90
+    public function size($key) {
91
+        $result = 0;
92
+        if ($this->hasKey($key)) {
93
+            $storage = $this->getStorage();
94
+            $result = $storage->filesize($key);
95
+        }
96
+        return $result;
97
+    }
98 98
 
99
-	/**
100
-	 * @param string $key
101
-	 * @param mixed $value
102
-	 * @param int $ttl
103
-	 * @return bool|mixed
104
-	 * @throws \OC\ForbiddenException
105
-	 */
106
-	public function set($key, $value, $ttl = 0) {
107
-		$storage = $this->getStorage();
108
-		$result = false;
109
-		// unique id to avoid chunk collision, just in case
110
-		$uniqueId = \OC::$server->getSecureRandom()->generate(
111
-			16,
112
-			ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER
113
-		);
99
+    /**
100
+     * @param string $key
101
+     * @param mixed $value
102
+     * @param int $ttl
103
+     * @return bool|mixed
104
+     * @throws \OC\ForbiddenException
105
+     */
106
+    public function set($key, $value, $ttl = 0) {
107
+        $storage = $this->getStorage();
108
+        $result = false;
109
+        // unique id to avoid chunk collision, just in case
110
+        $uniqueId = \OC::$server->getSecureRandom()->generate(
111
+            16,
112
+            ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER
113
+        );
114 114
 
115
-		// use part file to prevent hasKey() to find the key
116
-		// while it is being written
117
-		$keyPart = $key . '.' . $uniqueId . '.part';
118
-		if ($storage and $storage->file_put_contents($keyPart, $value)) {
119
-			if ($ttl === 0) {
120
-				$ttl = 86400; // 60*60*24
121
-			}
122
-			$result = $storage->touch($keyPart, time() + $ttl);
123
-			$result &= $storage->rename($keyPart, $key);
124
-		}
125
-		return $result;
126
-	}
115
+        // use part file to prevent hasKey() to find the key
116
+        // while it is being written
117
+        $keyPart = $key . '.' . $uniqueId . '.part';
118
+        if ($storage and $storage->file_put_contents($keyPart, $value)) {
119
+            if ($ttl === 0) {
120
+                $ttl = 86400; // 60*60*24
121
+            }
122
+            $result = $storage->touch($keyPart, time() + $ttl);
123
+            $result &= $storage->rename($keyPart, $key);
124
+        }
125
+        return $result;
126
+    }
127 127
 
128
-	/**
129
-	 * @param string $key
130
-	 * @return bool
131
-	 * @throws \OC\ForbiddenException
132
-	 */
133
-	public function hasKey($key) {
134
-		$storage = $this->getStorage();
135
-		if ($storage && $storage->is_file($key) && $storage->isReadable($key)) {
136
-			return true;
137
-		}
138
-		return false;
139
-	}
128
+    /**
129
+     * @param string $key
130
+     * @return bool
131
+     * @throws \OC\ForbiddenException
132
+     */
133
+    public function hasKey($key) {
134
+        $storage = $this->getStorage();
135
+        if ($storage && $storage->is_file($key) && $storage->isReadable($key)) {
136
+            return true;
137
+        }
138
+        return false;
139
+    }
140 140
 
141
-	/**
142
-	 * @param string $key
143
-	 * @return bool|mixed
144
-	 * @throws \OC\ForbiddenException
145
-	 */
146
-	public function remove($key) {
147
-		$storage = $this->getStorage();
148
-		if (!$storage) {
149
-			return false;
150
-		}
151
-		return $storage->unlink($key);
152
-	}
141
+    /**
142
+     * @param string $key
143
+     * @return bool|mixed
144
+     * @throws \OC\ForbiddenException
145
+     */
146
+    public function remove($key) {
147
+        $storage = $this->getStorage();
148
+        if (!$storage) {
149
+            return false;
150
+        }
151
+        return $storage->unlink($key);
152
+    }
153 153
 
154
-	/**
155
-	 * @param string $prefix
156
-	 * @return bool
157
-	 * @throws \OC\ForbiddenException
158
-	 */
159
-	public function clear($prefix = '') {
160
-		$storage = $this->getStorage();
161
-		if ($storage and $storage->is_dir('/')) {
162
-			$dh = $storage->opendir('/');
163
-			if (is_resource($dh)) {
164
-				while (($file = readdir($dh)) !== false) {
165
-					if ($file != '.' and $file != '..' and ($prefix === '' || strpos($file, $prefix) === 0)) {
166
-						$storage->unlink('/' . $file);
167
-					}
168
-				}
169
-			}
170
-		}
171
-		return true;
172
-	}
154
+    /**
155
+     * @param string $prefix
156
+     * @return bool
157
+     * @throws \OC\ForbiddenException
158
+     */
159
+    public function clear($prefix = '') {
160
+        $storage = $this->getStorage();
161
+        if ($storage and $storage->is_dir('/')) {
162
+            $dh = $storage->opendir('/');
163
+            if (is_resource($dh)) {
164
+                while (($file = readdir($dh)) !== false) {
165
+                    if ($file != '.' and $file != '..' and ($prefix === '' || strpos($file, $prefix) === 0)) {
166
+                        $storage->unlink('/' . $file);
167
+                    }
168
+                }
169
+            }
170
+        }
171
+        return true;
172
+    }
173 173
 
174
-	/**
175
-	 * Runs GC
176
-	 * @throws \OC\ForbiddenException
177
-	 */
178
-	public function gc() {
179
-		$storage = $this->getStorage();
180
-		if ($storage) {
181
-			// extra hour safety, in case of stray part chunks that take longer to write,
182
-			// because touch() is only called after the chunk was finished
183
-			$now = time() - 3600;
184
-			$dh = $storage->opendir('/');
185
-			if (!is_resource($dh)) {
186
-				return null;
187
-			}
188
-			while (($file = readdir($dh)) !== false) {
189
-				if ($file != '.' and $file != '..') {
190
-					try {
191
-						$mtime = $storage->filemtime('/' . $file);
192
-						if ($mtime < $now) {
193
-							$storage->unlink('/' . $file);
194
-						}
195
-					} catch (\OCP\Lock\LockedException $e) {
196
-						// ignore locked chunks
197
-						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']);
198
-					} catch (\OCP\Files\ForbiddenException $e) {
199
-						\OC::$server->getLogger()->debug('Could not cleanup forbidden chunk "' . $file . '"', ['app' => 'core']);
200
-					} catch (\OCP\Files\LockNotAcquiredException $e) {
201
-						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']);
202
-					}
203
-				}
204
-			}
205
-		}
206
-	}
174
+    /**
175
+     * Runs GC
176
+     * @throws \OC\ForbiddenException
177
+     */
178
+    public function gc() {
179
+        $storage = $this->getStorage();
180
+        if ($storage) {
181
+            // extra hour safety, in case of stray part chunks that take longer to write,
182
+            // because touch() is only called after the chunk was finished
183
+            $now = time() - 3600;
184
+            $dh = $storage->opendir('/');
185
+            if (!is_resource($dh)) {
186
+                return null;
187
+            }
188
+            while (($file = readdir($dh)) !== false) {
189
+                if ($file != '.' and $file != '..') {
190
+                    try {
191
+                        $mtime = $storage->filemtime('/' . $file);
192
+                        if ($mtime < $now) {
193
+                            $storage->unlink('/' . $file);
194
+                        }
195
+                    } catch (\OCP\Lock\LockedException $e) {
196
+                        // ignore locked chunks
197
+                        \OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']);
198
+                    } catch (\OCP\Files\ForbiddenException $e) {
199
+                        \OC::$server->getLogger()->debug('Could not cleanup forbidden chunk "' . $file . '"', ['app' => 'core']);
200
+                    } catch (\OCP\Files\LockNotAcquiredException $e) {
201
+                        \OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']);
202
+                    }
203
+                }
204
+            }
205
+        }
206
+    }
207 207
 }
Please login to merge, or discard this patch.
lib/private/Files/Storage/DAV.php 1 patch
Indentation   +809 added lines, -809 removed lines patch added patch discarded remove patch
@@ -65,813 +65,813 @@
 block discarded – undo
65 65
  * @package OC\Files\Storage
66 66
  */
67 67
 class DAV extends Common {
68
-	/** @var string */
69
-	protected $password;
70
-	/** @var string */
71
-	protected $user;
72
-	/** @var string|null */
73
-	protected $authType;
74
-	/** @var string */
75
-	protected $host;
76
-	/** @var bool */
77
-	protected $secure;
78
-	/** @var string */
79
-	protected $root;
80
-	/** @var string */
81
-	protected $certPath;
82
-	/** @var bool */
83
-	protected $ready;
84
-	/** @var Client */
85
-	protected $client;
86
-	/** @var ArrayCache */
87
-	protected $statCache;
88
-	/** @var IClientService */
89
-	protected $httpClientService;
90
-	/** @var ICertificateManager */
91
-	protected $certManager;
92
-
93
-	/**
94
-	 * @param array $params
95
-	 * @throws \Exception
96
-	 */
97
-	public function __construct($params) {
98
-		$this->statCache = new ArrayCache();
99
-		$this->httpClientService = \OC::$server->getHTTPClientService();
100
-		if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
101
-			$host = $params['host'];
102
-			//remove leading http[s], will be generated in createBaseUri()
103
-			if (substr($host, 0, 8) == "https://") {
104
-				$host = substr($host, 8);
105
-			} elseif (substr($host, 0, 7) == "http://") {
106
-				$host = substr($host, 7);
107
-			}
108
-			$this->host = $host;
109
-			$this->user = $params['user'];
110
-			$this->password = $params['password'];
111
-			if (isset($params['authType'])) {
112
-				$this->authType = $params['authType'];
113
-			}
114
-			if (isset($params['secure'])) {
115
-				if (is_string($params['secure'])) {
116
-					$this->secure = ($params['secure'] === 'true');
117
-				} else {
118
-					$this->secure = (bool)$params['secure'];
119
-				}
120
-			} else {
121
-				$this->secure = false;
122
-			}
123
-			if ($this->secure === true) {
124
-				// inject mock for testing
125
-				$this->certManager = \OC::$server->getCertificateManager();
126
-			}
127
-			$this->root = $params['root'] ?? '/';
128
-			$this->root = '/' . ltrim($this->root, '/');
129
-			$this->root = rtrim($this->root, '/') . '/';
130
-		} else {
131
-			throw new \Exception('Invalid webdav storage configuration');
132
-		}
133
-	}
134
-
135
-	protected function init() {
136
-		if ($this->ready) {
137
-			return;
138
-		}
139
-		$this->ready = true;
140
-
141
-		$settings = [
142
-			'baseUri' => $this->createBaseUri(),
143
-			'userName' => $this->user,
144
-			'password' => $this->password,
145
-		];
146
-		if ($this->authType !== null) {
147
-			$settings['authType'] = $this->authType;
148
-		}
149
-
150
-		$proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
151
-		if ($proxy !== '') {
152
-			$settings['proxy'] = $proxy;
153
-		}
154
-
155
-		$this->client = new Client($settings);
156
-		$this->client->setThrowExceptions(true);
157
-
158
-		if ($this->secure === true) {
159
-			$certPath = $this->certManager->getAbsoluteBundlePath();
160
-			if (file_exists($certPath)) {
161
-				$this->certPath = $certPath;
162
-			}
163
-			if ($this->certPath) {
164
-				$this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
165
-			}
166
-		}
167
-	}
168
-
169
-	/**
170
-	 * Clear the stat cache
171
-	 */
172
-	public function clearStatCache() {
173
-		$this->statCache->clear();
174
-	}
175
-
176
-	/** {@inheritdoc} */
177
-	public function getId() {
178
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
179
-	}
180
-
181
-	/** {@inheritdoc} */
182
-	public function createBaseUri() {
183
-		$baseUri = 'http';
184
-		if ($this->secure) {
185
-			$baseUri .= 's';
186
-		}
187
-		$baseUri .= '://' . $this->host . $this->root;
188
-		return $baseUri;
189
-	}
190
-
191
-	/** {@inheritdoc} */
192
-	public function mkdir($path) {
193
-		$this->init();
194
-		$path = $this->cleanPath($path);
195
-		$result = $this->simpleResponse('MKCOL', $path, null, 201);
196
-		if ($result) {
197
-			$this->statCache->set($path, true);
198
-		}
199
-		return $result;
200
-	}
201
-
202
-	/** {@inheritdoc} */
203
-	public function rmdir($path) {
204
-		$this->init();
205
-		$path = $this->cleanPath($path);
206
-		// FIXME: some WebDAV impl return 403 when trying to DELETE
207
-		// a non-empty folder
208
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
209
-		$this->statCache->clear($path . '/');
210
-		$this->statCache->remove($path);
211
-		return $result;
212
-	}
213
-
214
-	/** {@inheritdoc} */
215
-	public function opendir($path) {
216
-		$this->init();
217
-		$path = $this->cleanPath($path);
218
-		try {
219
-			$response = $this->client->propFind(
220
-				$this->encodePath($path),
221
-				['{DAV:}getetag'],
222
-				1
223
-			);
224
-			if ($response === false) {
225
-				return false;
226
-			}
227
-			$content = [];
228
-			$files = array_keys($response);
229
-			array_shift($files); //the first entry is the current directory
230
-
231
-			if (!$this->statCache->hasKey($path)) {
232
-				$this->statCache->set($path, true);
233
-			}
234
-			foreach ($files as $file) {
235
-				$file = urldecode($file);
236
-				// do not store the real entry, we might not have all properties
237
-				if (!$this->statCache->hasKey($path)) {
238
-					$this->statCache->set($file, true);
239
-				}
240
-				$file = basename($file);
241
-				$content[] = $file;
242
-			}
243
-			return IteratorDirectory::wrap($content);
244
-		} catch (\Exception $e) {
245
-			$this->convertException($e, $path);
246
-		}
247
-		return false;
248
-	}
249
-
250
-	/**
251
-	 * Propfind call with cache handling.
252
-	 *
253
-	 * First checks if information is cached.
254
-	 * If not, request it from the server then store to cache.
255
-	 *
256
-	 * @param string $path path to propfind
257
-	 *
258
-	 * @return array|boolean propfind response or false if the entry was not found
259
-	 *
260
-	 * @throws ClientHttpException
261
-	 */
262
-	protected function propfind($path) {
263
-		$path = $this->cleanPath($path);
264
-		$cachedResponse = $this->statCache->get($path);
265
-		// we either don't know it, or we know it exists but need more details
266
-		if (is_null($cachedResponse) || $cachedResponse === true) {
267
-			$this->init();
268
-			try {
269
-				$response = $this->client->propFind(
270
-					$this->encodePath($path),
271
-					[
272
-						'{DAV:}getlastmodified',
273
-						'{DAV:}getcontentlength',
274
-						'{DAV:}getcontenttype',
275
-						'{http://owncloud.org/ns}permissions',
276
-						'{http://open-collaboration-services.org/ns}share-permissions',
277
-						'{DAV:}resourcetype',
278
-						'{DAV:}getetag',
279
-					]
280
-				);
281
-				$this->statCache->set($path, $response);
282
-			} catch (ClientHttpException $e) {
283
-				if ($e->getHttpStatus() === 404 || $e->getHttpStatus() === 405) {
284
-					$this->statCache->clear($path . '/');
285
-					$this->statCache->set($path, false);
286
-					return false;
287
-				}
288
-				$this->convertException($e, $path);
289
-			} catch (\Exception $e) {
290
-				$this->convertException($e, $path);
291
-			}
292
-		} else {
293
-			$response = $cachedResponse;
294
-		}
295
-		return $response;
296
-	}
297
-
298
-	/** {@inheritdoc} */
299
-	public function filetype($path) {
300
-		try {
301
-			$response = $this->propfind($path);
302
-			if ($response === false) {
303
-				return false;
304
-			}
305
-			$responseType = [];
306
-			if (isset($response["{DAV:}resourcetype"])) {
307
-				/** @var ResourceType[] $response */
308
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
309
-			}
310
-			return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
311
-		} catch (\Exception $e) {
312
-			$this->convertException($e, $path);
313
-		}
314
-		return false;
315
-	}
316
-
317
-	/** {@inheritdoc} */
318
-	public function file_exists($path) {
319
-		try {
320
-			$path = $this->cleanPath($path);
321
-			$cachedState = $this->statCache->get($path);
322
-			if ($cachedState === false) {
323
-				// we know the file doesn't exist
324
-				return false;
325
-			} elseif (!is_null($cachedState)) {
326
-				return true;
327
-			}
328
-			// need to get from server
329
-			return ($this->propfind($path) !== false);
330
-		} catch (\Exception $e) {
331
-			$this->convertException($e, $path);
332
-		}
333
-		return false;
334
-	}
335
-
336
-	/** {@inheritdoc} */
337
-	public function unlink($path) {
338
-		$this->init();
339
-		$path = $this->cleanPath($path);
340
-		$result = $this->simpleResponse('DELETE', $path, null, 204);
341
-		$this->statCache->clear($path . '/');
342
-		$this->statCache->remove($path);
343
-		return $result;
344
-	}
345
-
346
-	/** {@inheritdoc} */
347
-	public function fopen($path, $mode) {
348
-		$this->init();
349
-		$path = $this->cleanPath($path);
350
-		switch ($mode) {
351
-			case 'r':
352
-			case 'rb':
353
-				try {
354
-					$response = $this->httpClientService
355
-						->newClient()
356
-						->get($this->createBaseUri() . $this->encodePath($path), [
357
-							'auth' => [$this->user, $this->password],
358
-							'stream' => true
359
-						]);
360
-				} catch (\GuzzleHttp\Exception\ClientException $e) {
361
-					if ($e->getResponse() instanceof ResponseInterface
362
-						&& $e->getResponse()->getStatusCode() === 404) {
363
-						return false;
364
-					} else {
365
-						throw $e;
366
-					}
367
-				}
368
-
369
-				if ($response->getStatusCode() !== Http::STATUS_OK) {
370
-					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
371
-						throw new \OCP\Lock\LockedException($path);
372
-					} else {
373
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), ILogger::ERROR);
374
-					}
375
-				}
376
-
377
-				return $response->getBody();
378
-			case 'w':
379
-			case 'wb':
380
-			case 'a':
381
-			case 'ab':
382
-			case 'r+':
383
-			case 'w+':
384
-			case 'wb+':
385
-			case 'a+':
386
-			case 'x':
387
-			case 'x+':
388
-			case 'c':
389
-			case 'c+':
390
-				//emulate these
391
-				$tempManager = \OC::$server->getTempManager();
392
-				if (strrpos($path, '.') !== false) {
393
-					$ext = substr($path, strrpos($path, '.'));
394
-				} else {
395
-					$ext = '';
396
-				}
397
-				if ($this->file_exists($path)) {
398
-					if (!$this->isUpdatable($path)) {
399
-						return false;
400
-					}
401
-					if ($mode === 'w' or $mode === 'w+') {
402
-						$tmpFile = $tempManager->getTemporaryFile($ext);
403
-					} else {
404
-						$tmpFile = $this->getCachedFile($path);
405
-					}
406
-				} else {
407
-					if (!$this->isCreatable(dirname($path))) {
408
-						return false;
409
-					}
410
-					$tmpFile = $tempManager->getTemporaryFile($ext);
411
-				}
412
-				$handle = fopen($tmpFile, $mode);
413
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
414
-					$this->writeBack($tmpFile, $path);
415
-				});
416
-		}
417
-	}
418
-
419
-	/**
420
-	 * @param string $tmpFile
421
-	 */
422
-	public function writeBack($tmpFile, $path) {
423
-		$this->uploadFile($tmpFile, $path);
424
-		unlink($tmpFile);
425
-	}
426
-
427
-	/** {@inheritdoc} */
428
-	public function free_space($path) {
429
-		$this->init();
430
-		$path = $this->cleanPath($path);
431
-		try {
432
-			// TODO: cacheable ?
433
-			$response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
434
-			if ($response === false) {
435
-				return FileInfo::SPACE_UNKNOWN;
436
-			}
437
-			if (isset($response['{DAV:}quota-available-bytes'])) {
438
-				return (int)$response['{DAV:}quota-available-bytes'];
439
-			} else {
440
-				return FileInfo::SPACE_UNKNOWN;
441
-			}
442
-		} catch (\Exception $e) {
443
-			return FileInfo::SPACE_UNKNOWN;
444
-		}
445
-	}
446
-
447
-	/** {@inheritdoc} */
448
-	public function touch($path, $mtime = null) {
449
-		$this->init();
450
-		if (is_null($mtime)) {
451
-			$mtime = time();
452
-		}
453
-		$path = $this->cleanPath($path);
454
-
455
-		// if file exists, update the mtime, else create a new empty file
456
-		if ($this->file_exists($path)) {
457
-			try {
458
-				$this->statCache->remove($path);
459
-				$this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
460
-				// non-owncloud clients might not have accepted the property, need to recheck it
461
-				$response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
462
-				if ($response === false) {
463
-					return false;
464
-				}
465
-				if (isset($response['{DAV:}getlastmodified'])) {
466
-					$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
467
-					if ($remoteMtime !== $mtime) {
468
-						// server has not accepted the mtime
469
-						return false;
470
-					}
471
-				}
472
-			} catch (ClientHttpException $e) {
473
-				if ($e->getHttpStatus() === 501) {
474
-					return false;
475
-				}
476
-				$this->convertException($e, $path);
477
-				return false;
478
-			} catch (\Exception $e) {
479
-				$this->convertException($e, $path);
480
-				return false;
481
-			}
482
-		} else {
483
-			$this->file_put_contents($path, '');
484
-		}
485
-		return true;
486
-	}
487
-
488
-	/**
489
-	 * @param string $path
490
-	 * @param mixed $data
491
-	 * @return int|false
492
-	 */
493
-	public function file_put_contents($path, $data) {
494
-		$path = $this->cleanPath($path);
495
-		$result = parent::file_put_contents($path, $data);
496
-		$this->statCache->remove($path);
497
-		return $result;
498
-	}
499
-
500
-	/**
501
-	 * @param string $path
502
-	 * @param string $target
503
-	 */
504
-	protected function uploadFile($path, $target) {
505
-		$this->init();
506
-
507
-		// invalidate
508
-		$target = $this->cleanPath($target);
509
-		$this->statCache->remove($target);
510
-		$source = fopen($path, 'r');
511
-
512
-		$this->httpClientService
513
-			->newClient()
514
-			->put($this->createBaseUri() . $this->encodePath($target), [
515
-				'body' => $source,
516
-				'auth' => [$this->user, $this->password]
517
-			]);
518
-
519
-		$this->removeCachedFile($target);
520
-	}
521
-
522
-	/** {@inheritdoc} */
523
-	public function rename($path1, $path2) {
524
-		$this->init();
525
-		$path1 = $this->cleanPath($path1);
526
-		$path2 = $this->cleanPath($path2);
527
-		try {
528
-			// overwrite directory ?
529
-			if ($this->is_dir($path2)) {
530
-				// needs trailing slash in destination
531
-				$path2 = rtrim($path2, '/') . '/';
532
-			}
533
-			$this->client->request(
534
-				'MOVE',
535
-				$this->encodePath($path1),
536
-				null,
537
-				[
538
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
539
-				]
540
-			);
541
-			$this->statCache->clear($path1 . '/');
542
-			$this->statCache->clear($path2 . '/');
543
-			$this->statCache->set($path1, false);
544
-			$this->statCache->set($path2, true);
545
-			$this->removeCachedFile($path1);
546
-			$this->removeCachedFile($path2);
547
-			return true;
548
-		} catch (\Exception $e) {
549
-			$this->convertException($e);
550
-		}
551
-		return false;
552
-	}
553
-
554
-	/** {@inheritdoc} */
555
-	public function copy($path1, $path2) {
556
-		$this->init();
557
-		$path1 = $this->cleanPath($path1);
558
-		$path2 = $this->cleanPath($path2);
559
-		try {
560
-			// overwrite directory ?
561
-			if ($this->is_dir($path2)) {
562
-				// needs trailing slash in destination
563
-				$path2 = rtrim($path2, '/') . '/';
564
-			}
565
-			$this->client->request(
566
-				'COPY',
567
-				$this->encodePath($path1),
568
-				null,
569
-				[
570
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
571
-				]
572
-			);
573
-			$this->statCache->clear($path2 . '/');
574
-			$this->statCache->set($path2, true);
575
-			$this->removeCachedFile($path2);
576
-			return true;
577
-		} catch (\Exception $e) {
578
-			$this->convertException($e);
579
-		}
580
-		return false;
581
-	}
582
-
583
-	/** {@inheritdoc} */
584
-	public function stat($path) {
585
-		try {
586
-			$response = $this->propfind($path);
587
-			if (!$response) {
588
-				return false;
589
-			}
590
-			return [
591
-				'mtime' => strtotime($response['{DAV:}getlastmodified']),
592
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
593
-			];
594
-		} catch (\Exception $e) {
595
-			$this->convertException($e, $path);
596
-		}
597
-		return [];
598
-	}
599
-
600
-	/** {@inheritdoc} */
601
-	public function getMimeType($path) {
602
-		$remoteMimetype = $this->getMimeTypeFromRemote($path);
603
-		if ($remoteMimetype === 'application/octet-stream') {
604
-			return \OC::$server->getMimeTypeDetector()->detectPath($path);
605
-		} else {
606
-			return $remoteMimetype;
607
-		}
608
-	}
609
-
610
-	public function getMimeTypeFromRemote($path) {
611
-		try {
612
-			$response = $this->propfind($path);
613
-			if ($response === false) {
614
-				return false;
615
-			}
616
-			$responseType = [];
617
-			if (isset($response["{DAV:}resourcetype"])) {
618
-				/** @var ResourceType[] $response */
619
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
620
-			}
621
-			$type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
622
-			if ($type == 'dir') {
623
-				return 'httpd/unix-directory';
624
-			} elseif (isset($response['{DAV:}getcontenttype'])) {
625
-				return $response['{DAV:}getcontenttype'];
626
-			} else {
627
-				return 'application/octet-stream';
628
-			}
629
-		} catch (\Exception $e) {
630
-			return false;
631
-		}
632
-	}
633
-
634
-	/**
635
-	 * @param string $path
636
-	 * @return string
637
-	 */
638
-	public function cleanPath($path) {
639
-		if ($path === '') {
640
-			return $path;
641
-		}
642
-		$path = Filesystem::normalizePath($path);
643
-		// remove leading slash
644
-		return substr($path, 1);
645
-	}
646
-
647
-	/**
648
-	 * URL encodes the given path but keeps the slashes
649
-	 *
650
-	 * @param string $path to encode
651
-	 * @return string encoded path
652
-	 */
653
-	protected function encodePath($path) {
654
-		// slashes need to stay
655
-		return str_replace('%2F', '/', rawurlencode($path));
656
-	}
657
-
658
-	/**
659
-	 * @param string $method
660
-	 * @param string $path
661
-	 * @param string|resource|null $body
662
-	 * @param int $expected
663
-	 * @return bool
664
-	 * @throws StorageInvalidException
665
-	 * @throws StorageNotAvailableException
666
-	 */
667
-	protected function simpleResponse($method, $path, $body, $expected) {
668
-		$path = $this->cleanPath($path);
669
-		try {
670
-			$response = $this->client->request($method, $this->encodePath($path), $body);
671
-			return $response['statusCode'] == $expected;
672
-		} catch (ClientHttpException $e) {
673
-			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
674
-				$this->statCache->clear($path . '/');
675
-				$this->statCache->set($path, false);
676
-				return false;
677
-			}
678
-
679
-			$this->convertException($e, $path);
680
-		} catch (\Exception $e) {
681
-			$this->convertException($e, $path);
682
-		}
683
-		return false;
684
-	}
685
-
686
-	/**
687
-	 * check if curl is installed
688
-	 */
689
-	public static function checkDependencies() {
690
-		return true;
691
-	}
692
-
693
-	/** {@inheritdoc} */
694
-	public function isUpdatable($path) {
695
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
696
-	}
697
-
698
-	/** {@inheritdoc} */
699
-	public function isCreatable($path) {
700
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
701
-	}
702
-
703
-	/** {@inheritdoc} */
704
-	public function isSharable($path) {
705
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
706
-	}
707
-
708
-	/** {@inheritdoc} */
709
-	public function isDeletable($path) {
710
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
711
-	}
712
-
713
-	/** {@inheritdoc} */
714
-	public function getPermissions($path) {
715
-		$this->init();
716
-		$path = $this->cleanPath($path);
717
-		$response = $this->propfind($path);
718
-		if ($response === false) {
719
-			return 0;
720
-		}
721
-		if (isset($response['{http://owncloud.org/ns}permissions'])) {
722
-			return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
723
-		} elseif ($this->is_dir($path)) {
724
-			return Constants::PERMISSION_ALL;
725
-		} elseif ($this->file_exists($path)) {
726
-			return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
727
-		} else {
728
-			return 0;
729
-		}
730
-	}
731
-
732
-	/** {@inheritdoc} */
733
-	public function getETag($path) {
734
-		$this->init();
735
-		$path = $this->cleanPath($path);
736
-		$response = $this->propfind($path);
737
-		if ($response === false) {
738
-			return null;
739
-		}
740
-		if (isset($response['{DAV:}getetag'])) {
741
-			$etag = trim($response['{DAV:}getetag'], '"');
742
-			if (strlen($etag) > 40) {
743
-				$etag = md5($etag);
744
-			}
745
-			return $etag;
746
-		}
747
-		return parent::getEtag($path);
748
-	}
749
-
750
-	/**
751
-	 * @param string $permissionsString
752
-	 * @return int
753
-	 */
754
-	protected function parsePermissions($permissionsString) {
755
-		$permissions = Constants::PERMISSION_READ;
756
-		if (strpos($permissionsString, 'R') !== false) {
757
-			$permissions |= Constants::PERMISSION_SHARE;
758
-		}
759
-		if (strpos($permissionsString, 'D') !== false) {
760
-			$permissions |= Constants::PERMISSION_DELETE;
761
-		}
762
-		if (strpos($permissionsString, 'W') !== false) {
763
-			$permissions |= Constants::PERMISSION_UPDATE;
764
-		}
765
-		if (strpos($permissionsString, 'CK') !== false) {
766
-			$permissions |= Constants::PERMISSION_CREATE;
767
-			$permissions |= Constants::PERMISSION_UPDATE;
768
-		}
769
-		return $permissions;
770
-	}
771
-
772
-	/**
773
-	 * check if a file or folder has been updated since $time
774
-	 *
775
-	 * @param string $path
776
-	 * @param int $time
777
-	 * @throws \OCP\Files\StorageNotAvailableException
778
-	 * @return bool
779
-	 */
780
-	public function hasUpdated($path, $time) {
781
-		$this->init();
782
-		$path = $this->cleanPath($path);
783
-		try {
784
-			// force refresh for $path
785
-			$this->statCache->remove($path);
786
-			$response = $this->propfind($path);
787
-			if ($response === false) {
788
-				if ($path === '') {
789
-					// if root is gone it means the storage is not available
790
-					throw new StorageNotAvailableException('root is gone');
791
-				}
792
-				return false;
793
-			}
794
-			if (isset($response['{DAV:}getetag'])) {
795
-				$cachedData = $this->getCache()->get($path);
796
-				$etag = null;
797
-				if (isset($response['{DAV:}getetag'])) {
798
-					$etag = trim($response['{DAV:}getetag'], '"');
799
-				}
800
-				if (!empty($etag) && $cachedData['etag'] !== $etag) {
801
-					return true;
802
-				} elseif (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
803
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
804
-					return $sharePermissions !== $cachedData['permissions'];
805
-				} elseif (isset($response['{http://owncloud.org/ns}permissions'])) {
806
-					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
807
-					return $permissions !== $cachedData['permissions'];
808
-				} else {
809
-					return false;
810
-				}
811
-			} else {
812
-				$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
813
-				return $remoteMtime > $time;
814
-			}
815
-		} catch (ClientHttpException $e) {
816
-			if ($e->getHttpStatus() === 405) {
817
-				if ($path === '') {
818
-					// if root is gone it means the storage is not available
819
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
820
-				}
821
-				return false;
822
-			}
823
-			$this->convertException($e, $path);
824
-			return false;
825
-		} catch (\Exception $e) {
826
-			$this->convertException($e, $path);
827
-			return false;
828
-		}
829
-	}
830
-
831
-	/**
832
-	 * Interpret the given exception and decide whether it is due to an
833
-	 * unavailable storage, invalid storage or other.
834
-	 * This will either throw StorageInvalidException, StorageNotAvailableException
835
-	 * or do nothing.
836
-	 *
837
-	 * @param Exception $e sabre exception
838
-	 * @param string $path optional path from the operation
839
-	 *
840
-	 * @throws StorageInvalidException if the storage is invalid, for example
841
-	 * when the authentication expired or is invalid
842
-	 * @throws StorageNotAvailableException if the storage is not available,
843
-	 * which might be temporary
844
-	 * @throws ForbiddenException if the action is not allowed
845
-	 */
846
-	protected function convertException(Exception $e, $path = '') {
847
-		\OC::$server->getLogger()->logException($e, ['app' => 'files_external', 'level' => ILogger::DEBUG]);
848
-		if ($e instanceof ClientHttpException) {
849
-			if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
850
-				throw new \OCP\Lock\LockedException($path);
851
-			}
852
-			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
853
-				// either password was changed or was invalid all along
854
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
855
-			} elseif ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
856
-				// ignore exception for MethodNotAllowed, false will be returned
857
-				return;
858
-			} elseif ($e->getHttpStatus() === Http::STATUS_FORBIDDEN) {
859
-				// The operation is forbidden. Fail somewhat gracefully
860
-				throw new ForbiddenException(get_class($e) . ':' . $e->getMessage(), false);
861
-			}
862
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
863
-		} elseif ($e instanceof ClientException) {
864
-			// connection timeout or refused, server could be temporarily down
865
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
866
-		} elseif ($e instanceof \InvalidArgumentException) {
867
-			// parse error because the server returned HTML instead of XML,
868
-			// possibly temporarily down
869
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
870
-		} elseif (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
871
-			// rethrow
872
-			throw $e;
873
-		}
874
-
875
-		// TODO: only log for now, but in the future need to wrap/rethrow exception
876
-	}
68
+    /** @var string */
69
+    protected $password;
70
+    /** @var string */
71
+    protected $user;
72
+    /** @var string|null */
73
+    protected $authType;
74
+    /** @var string */
75
+    protected $host;
76
+    /** @var bool */
77
+    protected $secure;
78
+    /** @var string */
79
+    protected $root;
80
+    /** @var string */
81
+    protected $certPath;
82
+    /** @var bool */
83
+    protected $ready;
84
+    /** @var Client */
85
+    protected $client;
86
+    /** @var ArrayCache */
87
+    protected $statCache;
88
+    /** @var IClientService */
89
+    protected $httpClientService;
90
+    /** @var ICertificateManager */
91
+    protected $certManager;
92
+
93
+    /**
94
+     * @param array $params
95
+     * @throws \Exception
96
+     */
97
+    public function __construct($params) {
98
+        $this->statCache = new ArrayCache();
99
+        $this->httpClientService = \OC::$server->getHTTPClientService();
100
+        if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
101
+            $host = $params['host'];
102
+            //remove leading http[s], will be generated in createBaseUri()
103
+            if (substr($host, 0, 8) == "https://") {
104
+                $host = substr($host, 8);
105
+            } elseif (substr($host, 0, 7) == "http://") {
106
+                $host = substr($host, 7);
107
+            }
108
+            $this->host = $host;
109
+            $this->user = $params['user'];
110
+            $this->password = $params['password'];
111
+            if (isset($params['authType'])) {
112
+                $this->authType = $params['authType'];
113
+            }
114
+            if (isset($params['secure'])) {
115
+                if (is_string($params['secure'])) {
116
+                    $this->secure = ($params['secure'] === 'true');
117
+                } else {
118
+                    $this->secure = (bool)$params['secure'];
119
+                }
120
+            } else {
121
+                $this->secure = false;
122
+            }
123
+            if ($this->secure === true) {
124
+                // inject mock for testing
125
+                $this->certManager = \OC::$server->getCertificateManager();
126
+            }
127
+            $this->root = $params['root'] ?? '/';
128
+            $this->root = '/' . ltrim($this->root, '/');
129
+            $this->root = rtrim($this->root, '/') . '/';
130
+        } else {
131
+            throw new \Exception('Invalid webdav storage configuration');
132
+        }
133
+    }
134
+
135
+    protected function init() {
136
+        if ($this->ready) {
137
+            return;
138
+        }
139
+        $this->ready = true;
140
+
141
+        $settings = [
142
+            'baseUri' => $this->createBaseUri(),
143
+            'userName' => $this->user,
144
+            'password' => $this->password,
145
+        ];
146
+        if ($this->authType !== null) {
147
+            $settings['authType'] = $this->authType;
148
+        }
149
+
150
+        $proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
151
+        if ($proxy !== '') {
152
+            $settings['proxy'] = $proxy;
153
+        }
154
+
155
+        $this->client = new Client($settings);
156
+        $this->client->setThrowExceptions(true);
157
+
158
+        if ($this->secure === true) {
159
+            $certPath = $this->certManager->getAbsoluteBundlePath();
160
+            if (file_exists($certPath)) {
161
+                $this->certPath = $certPath;
162
+            }
163
+            if ($this->certPath) {
164
+                $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
165
+            }
166
+        }
167
+    }
168
+
169
+    /**
170
+     * Clear the stat cache
171
+     */
172
+    public function clearStatCache() {
173
+        $this->statCache->clear();
174
+    }
175
+
176
+    /** {@inheritdoc} */
177
+    public function getId() {
178
+        return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
179
+    }
180
+
181
+    /** {@inheritdoc} */
182
+    public function createBaseUri() {
183
+        $baseUri = 'http';
184
+        if ($this->secure) {
185
+            $baseUri .= 's';
186
+        }
187
+        $baseUri .= '://' . $this->host . $this->root;
188
+        return $baseUri;
189
+    }
190
+
191
+    /** {@inheritdoc} */
192
+    public function mkdir($path) {
193
+        $this->init();
194
+        $path = $this->cleanPath($path);
195
+        $result = $this->simpleResponse('MKCOL', $path, null, 201);
196
+        if ($result) {
197
+            $this->statCache->set($path, true);
198
+        }
199
+        return $result;
200
+    }
201
+
202
+    /** {@inheritdoc} */
203
+    public function rmdir($path) {
204
+        $this->init();
205
+        $path = $this->cleanPath($path);
206
+        // FIXME: some WebDAV impl return 403 when trying to DELETE
207
+        // a non-empty folder
208
+        $result = $this->simpleResponse('DELETE', $path . '/', null, 204);
209
+        $this->statCache->clear($path . '/');
210
+        $this->statCache->remove($path);
211
+        return $result;
212
+    }
213
+
214
+    /** {@inheritdoc} */
215
+    public function opendir($path) {
216
+        $this->init();
217
+        $path = $this->cleanPath($path);
218
+        try {
219
+            $response = $this->client->propFind(
220
+                $this->encodePath($path),
221
+                ['{DAV:}getetag'],
222
+                1
223
+            );
224
+            if ($response === false) {
225
+                return false;
226
+            }
227
+            $content = [];
228
+            $files = array_keys($response);
229
+            array_shift($files); //the first entry is the current directory
230
+
231
+            if (!$this->statCache->hasKey($path)) {
232
+                $this->statCache->set($path, true);
233
+            }
234
+            foreach ($files as $file) {
235
+                $file = urldecode($file);
236
+                // do not store the real entry, we might not have all properties
237
+                if (!$this->statCache->hasKey($path)) {
238
+                    $this->statCache->set($file, true);
239
+                }
240
+                $file = basename($file);
241
+                $content[] = $file;
242
+            }
243
+            return IteratorDirectory::wrap($content);
244
+        } catch (\Exception $e) {
245
+            $this->convertException($e, $path);
246
+        }
247
+        return false;
248
+    }
249
+
250
+    /**
251
+     * Propfind call with cache handling.
252
+     *
253
+     * First checks if information is cached.
254
+     * If not, request it from the server then store to cache.
255
+     *
256
+     * @param string $path path to propfind
257
+     *
258
+     * @return array|boolean propfind response or false if the entry was not found
259
+     *
260
+     * @throws ClientHttpException
261
+     */
262
+    protected function propfind($path) {
263
+        $path = $this->cleanPath($path);
264
+        $cachedResponse = $this->statCache->get($path);
265
+        // we either don't know it, or we know it exists but need more details
266
+        if (is_null($cachedResponse) || $cachedResponse === true) {
267
+            $this->init();
268
+            try {
269
+                $response = $this->client->propFind(
270
+                    $this->encodePath($path),
271
+                    [
272
+                        '{DAV:}getlastmodified',
273
+                        '{DAV:}getcontentlength',
274
+                        '{DAV:}getcontenttype',
275
+                        '{http://owncloud.org/ns}permissions',
276
+                        '{http://open-collaboration-services.org/ns}share-permissions',
277
+                        '{DAV:}resourcetype',
278
+                        '{DAV:}getetag',
279
+                    ]
280
+                );
281
+                $this->statCache->set($path, $response);
282
+            } catch (ClientHttpException $e) {
283
+                if ($e->getHttpStatus() === 404 || $e->getHttpStatus() === 405) {
284
+                    $this->statCache->clear($path . '/');
285
+                    $this->statCache->set($path, false);
286
+                    return false;
287
+                }
288
+                $this->convertException($e, $path);
289
+            } catch (\Exception $e) {
290
+                $this->convertException($e, $path);
291
+            }
292
+        } else {
293
+            $response = $cachedResponse;
294
+        }
295
+        return $response;
296
+    }
297
+
298
+    /** {@inheritdoc} */
299
+    public function filetype($path) {
300
+        try {
301
+            $response = $this->propfind($path);
302
+            if ($response === false) {
303
+                return false;
304
+            }
305
+            $responseType = [];
306
+            if (isset($response["{DAV:}resourcetype"])) {
307
+                /** @var ResourceType[] $response */
308
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
309
+            }
310
+            return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
311
+        } catch (\Exception $e) {
312
+            $this->convertException($e, $path);
313
+        }
314
+        return false;
315
+    }
316
+
317
+    /** {@inheritdoc} */
318
+    public function file_exists($path) {
319
+        try {
320
+            $path = $this->cleanPath($path);
321
+            $cachedState = $this->statCache->get($path);
322
+            if ($cachedState === false) {
323
+                // we know the file doesn't exist
324
+                return false;
325
+            } elseif (!is_null($cachedState)) {
326
+                return true;
327
+            }
328
+            // need to get from server
329
+            return ($this->propfind($path) !== false);
330
+        } catch (\Exception $e) {
331
+            $this->convertException($e, $path);
332
+        }
333
+        return false;
334
+    }
335
+
336
+    /** {@inheritdoc} */
337
+    public function unlink($path) {
338
+        $this->init();
339
+        $path = $this->cleanPath($path);
340
+        $result = $this->simpleResponse('DELETE', $path, null, 204);
341
+        $this->statCache->clear($path . '/');
342
+        $this->statCache->remove($path);
343
+        return $result;
344
+    }
345
+
346
+    /** {@inheritdoc} */
347
+    public function fopen($path, $mode) {
348
+        $this->init();
349
+        $path = $this->cleanPath($path);
350
+        switch ($mode) {
351
+            case 'r':
352
+            case 'rb':
353
+                try {
354
+                    $response = $this->httpClientService
355
+                        ->newClient()
356
+                        ->get($this->createBaseUri() . $this->encodePath($path), [
357
+                            'auth' => [$this->user, $this->password],
358
+                            'stream' => true
359
+                        ]);
360
+                } catch (\GuzzleHttp\Exception\ClientException $e) {
361
+                    if ($e->getResponse() instanceof ResponseInterface
362
+                        && $e->getResponse()->getStatusCode() === 404) {
363
+                        return false;
364
+                    } else {
365
+                        throw $e;
366
+                    }
367
+                }
368
+
369
+                if ($response->getStatusCode() !== Http::STATUS_OK) {
370
+                    if ($response->getStatusCode() === Http::STATUS_LOCKED) {
371
+                        throw new \OCP\Lock\LockedException($path);
372
+                    } else {
373
+                        Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), ILogger::ERROR);
374
+                    }
375
+                }
376
+
377
+                return $response->getBody();
378
+            case 'w':
379
+            case 'wb':
380
+            case 'a':
381
+            case 'ab':
382
+            case 'r+':
383
+            case 'w+':
384
+            case 'wb+':
385
+            case 'a+':
386
+            case 'x':
387
+            case 'x+':
388
+            case 'c':
389
+            case 'c+':
390
+                //emulate these
391
+                $tempManager = \OC::$server->getTempManager();
392
+                if (strrpos($path, '.') !== false) {
393
+                    $ext = substr($path, strrpos($path, '.'));
394
+                } else {
395
+                    $ext = '';
396
+                }
397
+                if ($this->file_exists($path)) {
398
+                    if (!$this->isUpdatable($path)) {
399
+                        return false;
400
+                    }
401
+                    if ($mode === 'w' or $mode === 'w+') {
402
+                        $tmpFile = $tempManager->getTemporaryFile($ext);
403
+                    } else {
404
+                        $tmpFile = $this->getCachedFile($path);
405
+                    }
406
+                } else {
407
+                    if (!$this->isCreatable(dirname($path))) {
408
+                        return false;
409
+                    }
410
+                    $tmpFile = $tempManager->getTemporaryFile($ext);
411
+                }
412
+                $handle = fopen($tmpFile, $mode);
413
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
414
+                    $this->writeBack($tmpFile, $path);
415
+                });
416
+        }
417
+    }
418
+
419
+    /**
420
+     * @param string $tmpFile
421
+     */
422
+    public function writeBack($tmpFile, $path) {
423
+        $this->uploadFile($tmpFile, $path);
424
+        unlink($tmpFile);
425
+    }
426
+
427
+    /** {@inheritdoc} */
428
+    public function free_space($path) {
429
+        $this->init();
430
+        $path = $this->cleanPath($path);
431
+        try {
432
+            // TODO: cacheable ?
433
+            $response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
434
+            if ($response === false) {
435
+                return FileInfo::SPACE_UNKNOWN;
436
+            }
437
+            if (isset($response['{DAV:}quota-available-bytes'])) {
438
+                return (int)$response['{DAV:}quota-available-bytes'];
439
+            } else {
440
+                return FileInfo::SPACE_UNKNOWN;
441
+            }
442
+        } catch (\Exception $e) {
443
+            return FileInfo::SPACE_UNKNOWN;
444
+        }
445
+    }
446
+
447
+    /** {@inheritdoc} */
448
+    public function touch($path, $mtime = null) {
449
+        $this->init();
450
+        if (is_null($mtime)) {
451
+            $mtime = time();
452
+        }
453
+        $path = $this->cleanPath($path);
454
+
455
+        // if file exists, update the mtime, else create a new empty file
456
+        if ($this->file_exists($path)) {
457
+            try {
458
+                $this->statCache->remove($path);
459
+                $this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
460
+                // non-owncloud clients might not have accepted the property, need to recheck it
461
+                $response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
462
+                if ($response === false) {
463
+                    return false;
464
+                }
465
+                if (isset($response['{DAV:}getlastmodified'])) {
466
+                    $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
467
+                    if ($remoteMtime !== $mtime) {
468
+                        // server has not accepted the mtime
469
+                        return false;
470
+                    }
471
+                }
472
+            } catch (ClientHttpException $e) {
473
+                if ($e->getHttpStatus() === 501) {
474
+                    return false;
475
+                }
476
+                $this->convertException($e, $path);
477
+                return false;
478
+            } catch (\Exception $e) {
479
+                $this->convertException($e, $path);
480
+                return false;
481
+            }
482
+        } else {
483
+            $this->file_put_contents($path, '');
484
+        }
485
+        return true;
486
+    }
487
+
488
+    /**
489
+     * @param string $path
490
+     * @param mixed $data
491
+     * @return int|false
492
+     */
493
+    public function file_put_contents($path, $data) {
494
+        $path = $this->cleanPath($path);
495
+        $result = parent::file_put_contents($path, $data);
496
+        $this->statCache->remove($path);
497
+        return $result;
498
+    }
499
+
500
+    /**
501
+     * @param string $path
502
+     * @param string $target
503
+     */
504
+    protected function uploadFile($path, $target) {
505
+        $this->init();
506
+
507
+        // invalidate
508
+        $target = $this->cleanPath($target);
509
+        $this->statCache->remove($target);
510
+        $source = fopen($path, 'r');
511
+
512
+        $this->httpClientService
513
+            ->newClient()
514
+            ->put($this->createBaseUri() . $this->encodePath($target), [
515
+                'body' => $source,
516
+                'auth' => [$this->user, $this->password]
517
+            ]);
518
+
519
+        $this->removeCachedFile($target);
520
+    }
521
+
522
+    /** {@inheritdoc} */
523
+    public function rename($path1, $path2) {
524
+        $this->init();
525
+        $path1 = $this->cleanPath($path1);
526
+        $path2 = $this->cleanPath($path2);
527
+        try {
528
+            // overwrite directory ?
529
+            if ($this->is_dir($path2)) {
530
+                // needs trailing slash in destination
531
+                $path2 = rtrim($path2, '/') . '/';
532
+            }
533
+            $this->client->request(
534
+                'MOVE',
535
+                $this->encodePath($path1),
536
+                null,
537
+                [
538
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
539
+                ]
540
+            );
541
+            $this->statCache->clear($path1 . '/');
542
+            $this->statCache->clear($path2 . '/');
543
+            $this->statCache->set($path1, false);
544
+            $this->statCache->set($path2, true);
545
+            $this->removeCachedFile($path1);
546
+            $this->removeCachedFile($path2);
547
+            return true;
548
+        } catch (\Exception $e) {
549
+            $this->convertException($e);
550
+        }
551
+        return false;
552
+    }
553
+
554
+    /** {@inheritdoc} */
555
+    public function copy($path1, $path2) {
556
+        $this->init();
557
+        $path1 = $this->cleanPath($path1);
558
+        $path2 = $this->cleanPath($path2);
559
+        try {
560
+            // overwrite directory ?
561
+            if ($this->is_dir($path2)) {
562
+                // needs trailing slash in destination
563
+                $path2 = rtrim($path2, '/') . '/';
564
+            }
565
+            $this->client->request(
566
+                'COPY',
567
+                $this->encodePath($path1),
568
+                null,
569
+                [
570
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
571
+                ]
572
+            );
573
+            $this->statCache->clear($path2 . '/');
574
+            $this->statCache->set($path2, true);
575
+            $this->removeCachedFile($path2);
576
+            return true;
577
+        } catch (\Exception $e) {
578
+            $this->convertException($e);
579
+        }
580
+        return false;
581
+    }
582
+
583
+    /** {@inheritdoc} */
584
+    public function stat($path) {
585
+        try {
586
+            $response = $this->propfind($path);
587
+            if (!$response) {
588
+                return false;
589
+            }
590
+            return [
591
+                'mtime' => strtotime($response['{DAV:}getlastmodified']),
592
+                'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
593
+            ];
594
+        } catch (\Exception $e) {
595
+            $this->convertException($e, $path);
596
+        }
597
+        return [];
598
+    }
599
+
600
+    /** {@inheritdoc} */
601
+    public function getMimeType($path) {
602
+        $remoteMimetype = $this->getMimeTypeFromRemote($path);
603
+        if ($remoteMimetype === 'application/octet-stream') {
604
+            return \OC::$server->getMimeTypeDetector()->detectPath($path);
605
+        } else {
606
+            return $remoteMimetype;
607
+        }
608
+    }
609
+
610
+    public function getMimeTypeFromRemote($path) {
611
+        try {
612
+            $response = $this->propfind($path);
613
+            if ($response === false) {
614
+                return false;
615
+            }
616
+            $responseType = [];
617
+            if (isset($response["{DAV:}resourcetype"])) {
618
+                /** @var ResourceType[] $response */
619
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
620
+            }
621
+            $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
622
+            if ($type == 'dir') {
623
+                return 'httpd/unix-directory';
624
+            } elseif (isset($response['{DAV:}getcontenttype'])) {
625
+                return $response['{DAV:}getcontenttype'];
626
+            } else {
627
+                return 'application/octet-stream';
628
+            }
629
+        } catch (\Exception $e) {
630
+            return false;
631
+        }
632
+    }
633
+
634
+    /**
635
+     * @param string $path
636
+     * @return string
637
+     */
638
+    public function cleanPath($path) {
639
+        if ($path === '') {
640
+            return $path;
641
+        }
642
+        $path = Filesystem::normalizePath($path);
643
+        // remove leading slash
644
+        return substr($path, 1);
645
+    }
646
+
647
+    /**
648
+     * URL encodes the given path but keeps the slashes
649
+     *
650
+     * @param string $path to encode
651
+     * @return string encoded path
652
+     */
653
+    protected function encodePath($path) {
654
+        // slashes need to stay
655
+        return str_replace('%2F', '/', rawurlencode($path));
656
+    }
657
+
658
+    /**
659
+     * @param string $method
660
+     * @param string $path
661
+     * @param string|resource|null $body
662
+     * @param int $expected
663
+     * @return bool
664
+     * @throws StorageInvalidException
665
+     * @throws StorageNotAvailableException
666
+     */
667
+    protected function simpleResponse($method, $path, $body, $expected) {
668
+        $path = $this->cleanPath($path);
669
+        try {
670
+            $response = $this->client->request($method, $this->encodePath($path), $body);
671
+            return $response['statusCode'] == $expected;
672
+        } catch (ClientHttpException $e) {
673
+            if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
674
+                $this->statCache->clear($path . '/');
675
+                $this->statCache->set($path, false);
676
+                return false;
677
+            }
678
+
679
+            $this->convertException($e, $path);
680
+        } catch (\Exception $e) {
681
+            $this->convertException($e, $path);
682
+        }
683
+        return false;
684
+    }
685
+
686
+    /**
687
+     * check if curl is installed
688
+     */
689
+    public static function checkDependencies() {
690
+        return true;
691
+    }
692
+
693
+    /** {@inheritdoc} */
694
+    public function isUpdatable($path) {
695
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
696
+    }
697
+
698
+    /** {@inheritdoc} */
699
+    public function isCreatable($path) {
700
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
701
+    }
702
+
703
+    /** {@inheritdoc} */
704
+    public function isSharable($path) {
705
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
706
+    }
707
+
708
+    /** {@inheritdoc} */
709
+    public function isDeletable($path) {
710
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
711
+    }
712
+
713
+    /** {@inheritdoc} */
714
+    public function getPermissions($path) {
715
+        $this->init();
716
+        $path = $this->cleanPath($path);
717
+        $response = $this->propfind($path);
718
+        if ($response === false) {
719
+            return 0;
720
+        }
721
+        if (isset($response['{http://owncloud.org/ns}permissions'])) {
722
+            return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
723
+        } elseif ($this->is_dir($path)) {
724
+            return Constants::PERMISSION_ALL;
725
+        } elseif ($this->file_exists($path)) {
726
+            return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
727
+        } else {
728
+            return 0;
729
+        }
730
+    }
731
+
732
+    /** {@inheritdoc} */
733
+    public function getETag($path) {
734
+        $this->init();
735
+        $path = $this->cleanPath($path);
736
+        $response = $this->propfind($path);
737
+        if ($response === false) {
738
+            return null;
739
+        }
740
+        if (isset($response['{DAV:}getetag'])) {
741
+            $etag = trim($response['{DAV:}getetag'], '"');
742
+            if (strlen($etag) > 40) {
743
+                $etag = md5($etag);
744
+            }
745
+            return $etag;
746
+        }
747
+        return parent::getEtag($path);
748
+    }
749
+
750
+    /**
751
+     * @param string $permissionsString
752
+     * @return int
753
+     */
754
+    protected function parsePermissions($permissionsString) {
755
+        $permissions = Constants::PERMISSION_READ;
756
+        if (strpos($permissionsString, 'R') !== false) {
757
+            $permissions |= Constants::PERMISSION_SHARE;
758
+        }
759
+        if (strpos($permissionsString, 'D') !== false) {
760
+            $permissions |= Constants::PERMISSION_DELETE;
761
+        }
762
+        if (strpos($permissionsString, 'W') !== false) {
763
+            $permissions |= Constants::PERMISSION_UPDATE;
764
+        }
765
+        if (strpos($permissionsString, 'CK') !== false) {
766
+            $permissions |= Constants::PERMISSION_CREATE;
767
+            $permissions |= Constants::PERMISSION_UPDATE;
768
+        }
769
+        return $permissions;
770
+    }
771
+
772
+    /**
773
+     * check if a file or folder has been updated since $time
774
+     *
775
+     * @param string $path
776
+     * @param int $time
777
+     * @throws \OCP\Files\StorageNotAvailableException
778
+     * @return bool
779
+     */
780
+    public function hasUpdated($path, $time) {
781
+        $this->init();
782
+        $path = $this->cleanPath($path);
783
+        try {
784
+            // force refresh for $path
785
+            $this->statCache->remove($path);
786
+            $response = $this->propfind($path);
787
+            if ($response === false) {
788
+                if ($path === '') {
789
+                    // if root is gone it means the storage is not available
790
+                    throw new StorageNotAvailableException('root is gone');
791
+                }
792
+                return false;
793
+            }
794
+            if (isset($response['{DAV:}getetag'])) {
795
+                $cachedData = $this->getCache()->get($path);
796
+                $etag = null;
797
+                if (isset($response['{DAV:}getetag'])) {
798
+                    $etag = trim($response['{DAV:}getetag'], '"');
799
+                }
800
+                if (!empty($etag) && $cachedData['etag'] !== $etag) {
801
+                    return true;
802
+                } elseif (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
803
+                    $sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
804
+                    return $sharePermissions !== $cachedData['permissions'];
805
+                } elseif (isset($response['{http://owncloud.org/ns}permissions'])) {
806
+                    $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
807
+                    return $permissions !== $cachedData['permissions'];
808
+                } else {
809
+                    return false;
810
+                }
811
+            } else {
812
+                $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
813
+                return $remoteMtime > $time;
814
+            }
815
+        } catch (ClientHttpException $e) {
816
+            if ($e->getHttpStatus() === 405) {
817
+                if ($path === '') {
818
+                    // if root is gone it means the storage is not available
819
+                    throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
820
+                }
821
+                return false;
822
+            }
823
+            $this->convertException($e, $path);
824
+            return false;
825
+        } catch (\Exception $e) {
826
+            $this->convertException($e, $path);
827
+            return false;
828
+        }
829
+    }
830
+
831
+    /**
832
+     * Interpret the given exception and decide whether it is due to an
833
+     * unavailable storage, invalid storage or other.
834
+     * This will either throw StorageInvalidException, StorageNotAvailableException
835
+     * or do nothing.
836
+     *
837
+     * @param Exception $e sabre exception
838
+     * @param string $path optional path from the operation
839
+     *
840
+     * @throws StorageInvalidException if the storage is invalid, for example
841
+     * when the authentication expired or is invalid
842
+     * @throws StorageNotAvailableException if the storage is not available,
843
+     * which might be temporary
844
+     * @throws ForbiddenException if the action is not allowed
845
+     */
846
+    protected function convertException(Exception $e, $path = '') {
847
+        \OC::$server->getLogger()->logException($e, ['app' => 'files_external', 'level' => ILogger::DEBUG]);
848
+        if ($e instanceof ClientHttpException) {
849
+            if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
850
+                throw new \OCP\Lock\LockedException($path);
851
+            }
852
+            if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
853
+                // either password was changed or was invalid all along
854
+                throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
855
+            } elseif ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
856
+                // ignore exception for MethodNotAllowed, false will be returned
857
+                return;
858
+            } elseif ($e->getHttpStatus() === Http::STATUS_FORBIDDEN) {
859
+                // The operation is forbidden. Fail somewhat gracefully
860
+                throw new ForbiddenException(get_class($e) . ':' . $e->getMessage(), false);
861
+            }
862
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
863
+        } elseif ($e instanceof ClientException) {
864
+            // connection timeout or refused, server could be temporarily down
865
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
866
+        } elseif ($e instanceof \InvalidArgumentException) {
867
+            // parse error because the server returned HTML instead of XML,
868
+            // possibly temporarily down
869
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
870
+        } elseif (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
871
+            // rethrow
872
+            throw $e;
873
+        }
874
+
875
+        // TODO: only log for now, but in the future need to wrap/rethrow exception
876
+    }
877 877
 }
Please login to merge, or discard this patch.
lib/private/Route/Router.php 1 patch
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[]|null */
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 ($this->routingFiles === null) {
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[]|null */
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 ($this->routingFiles === null) {
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.
lib/private/User/User.php 1 patch
Indentation   +450 added lines, -450 removed lines patch added patch discarded remove patch
@@ -58,454 +58,454 @@
 block discarded – undo
58 58
 use Symfony\Component\EventDispatcher\GenericEvent;
59 59
 
60 60
 class User implements IUser {
61
-	/** @var string */
62
-	private $uid;
63
-
64
-	/** @var string|null */
65
-	private $displayName;
66
-
67
-	/** @var UserInterface|null */
68
-	private $backend;
69
-	/** @var EventDispatcherInterface */
70
-	private $legacyDispatcher;
71
-
72
-	/** @var IEventDispatcher */
73
-	private $dispatcher;
74
-
75
-	/** @var bool */
76
-	private $enabled;
77
-
78
-	/** @var Emitter|Manager */
79
-	private $emitter;
80
-
81
-	/** @var string */
82
-	private $home;
83
-
84
-	/** @var int */
85
-	private $lastLogin;
86
-
87
-	/** @var \OCP\IConfig */
88
-	private $config;
89
-
90
-	/** @var IAvatarManager */
91
-	private $avatarManager;
92
-
93
-	/** @var IURLGenerator */
94
-	private $urlGenerator;
95
-
96
-	public function __construct(string $uid, ?UserInterface $backend, EventDispatcherInterface $dispatcher, $emitter = null, IConfig $config = null, $urlGenerator = null) {
97
-		$this->uid = $uid;
98
-		$this->backend = $backend;
99
-		$this->legacyDispatcher = $dispatcher;
100
-		$this->emitter = $emitter;
101
-		if (is_null($config)) {
102
-			$config = \OC::$server->getConfig();
103
-		}
104
-		$this->config = $config;
105
-		$this->urlGenerator = $urlGenerator;
106
-		$enabled = $this->config->getUserValue($uid, 'core', 'enabled', 'true');
107
-		$this->enabled = ($enabled === 'true');
108
-		$this->lastLogin = $this->config->getUserValue($uid, 'login', 'lastLogin', 0);
109
-		if (is_null($this->urlGenerator)) {
110
-			$this->urlGenerator = \OC::$server->getURLGenerator();
111
-		}
112
-		// TODO: inject
113
-		$this->dispatcher = \OC::$server->query(IEventDispatcher::class);
114
-	}
115
-
116
-	/**
117
-	 * get the user id
118
-	 *
119
-	 * @return string
120
-	 */
121
-	public function getUID() {
122
-		return $this->uid;
123
-	}
124
-
125
-	/**
126
-	 * get the display name for the user, if no specific display name is set it will fallback to the user id
127
-	 *
128
-	 * @return string
129
-	 */
130
-	public function getDisplayName() {
131
-		if ($this->displayName === null) {
132
-			$displayName = '';
133
-			if ($this->backend && $this->backend->implementsActions(Backend::GET_DISPLAYNAME)) {
134
-				// get display name and strip whitespace from the beginning and end of it
135
-				$backendDisplayName = $this->backend->getDisplayName($this->uid);
136
-				if (is_string($backendDisplayName)) {
137
-					$displayName = trim($backendDisplayName);
138
-				}
139
-			}
140
-
141
-			if (!empty($displayName)) {
142
-				$this->displayName = $displayName;
143
-			} else {
144
-				$this->displayName = $this->uid;
145
-			}
146
-		}
147
-		return $this->displayName;
148
-	}
149
-
150
-	/**
151
-	 * set the displayname for the user
152
-	 *
153
-	 * @param string $displayName
154
-	 * @return bool
155
-	 */
156
-	public function setDisplayName($displayName) {
157
-		$displayName = trim($displayName);
158
-		$oldDisplayName = $this->getDisplayName();
159
-		if ($this->backend->implementsActions(Backend::SET_DISPLAYNAME) && !empty($displayName) && $displayName !== $oldDisplayName) {
160
-			$result = $this->backend->setDisplayName($this->uid, $displayName);
161
-			if ($result) {
162
-				$this->displayName = $displayName;
163
-				$this->triggerChange('displayName', $displayName, $oldDisplayName);
164
-			}
165
-			return $result !== false;
166
-		}
167
-		return false;
168
-	}
169
-
170
-	/**
171
-	 * set the email address of the user
172
-	 *
173
-	 * @param string|null $mailAddress
174
-	 * @return void
175
-	 * @since 9.0.0
176
-	 */
177
-	public function setEMailAddress($mailAddress) {
178
-		$oldMailAddress = $this->getEMailAddress();
179
-		if ($oldMailAddress !== $mailAddress) {
180
-			if ($mailAddress === '') {
181
-				$this->config->deleteUserValue($this->uid, 'settings', 'email');
182
-			} else {
183
-				$this->config->setUserValue($this->uid, 'settings', 'email', $mailAddress);
184
-			}
185
-			$this->triggerChange('eMailAddress', $mailAddress, $oldMailAddress);
186
-		}
187
-	}
188
-
189
-	/**
190
-	 * returns the timestamp of the user's last login or 0 if the user did never
191
-	 * login
192
-	 *
193
-	 * @return int
194
-	 */
195
-	public function getLastLogin() {
196
-		return $this->lastLogin;
197
-	}
198
-
199
-	/**
200
-	 * updates the timestamp of the most recent login of this user
201
-	 */
202
-	public function updateLastLoginTimestamp() {
203
-		$firstTimeLogin = ($this->lastLogin === 0);
204
-		$this->lastLogin = time();
205
-		$this->config->setUserValue(
206
-			$this->uid, 'login', 'lastLogin', $this->lastLogin);
207
-
208
-		return $firstTimeLogin;
209
-	}
210
-
211
-	/**
212
-	 * Delete the user
213
-	 *
214
-	 * @return bool
215
-	 */
216
-	public function delete() {
217
-		/** @deprecated 21.0.0 use BeforeUserDeletedEvent event with the IEventDispatcher instead */
218
-		$this->legacyDispatcher->dispatch(IUser::class . '::preDelete', new GenericEvent($this));
219
-		if ($this->emitter) {
220
-			/** @deprecated 21.0.0 use BeforeUserDeletedEvent event with the IEventDispatcher instead */
221
-			$this->emitter->emit('\OC\User', 'preDelete', [$this]);
222
-		}
223
-		$this->dispatcher->dispatchTyped(new BeforeUserDeletedEvent($this));
224
-		// get the home now because it won't return it after user deletion
225
-		$homePath = $this->getHome();
226
-		$result = $this->backend->deleteUser($this->uid);
227
-		if ($result) {
228
-
229
-			// FIXME: Feels like an hack - suggestions?
230
-
231
-			$groupManager = \OC::$server->getGroupManager();
232
-			// We have to delete the user from all groups
233
-			foreach ($groupManager->getUserGroupIds($this) as $groupId) {
234
-				$group = $groupManager->get($groupId);
235
-				if ($group) {
236
-					$this->dispatcher->dispatchTyped(new BeforeUserRemovedEvent($group, $this));
237
-					$group->removeUser($this);
238
-					$this->dispatcher->dispatchTyped(new UserRemovedEvent($group, $this));
239
-				}
240
-			}
241
-			// Delete the user's keys in preferences
242
-			\OC::$server->getConfig()->deleteAllUserValues($this->uid);
243
-
244
-			// Delete user files in /data/
245
-			if ($homePath !== false) {
246
-				// FIXME: this operates directly on FS, should use View instead...
247
-				// also this is not testable/mockable...
248
-				\OC_Helper::rmdirr($homePath);
249
-			}
250
-
251
-			// Delete the users entry in the storage table
252
-			Storage::remove('home::' . $this->uid);
253
-
254
-			\OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->uid);
255
-			\OC::$server->getCommentsManager()->deleteReadMarksFromUser($this);
256
-
257
-			/** @var IAvatarManager $avatarManager */
258
-			$avatarManager = \OC::$server->query(AvatarManager::class);
259
-			$avatarManager->deleteUserAvatar($this->uid);
260
-
261
-			$notification = \OC::$server->getNotificationManager()->createNotification();
262
-			$notification->setUser($this->uid);
263
-			\OC::$server->getNotificationManager()->markProcessed($notification);
264
-
265
-			/** @var AccountManager $accountManager */
266
-			$accountManager = \OC::$server->query(AccountManager::class);
267
-			$accountManager->deleteUser($this);
268
-
269
-			/** @deprecated 21.0.0 use UserDeletedEvent event with the IEventDispatcher instead */
270
-			$this->legacyDispatcher->dispatch(IUser::class . '::postDelete', new GenericEvent($this));
271
-			if ($this->emitter) {
272
-				/** @deprecated 21.0.0 use UserDeletedEvent event with the IEventDispatcher instead */
273
-				$this->emitter->emit('\OC\User', 'postDelete', [$this]);
274
-			}
275
-			$this->dispatcher->dispatchTyped(new UserDeletedEvent($this));
276
-		}
277
-		return !($result === false);
278
-	}
279
-
280
-	/**
281
-	 * Set the password of the user
282
-	 *
283
-	 * @param string $password
284
-	 * @param string $recoveryPassword for the encryption app to reset encryption keys
285
-	 * @return bool
286
-	 */
287
-	public function setPassword($password, $recoveryPassword = null) {
288
-		$this->legacyDispatcher->dispatch(IUser::class . '::preSetPassword', new GenericEvent($this, [
289
-			'password' => $password,
290
-			'recoveryPassword' => $recoveryPassword,
291
-		]));
292
-		if ($this->emitter) {
293
-			$this->emitter->emit('\OC\User', 'preSetPassword', [$this, $password, $recoveryPassword]);
294
-		}
295
-		if ($this->backend->implementsActions(Backend::SET_PASSWORD)) {
296
-			$result = $this->backend->setPassword($this->uid, $password);
297
-			$this->legacyDispatcher->dispatch(IUser::class . '::postSetPassword', new GenericEvent($this, [
298
-				'password' => $password,
299
-				'recoveryPassword' => $recoveryPassword,
300
-			]));
301
-			if ($this->emitter) {
302
-				$this->emitter->emit('\OC\User', 'postSetPassword', [$this, $password, $recoveryPassword]);
303
-			}
304
-			return !($result === false);
305
-		} else {
306
-			return false;
307
-		}
308
-	}
309
-
310
-	/**
311
-	 * get the users home folder to mount
312
-	 *
313
-	 * @return string
314
-	 */
315
-	public function getHome() {
316
-		if (!$this->home) {
317
-			if ($this->backend->implementsActions(Backend::GET_HOME) and $home = $this->backend->getHome($this->uid)) {
318
-				$this->home = $home;
319
-			} elseif ($this->config) {
320
-				$this->home = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $this->uid;
321
-			} else {
322
-				$this->home = \OC::$SERVERROOT . '/data/' . $this->uid;
323
-			}
324
-		}
325
-		return $this->home;
326
-	}
327
-
328
-	/**
329
-	 * Get the name of the backend class the user is connected with
330
-	 *
331
-	 * @return string
332
-	 */
333
-	public function getBackendClassName() {
334
-		if ($this->backend instanceof IUserBackend) {
335
-			return $this->backend->getBackendName();
336
-		}
337
-		return get_class($this->backend);
338
-	}
339
-
340
-	public function getBackend() {
341
-		return $this->backend;
342
-	}
343
-
344
-	/**
345
-	 * check if the backend allows the user to change his avatar on Personal page
346
-	 *
347
-	 * @return bool
348
-	 */
349
-	public function canChangeAvatar() {
350
-		if ($this->backend->implementsActions(Backend::PROVIDE_AVATAR)) {
351
-			return $this->backend->canChangeAvatar($this->uid);
352
-		}
353
-		return true;
354
-	}
355
-
356
-	/**
357
-	 * check if the backend supports changing passwords
358
-	 *
359
-	 * @return bool
360
-	 */
361
-	public function canChangePassword() {
362
-		return $this->backend->implementsActions(Backend::SET_PASSWORD);
363
-	}
364
-
365
-	/**
366
-	 * check if the backend supports changing display names
367
-	 *
368
-	 * @return bool
369
-	 */
370
-	public function canChangeDisplayName() {
371
-		if ($this->config->getSystemValue('allow_user_to_change_display_name') === false) {
372
-			return false;
373
-		}
374
-		return $this->backend->implementsActions(Backend::SET_DISPLAYNAME);
375
-	}
376
-
377
-	/**
378
-	 * check if the user is enabled
379
-	 *
380
-	 * @return bool
381
-	 */
382
-	public function isEnabled() {
383
-		return $this->enabled;
384
-	}
385
-
386
-	/**
387
-	 * set the enabled status for the user
388
-	 *
389
-	 * @param bool $enabled
390
-	 */
391
-	public function setEnabled(bool $enabled = true) {
392
-		$oldStatus = $this->isEnabled();
393
-		$this->enabled = $enabled;
394
-		if ($oldStatus !== $this->enabled) {
395
-			// TODO: First change the value, then trigger the event as done for all other properties.
396
-			$this->triggerChange('enabled', $enabled, $oldStatus);
397
-			$this->config->setUserValue($this->uid, 'core', 'enabled', $enabled ? 'true' : 'false');
398
-		}
399
-	}
400
-
401
-	/**
402
-	 * get the users email address
403
-	 *
404
-	 * @return string|null
405
-	 * @since 9.0.0
406
-	 */
407
-	public function getEMailAddress() {
408
-		return $this->config->getUserValue($this->uid, 'settings', 'email', null);
409
-	}
410
-
411
-	/**
412
-	 * get the users' quota
413
-	 *
414
-	 * @return string
415
-	 * @since 9.0.0
416
-	 */
417
-	public function getQuota() {
418
-		// allow apps to modify the user quota by hooking into the event
419
-		$event = new GetQuotaEvent($this);
420
-		$this->dispatcher->dispatchTyped($event);
421
-		$overwriteQuota = $event->getQuota();
422
-		if ($overwriteQuota) {
423
-			$quota = $overwriteQuota;
424
-		} else {
425
-			$quota = $this->config->getUserValue($this->uid, 'files', 'quota', 'default');
426
-		}
427
-		if ($quota === 'default') {
428
-			$quota = $this->config->getAppValue('files', 'default_quota', 'none');
429
-		}
430
-		return $quota;
431
-	}
432
-
433
-	/**
434
-	 * set the users' quota
435
-	 *
436
-	 * @param string $quota
437
-	 * @return void
438
-	 * @since 9.0.0
439
-	 */
440
-	public function setQuota($quota) {
441
-		$oldQuota = $this->config->getUserValue($this->uid, 'files', 'quota', '');
442
-		if ($quota !== 'none' and $quota !== 'default') {
443
-			$quota = OC_Helper::computerFileSize($quota);
444
-			$quota = OC_Helper::humanFileSize($quota);
445
-		}
446
-		if ($quota !== $oldQuota) {
447
-			$this->config->setUserValue($this->uid, 'files', 'quota', $quota);
448
-			$this->triggerChange('quota', $quota, $oldQuota);
449
-		}
450
-	}
451
-
452
-	/**
453
-	 * get the avatar image if it exists
454
-	 *
455
-	 * @param int $size
456
-	 * @return IImage|null
457
-	 * @since 9.0.0
458
-	 */
459
-	public function getAvatarImage($size) {
460
-		// delay the initialization
461
-		if (is_null($this->avatarManager)) {
462
-			$this->avatarManager = \OC::$server->getAvatarManager();
463
-		}
464
-
465
-		$avatar = $this->avatarManager->getAvatar($this->uid);
466
-		$image = $avatar->get(-1);
467
-		if ($image) {
468
-			return $image;
469
-		}
470
-
471
-		return null;
472
-	}
473
-
474
-	/**
475
-	 * get the federation cloud id
476
-	 *
477
-	 * @return string
478
-	 * @since 9.0.0
479
-	 */
480
-	public function getCloudId() {
481
-		$uid = $this->getUID();
482
-		$server = $this->urlGenerator->getAbsoluteURL('/');
483
-		$server = rtrim($this->removeProtocolFromUrl($server), '/');
484
-		return \OC::$server->getCloudIdManager()->getCloudId($uid, $server)->getId();
485
-	}
486
-
487
-	/**
488
-	 * @param string $url
489
-	 * @return string
490
-	 */
491
-	private function removeProtocolFromUrl($url) {
492
-		if (strpos($url, 'https://') === 0) {
493
-			return substr($url, strlen('https://'));
494
-		} elseif (strpos($url, 'http://') === 0) {
495
-			return substr($url, strlen('http://'));
496
-		}
497
-
498
-		return $url;
499
-	}
500
-
501
-	public function triggerChange($feature, $value = null, $oldValue = null) {
502
-		$this->legacyDispatcher->dispatch(IUser::class . '::changeUser', new GenericEvent($this, [
503
-			'feature' => $feature,
504
-			'value' => $value,
505
-			'oldValue' => $oldValue,
506
-		]));
507
-		if ($this->emitter) {
508
-			$this->emitter->emit('\OC\User', 'changeUser', [$this, $feature, $value, $oldValue]);
509
-		}
510
-	}
61
+    /** @var string */
62
+    private $uid;
63
+
64
+    /** @var string|null */
65
+    private $displayName;
66
+
67
+    /** @var UserInterface|null */
68
+    private $backend;
69
+    /** @var EventDispatcherInterface */
70
+    private $legacyDispatcher;
71
+
72
+    /** @var IEventDispatcher */
73
+    private $dispatcher;
74
+
75
+    /** @var bool */
76
+    private $enabled;
77
+
78
+    /** @var Emitter|Manager */
79
+    private $emitter;
80
+
81
+    /** @var string */
82
+    private $home;
83
+
84
+    /** @var int */
85
+    private $lastLogin;
86
+
87
+    /** @var \OCP\IConfig */
88
+    private $config;
89
+
90
+    /** @var IAvatarManager */
91
+    private $avatarManager;
92
+
93
+    /** @var IURLGenerator */
94
+    private $urlGenerator;
95
+
96
+    public function __construct(string $uid, ?UserInterface $backend, EventDispatcherInterface $dispatcher, $emitter = null, IConfig $config = null, $urlGenerator = null) {
97
+        $this->uid = $uid;
98
+        $this->backend = $backend;
99
+        $this->legacyDispatcher = $dispatcher;
100
+        $this->emitter = $emitter;
101
+        if (is_null($config)) {
102
+            $config = \OC::$server->getConfig();
103
+        }
104
+        $this->config = $config;
105
+        $this->urlGenerator = $urlGenerator;
106
+        $enabled = $this->config->getUserValue($uid, 'core', 'enabled', 'true');
107
+        $this->enabled = ($enabled === 'true');
108
+        $this->lastLogin = $this->config->getUserValue($uid, 'login', 'lastLogin', 0);
109
+        if (is_null($this->urlGenerator)) {
110
+            $this->urlGenerator = \OC::$server->getURLGenerator();
111
+        }
112
+        // TODO: inject
113
+        $this->dispatcher = \OC::$server->query(IEventDispatcher::class);
114
+    }
115
+
116
+    /**
117
+     * get the user id
118
+     *
119
+     * @return string
120
+     */
121
+    public function getUID() {
122
+        return $this->uid;
123
+    }
124
+
125
+    /**
126
+     * get the display name for the user, if no specific display name is set it will fallback to the user id
127
+     *
128
+     * @return string
129
+     */
130
+    public function getDisplayName() {
131
+        if ($this->displayName === null) {
132
+            $displayName = '';
133
+            if ($this->backend && $this->backend->implementsActions(Backend::GET_DISPLAYNAME)) {
134
+                // get display name and strip whitespace from the beginning and end of it
135
+                $backendDisplayName = $this->backend->getDisplayName($this->uid);
136
+                if (is_string($backendDisplayName)) {
137
+                    $displayName = trim($backendDisplayName);
138
+                }
139
+            }
140
+
141
+            if (!empty($displayName)) {
142
+                $this->displayName = $displayName;
143
+            } else {
144
+                $this->displayName = $this->uid;
145
+            }
146
+        }
147
+        return $this->displayName;
148
+    }
149
+
150
+    /**
151
+     * set the displayname for the user
152
+     *
153
+     * @param string $displayName
154
+     * @return bool
155
+     */
156
+    public function setDisplayName($displayName) {
157
+        $displayName = trim($displayName);
158
+        $oldDisplayName = $this->getDisplayName();
159
+        if ($this->backend->implementsActions(Backend::SET_DISPLAYNAME) && !empty($displayName) && $displayName !== $oldDisplayName) {
160
+            $result = $this->backend->setDisplayName($this->uid, $displayName);
161
+            if ($result) {
162
+                $this->displayName = $displayName;
163
+                $this->triggerChange('displayName', $displayName, $oldDisplayName);
164
+            }
165
+            return $result !== false;
166
+        }
167
+        return false;
168
+    }
169
+
170
+    /**
171
+     * set the email address of the user
172
+     *
173
+     * @param string|null $mailAddress
174
+     * @return void
175
+     * @since 9.0.0
176
+     */
177
+    public function setEMailAddress($mailAddress) {
178
+        $oldMailAddress = $this->getEMailAddress();
179
+        if ($oldMailAddress !== $mailAddress) {
180
+            if ($mailAddress === '') {
181
+                $this->config->deleteUserValue($this->uid, 'settings', 'email');
182
+            } else {
183
+                $this->config->setUserValue($this->uid, 'settings', 'email', $mailAddress);
184
+            }
185
+            $this->triggerChange('eMailAddress', $mailAddress, $oldMailAddress);
186
+        }
187
+    }
188
+
189
+    /**
190
+     * returns the timestamp of the user's last login or 0 if the user did never
191
+     * login
192
+     *
193
+     * @return int
194
+     */
195
+    public function getLastLogin() {
196
+        return $this->lastLogin;
197
+    }
198
+
199
+    /**
200
+     * updates the timestamp of the most recent login of this user
201
+     */
202
+    public function updateLastLoginTimestamp() {
203
+        $firstTimeLogin = ($this->lastLogin === 0);
204
+        $this->lastLogin = time();
205
+        $this->config->setUserValue(
206
+            $this->uid, 'login', 'lastLogin', $this->lastLogin);
207
+
208
+        return $firstTimeLogin;
209
+    }
210
+
211
+    /**
212
+     * Delete the user
213
+     *
214
+     * @return bool
215
+     */
216
+    public function delete() {
217
+        /** @deprecated 21.0.0 use BeforeUserDeletedEvent event with the IEventDispatcher instead */
218
+        $this->legacyDispatcher->dispatch(IUser::class . '::preDelete', new GenericEvent($this));
219
+        if ($this->emitter) {
220
+            /** @deprecated 21.0.0 use BeforeUserDeletedEvent event with the IEventDispatcher instead */
221
+            $this->emitter->emit('\OC\User', 'preDelete', [$this]);
222
+        }
223
+        $this->dispatcher->dispatchTyped(new BeforeUserDeletedEvent($this));
224
+        // get the home now because it won't return it after user deletion
225
+        $homePath = $this->getHome();
226
+        $result = $this->backend->deleteUser($this->uid);
227
+        if ($result) {
228
+
229
+            // FIXME: Feels like an hack - suggestions?
230
+
231
+            $groupManager = \OC::$server->getGroupManager();
232
+            // We have to delete the user from all groups
233
+            foreach ($groupManager->getUserGroupIds($this) as $groupId) {
234
+                $group = $groupManager->get($groupId);
235
+                if ($group) {
236
+                    $this->dispatcher->dispatchTyped(new BeforeUserRemovedEvent($group, $this));
237
+                    $group->removeUser($this);
238
+                    $this->dispatcher->dispatchTyped(new UserRemovedEvent($group, $this));
239
+                }
240
+            }
241
+            // Delete the user's keys in preferences
242
+            \OC::$server->getConfig()->deleteAllUserValues($this->uid);
243
+
244
+            // Delete user files in /data/
245
+            if ($homePath !== false) {
246
+                // FIXME: this operates directly on FS, should use View instead...
247
+                // also this is not testable/mockable...
248
+                \OC_Helper::rmdirr($homePath);
249
+            }
250
+
251
+            // Delete the users entry in the storage table
252
+            Storage::remove('home::' . $this->uid);
253
+
254
+            \OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->uid);
255
+            \OC::$server->getCommentsManager()->deleteReadMarksFromUser($this);
256
+
257
+            /** @var IAvatarManager $avatarManager */
258
+            $avatarManager = \OC::$server->query(AvatarManager::class);
259
+            $avatarManager->deleteUserAvatar($this->uid);
260
+
261
+            $notification = \OC::$server->getNotificationManager()->createNotification();
262
+            $notification->setUser($this->uid);
263
+            \OC::$server->getNotificationManager()->markProcessed($notification);
264
+
265
+            /** @var AccountManager $accountManager */
266
+            $accountManager = \OC::$server->query(AccountManager::class);
267
+            $accountManager->deleteUser($this);
268
+
269
+            /** @deprecated 21.0.0 use UserDeletedEvent event with the IEventDispatcher instead */
270
+            $this->legacyDispatcher->dispatch(IUser::class . '::postDelete', new GenericEvent($this));
271
+            if ($this->emitter) {
272
+                /** @deprecated 21.0.0 use UserDeletedEvent event with the IEventDispatcher instead */
273
+                $this->emitter->emit('\OC\User', 'postDelete', [$this]);
274
+            }
275
+            $this->dispatcher->dispatchTyped(new UserDeletedEvent($this));
276
+        }
277
+        return !($result === false);
278
+    }
279
+
280
+    /**
281
+     * Set the password of the user
282
+     *
283
+     * @param string $password
284
+     * @param string $recoveryPassword for the encryption app to reset encryption keys
285
+     * @return bool
286
+     */
287
+    public function setPassword($password, $recoveryPassword = null) {
288
+        $this->legacyDispatcher->dispatch(IUser::class . '::preSetPassword', new GenericEvent($this, [
289
+            'password' => $password,
290
+            'recoveryPassword' => $recoveryPassword,
291
+        ]));
292
+        if ($this->emitter) {
293
+            $this->emitter->emit('\OC\User', 'preSetPassword', [$this, $password, $recoveryPassword]);
294
+        }
295
+        if ($this->backend->implementsActions(Backend::SET_PASSWORD)) {
296
+            $result = $this->backend->setPassword($this->uid, $password);
297
+            $this->legacyDispatcher->dispatch(IUser::class . '::postSetPassword', new GenericEvent($this, [
298
+                'password' => $password,
299
+                'recoveryPassword' => $recoveryPassword,
300
+            ]));
301
+            if ($this->emitter) {
302
+                $this->emitter->emit('\OC\User', 'postSetPassword', [$this, $password, $recoveryPassword]);
303
+            }
304
+            return !($result === false);
305
+        } else {
306
+            return false;
307
+        }
308
+    }
309
+
310
+    /**
311
+     * get the users home folder to mount
312
+     *
313
+     * @return string
314
+     */
315
+    public function getHome() {
316
+        if (!$this->home) {
317
+            if ($this->backend->implementsActions(Backend::GET_HOME) and $home = $this->backend->getHome($this->uid)) {
318
+                $this->home = $home;
319
+            } elseif ($this->config) {
320
+                $this->home = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $this->uid;
321
+            } else {
322
+                $this->home = \OC::$SERVERROOT . '/data/' . $this->uid;
323
+            }
324
+        }
325
+        return $this->home;
326
+    }
327
+
328
+    /**
329
+     * Get the name of the backend class the user is connected with
330
+     *
331
+     * @return string
332
+     */
333
+    public function getBackendClassName() {
334
+        if ($this->backend instanceof IUserBackend) {
335
+            return $this->backend->getBackendName();
336
+        }
337
+        return get_class($this->backend);
338
+    }
339
+
340
+    public function getBackend() {
341
+        return $this->backend;
342
+    }
343
+
344
+    /**
345
+     * check if the backend allows the user to change his avatar on Personal page
346
+     *
347
+     * @return bool
348
+     */
349
+    public function canChangeAvatar() {
350
+        if ($this->backend->implementsActions(Backend::PROVIDE_AVATAR)) {
351
+            return $this->backend->canChangeAvatar($this->uid);
352
+        }
353
+        return true;
354
+    }
355
+
356
+    /**
357
+     * check if the backend supports changing passwords
358
+     *
359
+     * @return bool
360
+     */
361
+    public function canChangePassword() {
362
+        return $this->backend->implementsActions(Backend::SET_PASSWORD);
363
+    }
364
+
365
+    /**
366
+     * check if the backend supports changing display names
367
+     *
368
+     * @return bool
369
+     */
370
+    public function canChangeDisplayName() {
371
+        if ($this->config->getSystemValue('allow_user_to_change_display_name') === false) {
372
+            return false;
373
+        }
374
+        return $this->backend->implementsActions(Backend::SET_DISPLAYNAME);
375
+    }
376
+
377
+    /**
378
+     * check if the user is enabled
379
+     *
380
+     * @return bool
381
+     */
382
+    public function isEnabled() {
383
+        return $this->enabled;
384
+    }
385
+
386
+    /**
387
+     * set the enabled status for the user
388
+     *
389
+     * @param bool $enabled
390
+     */
391
+    public function setEnabled(bool $enabled = true) {
392
+        $oldStatus = $this->isEnabled();
393
+        $this->enabled = $enabled;
394
+        if ($oldStatus !== $this->enabled) {
395
+            // TODO: First change the value, then trigger the event as done for all other properties.
396
+            $this->triggerChange('enabled', $enabled, $oldStatus);
397
+            $this->config->setUserValue($this->uid, 'core', 'enabled', $enabled ? 'true' : 'false');
398
+        }
399
+    }
400
+
401
+    /**
402
+     * get the users email address
403
+     *
404
+     * @return string|null
405
+     * @since 9.0.0
406
+     */
407
+    public function getEMailAddress() {
408
+        return $this->config->getUserValue($this->uid, 'settings', 'email', null);
409
+    }
410
+
411
+    /**
412
+     * get the users' quota
413
+     *
414
+     * @return string
415
+     * @since 9.0.0
416
+     */
417
+    public function getQuota() {
418
+        // allow apps to modify the user quota by hooking into the event
419
+        $event = new GetQuotaEvent($this);
420
+        $this->dispatcher->dispatchTyped($event);
421
+        $overwriteQuota = $event->getQuota();
422
+        if ($overwriteQuota) {
423
+            $quota = $overwriteQuota;
424
+        } else {
425
+            $quota = $this->config->getUserValue($this->uid, 'files', 'quota', 'default');
426
+        }
427
+        if ($quota === 'default') {
428
+            $quota = $this->config->getAppValue('files', 'default_quota', 'none');
429
+        }
430
+        return $quota;
431
+    }
432
+
433
+    /**
434
+     * set the users' quota
435
+     *
436
+     * @param string $quota
437
+     * @return void
438
+     * @since 9.0.0
439
+     */
440
+    public function setQuota($quota) {
441
+        $oldQuota = $this->config->getUserValue($this->uid, 'files', 'quota', '');
442
+        if ($quota !== 'none' and $quota !== 'default') {
443
+            $quota = OC_Helper::computerFileSize($quota);
444
+            $quota = OC_Helper::humanFileSize($quota);
445
+        }
446
+        if ($quota !== $oldQuota) {
447
+            $this->config->setUserValue($this->uid, 'files', 'quota', $quota);
448
+            $this->triggerChange('quota', $quota, $oldQuota);
449
+        }
450
+    }
451
+
452
+    /**
453
+     * get the avatar image if it exists
454
+     *
455
+     * @param int $size
456
+     * @return IImage|null
457
+     * @since 9.0.0
458
+     */
459
+    public function getAvatarImage($size) {
460
+        // delay the initialization
461
+        if (is_null($this->avatarManager)) {
462
+            $this->avatarManager = \OC::$server->getAvatarManager();
463
+        }
464
+
465
+        $avatar = $this->avatarManager->getAvatar($this->uid);
466
+        $image = $avatar->get(-1);
467
+        if ($image) {
468
+            return $image;
469
+        }
470
+
471
+        return null;
472
+    }
473
+
474
+    /**
475
+     * get the federation cloud id
476
+     *
477
+     * @return string
478
+     * @since 9.0.0
479
+     */
480
+    public function getCloudId() {
481
+        $uid = $this->getUID();
482
+        $server = $this->urlGenerator->getAbsoluteURL('/');
483
+        $server = rtrim($this->removeProtocolFromUrl($server), '/');
484
+        return \OC::$server->getCloudIdManager()->getCloudId($uid, $server)->getId();
485
+    }
486
+
487
+    /**
488
+     * @param string $url
489
+     * @return string
490
+     */
491
+    private function removeProtocolFromUrl($url) {
492
+        if (strpos($url, 'https://') === 0) {
493
+            return substr($url, strlen('https://'));
494
+        } elseif (strpos($url, 'http://') === 0) {
495
+            return substr($url, strlen('http://'));
496
+        }
497
+
498
+        return $url;
499
+    }
500
+
501
+    public function triggerChange($feature, $value = null, $oldValue = null) {
502
+        $this->legacyDispatcher->dispatch(IUser::class . '::changeUser', new GenericEvent($this, [
503
+            'feature' => $feature,
504
+            'value' => $value,
505
+            'oldValue' => $oldValue,
506
+        ]));
507
+        if ($this->emitter) {
508
+            $this->emitter->emit('\OC\User', 'changeUser', [$this, $feature, $value, $oldValue]);
509
+        }
510
+    }
511 511
 }
Please login to merge, or discard this patch.