Completed
Pull Request — master (#8054)
by Morris
32:31 queued 18:18
created
apps/files_external/lib/Lib/Storage/OwnCloud.php 3 patches
Indentation   +36 added lines, -36 removed lines patch added patch discarded remove patch
@@ -35,45 +35,45 @@
 block discarded – undo
35 35
  *
36 36
  */
37 37
 class OwnCloud extends \OC\Files\Storage\DAV{
38
-	const OC_URL_SUFFIX = 'remote.php/webdav';
38
+    const OC_URL_SUFFIX = 'remote.php/webdav';
39 39
 
40
-	public function __construct($params) {
41
-		// extract context path from host if specified
42
-		// (owncloud install path on host)
43
-		$host = $params['host'];
44
-		// strip protocol
45
-		if (substr($host, 0, 8) === "https://") {
46
-			$host = substr($host, 8);
47
-			$params['secure'] = true;
48
-		} else if (substr($host, 0, 7) === "http://") {
49
-			$host = substr($host, 7);
50
-			$params['secure'] = false;
51
-		}
52
-		$contextPath = '';
53
-		$hostSlashPos = strpos($host, '/');
54
-		if ($hostSlashPos !== false){
55
-			$contextPath = substr($host, $hostSlashPos);
56
-			$host = substr($host, 0, $hostSlashPos);
57
-		}
40
+    public function __construct($params) {
41
+        // extract context path from host if specified
42
+        // (owncloud install path on host)
43
+        $host = $params['host'];
44
+        // strip protocol
45
+        if (substr($host, 0, 8) === "https://") {
46
+            $host = substr($host, 8);
47
+            $params['secure'] = true;
48
+        } else if (substr($host, 0, 7) === "http://") {
49
+            $host = substr($host, 7);
50
+            $params['secure'] = false;
51
+        }
52
+        $contextPath = '';
53
+        $hostSlashPos = strpos($host, '/');
54
+        if ($hostSlashPos !== false){
55
+            $contextPath = substr($host, $hostSlashPos);
56
+            $host = substr($host, 0, $hostSlashPos);
57
+        }
58 58
 
59
-		if (substr($contextPath, -1) !== '/'){
60
-			$contextPath .= '/';
61
-		}
59
+        if (substr($contextPath, -1) !== '/'){
60
+            $contextPath .= '/';
61
+        }
62 62
 
63
-		if (isset($params['root'])){
64
-			$root = $params['root'];
65
-			if ($root[0] !== '/'){
66
-				$root = '/' . $root;
67
-			}
68
-		}
69
-		else{
70
-			$root = '/';
71
-		}
63
+        if (isset($params['root'])){
64
+            $root = $params['root'];
65
+            if ($root[0] !== '/'){
66
+                $root = '/' . $root;
67
+            }
68
+        }
69
+        else{
70
+            $root = '/';
71
+        }
72 72
 
73
-		$params['host'] = $host;
74
-		$params['root'] = $contextPath . self::OC_URL_SUFFIX . $root;
75
-		$params['authType'] = Client::AUTH_BASIC;
73
+        $params['host'] = $host;
74
+        $params['root'] = $contextPath . self::OC_URL_SUFFIX . $root;
75
+        $params['authType'] = Client::AUTH_BASIC;
76 76
 
77
-		parent::__construct($params);
78
-	}
77
+        parent::__construct($params);
78
+    }
79 79
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -34,7 +34,7 @@  discard block
 block discarded – undo
34 34
  * http://%host/%context/remote.php/webdav/%root
35 35
  *
36 36
  */
37
-class OwnCloud extends \OC\Files\Storage\DAV{
37
+class OwnCloud extends \OC\Files\Storage\DAV {
38 38
 	const OC_URL_SUFFIX = 'remote.php/webdav';
39 39
 
40 40
 	public function __construct($params) {
@@ -51,27 +51,27 @@  discard block
 block discarded – undo
51 51
 		}
52 52
 		$contextPath = '';
53 53
 		$hostSlashPos = strpos($host, '/');
54
-		if ($hostSlashPos !== false){
54
+		if ($hostSlashPos !== false) {
55 55
 			$contextPath = substr($host, $hostSlashPos);
56 56
 			$host = substr($host, 0, $hostSlashPos);
57 57
 		}
58 58
 
59
-		if (substr($contextPath, -1) !== '/'){
59
+		if (substr($contextPath, -1) !== '/') {
60 60
 			$contextPath .= '/';
61 61
 		}
62 62
 
63
-		if (isset($params['root'])){
63
+		if (isset($params['root'])) {
64 64
 			$root = $params['root'];
65
-			if ($root[0] !== '/'){
66
-				$root = '/' . $root;
65
+			if ($root[0] !== '/') {
66
+				$root = '/'.$root;
67 67
 			}
68 68
 		}
69
-		else{
69
+		else {
70 70
 			$root = '/';
71 71
 		}
72 72
 
73 73
 		$params['host'] = $host;
74
-		$params['root'] = $contextPath . self::OC_URL_SUFFIX . $root;
74
+		$params['root'] = $contextPath.self::OC_URL_SUFFIX.$root;
75 75
 		$params['authType'] = Client::AUTH_BASIC;
76 76
 
77 77
 		parent::__construct($params);
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -65,8 +65,7 @@
 block discarded – undo
65 65
 			if ($root[0] !== '/'){
66 66
 				$root = '/' . $root;
67 67
 			}
68
-		}
69
-		else{
68
+		} else{
70 69
 			$root = '/';
71 70
 		}
72 71
 
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Storage/SFTP.php 2 patches
Indentation   +424 added lines, -424 removed lines patch added patch discarded remove patch
@@ -42,428 +42,428 @@
 block discarded – undo
42 42
 * provide access to SFTP servers.
43 43
 */
44 44
 class SFTP extends \OC\Files\Storage\Common {
45
-	private $host;
46
-	private $user;
47
-	private $root;
48
-	private $port = 22;
49
-
50
-	private $auth;
51
-
52
-	/**
53
-	 * @var \phpseclib\Net\SFTP
54
-	 */
55
-	protected $client;
56
-
57
-	/**
58
-	 * @param string $host protocol://server:port
59
-	 * @return array [$server, $port]
60
-	 */
61
-	private function splitHost($host) {
62
-		$input = $host;
63
-		if (strpos($host, '://') === false) {
64
-			// add a protocol to fix parse_url behavior with ipv6
65
-			$host = 'http://' . $host;
66
-		}
67
-
68
-		$parsed = parse_url($host);
69
-		if(is_array($parsed) && isset($parsed['port'])) {
70
-			return [$parsed['host'], $parsed['port']];
71
-		} else if (is_array($parsed)) {
72
-			return [$parsed['host'], 22];
73
-		} else {
74
-			return [$input, 22];
75
-		}
76
-	}
77
-
78
-	/**
79
-	 * {@inheritdoc}
80
-	 */
81
-	public function __construct($params) {
82
-		// Register sftp://
83
-		Stream::register();
84
-
85
-		$parsedHost =  $this->splitHost($params['host']);
86
-
87
-		$this->host = $parsedHost[0];
88
-		$this->port = $parsedHost[1];
89
-
90
-		if (!isset($params['user'])) {
91
-			throw new \UnexpectedValueException('no authentication parameters specified');
92
-		}
93
-		$this->user = $params['user'];
94
-
95
-		if (isset($params['public_key_auth'])) {
96
-			$this->auth = $params['public_key_auth'];
97
-		} elseif (isset($params['password'])) {
98
-			$this->auth = $params['password'];
99
-		} else {
100
-			throw new \UnexpectedValueException('no authentication parameters specified');
101
-		}
102
-
103
-		$this->root
104
-			= isset($params['root']) ? $this->cleanPath($params['root']) : '/';
105
-
106
-		if ($this->root[0] !== '/') {
107
-			 $this->root = '/' . $this->root;
108
-		}
109
-
110
-		if ($this->root[strlen($this->root) - 1] !== '/') {
111
-			$this->root .= '/';
112
-		}
113
-	}
114
-
115
-	/**
116
-	 * Returns the connection.
117
-	 *
118
-	 * @return \phpseclib\Net\SFTP connected client instance
119
-	 * @throws \Exception when the connection failed
120
-	 */
121
-	public function getConnection() {
122
-		if (!is_null($this->client)) {
123
-			return $this->client;
124
-		}
125
-
126
-		$hostKeys = $this->readHostKeys();
127
-		$this->client = new \phpseclib\Net\SFTP($this->host, $this->port);
128
-
129
-		// The SSH Host Key MUST be verified before login().
130
-		$currentHostKey = $this->client->getServerPublicHostKey();
131
-		if (array_key_exists($this->host, $hostKeys)) {
132
-			if ($hostKeys[$this->host] !== $currentHostKey) {
133
-				throw new \Exception('Host public key does not match known key');
134
-			}
135
-		} else {
136
-			$hostKeys[$this->host] = $currentHostKey;
137
-			$this->writeHostKeys($hostKeys);
138
-		}
139
-
140
-		if (!$this->client->login($this->user, $this->auth)) {
141
-			throw new \Exception('Login failed');
142
-		}
143
-		return $this->client;
144
-	}
145
-
146
-	/**
147
-	 * {@inheritdoc}
148
-	 */
149
-	public function test() {
150
-		if (
151
-			!isset($this->host)
152
-			|| !isset($this->user)
153
-		) {
154
-			return false;
155
-		}
156
-		return $this->getConnection()->nlist() !== false;
157
-	}
158
-
159
-	/**
160
-	 * {@inheritdoc}
161
-	 */
162
-	public function getId(){
163
-		$id = 'sftp::' . $this->user . '@' . $this->host;
164
-		if ($this->port !== 22) {
165
-			$id .= ':' . $this->port;
166
-		}
167
-		// note: this will double the root slash,
168
-		// we should not change it to keep compatible with
169
-		// old storage ids
170
-		$id .= '/' . $this->root;
171
-		return $id;
172
-	}
173
-
174
-	/**
175
-	 * @return string
176
-	 */
177
-	public function getHost() {
178
-		return $this->host;
179
-	}
180
-
181
-	/**
182
-	 * @return string
183
-	 */
184
-	public function getRoot() {
185
-		return $this->root;
186
-	}
187
-
188
-	/**
189
-	 * @return mixed
190
-	 */
191
-	public function getUser() {
192
-		return $this->user;
193
-	}
194
-
195
-	/**
196
-	 * @param string $path
197
-	 * @return string
198
-	 */
199
-	private function absPath($path) {
200
-		return $this->root . $this->cleanPath($path);
201
-	}
202
-
203
-	/**
204
-	 * @return string|false
205
-	 */
206
-	private function hostKeysPath() {
207
-		try {
208
-			$storage_view = \OCP\Files::getStorage('files_external');
209
-			if ($storage_view) {
210
-				return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') .
211
-					$storage_view->getAbsolutePath('') .
212
-					'ssh_hostKeys';
213
-			}
214
-		} catch (\Exception $e) {
215
-		}
216
-		return false;
217
-	}
218
-
219
-	/**
220
-	 * @param $keys
221
-	 * @return bool
222
-	 */
223
-	protected function writeHostKeys($keys) {
224
-		try {
225
-			$keyPath = $this->hostKeysPath();
226
-			if ($keyPath && file_exists($keyPath)) {
227
-				$fp = fopen($keyPath, 'w');
228
-				foreach ($keys as $host => $key) {
229
-					fwrite($fp, $host . '::' . $key . "\n");
230
-				}
231
-				fclose($fp);
232
-				return true;
233
-			}
234
-		} catch (\Exception $e) {
235
-		}
236
-		return false;
237
-	}
238
-
239
-	/**
240
-	 * @return array
241
-	 */
242
-	protected function readHostKeys() {
243
-		try {
244
-			$keyPath = $this->hostKeysPath();
245
-			if (file_exists($keyPath)) {
246
-				$hosts = array();
247
-				$keys = array();
248
-				$lines = file($keyPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
249
-				if ($lines) {
250
-					foreach ($lines as $line) {
251
-						$hostKeyArray = explode("::", $line, 2);
252
-						if (count($hostKeyArray) === 2) {
253
-							$hosts[] = $hostKeyArray[0];
254
-							$keys[] = $hostKeyArray[1];
255
-						}
256
-					}
257
-					return array_combine($hosts, $keys);
258
-				}
259
-			}
260
-		} catch (\Exception $e) {
261
-		}
262
-		return array();
263
-	}
264
-
265
-	/**
266
-	 * {@inheritdoc}
267
-	 */
268
-	public function mkdir($path) {
269
-		try {
270
-			return $this->getConnection()->mkdir($this->absPath($path));
271
-		} catch (\Exception $e) {
272
-			return false;
273
-		}
274
-	}
275
-
276
-	/**
277
-	 * {@inheritdoc}
278
-	 */
279
-	public function rmdir($path) {
280
-		try {
281
-			$result = $this->getConnection()->delete($this->absPath($path), true);
282
-			// workaround: stray stat cache entry when deleting empty folders
283
-			// see https://github.com/phpseclib/phpseclib/issues/706
284
-			$this->getConnection()->clearStatCache();
285
-			return $result;
286
-		} catch (\Exception $e) {
287
-			return false;
288
-		}
289
-	}
290
-
291
-	/**
292
-	 * {@inheritdoc}
293
-	 */
294
-	public function opendir($path) {
295
-		try {
296
-			$list = $this->getConnection()->nlist($this->absPath($path));
297
-			if ($list === false) {
298
-				return false;
299
-			}
300
-
301
-			$id = md5('sftp:' . $path);
302
-			$dirStream = array();
303
-			foreach($list as $file) {
304
-				if ($file !== '.' && $file !== '..') {
305
-					$dirStream[] = $file;
306
-				}
307
-			}
308
-			return IteratorDirectory::wrap($dirStream);
309
-		} catch(\Exception $e) {
310
-			return false;
311
-		}
312
-	}
313
-
314
-	/**
315
-	 * {@inheritdoc}
316
-	 */
317
-	public function filetype($path) {
318
-		try {
319
-			$stat = $this->getConnection()->stat($this->absPath($path));
320
-			if ((int) $stat['type'] === NET_SFTP_TYPE_REGULAR) {
321
-				return 'file';
322
-			}
323
-
324
-			if ((int) $stat['type'] === NET_SFTP_TYPE_DIRECTORY) {
325
-				return 'dir';
326
-			}
327
-		} catch (\Exception $e) {
328
-
329
-		}
330
-		return false;
331
-	}
332
-
333
-	/**
334
-	 * {@inheritdoc}
335
-	 */
336
-	public function file_exists($path) {
337
-		try {
338
-			return $this->getConnection()->stat($this->absPath($path)) !== false;
339
-		} catch (\Exception $e) {
340
-			return false;
341
-		}
342
-	}
343
-
344
-	/**
345
-	 * {@inheritdoc}
346
-	 */
347
-	public function unlink($path) {
348
-		try {
349
-			return $this->getConnection()->delete($this->absPath($path), true);
350
-		} catch (\Exception $e) {
351
-			return false;
352
-		}
353
-	}
354
-
355
-	/**
356
-	 * {@inheritdoc}
357
-	 */
358
-	public function fopen($path, $mode) {
359
-		try {
360
-			$absPath = $this->absPath($path);
361
-			switch($mode) {
362
-				case 'r':
363
-				case 'rb':
364
-					if ( !$this->file_exists($path)) {
365
-						return false;
366
-					}
367
-				case 'w':
368
-				case 'wb':
369
-				case 'a':
370
-				case 'ab':
371
-				case 'r+':
372
-				case 'w+':
373
-				case 'wb+':
374
-				case 'a+':
375
-				case 'x':
376
-				case 'x+':
377
-				case 'c':
378
-				case 'c+':
379
-					$context = stream_context_create(array('sftp' => array('session' => $this->getConnection())));
380
-					$handle = fopen($this->constructUrl($path), $mode, false, $context);
381
-					return RetryWrapper::wrap($handle);
382
-			}
383
-		} catch (\Exception $e) {
384
-		}
385
-		return false;
386
-	}
387
-
388
-	/**
389
-	 * {@inheritdoc}
390
-	 */
391
-	public function touch($path, $mtime=null) {
392
-		try {
393
-			if (!is_null($mtime)) {
394
-				return false;
395
-			}
396
-			if (!$this->file_exists($path)) {
397
-				$this->getConnection()->put($this->absPath($path), '');
398
-			} else {
399
-				return false;
400
-			}
401
-		} catch (\Exception $e) {
402
-			return false;
403
-		}
404
-		return true;
405
-	}
406
-
407
-	/**
408
-	 * @param string $path
409
-	 * @param string $target
410
-	 * @throws \Exception
411
-	 */
412
-	public function getFile($path, $target) {
413
-		$this->getConnection()->get($path, $target);
414
-	}
415
-
416
-	/**
417
-	 * @param string $path
418
-	 * @param string $target
419
-	 * @throws \Exception
420
-	 */
421
-	public function uploadFile($path, $target) {
422
-		$this->getConnection()->put($target, $path, NET_SFTP_LOCAL_FILE);
423
-	}
424
-
425
-	/**
426
-	 * {@inheritdoc}
427
-	 */
428
-	public function rename($source, $target) {
429
-		try {
430
-			if ($this->file_exists($target)) {
431
-				$this->unlink($target);
432
-			}
433
-			return $this->getConnection()->rename(
434
-				$this->absPath($source),
435
-				$this->absPath($target)
436
-			);
437
-		} catch (\Exception $e) {
438
-			return false;
439
-		}
440
-	}
441
-
442
-	/**
443
-	 * {@inheritdoc}
444
-	 */
445
-	public function stat($path) {
446
-		try {
447
-			$stat = $this->getConnection()->stat($this->absPath($path));
448
-
449
-			$mtime = $stat ? $stat['mtime'] : -1;
450
-			$size = $stat ? $stat['size'] : 0;
451
-
452
-			return array('mtime' => $mtime, 'size' => $size, 'ctime' => -1);
453
-		} catch (\Exception $e) {
454
-			return false;
455
-		}
456
-	}
457
-
458
-	/**
459
-	 * @param string $path
460
-	 * @return string
461
-	 */
462
-	public function constructUrl($path) {
463
-		// Do not pass the password here. We want to use the Net_SFTP object
464
-		// supplied via stream context or fail. We only supply username and
465
-		// hostname because this might show up in logs (they are not used).
466
-		$url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
467
-		return $url;
468
-	}
45
+    private $host;
46
+    private $user;
47
+    private $root;
48
+    private $port = 22;
49
+
50
+    private $auth;
51
+
52
+    /**
53
+     * @var \phpseclib\Net\SFTP
54
+     */
55
+    protected $client;
56
+
57
+    /**
58
+     * @param string $host protocol://server:port
59
+     * @return array [$server, $port]
60
+     */
61
+    private function splitHost($host) {
62
+        $input = $host;
63
+        if (strpos($host, '://') === false) {
64
+            // add a protocol to fix parse_url behavior with ipv6
65
+            $host = 'http://' . $host;
66
+        }
67
+
68
+        $parsed = parse_url($host);
69
+        if(is_array($parsed) && isset($parsed['port'])) {
70
+            return [$parsed['host'], $parsed['port']];
71
+        } else if (is_array($parsed)) {
72
+            return [$parsed['host'], 22];
73
+        } else {
74
+            return [$input, 22];
75
+        }
76
+    }
77
+
78
+    /**
79
+     * {@inheritdoc}
80
+     */
81
+    public function __construct($params) {
82
+        // Register sftp://
83
+        Stream::register();
84
+
85
+        $parsedHost =  $this->splitHost($params['host']);
86
+
87
+        $this->host = $parsedHost[0];
88
+        $this->port = $parsedHost[1];
89
+
90
+        if (!isset($params['user'])) {
91
+            throw new \UnexpectedValueException('no authentication parameters specified');
92
+        }
93
+        $this->user = $params['user'];
94
+
95
+        if (isset($params['public_key_auth'])) {
96
+            $this->auth = $params['public_key_auth'];
97
+        } elseif (isset($params['password'])) {
98
+            $this->auth = $params['password'];
99
+        } else {
100
+            throw new \UnexpectedValueException('no authentication parameters specified');
101
+        }
102
+
103
+        $this->root
104
+            = isset($params['root']) ? $this->cleanPath($params['root']) : '/';
105
+
106
+        if ($this->root[0] !== '/') {
107
+                $this->root = '/' . $this->root;
108
+        }
109
+
110
+        if ($this->root[strlen($this->root) - 1] !== '/') {
111
+            $this->root .= '/';
112
+        }
113
+    }
114
+
115
+    /**
116
+     * Returns the connection.
117
+     *
118
+     * @return \phpseclib\Net\SFTP connected client instance
119
+     * @throws \Exception when the connection failed
120
+     */
121
+    public function getConnection() {
122
+        if (!is_null($this->client)) {
123
+            return $this->client;
124
+        }
125
+
126
+        $hostKeys = $this->readHostKeys();
127
+        $this->client = new \phpseclib\Net\SFTP($this->host, $this->port);
128
+
129
+        // The SSH Host Key MUST be verified before login().
130
+        $currentHostKey = $this->client->getServerPublicHostKey();
131
+        if (array_key_exists($this->host, $hostKeys)) {
132
+            if ($hostKeys[$this->host] !== $currentHostKey) {
133
+                throw new \Exception('Host public key does not match known key');
134
+            }
135
+        } else {
136
+            $hostKeys[$this->host] = $currentHostKey;
137
+            $this->writeHostKeys($hostKeys);
138
+        }
139
+
140
+        if (!$this->client->login($this->user, $this->auth)) {
141
+            throw new \Exception('Login failed');
142
+        }
143
+        return $this->client;
144
+    }
145
+
146
+    /**
147
+     * {@inheritdoc}
148
+     */
149
+    public function test() {
150
+        if (
151
+            !isset($this->host)
152
+            || !isset($this->user)
153
+        ) {
154
+            return false;
155
+        }
156
+        return $this->getConnection()->nlist() !== false;
157
+    }
158
+
159
+    /**
160
+     * {@inheritdoc}
161
+     */
162
+    public function getId(){
163
+        $id = 'sftp::' . $this->user . '@' . $this->host;
164
+        if ($this->port !== 22) {
165
+            $id .= ':' . $this->port;
166
+        }
167
+        // note: this will double the root slash,
168
+        // we should not change it to keep compatible with
169
+        // old storage ids
170
+        $id .= '/' . $this->root;
171
+        return $id;
172
+    }
173
+
174
+    /**
175
+     * @return string
176
+     */
177
+    public function getHost() {
178
+        return $this->host;
179
+    }
180
+
181
+    /**
182
+     * @return string
183
+     */
184
+    public function getRoot() {
185
+        return $this->root;
186
+    }
187
+
188
+    /**
189
+     * @return mixed
190
+     */
191
+    public function getUser() {
192
+        return $this->user;
193
+    }
194
+
195
+    /**
196
+     * @param string $path
197
+     * @return string
198
+     */
199
+    private function absPath($path) {
200
+        return $this->root . $this->cleanPath($path);
201
+    }
202
+
203
+    /**
204
+     * @return string|false
205
+     */
206
+    private function hostKeysPath() {
207
+        try {
208
+            $storage_view = \OCP\Files::getStorage('files_external');
209
+            if ($storage_view) {
210
+                return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') .
211
+                    $storage_view->getAbsolutePath('') .
212
+                    'ssh_hostKeys';
213
+            }
214
+        } catch (\Exception $e) {
215
+        }
216
+        return false;
217
+    }
218
+
219
+    /**
220
+     * @param $keys
221
+     * @return bool
222
+     */
223
+    protected function writeHostKeys($keys) {
224
+        try {
225
+            $keyPath = $this->hostKeysPath();
226
+            if ($keyPath && file_exists($keyPath)) {
227
+                $fp = fopen($keyPath, 'w');
228
+                foreach ($keys as $host => $key) {
229
+                    fwrite($fp, $host . '::' . $key . "\n");
230
+                }
231
+                fclose($fp);
232
+                return true;
233
+            }
234
+        } catch (\Exception $e) {
235
+        }
236
+        return false;
237
+    }
238
+
239
+    /**
240
+     * @return array
241
+     */
242
+    protected function readHostKeys() {
243
+        try {
244
+            $keyPath = $this->hostKeysPath();
245
+            if (file_exists($keyPath)) {
246
+                $hosts = array();
247
+                $keys = array();
248
+                $lines = file($keyPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
249
+                if ($lines) {
250
+                    foreach ($lines as $line) {
251
+                        $hostKeyArray = explode("::", $line, 2);
252
+                        if (count($hostKeyArray) === 2) {
253
+                            $hosts[] = $hostKeyArray[0];
254
+                            $keys[] = $hostKeyArray[1];
255
+                        }
256
+                    }
257
+                    return array_combine($hosts, $keys);
258
+                }
259
+            }
260
+        } catch (\Exception $e) {
261
+        }
262
+        return array();
263
+    }
264
+
265
+    /**
266
+     * {@inheritdoc}
267
+     */
268
+    public function mkdir($path) {
269
+        try {
270
+            return $this->getConnection()->mkdir($this->absPath($path));
271
+        } catch (\Exception $e) {
272
+            return false;
273
+        }
274
+    }
275
+
276
+    /**
277
+     * {@inheritdoc}
278
+     */
279
+    public function rmdir($path) {
280
+        try {
281
+            $result = $this->getConnection()->delete($this->absPath($path), true);
282
+            // workaround: stray stat cache entry when deleting empty folders
283
+            // see https://github.com/phpseclib/phpseclib/issues/706
284
+            $this->getConnection()->clearStatCache();
285
+            return $result;
286
+        } catch (\Exception $e) {
287
+            return false;
288
+        }
289
+    }
290
+
291
+    /**
292
+     * {@inheritdoc}
293
+     */
294
+    public function opendir($path) {
295
+        try {
296
+            $list = $this->getConnection()->nlist($this->absPath($path));
297
+            if ($list === false) {
298
+                return false;
299
+            }
300
+
301
+            $id = md5('sftp:' . $path);
302
+            $dirStream = array();
303
+            foreach($list as $file) {
304
+                if ($file !== '.' && $file !== '..') {
305
+                    $dirStream[] = $file;
306
+                }
307
+            }
308
+            return IteratorDirectory::wrap($dirStream);
309
+        } catch(\Exception $e) {
310
+            return false;
311
+        }
312
+    }
313
+
314
+    /**
315
+     * {@inheritdoc}
316
+     */
317
+    public function filetype($path) {
318
+        try {
319
+            $stat = $this->getConnection()->stat($this->absPath($path));
320
+            if ((int) $stat['type'] === NET_SFTP_TYPE_REGULAR) {
321
+                return 'file';
322
+            }
323
+
324
+            if ((int) $stat['type'] === NET_SFTP_TYPE_DIRECTORY) {
325
+                return 'dir';
326
+            }
327
+        } catch (\Exception $e) {
328
+
329
+        }
330
+        return false;
331
+    }
332
+
333
+    /**
334
+     * {@inheritdoc}
335
+     */
336
+    public function file_exists($path) {
337
+        try {
338
+            return $this->getConnection()->stat($this->absPath($path)) !== false;
339
+        } catch (\Exception $e) {
340
+            return false;
341
+        }
342
+    }
343
+
344
+    /**
345
+     * {@inheritdoc}
346
+     */
347
+    public function unlink($path) {
348
+        try {
349
+            return $this->getConnection()->delete($this->absPath($path), true);
350
+        } catch (\Exception $e) {
351
+            return false;
352
+        }
353
+    }
354
+
355
+    /**
356
+     * {@inheritdoc}
357
+     */
358
+    public function fopen($path, $mode) {
359
+        try {
360
+            $absPath = $this->absPath($path);
361
+            switch($mode) {
362
+                case 'r':
363
+                case 'rb':
364
+                    if ( !$this->file_exists($path)) {
365
+                        return false;
366
+                    }
367
+                case 'w':
368
+                case 'wb':
369
+                case 'a':
370
+                case 'ab':
371
+                case 'r+':
372
+                case 'w+':
373
+                case 'wb+':
374
+                case 'a+':
375
+                case 'x':
376
+                case 'x+':
377
+                case 'c':
378
+                case 'c+':
379
+                    $context = stream_context_create(array('sftp' => array('session' => $this->getConnection())));
380
+                    $handle = fopen($this->constructUrl($path), $mode, false, $context);
381
+                    return RetryWrapper::wrap($handle);
382
+            }
383
+        } catch (\Exception $e) {
384
+        }
385
+        return false;
386
+    }
387
+
388
+    /**
389
+     * {@inheritdoc}
390
+     */
391
+    public function touch($path, $mtime=null) {
392
+        try {
393
+            if (!is_null($mtime)) {
394
+                return false;
395
+            }
396
+            if (!$this->file_exists($path)) {
397
+                $this->getConnection()->put($this->absPath($path), '');
398
+            } else {
399
+                return false;
400
+            }
401
+        } catch (\Exception $e) {
402
+            return false;
403
+        }
404
+        return true;
405
+    }
406
+
407
+    /**
408
+     * @param string $path
409
+     * @param string $target
410
+     * @throws \Exception
411
+     */
412
+    public function getFile($path, $target) {
413
+        $this->getConnection()->get($path, $target);
414
+    }
415
+
416
+    /**
417
+     * @param string $path
418
+     * @param string $target
419
+     * @throws \Exception
420
+     */
421
+    public function uploadFile($path, $target) {
422
+        $this->getConnection()->put($target, $path, NET_SFTP_LOCAL_FILE);
423
+    }
424
+
425
+    /**
426
+     * {@inheritdoc}
427
+     */
428
+    public function rename($source, $target) {
429
+        try {
430
+            if ($this->file_exists($target)) {
431
+                $this->unlink($target);
432
+            }
433
+            return $this->getConnection()->rename(
434
+                $this->absPath($source),
435
+                $this->absPath($target)
436
+            );
437
+        } catch (\Exception $e) {
438
+            return false;
439
+        }
440
+    }
441
+
442
+    /**
443
+     * {@inheritdoc}
444
+     */
445
+    public function stat($path) {
446
+        try {
447
+            $stat = $this->getConnection()->stat($this->absPath($path));
448
+
449
+            $mtime = $stat ? $stat['mtime'] : -1;
450
+            $size = $stat ? $stat['size'] : 0;
451
+
452
+            return array('mtime' => $mtime, 'size' => $size, 'ctime' => -1);
453
+        } catch (\Exception $e) {
454
+            return false;
455
+        }
456
+    }
457
+
458
+    /**
459
+     * @param string $path
460
+     * @return string
461
+     */
462
+    public function constructUrl($path) {
463
+        // Do not pass the password here. We want to use the Net_SFTP object
464
+        // supplied via stream context or fail. We only supply username and
465
+        // hostname because this might show up in logs (they are not used).
466
+        $url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
467
+        return $url;
468
+    }
469 469
 }
Please login to merge, or discard this patch.
Spacing   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -62,11 +62,11 @@  discard block
 block discarded – undo
62 62
 		$input = $host;
63 63
 		if (strpos($host, '://') === false) {
64 64
 			// add a protocol to fix parse_url behavior with ipv6
65
-			$host = 'http://' . $host;
65
+			$host = 'http://'.$host;
66 66
 		}
67 67
 
68 68
 		$parsed = parse_url($host);
69
-		if(is_array($parsed) && isset($parsed['port'])) {
69
+		if (is_array($parsed) && isset($parsed['port'])) {
70 70
 			return [$parsed['host'], $parsed['port']];
71 71
 		} else if (is_array($parsed)) {
72 72
 			return [$parsed['host'], 22];
@@ -82,7 +82,7 @@  discard block
 block discarded – undo
82 82
 		// Register sftp://
83 83
 		Stream::register();
84 84
 
85
-		$parsedHost =  $this->splitHost($params['host']);
85
+		$parsedHost = $this->splitHost($params['host']);
86 86
 
87 87
 		$this->host = $parsedHost[0];
88 88
 		$this->port = $parsedHost[1];
@@ -104,7 +104,7 @@  discard block
 block discarded – undo
104 104
 			= isset($params['root']) ? $this->cleanPath($params['root']) : '/';
105 105
 
106 106
 		if ($this->root[0] !== '/') {
107
-			 $this->root = '/' . $this->root;
107
+			 $this->root = '/'.$this->root;
108 108
 		}
109 109
 
110 110
 		if ($this->root[strlen($this->root) - 1] !== '/') {
@@ -159,15 +159,15 @@  discard block
 block discarded – undo
159 159
 	/**
160 160
 	 * {@inheritdoc}
161 161
 	 */
162
-	public function getId(){
163
-		$id = 'sftp::' . $this->user . '@' . $this->host;
162
+	public function getId() {
163
+		$id = 'sftp::'.$this->user.'@'.$this->host;
164 164
 		if ($this->port !== 22) {
165
-			$id .= ':' . $this->port;
165
+			$id .= ':'.$this->port;
166 166
 		}
167 167
 		// note: this will double the root slash,
168 168
 		// we should not change it to keep compatible with
169 169
 		// old storage ids
170
-		$id .= '/' . $this->root;
170
+		$id .= '/'.$this->root;
171 171
 		return $id;
172 172
 	}
173 173
 
@@ -197,7 +197,7 @@  discard block
 block discarded – undo
197 197
 	 * @return string
198 198
 	 */
199 199
 	private function absPath($path) {
200
-		return $this->root . $this->cleanPath($path);
200
+		return $this->root.$this->cleanPath($path);
201 201
 	}
202 202
 
203 203
 	/**
@@ -207,8 +207,8 @@  discard block
 block discarded – undo
207 207
 		try {
208 208
 			$storage_view = \OCP\Files::getStorage('files_external');
209 209
 			if ($storage_view) {
210
-				return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') .
211
-					$storage_view->getAbsolutePath('') .
210
+				return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data').
211
+					$storage_view->getAbsolutePath('').
212 212
 					'ssh_hostKeys';
213 213
 			}
214 214
 		} catch (\Exception $e) {
@@ -226,7 +226,7 @@  discard block
 block discarded – undo
226 226
 			if ($keyPath && file_exists($keyPath)) {
227 227
 				$fp = fopen($keyPath, 'w');
228 228
 				foreach ($keys as $host => $key) {
229
-					fwrite($fp, $host . '::' . $key . "\n");
229
+					fwrite($fp, $host.'::'.$key."\n");
230 230
 				}
231 231
 				fclose($fp);
232 232
 				return true;
@@ -298,15 +298,15 @@  discard block
 block discarded – undo
298 298
 				return false;
299 299
 			}
300 300
 
301
-			$id = md5('sftp:' . $path);
301
+			$id = md5('sftp:'.$path);
302 302
 			$dirStream = array();
303
-			foreach($list as $file) {
303
+			foreach ($list as $file) {
304 304
 				if ($file !== '.' && $file !== '..') {
305 305
 					$dirStream[] = $file;
306 306
 				}
307 307
 			}
308 308
 			return IteratorDirectory::wrap($dirStream);
309
-		} catch(\Exception $e) {
309
+		} catch (\Exception $e) {
310 310
 			return false;
311 311
 		}
312 312
 	}
@@ -358,10 +358,10 @@  discard block
 block discarded – undo
358 358
 	public function fopen($path, $mode) {
359 359
 		try {
360 360
 			$absPath = $this->absPath($path);
361
-			switch($mode) {
361
+			switch ($mode) {
362 362
 				case 'r':
363 363
 				case 'rb':
364
-					if ( !$this->file_exists($path)) {
364
+					if (!$this->file_exists($path)) {
365 365
 						return false;
366 366
 					}
367 367
 				case 'w':
@@ -388,7 +388,7 @@  discard block
 block discarded – undo
388 388
 	/**
389 389
 	 * {@inheritdoc}
390 390
 	 */
391
-	public function touch($path, $mtime=null) {
391
+	public function touch($path, $mtime = null) {
392 392
 		try {
393 393
 			if (!is_null($mtime)) {
394 394
 				return false;
@@ -463,7 +463,7 @@  discard block
 block discarded – undo
463 463
 		// Do not pass the password here. We want to use the Net_SFTP object
464 464
 		// supplied via stream context or fail. We only supply username and
465 465
 		// hostname because this might show up in logs (they are not used).
466
-		$url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
466
+		$url = 'sftp://'.urlencode($this->user).'@'.$this->host.':'.$this->port.$this->root.$path;
467 467
 		return $url;
468 468
 	}
469 469
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Storage/SMB.php 2 patches
Indentation   +474 added lines, -474 removed lines patch added patch discarded remove patch
@@ -55,478 +55,478 @@
 block discarded – undo
55 55
 use OCP\Util;
56 56
 
57 57
 class SMB extends Common implements INotifyStorage {
58
-	/**
59
-	 * @var \Icewind\SMB\Server
60
-	 */
61
-	protected $server;
62
-
63
-	/**
64
-	 * @var \Icewind\SMB\Share
65
-	 */
66
-	protected $share;
67
-
68
-	/**
69
-	 * @var string
70
-	 */
71
-	protected $root;
72
-
73
-	/**
74
-	 * @var \Icewind\SMB\FileInfo[]
75
-	 */
76
-	protected $statCache;
77
-
78
-	public function __construct($params) {
79
-		if (isset($params['host']) && isset($params['user']) && isset($params['password']) && isset($params['share'])) {
80
-			if (Server::NativeAvailable()) {
81
-				$this->server = new NativeServer($params['host'], $params['user'], $params['password']);
82
-			} else {
83
-				$this->server = new Server($params['host'], $params['user'], $params['password']);
84
-			}
85
-			$this->share = $this->server->getShare(trim($params['share'], '/'));
86
-
87
-			$this->root = isset($params['root']) ? $params['root'] : '/';
88
-			if (!$this->root || $this->root[0] !== '/') {
89
-				$this->root = '/' . $this->root;
90
-			}
91
-			if ($this->root[strlen($this->root) - 1] !== '/') {
92
-				$this->root .= '/';
93
-			}
94
-		} else {
95
-			throw new \Exception('Invalid configuration');
96
-		}
97
-		$this->statCache = new CappedMemoryCache();
98
-		parent::__construct($params);
99
-	}
100
-
101
-	/**
102
-	 * @return string
103
-	 */
104
-	public function getId() {
105
-		// FIXME: double slash to keep compatible with the old storage ids,
106
-		// failure to do so will lead to creation of a new storage id and
107
-		// loss of shares from the storage
108
-		return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
109
-	}
110
-
111
-	/**
112
-	 * @param string $path
113
-	 * @return string
114
-	 */
115
-	protected function buildPath($path) {
116
-		return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
117
-	}
118
-
119
-	protected function relativePath($fullPath) {
120
-		if ($fullPath === $this->root) {
121
-			return '';
122
-		} else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
123
-			return substr($fullPath, strlen($this->root));
124
-		} else {
125
-			return null;
126
-		}
127
-	}
128
-
129
-	/**
130
-	 * @param string $path
131
-	 * @return \Icewind\SMB\IFileInfo
132
-	 * @throws StorageNotAvailableException
133
-	 */
134
-	protected function getFileInfo($path) {
135
-		try {
136
-			$path = $this->buildPath($path);
137
-			if (!isset($this->statCache[$path])) {
138
-				$this->statCache[$path] = $this->share->stat($path);
139
-			}
140
-			return $this->statCache[$path];
141
-		} catch (ConnectException $e) {
142
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
143
-		}
144
-	}
145
-
146
-	/**
147
-	 * @param string $path
148
-	 * @return \Icewind\SMB\IFileInfo[]
149
-	 * @throws StorageNotAvailableException
150
-	 */
151
-	protected function getFolderContents($path) {
152
-		try {
153
-			$path = $this->buildPath($path);
154
-			$files = $this->share->dir($path);
155
-			foreach ($files as $file) {
156
-				$this->statCache[$path . '/' . $file->getName()] = $file;
157
-			}
158
-			return array_filter($files, function (IFileInfo $file) {
159
-				return !$file->isHidden();
160
-			});
161
-		} catch (ConnectException $e) {
162
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
163
-		}
164
-	}
165
-
166
-	/**
167
-	 * @param \Icewind\SMB\IFileInfo $info
168
-	 * @return array
169
-	 */
170
-	protected function formatInfo($info) {
171
-		$result = [
172
-			'size' => $info->getSize(),
173
-			'mtime' => $info->getMTime(),
174
-		];
175
-		if ($info->isDirectory()) {
176
-			$result['type'] = 'dir';
177
-		} else {
178
-			$result['type'] = 'file';
179
-		}
180
-		return $result;
181
-	}
182
-
183
-	/**
184
-	 * Rename the files. If the source or the target is the root, the rename won't happen.
185
-	 *
186
-	 * @param string $source the old name of the path
187
-	 * @param string $target the new name of the path
188
-	 * @return bool true if the rename is successful, false otherwise
189
-	 */
190
-	public function rename($source, $target) {
191
-		if ($this->isRootDir($source) || $this->isRootDir($target)) {
192
-			return false;
193
-		}
194
-
195
-		$absoluteSource = $this->buildPath($source);
196
-		$absoluteTarget = $this->buildPath($target);
197
-		try {
198
-			$result = $this->share->rename($absoluteSource, $absoluteTarget);
199
-		} catch (AlreadyExistsException $e) {
200
-			$this->remove($target);
201
-			$result = $this->share->rename($absoluteSource, $absoluteTarget);
202
-		} catch (\Exception $e) {
203
-			\OC::$server->getLogger()->logException($e, ['level' => Util::WARN]);
204
-			return false;
205
-		}
206
-		unset($this->statCache[$absoluteSource], $this->statCache[$absoluteTarget]);
207
-		return $result;
208
-	}
209
-
210
-	public function stat($path) {
211
-		try {
212
-			$result = $this->formatInfo($this->getFileInfo($path));
213
-		} catch (ForbiddenException $e) {
214
-			return false;
215
-		} catch (NotFoundException $e) {
216
-			return false;
217
-		}
218
-		if ($this->remoteIsShare() && $this->isRootDir($path)) {
219
-			$result['mtime'] = $this->shareMTime();
220
-		}
221
-		return $result;
222
-	}
223
-
224
-	/**
225
-	 * get the best guess for the modification time of the share
226
-	 *
227
-	 * @return int
228
-	 */
229
-	private function shareMTime() {
230
-		$highestMTime = 0;
231
-		$files = $this->share->dir($this->root);
232
-		foreach ($files as $fileInfo) {
233
-			if ($fileInfo->getMTime() > $highestMTime) {
234
-				$highestMTime = $fileInfo->getMTime();
235
-			}
236
-		}
237
-		return $highestMTime;
238
-	}
239
-
240
-	/**
241
-	 * Check if the path is our root dir (not the smb one)
242
-	 *
243
-	 * @param string $path the path
244
-	 * @return bool
245
-	 */
246
-	private function isRootDir($path) {
247
-		return $path === '' || $path === '/' || $path === '.';
248
-	}
249
-
250
-	/**
251
-	 * Check if our root points to a smb share
252
-	 *
253
-	 * @return bool true if our root points to a share false otherwise
254
-	 */
255
-	private function remoteIsShare() {
256
-		return $this->share->getName() && (!$this->root || $this->root === '/');
257
-	}
258
-
259
-	/**
260
-	 * @param string $path
261
-	 * @return bool
262
-	 */
263
-	public function unlink($path) {
264
-		if ($this->isRootDir($path)) {
265
-			return false;
266
-		}
267
-
268
-		try {
269
-			if ($this->is_dir($path)) {
270
-				return $this->rmdir($path);
271
-			} else {
272
-				$path = $this->buildPath($path);
273
-				unset($this->statCache[$path]);
274
-				$this->share->del($path);
275
-				return true;
276
-			}
277
-		} catch (NotFoundException $e) {
278
-			return false;
279
-		} catch (ForbiddenException $e) {
280
-			return false;
281
-		} catch (ConnectException $e) {
282
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
283
-		}
284
-	}
285
-
286
-	/**
287
-	 * check if a file or folder has been updated since $time
288
-	 *
289
-	 * @param string $path
290
-	 * @param int $time
291
-	 * @return bool
292
-	 */
293
-	public function hasUpdated($path, $time) {
294
-		if (!$path and $this->root === '/') {
295
-			// mtime doesn't work for shares, but giving the nature of the backend,
296
-			// doing a full update is still just fast enough
297
-			return true;
298
-		} else {
299
-			$actualTime = $this->filemtime($path);
300
-			return $actualTime > $time;
301
-		}
302
-	}
303
-
304
-	/**
305
-	 * @param string $path
306
-	 * @param string $mode
307
-	 * @return resource|false
308
-	 */
309
-	public function fopen($path, $mode) {
310
-		$fullPath = $this->buildPath($path);
311
-		try {
312
-			switch ($mode) {
313
-				case 'r':
314
-				case 'rb':
315
-					if (!$this->file_exists($path)) {
316
-						return false;
317
-					}
318
-					return $this->share->read($fullPath);
319
-				case 'w':
320
-				case 'wb':
321
-					$source = $this->share->write($fullPath);
322
-					return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
323
-						unset($this->statCache[$fullPath]);
324
-					});
325
-				case 'a':
326
-				case 'ab':
327
-				case 'r+':
328
-				case 'w+':
329
-				case 'wb+':
330
-				case 'a+':
331
-				case 'x':
332
-				case 'x+':
333
-				case 'c':
334
-				case 'c+':
335
-					//emulate these
336
-					if (strrpos($path, '.') !== false) {
337
-						$ext = substr($path, strrpos($path, '.'));
338
-					} else {
339
-						$ext = '';
340
-					}
341
-					if ($this->file_exists($path)) {
342
-						if (!$this->isUpdatable($path)) {
343
-							return false;
344
-						}
345
-						$tmpFile = $this->getCachedFile($path);
346
-					} else {
347
-						if (!$this->isCreatable(dirname($path))) {
348
-							return false;
349
-						}
350
-						$tmpFile = \OCP\Files::tmpFile($ext);
351
-					}
352
-					$source = fopen($tmpFile, $mode);
353
-					$share = $this->share;
354
-					return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
355
-						unset($this->statCache[$fullPath]);
356
-						$share->put($tmpFile, $fullPath);
357
-						unlink($tmpFile);
358
-					});
359
-			}
360
-			return false;
361
-		} catch (NotFoundException $e) {
362
-			return false;
363
-		} catch (ForbiddenException $e) {
364
-			return false;
365
-		} catch (ConnectException $e) {
366
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
367
-		}
368
-	}
369
-
370
-	public function rmdir($path) {
371
-		if ($this->isRootDir($path)) {
372
-			return false;
373
-		}
374
-
375
-		try {
376
-			$this->statCache = array();
377
-			$content = $this->share->dir($this->buildPath($path));
378
-			foreach ($content as $file) {
379
-				if ($file->isDirectory()) {
380
-					$this->rmdir($path . '/' . $file->getName());
381
-				} else {
382
-					$this->share->del($file->getPath());
383
-				}
384
-			}
385
-			$this->share->rmdir($this->buildPath($path));
386
-			return true;
387
-		} catch (NotFoundException $e) {
388
-			return false;
389
-		} catch (ForbiddenException $e) {
390
-			return false;
391
-		} catch (ConnectException $e) {
392
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
393
-		}
394
-	}
395
-
396
-	public function touch($path, $time = null) {
397
-		try {
398
-			if (!$this->file_exists($path)) {
399
-				$fh = $this->share->write($this->buildPath($path));
400
-				fclose($fh);
401
-				return true;
402
-			}
403
-			return false;
404
-		} catch (ConnectException $e) {
405
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
406
-		}
407
-	}
408
-
409
-	public function opendir($path) {
410
-		try {
411
-			$files = $this->getFolderContents($path);
412
-		} catch (NotFoundException $e) {
413
-			return false;
414
-		} catch (ForbiddenException $e) {
415
-			return false;
416
-		}
417
-		$names = array_map(function ($info) {
418
-			/** @var \Icewind\SMB\IFileInfo $info */
419
-			return $info->getName();
420
-		}, $files);
421
-		return IteratorDirectory::wrap($names);
422
-	}
423
-
424
-	public function filetype($path) {
425
-		try {
426
-			return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
427
-		} catch (NotFoundException $e) {
428
-			return false;
429
-		} catch (ForbiddenException $e) {
430
-			return false;
431
-		}
432
-	}
433
-
434
-	public function mkdir($path) {
435
-		$path = $this->buildPath($path);
436
-		try {
437
-			$this->share->mkdir($path);
438
-			return true;
439
-		} catch (ConnectException $e) {
440
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
441
-		} catch (Exception $e) {
442
-			return false;
443
-		}
444
-	}
445
-
446
-	public function file_exists($path) {
447
-		try {
448
-			$this->getFileInfo($path);
449
-			return true;
450
-		} catch (NotFoundException $e) {
451
-			return false;
452
-		} catch (ForbiddenException $e) {
453
-			return false;
454
-		} catch (ConnectException $e) {
455
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
456
-		}
457
-	}
458
-
459
-	public function isReadable($path) {
460
-		try {
461
-			$info = $this->getFileInfo($path);
462
-			return !$info->isHidden();
463
-		} catch (NotFoundException $e) {
464
-			return false;
465
-		} catch (ForbiddenException $e) {
466
-			return false;
467
-		}
468
-	}
469
-
470
-	public function isUpdatable($path) {
471
-		try {
472
-			$info = $this->getFileInfo($path);
473
-			// following windows behaviour for read-only folders: they can be written into
474
-			// (https://support.microsoft.com/en-us/kb/326549 - "cause" section)
475
-			return !$info->isHidden() && (!$info->isReadOnly() || $this->is_dir($path));
476
-		} catch (NotFoundException $e) {
477
-			return false;
478
-		} catch (ForbiddenException $e) {
479
-			return false;
480
-		}
481
-	}
482
-
483
-	public function isDeletable($path) {
484
-		try {
485
-			$info = $this->getFileInfo($path);
486
-			return !$info->isHidden() && !$info->isReadOnly();
487
-		} catch (NotFoundException $e) {
488
-			return false;
489
-		} catch (ForbiddenException $e) {
490
-			return false;
491
-		}
492
-	}
493
-
494
-	/**
495
-	 * check if smbclient is installed
496
-	 */
497
-	public static function checkDependencies() {
498
-		return (
499
-			(bool)\OC_Helper::findBinaryPath('smbclient')
500
-			|| Server::NativeAvailable()
501
-		) ? true : ['smbclient'];
502
-	}
503
-
504
-	/**
505
-	 * Test a storage for availability
506
-	 *
507
-	 * @return bool
508
-	 */
509
-	public function test() {
510
-		try {
511
-			return parent::test();
512
-		} catch (Exception $e) {
513
-			return false;
514
-		}
515
-	}
516
-
517
-	public function listen($path, callable $callback) {
518
-		$this->notify($path)->listen(function (IChange $change) use ($callback) {
519
-			if ($change instanceof IRenameChange) {
520
-				return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
521
-			} else {
522
-				return $callback($change->getType(), $change->getPath());
523
-			}
524
-		});
525
-	}
526
-
527
-	public function notify($path) {
528
-		$path = '/' . ltrim($path, '/');
529
-		$shareNotifyHandler = $this->share->notify($this->buildPath($path));
530
-		return new SMBNotifyHandler($shareNotifyHandler, $this->root);
531
-	}
58
+    /**
59
+     * @var \Icewind\SMB\Server
60
+     */
61
+    protected $server;
62
+
63
+    /**
64
+     * @var \Icewind\SMB\Share
65
+     */
66
+    protected $share;
67
+
68
+    /**
69
+     * @var string
70
+     */
71
+    protected $root;
72
+
73
+    /**
74
+     * @var \Icewind\SMB\FileInfo[]
75
+     */
76
+    protected $statCache;
77
+
78
+    public function __construct($params) {
79
+        if (isset($params['host']) && isset($params['user']) && isset($params['password']) && isset($params['share'])) {
80
+            if (Server::NativeAvailable()) {
81
+                $this->server = new NativeServer($params['host'], $params['user'], $params['password']);
82
+            } else {
83
+                $this->server = new Server($params['host'], $params['user'], $params['password']);
84
+            }
85
+            $this->share = $this->server->getShare(trim($params['share'], '/'));
86
+
87
+            $this->root = isset($params['root']) ? $params['root'] : '/';
88
+            if (!$this->root || $this->root[0] !== '/') {
89
+                $this->root = '/' . $this->root;
90
+            }
91
+            if ($this->root[strlen($this->root) - 1] !== '/') {
92
+                $this->root .= '/';
93
+            }
94
+        } else {
95
+            throw new \Exception('Invalid configuration');
96
+        }
97
+        $this->statCache = new CappedMemoryCache();
98
+        parent::__construct($params);
99
+    }
100
+
101
+    /**
102
+     * @return string
103
+     */
104
+    public function getId() {
105
+        // FIXME: double slash to keep compatible with the old storage ids,
106
+        // failure to do so will lead to creation of a new storage id and
107
+        // loss of shares from the storage
108
+        return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
109
+    }
110
+
111
+    /**
112
+     * @param string $path
113
+     * @return string
114
+     */
115
+    protected function buildPath($path) {
116
+        return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
117
+    }
118
+
119
+    protected function relativePath($fullPath) {
120
+        if ($fullPath === $this->root) {
121
+            return '';
122
+        } else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
123
+            return substr($fullPath, strlen($this->root));
124
+        } else {
125
+            return null;
126
+        }
127
+    }
128
+
129
+    /**
130
+     * @param string $path
131
+     * @return \Icewind\SMB\IFileInfo
132
+     * @throws StorageNotAvailableException
133
+     */
134
+    protected function getFileInfo($path) {
135
+        try {
136
+            $path = $this->buildPath($path);
137
+            if (!isset($this->statCache[$path])) {
138
+                $this->statCache[$path] = $this->share->stat($path);
139
+            }
140
+            return $this->statCache[$path];
141
+        } catch (ConnectException $e) {
142
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
143
+        }
144
+    }
145
+
146
+    /**
147
+     * @param string $path
148
+     * @return \Icewind\SMB\IFileInfo[]
149
+     * @throws StorageNotAvailableException
150
+     */
151
+    protected function getFolderContents($path) {
152
+        try {
153
+            $path = $this->buildPath($path);
154
+            $files = $this->share->dir($path);
155
+            foreach ($files as $file) {
156
+                $this->statCache[$path . '/' . $file->getName()] = $file;
157
+            }
158
+            return array_filter($files, function (IFileInfo $file) {
159
+                return !$file->isHidden();
160
+            });
161
+        } catch (ConnectException $e) {
162
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
163
+        }
164
+    }
165
+
166
+    /**
167
+     * @param \Icewind\SMB\IFileInfo $info
168
+     * @return array
169
+     */
170
+    protected function formatInfo($info) {
171
+        $result = [
172
+            'size' => $info->getSize(),
173
+            'mtime' => $info->getMTime(),
174
+        ];
175
+        if ($info->isDirectory()) {
176
+            $result['type'] = 'dir';
177
+        } else {
178
+            $result['type'] = 'file';
179
+        }
180
+        return $result;
181
+    }
182
+
183
+    /**
184
+     * Rename the files. If the source or the target is the root, the rename won't happen.
185
+     *
186
+     * @param string $source the old name of the path
187
+     * @param string $target the new name of the path
188
+     * @return bool true if the rename is successful, false otherwise
189
+     */
190
+    public function rename($source, $target) {
191
+        if ($this->isRootDir($source) || $this->isRootDir($target)) {
192
+            return false;
193
+        }
194
+
195
+        $absoluteSource = $this->buildPath($source);
196
+        $absoluteTarget = $this->buildPath($target);
197
+        try {
198
+            $result = $this->share->rename($absoluteSource, $absoluteTarget);
199
+        } catch (AlreadyExistsException $e) {
200
+            $this->remove($target);
201
+            $result = $this->share->rename($absoluteSource, $absoluteTarget);
202
+        } catch (\Exception $e) {
203
+            \OC::$server->getLogger()->logException($e, ['level' => Util::WARN]);
204
+            return false;
205
+        }
206
+        unset($this->statCache[$absoluteSource], $this->statCache[$absoluteTarget]);
207
+        return $result;
208
+    }
209
+
210
+    public function stat($path) {
211
+        try {
212
+            $result = $this->formatInfo($this->getFileInfo($path));
213
+        } catch (ForbiddenException $e) {
214
+            return false;
215
+        } catch (NotFoundException $e) {
216
+            return false;
217
+        }
218
+        if ($this->remoteIsShare() && $this->isRootDir($path)) {
219
+            $result['mtime'] = $this->shareMTime();
220
+        }
221
+        return $result;
222
+    }
223
+
224
+    /**
225
+     * get the best guess for the modification time of the share
226
+     *
227
+     * @return int
228
+     */
229
+    private function shareMTime() {
230
+        $highestMTime = 0;
231
+        $files = $this->share->dir($this->root);
232
+        foreach ($files as $fileInfo) {
233
+            if ($fileInfo->getMTime() > $highestMTime) {
234
+                $highestMTime = $fileInfo->getMTime();
235
+            }
236
+        }
237
+        return $highestMTime;
238
+    }
239
+
240
+    /**
241
+     * Check if the path is our root dir (not the smb one)
242
+     *
243
+     * @param string $path the path
244
+     * @return bool
245
+     */
246
+    private function isRootDir($path) {
247
+        return $path === '' || $path === '/' || $path === '.';
248
+    }
249
+
250
+    /**
251
+     * Check if our root points to a smb share
252
+     *
253
+     * @return bool true if our root points to a share false otherwise
254
+     */
255
+    private function remoteIsShare() {
256
+        return $this->share->getName() && (!$this->root || $this->root === '/');
257
+    }
258
+
259
+    /**
260
+     * @param string $path
261
+     * @return bool
262
+     */
263
+    public function unlink($path) {
264
+        if ($this->isRootDir($path)) {
265
+            return false;
266
+        }
267
+
268
+        try {
269
+            if ($this->is_dir($path)) {
270
+                return $this->rmdir($path);
271
+            } else {
272
+                $path = $this->buildPath($path);
273
+                unset($this->statCache[$path]);
274
+                $this->share->del($path);
275
+                return true;
276
+            }
277
+        } catch (NotFoundException $e) {
278
+            return false;
279
+        } catch (ForbiddenException $e) {
280
+            return false;
281
+        } catch (ConnectException $e) {
282
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
283
+        }
284
+    }
285
+
286
+    /**
287
+     * check if a file or folder has been updated since $time
288
+     *
289
+     * @param string $path
290
+     * @param int $time
291
+     * @return bool
292
+     */
293
+    public function hasUpdated($path, $time) {
294
+        if (!$path and $this->root === '/') {
295
+            // mtime doesn't work for shares, but giving the nature of the backend,
296
+            // doing a full update is still just fast enough
297
+            return true;
298
+        } else {
299
+            $actualTime = $this->filemtime($path);
300
+            return $actualTime > $time;
301
+        }
302
+    }
303
+
304
+    /**
305
+     * @param string $path
306
+     * @param string $mode
307
+     * @return resource|false
308
+     */
309
+    public function fopen($path, $mode) {
310
+        $fullPath = $this->buildPath($path);
311
+        try {
312
+            switch ($mode) {
313
+                case 'r':
314
+                case 'rb':
315
+                    if (!$this->file_exists($path)) {
316
+                        return false;
317
+                    }
318
+                    return $this->share->read($fullPath);
319
+                case 'w':
320
+                case 'wb':
321
+                    $source = $this->share->write($fullPath);
322
+                    return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
323
+                        unset($this->statCache[$fullPath]);
324
+                    });
325
+                case 'a':
326
+                case 'ab':
327
+                case 'r+':
328
+                case 'w+':
329
+                case 'wb+':
330
+                case 'a+':
331
+                case 'x':
332
+                case 'x+':
333
+                case 'c':
334
+                case 'c+':
335
+                    //emulate these
336
+                    if (strrpos($path, '.') !== false) {
337
+                        $ext = substr($path, strrpos($path, '.'));
338
+                    } else {
339
+                        $ext = '';
340
+                    }
341
+                    if ($this->file_exists($path)) {
342
+                        if (!$this->isUpdatable($path)) {
343
+                            return false;
344
+                        }
345
+                        $tmpFile = $this->getCachedFile($path);
346
+                    } else {
347
+                        if (!$this->isCreatable(dirname($path))) {
348
+                            return false;
349
+                        }
350
+                        $tmpFile = \OCP\Files::tmpFile($ext);
351
+                    }
352
+                    $source = fopen($tmpFile, $mode);
353
+                    $share = $this->share;
354
+                    return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
355
+                        unset($this->statCache[$fullPath]);
356
+                        $share->put($tmpFile, $fullPath);
357
+                        unlink($tmpFile);
358
+                    });
359
+            }
360
+            return false;
361
+        } catch (NotFoundException $e) {
362
+            return false;
363
+        } catch (ForbiddenException $e) {
364
+            return false;
365
+        } catch (ConnectException $e) {
366
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
367
+        }
368
+    }
369
+
370
+    public function rmdir($path) {
371
+        if ($this->isRootDir($path)) {
372
+            return false;
373
+        }
374
+
375
+        try {
376
+            $this->statCache = array();
377
+            $content = $this->share->dir($this->buildPath($path));
378
+            foreach ($content as $file) {
379
+                if ($file->isDirectory()) {
380
+                    $this->rmdir($path . '/' . $file->getName());
381
+                } else {
382
+                    $this->share->del($file->getPath());
383
+                }
384
+            }
385
+            $this->share->rmdir($this->buildPath($path));
386
+            return true;
387
+        } catch (NotFoundException $e) {
388
+            return false;
389
+        } catch (ForbiddenException $e) {
390
+            return false;
391
+        } catch (ConnectException $e) {
392
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
393
+        }
394
+    }
395
+
396
+    public function touch($path, $time = null) {
397
+        try {
398
+            if (!$this->file_exists($path)) {
399
+                $fh = $this->share->write($this->buildPath($path));
400
+                fclose($fh);
401
+                return true;
402
+            }
403
+            return false;
404
+        } catch (ConnectException $e) {
405
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
406
+        }
407
+    }
408
+
409
+    public function opendir($path) {
410
+        try {
411
+            $files = $this->getFolderContents($path);
412
+        } catch (NotFoundException $e) {
413
+            return false;
414
+        } catch (ForbiddenException $e) {
415
+            return false;
416
+        }
417
+        $names = array_map(function ($info) {
418
+            /** @var \Icewind\SMB\IFileInfo $info */
419
+            return $info->getName();
420
+        }, $files);
421
+        return IteratorDirectory::wrap($names);
422
+    }
423
+
424
+    public function filetype($path) {
425
+        try {
426
+            return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
427
+        } catch (NotFoundException $e) {
428
+            return false;
429
+        } catch (ForbiddenException $e) {
430
+            return false;
431
+        }
432
+    }
433
+
434
+    public function mkdir($path) {
435
+        $path = $this->buildPath($path);
436
+        try {
437
+            $this->share->mkdir($path);
438
+            return true;
439
+        } catch (ConnectException $e) {
440
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
441
+        } catch (Exception $e) {
442
+            return false;
443
+        }
444
+    }
445
+
446
+    public function file_exists($path) {
447
+        try {
448
+            $this->getFileInfo($path);
449
+            return true;
450
+        } catch (NotFoundException $e) {
451
+            return false;
452
+        } catch (ForbiddenException $e) {
453
+            return false;
454
+        } catch (ConnectException $e) {
455
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
456
+        }
457
+    }
458
+
459
+    public function isReadable($path) {
460
+        try {
461
+            $info = $this->getFileInfo($path);
462
+            return !$info->isHidden();
463
+        } catch (NotFoundException $e) {
464
+            return false;
465
+        } catch (ForbiddenException $e) {
466
+            return false;
467
+        }
468
+    }
469
+
470
+    public function isUpdatable($path) {
471
+        try {
472
+            $info = $this->getFileInfo($path);
473
+            // following windows behaviour for read-only folders: they can be written into
474
+            // (https://support.microsoft.com/en-us/kb/326549 - "cause" section)
475
+            return !$info->isHidden() && (!$info->isReadOnly() || $this->is_dir($path));
476
+        } catch (NotFoundException $e) {
477
+            return false;
478
+        } catch (ForbiddenException $e) {
479
+            return false;
480
+        }
481
+    }
482
+
483
+    public function isDeletable($path) {
484
+        try {
485
+            $info = $this->getFileInfo($path);
486
+            return !$info->isHidden() && !$info->isReadOnly();
487
+        } catch (NotFoundException $e) {
488
+            return false;
489
+        } catch (ForbiddenException $e) {
490
+            return false;
491
+        }
492
+    }
493
+
494
+    /**
495
+     * check if smbclient is installed
496
+     */
497
+    public static function checkDependencies() {
498
+        return (
499
+            (bool)\OC_Helper::findBinaryPath('smbclient')
500
+            || Server::NativeAvailable()
501
+        ) ? true : ['smbclient'];
502
+    }
503
+
504
+    /**
505
+     * Test a storage for availability
506
+     *
507
+     * @return bool
508
+     */
509
+    public function test() {
510
+        try {
511
+            return parent::test();
512
+        } catch (Exception $e) {
513
+            return false;
514
+        }
515
+    }
516
+
517
+    public function listen($path, callable $callback) {
518
+        $this->notify($path)->listen(function (IChange $change) use ($callback) {
519
+            if ($change instanceof IRenameChange) {
520
+                return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
521
+            } else {
522
+                return $callback($change->getType(), $change->getPath());
523
+            }
524
+        });
525
+    }
526
+
527
+    public function notify($path) {
528
+        $path = '/' . ltrim($path, '/');
529
+        $shareNotifyHandler = $this->share->notify($this->buildPath($path));
530
+        return new SMBNotifyHandler($shareNotifyHandler, $this->root);
531
+    }
532 532
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -86,7 +86,7 @@  discard block
 block discarded – undo
86 86
 
87 87
 			$this->root = isset($params['root']) ? $params['root'] : '/';
88 88
 			if (!$this->root || $this->root[0] !== '/') {
89
-				$this->root = '/' . $this->root;
89
+				$this->root = '/'.$this->root;
90 90
 			}
91 91
 			if ($this->root[strlen($this->root) - 1] !== '/') {
92 92
 				$this->root .= '/';
@@ -105,7 +105,7 @@  discard block
 block discarded – undo
105 105
 		// FIXME: double slash to keep compatible with the old storage ids,
106 106
 		// failure to do so will lead to creation of a new storage id and
107 107
 		// loss of shares from the storage
108
-		return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
108
+		return 'smb::'.$this->server->getUser().'@'.$this->server->getHost().'//'.$this->share->getName().'/'.$this->root;
109 109
 	}
110 110
 
111 111
 	/**
@@ -113,7 +113,7 @@  discard block
 block discarded – undo
113 113
 	 * @return string
114 114
 	 */
115 115
 	protected function buildPath($path) {
116
-		return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
116
+		return Filesystem::normalizePath($this->root.'/'.$path, true, false, true);
117 117
 	}
118 118
 
119 119
 	protected function relativePath($fullPath) {
@@ -153,9 +153,9 @@  discard block
 block discarded – undo
153 153
 			$path = $this->buildPath($path);
154 154
 			$files = $this->share->dir($path);
155 155
 			foreach ($files as $file) {
156
-				$this->statCache[$path . '/' . $file->getName()] = $file;
156
+				$this->statCache[$path.'/'.$file->getName()] = $file;
157 157
 			}
158
-			return array_filter($files, function (IFileInfo $file) {
158
+			return array_filter($files, function(IFileInfo $file) {
159 159
 				return !$file->isHidden();
160 160
 			});
161 161
 		} catch (ConnectException $e) {
@@ -319,7 +319,7 @@  discard block
 block discarded – undo
319 319
 				case 'w':
320 320
 				case 'wb':
321 321
 					$source = $this->share->write($fullPath);
322
-					return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
322
+					return CallBackWrapper::wrap($source, null, null, function() use ($fullPath) {
323 323
 						unset($this->statCache[$fullPath]);
324 324
 					});
325 325
 				case 'a':
@@ -351,7 +351,7 @@  discard block
 block discarded – undo
351 351
 					}
352 352
 					$source = fopen($tmpFile, $mode);
353 353
 					$share = $this->share;
354
-					return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
354
+					return CallbackWrapper::wrap($source, null, null, function() use ($tmpFile, $fullPath, $share) {
355 355
 						unset($this->statCache[$fullPath]);
356 356
 						$share->put($tmpFile, $fullPath);
357 357
 						unlink($tmpFile);
@@ -377,7 +377,7 @@  discard block
 block discarded – undo
377 377
 			$content = $this->share->dir($this->buildPath($path));
378 378
 			foreach ($content as $file) {
379 379
 				if ($file->isDirectory()) {
380
-					$this->rmdir($path . '/' . $file->getName());
380
+					$this->rmdir($path.'/'.$file->getName());
381 381
 				} else {
382 382
 					$this->share->del($file->getPath());
383 383
 				}
@@ -414,7 +414,7 @@  discard block
 block discarded – undo
414 414
 		} catch (ForbiddenException $e) {
415 415
 			return false;
416 416
 		}
417
-		$names = array_map(function ($info) {
417
+		$names = array_map(function($info) {
418 418
 			/** @var \Icewind\SMB\IFileInfo $info */
419 419
 			return $info->getName();
420 420
 		}, $files);
@@ -496,7 +496,7 @@  discard block
 block discarded – undo
496 496
 	 */
497 497
 	public static function checkDependencies() {
498 498
 		return (
499
-			(bool)\OC_Helper::findBinaryPath('smbclient')
499
+			(bool) \OC_Helper::findBinaryPath('smbclient')
500 500
 			|| Server::NativeAvailable()
501 501
 		) ? true : ['smbclient'];
502 502
 	}
@@ -515,7 +515,7 @@  discard block
 block discarded – undo
515 515
 	}
516 516
 
517 517
 	public function listen($path, callable $callback) {
518
-		$this->notify($path)->listen(function (IChange $change) use ($callback) {
518
+		$this->notify($path)->listen(function(IChange $change) use ($callback) {
519 519
 			if ($change instanceof IRenameChange) {
520 520
 				return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
521 521
 			} else {
@@ -525,7 +525,7 @@  discard block
 block discarded – undo
525 525
 	}
526 526
 
527 527
 	public function notify($path) {
528
-		$path = '/' . ltrim($path, '/');
528
+		$path = '/'.ltrim($path, '/');
529 529
 		$shareNotifyHandler = $this->share->notify($this->buildPath($path));
530 530
 		return new SMBNotifyHandler($shareNotifyHandler, $this->root);
531 531
 	}
Please login to merge, or discard this patch.
lib/private/Avatar.php 1 patch
Indentation   +385 added lines, -385 removed lines patch added patch discarded remove patch
@@ -46,390 +46,390 @@
 block discarded – undo
46 46
  */
47 47
 
48 48
 class Avatar implements IAvatar {
49
-	/** @var ISimpleFolder */
50
-	private $folder;
51
-	/** @var IL10N */
52
-	private $l;
53
-	/** @var User */
54
-	private $user;
55
-	/** @var ILogger  */
56
-	private $logger;
57
-	/** @var IConfig */
58
-	private $config;
59
-
60
-	/**
61
-	 * constructor
62
-	 *
63
-	 * @param ISimpleFolder $folder The folder where the avatars are
64
-	 * @param IL10N $l
65
-	 * @param User $user
66
-	 * @param ILogger $logger
67
-	 * @param IConfig $config
68
-	 */
69
-	public function __construct(ISimpleFolder $folder,
70
-								IL10N $l,
71
-								$user,
72
-								ILogger $logger,
73
-								IConfig $config) {
74
-		$this->folder = $folder;
75
-		$this->l = $l;
76
-		$this->user = $user;
77
-		$this->logger = $logger;
78
-		$this->config = $config;
79
-	}
80
-
81
-	/**
82
-	 * @inheritdoc
83
-	 */
84
-	public function get ($size = 64) {
85
-		try {
86
-			$file = $this->getFile($size);
87
-		} catch (NotFoundException $e) {
88
-			return false;
89
-		}
90
-
91
-		$avatar = new OC_Image();
92
-		$avatar->loadFromData($file->getContent());
93
-		return $avatar;
94
-	}
95
-
96
-	/**
97
-	 * Check if an avatar exists for the user
98
-	 *
99
-	 * @return bool
100
-	 */
101
-	public function exists() {
102
-
103
-		return $this->folder->fileExists('avatar.jpg') || $this->folder->fileExists('avatar.png');
104
-	}
105
-
106
-	/**
107
-	 * sets the users avatar
108
-	 * @param IImage|resource|string $data An image object, imagedata or path to set a new avatar
109
-	 * @throws \Exception if the provided file is not a jpg or png image
110
-	 * @throws \Exception if the provided image is not valid
111
-	 * @throws NotSquareException if the image is not square
112
-	 * @return void
113
-	*/
114
-	public function set ($data) {
115
-
116
-		if($data instanceOf IImage) {
117
-			$img = $data;
118
-			$data = $img->data();
119
-		} else {
120
-			$img = new OC_Image();
121
-			if (is_resource($data) && get_resource_type($data) === "gd") {
122
-				$img->setResource($data);
123
-			} elseif(is_resource($data)) {
124
-				$img->loadFromFileHandle($data);
125
-			} else {
126
-				try {
127
-					// detect if it is a path or maybe the images as string
128
-					$result = @realpath($data);
129
-					if ($result === false || $result === null) {
130
-						$img->loadFromData($data);
131
-					} else {
132
-						$img->loadFromFile($data);
133
-					}
134
-				} catch (\Error $e) {
135
-					$img->loadFromData($data);
136
-				}
137
-			}
138
-		}
139
-		$type = substr($img->mimeType(), -3);
140
-		if ($type === 'peg') {
141
-			$type = 'jpg';
142
-		}
143
-		if ($type !== 'jpg' && $type !== 'png') {
144
-			throw new \Exception($this->l->t('Unknown filetype'));
145
-		}
146
-
147
-		if (!$img->valid()) {
148
-			throw new \Exception($this->l->t('Invalid image'));
149
-		}
150
-
151
-		if (!($img->height() === $img->width())) {
152
-			throw new NotSquareException($this->l->t('Avatar image is not square'));
153
-		}
154
-
155
-		$this->remove();
156
-		$file = $this->folder->newFile('avatar.'.$type);
157
-		$file->putContent($data);
158
-
159
-		try {
160
-			$generated = $this->folder->getFile('generated');
161
-			$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'false');
162
-			$generated->delete();
163
-		} catch (NotFoundException $e) {
164
-			//
165
-		}
166
-		$this->user->triggerChange('avatar', $file);
167
-	}
168
-
169
-	/**
170
-	 * remove the users avatar
171
-	 * @return void
172
-	*/
173
-	public function remove () {
174
-		$avatars = $this->folder->getDirectoryListing();
175
-
176
-		$this->config->setUserValue($this->user->getUID(), 'avatar', 'version',
177
-			(int)$this->config->getUserValue($this->user->getUID(), 'avatar', 'version', 0) + 1);
178
-
179
-		foreach ($avatars as $avatar) {
180
-			$avatar->delete();
181
-		}
182
-		$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true');
183
-		$this->user->triggerChange('avatar', '');
184
-	}
185
-
186
-	/**
187
-	 * @inheritdoc
188
-	 */
189
-	public function getFile($size) {
190
-		try {
191
-			$ext = $this->getExtension();
192
-		} catch (NotFoundException $e) {
193
-			$data = $this->generateAvatar($this->user->getDisplayName(), 1024);
194
-			$avatar = $this->folder->newFile('avatar.png');
195
-			$avatar->putContent($data);
196
-			$ext = 'png';
197
-
198
-			$this->folder->newFile('generated');
199
-			$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true');
200
-		}
201
-
202
-		if ($size === -1) {
203
-			$path = 'avatar.' . $ext;
204
-		} else {
205
-			$path = 'avatar.' . $size . '.' . $ext;
206
-		}
207
-
208
-		try {
209
-			$file = $this->folder->getFile($path);
210
-		} catch (NotFoundException $e) {
211
-			if ($size <= 0) {
212
-				throw new NotFoundException;
213
-			}
214
-
215
-			if ($this->folder->fileExists('generated')) {
216
-				$data = $this->generateAvatar($this->user->getDisplayName(), $size);
217
-
218
-			} else {
219
-				$avatar = new OC_Image();
220
-				/** @var ISimpleFile $file */
221
-				$file = $this->folder->getFile('avatar.' . $ext);
222
-				$avatar->loadFromData($file->getContent());
223
-				$avatar->resize($size);
224
-				$data = $avatar->data();
225
-			}
226
-
227
-			try {
228
-				$file = $this->folder->newFile($path);
229
-				$file->putContent($data);
230
-			} catch (NotPermittedException $e) {
231
-				$this->logger->error('Failed to save avatar for ' . $this->user->getUID());
232
-				throw new NotFoundException();
233
-			}
234
-
235
-		}
236
-
237
-		if($this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', null) === null) {
238
-			$generated = $this->folder->fileExists('generated') ? 'true' : 'false';
239
-			$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', $generated);
240
-		}
241
-
242
-		return $file;
243
-	}
244
-
245
-	/**
246
-	 * Get the extension of the avatar. If there is no avatar throw Exception
247
-	 *
248
-	 * @return string
249
-	 * @throws NotFoundException
250
-	 */
251
-	private function getExtension() {
252
-		if ($this->folder->fileExists('avatar.jpg')) {
253
-			return 'jpg';
254
-		} elseif ($this->folder->fileExists('avatar.png')) {
255
-			return 'png';
256
-		}
257
-		throw new NotFoundException;
258
-	}
259
-
260
-	/**
261
-	 * @param string $userDisplayName
262
-	 * @param int $size
263
-	 * @return string
264
-	 */
265
-	private function generateAvatar($userDisplayName, $size) {
266
-		$text = strtoupper($userDisplayName[0]);
267
-		$backgroundColor = $this->avatarBackgroundColor($userDisplayName);
268
-
269
-		$im = imagecreatetruecolor($size, $size);
270
-		$background = imagecolorallocate($im, $backgroundColor[0], $backgroundColor[1], $backgroundColor[2]);
271
-		$white = imagecolorallocate($im, 255, 255, 255);
272
-		imagefilledrectangle($im, 0, 0, $size, $size, $background);
273
-
274
-		$font = __DIR__ . '/../../core/fonts/OpenSans-Semibold.woff';
275
-
276
-		$fontSize = $size * 0.4;
277
-		$box = imagettfbbox($fontSize, 0, $font, $text);
278
-
279
-		$x = ($size - ($box[2] - $box[0])) / 2;
280
-		$y = ($size - ($box[1] - $box[7])) / 2;
281
-		$x += 1;
282
-		$y -= $box[7];
283
-		imagettftext($im, $fontSize, 0, $x, $y, $white, $font, $text);
284
-
285
-		ob_start();
286
-		imagepng($im);
287
-		$data = ob_get_contents();
288
-		ob_end_clean();
289
-
290
-		return $data;
291
-	}
292
-
293
-	/**
294
-	 * @param int $r
295
-	 * @param int $g
296
-	 * @param int $b
297
-	 * @return double[] Array containing h s l in [0, 1] range
298
-	 */
299
-	private function rgbToHsl($r, $g, $b) {
300
-		$r /= 255.0;
301
-		$g /= 255.0;
302
-		$b /= 255.0;
303
-
304
-		$max = max($r, $g, $b);
305
-		$min = min($r, $g, $b);
306
-
307
-
308
-		$h = ($max + $min) / 2.0;
309
-		$l = ($max + $min) / 2.0;
310
-
311
-		if($max === $min) {
312
-			$h = $s = 0; // Achromatic
313
-		} else {
314
-			$d = $max - $min;
315
-			$s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min);
316
-			switch($max) {
317
-				case $r:
318
-					$h = ($g - $b) / $d + ($g < $b ? 6 : 0);
319
-					break;
320
-				case $g:
321
-					$h = ($b - $r) / $d + 2.0;
322
-					break;
323
-				case $b:
324
-					$h = ($r - $g) / $d + 4.0;
325
-					break;
326
-			}
327
-			$h /= 6.0;
328
-		}
329
-		return [$h, $s, $l];
330
-
331
-	}
332
-
333
-	/**
334
-	 * @param string $text
335
-	 * @return int[] Array containting r g b in the range [0, 255]
336
-	 */
337
-	private function avatarBackgroundColor($text) {
338
-		$hash = preg_replace('/[^0-9a-f]+/', '', $text);
339
-
340
-		$hash = md5($hash);
341
-		$hashChars = str_split($hash);
342
-
343
-
344
-		// Init vars
345
-		$result = ['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'];
346
-		$rgb = [0, 0, 0];
347
-		$sat = 0.70;
348
-		$lum = 0.68;
349
-		$modulo = 16;
350
-
351
-
352
-		// Splitting evenly the string
353
-		foreach($hashChars as  $i => $char) {
354
-			$result[$i % $modulo] .= intval($char, 16);
355
-		}
356
-
357
-		// Converting our data into a usable rgb format
358
-		// Start at 1 because 16%3=1 but 15%3=0 and makes the repartition even
359
-		for($count = 1; $count < $modulo; $count++) {
360
-			$rgb[$count%3] += (int)$result[$count];
361
-		}
362
-
363
-		// Reduce values bigger than rgb requirements
364
-		$rgb[0] %= 255;
365
-		$rgb[1] %= 255;
366
-		$rgb[2] %= 255;
367
-
368
-		$hsl = $this->rgbToHsl($rgb[0], $rgb[1], $rgb[2]);
369
-
370
-		// Classic formula to check the brightness for our eye
371
-		// If too bright, lower the sat
372
-		$bright = sqrt(0.299 * ($rgb[0] ** 2) + 0.587 * ($rgb[1] ** 2) + 0.114 * ($rgb[2] ** 2));
373
-		if ($bright >= 200) {
374
-			$sat = 0.60;
375
-		}
376
-
377
-		return $this->hslToRgb($hsl[0], $sat, $lum);
378
-	}
379
-
380
-	/**
381
-	 * @param double $h Hue in range [0, 1]
382
-	 * @param double $s Saturation in range [0, 1]
383
-	 * @param double $l Lightness in range [0, 1]
384
-	 * @return int[] Array containing r g b in the range [0, 255]
385
-	 */
386
-	private function hslToRgb($h, $s, $l){
387
-		$hue2rgb = function ($p, $q, $t){
388
-			if($t < 0) {
389
-				$t += 1;
390
-			}
391
-			if($t > 1) {
392
-				$t -= 1;
393
-			}
394
-			if($t < 1/6) {
395
-				return $p + ($q - $p) * 6 * $t;
396
-			}
397
-			if($t < 1/2) {
398
-				return $q;
399
-			}
400
-			if($t < 2/3) {
401
-				return $p + ($q - $p) * (2/3 - $t) * 6;
402
-			}
403
-			return $p;
404
-		};
405
-
406
-		if($s === 0){
407
-			$r = $l;
408
-			$g = $l;
409
-			$b = $l; // achromatic
410
-		}else{
411
-			$q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
412
-			$p = 2 * $l - $q;
413
-			$r = $hue2rgb($p, $q, $h + 1/3);
414
-			$g = $hue2rgb($p, $q, $h);
415
-			$b = $hue2rgb($p, $q, $h - 1/3);
416
-		}
417
-
418
-		return array(round($r * 255), round($g * 255), round($b * 255));
419
-	}
420
-
421
-	public function userChanged($feature, $oldValue, $newValue) {
422
-		// We only change the avatar on display name changes
423
-		if ($feature !== 'displayName') {
424
-			return;
425
-		}
426
-
427
-		// If the avatar is not generated (so an uploaded image) we skip this
428
-		if (!$this->folder->fileExists('generated')) {
429
-			return;
430
-		}
431
-
432
-		$this->remove();
433
-	}
49
+    /** @var ISimpleFolder */
50
+    private $folder;
51
+    /** @var IL10N */
52
+    private $l;
53
+    /** @var User */
54
+    private $user;
55
+    /** @var ILogger  */
56
+    private $logger;
57
+    /** @var IConfig */
58
+    private $config;
59
+
60
+    /**
61
+     * constructor
62
+     *
63
+     * @param ISimpleFolder $folder The folder where the avatars are
64
+     * @param IL10N $l
65
+     * @param User $user
66
+     * @param ILogger $logger
67
+     * @param IConfig $config
68
+     */
69
+    public function __construct(ISimpleFolder $folder,
70
+                                IL10N $l,
71
+                                $user,
72
+                                ILogger $logger,
73
+                                IConfig $config) {
74
+        $this->folder = $folder;
75
+        $this->l = $l;
76
+        $this->user = $user;
77
+        $this->logger = $logger;
78
+        $this->config = $config;
79
+    }
80
+
81
+    /**
82
+     * @inheritdoc
83
+     */
84
+    public function get ($size = 64) {
85
+        try {
86
+            $file = $this->getFile($size);
87
+        } catch (NotFoundException $e) {
88
+            return false;
89
+        }
90
+
91
+        $avatar = new OC_Image();
92
+        $avatar->loadFromData($file->getContent());
93
+        return $avatar;
94
+    }
95
+
96
+    /**
97
+     * Check if an avatar exists for the user
98
+     *
99
+     * @return bool
100
+     */
101
+    public function exists() {
102
+
103
+        return $this->folder->fileExists('avatar.jpg') || $this->folder->fileExists('avatar.png');
104
+    }
105
+
106
+    /**
107
+     * sets the users avatar
108
+     * @param IImage|resource|string $data An image object, imagedata or path to set a new avatar
109
+     * @throws \Exception if the provided file is not a jpg or png image
110
+     * @throws \Exception if the provided image is not valid
111
+     * @throws NotSquareException if the image is not square
112
+     * @return void
113
+     */
114
+    public function set ($data) {
115
+
116
+        if($data instanceOf IImage) {
117
+            $img = $data;
118
+            $data = $img->data();
119
+        } else {
120
+            $img = new OC_Image();
121
+            if (is_resource($data) && get_resource_type($data) === "gd") {
122
+                $img->setResource($data);
123
+            } elseif(is_resource($data)) {
124
+                $img->loadFromFileHandle($data);
125
+            } else {
126
+                try {
127
+                    // detect if it is a path or maybe the images as string
128
+                    $result = @realpath($data);
129
+                    if ($result === false || $result === null) {
130
+                        $img->loadFromData($data);
131
+                    } else {
132
+                        $img->loadFromFile($data);
133
+                    }
134
+                } catch (\Error $e) {
135
+                    $img->loadFromData($data);
136
+                }
137
+            }
138
+        }
139
+        $type = substr($img->mimeType(), -3);
140
+        if ($type === 'peg') {
141
+            $type = 'jpg';
142
+        }
143
+        if ($type !== 'jpg' && $type !== 'png') {
144
+            throw new \Exception($this->l->t('Unknown filetype'));
145
+        }
146
+
147
+        if (!$img->valid()) {
148
+            throw new \Exception($this->l->t('Invalid image'));
149
+        }
150
+
151
+        if (!($img->height() === $img->width())) {
152
+            throw new NotSquareException($this->l->t('Avatar image is not square'));
153
+        }
154
+
155
+        $this->remove();
156
+        $file = $this->folder->newFile('avatar.'.$type);
157
+        $file->putContent($data);
158
+
159
+        try {
160
+            $generated = $this->folder->getFile('generated');
161
+            $this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'false');
162
+            $generated->delete();
163
+        } catch (NotFoundException $e) {
164
+            //
165
+        }
166
+        $this->user->triggerChange('avatar', $file);
167
+    }
168
+
169
+    /**
170
+     * remove the users avatar
171
+     * @return void
172
+     */
173
+    public function remove () {
174
+        $avatars = $this->folder->getDirectoryListing();
175
+
176
+        $this->config->setUserValue($this->user->getUID(), 'avatar', 'version',
177
+            (int)$this->config->getUserValue($this->user->getUID(), 'avatar', 'version', 0) + 1);
178
+
179
+        foreach ($avatars as $avatar) {
180
+            $avatar->delete();
181
+        }
182
+        $this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true');
183
+        $this->user->triggerChange('avatar', '');
184
+    }
185
+
186
+    /**
187
+     * @inheritdoc
188
+     */
189
+    public function getFile($size) {
190
+        try {
191
+            $ext = $this->getExtension();
192
+        } catch (NotFoundException $e) {
193
+            $data = $this->generateAvatar($this->user->getDisplayName(), 1024);
194
+            $avatar = $this->folder->newFile('avatar.png');
195
+            $avatar->putContent($data);
196
+            $ext = 'png';
197
+
198
+            $this->folder->newFile('generated');
199
+            $this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true');
200
+        }
201
+
202
+        if ($size === -1) {
203
+            $path = 'avatar.' . $ext;
204
+        } else {
205
+            $path = 'avatar.' . $size . '.' . $ext;
206
+        }
207
+
208
+        try {
209
+            $file = $this->folder->getFile($path);
210
+        } catch (NotFoundException $e) {
211
+            if ($size <= 0) {
212
+                throw new NotFoundException;
213
+            }
214
+
215
+            if ($this->folder->fileExists('generated')) {
216
+                $data = $this->generateAvatar($this->user->getDisplayName(), $size);
217
+
218
+            } else {
219
+                $avatar = new OC_Image();
220
+                /** @var ISimpleFile $file */
221
+                $file = $this->folder->getFile('avatar.' . $ext);
222
+                $avatar->loadFromData($file->getContent());
223
+                $avatar->resize($size);
224
+                $data = $avatar->data();
225
+            }
226
+
227
+            try {
228
+                $file = $this->folder->newFile($path);
229
+                $file->putContent($data);
230
+            } catch (NotPermittedException $e) {
231
+                $this->logger->error('Failed to save avatar for ' . $this->user->getUID());
232
+                throw new NotFoundException();
233
+            }
234
+
235
+        }
236
+
237
+        if($this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', null) === null) {
238
+            $generated = $this->folder->fileExists('generated') ? 'true' : 'false';
239
+            $this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', $generated);
240
+        }
241
+
242
+        return $file;
243
+    }
244
+
245
+    /**
246
+     * Get the extension of the avatar. If there is no avatar throw Exception
247
+     *
248
+     * @return string
249
+     * @throws NotFoundException
250
+     */
251
+    private function getExtension() {
252
+        if ($this->folder->fileExists('avatar.jpg')) {
253
+            return 'jpg';
254
+        } elseif ($this->folder->fileExists('avatar.png')) {
255
+            return 'png';
256
+        }
257
+        throw new NotFoundException;
258
+    }
259
+
260
+    /**
261
+     * @param string $userDisplayName
262
+     * @param int $size
263
+     * @return string
264
+     */
265
+    private function generateAvatar($userDisplayName, $size) {
266
+        $text = strtoupper($userDisplayName[0]);
267
+        $backgroundColor = $this->avatarBackgroundColor($userDisplayName);
268
+
269
+        $im = imagecreatetruecolor($size, $size);
270
+        $background = imagecolorallocate($im, $backgroundColor[0], $backgroundColor[1], $backgroundColor[2]);
271
+        $white = imagecolorallocate($im, 255, 255, 255);
272
+        imagefilledrectangle($im, 0, 0, $size, $size, $background);
273
+
274
+        $font = __DIR__ . '/../../core/fonts/OpenSans-Semibold.woff';
275
+
276
+        $fontSize = $size * 0.4;
277
+        $box = imagettfbbox($fontSize, 0, $font, $text);
278
+
279
+        $x = ($size - ($box[2] - $box[0])) / 2;
280
+        $y = ($size - ($box[1] - $box[7])) / 2;
281
+        $x += 1;
282
+        $y -= $box[7];
283
+        imagettftext($im, $fontSize, 0, $x, $y, $white, $font, $text);
284
+
285
+        ob_start();
286
+        imagepng($im);
287
+        $data = ob_get_contents();
288
+        ob_end_clean();
289
+
290
+        return $data;
291
+    }
292
+
293
+    /**
294
+     * @param int $r
295
+     * @param int $g
296
+     * @param int $b
297
+     * @return double[] Array containing h s l in [0, 1] range
298
+     */
299
+    private function rgbToHsl($r, $g, $b) {
300
+        $r /= 255.0;
301
+        $g /= 255.0;
302
+        $b /= 255.0;
303
+
304
+        $max = max($r, $g, $b);
305
+        $min = min($r, $g, $b);
306
+
307
+
308
+        $h = ($max + $min) / 2.0;
309
+        $l = ($max + $min) / 2.0;
310
+
311
+        if($max === $min) {
312
+            $h = $s = 0; // Achromatic
313
+        } else {
314
+            $d = $max - $min;
315
+            $s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min);
316
+            switch($max) {
317
+                case $r:
318
+                    $h = ($g - $b) / $d + ($g < $b ? 6 : 0);
319
+                    break;
320
+                case $g:
321
+                    $h = ($b - $r) / $d + 2.0;
322
+                    break;
323
+                case $b:
324
+                    $h = ($r - $g) / $d + 4.0;
325
+                    break;
326
+            }
327
+            $h /= 6.0;
328
+        }
329
+        return [$h, $s, $l];
330
+
331
+    }
332
+
333
+    /**
334
+     * @param string $text
335
+     * @return int[] Array containting r g b in the range [0, 255]
336
+     */
337
+    private function avatarBackgroundColor($text) {
338
+        $hash = preg_replace('/[^0-9a-f]+/', '', $text);
339
+
340
+        $hash = md5($hash);
341
+        $hashChars = str_split($hash);
342
+
343
+
344
+        // Init vars
345
+        $result = ['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'];
346
+        $rgb = [0, 0, 0];
347
+        $sat = 0.70;
348
+        $lum = 0.68;
349
+        $modulo = 16;
350
+
351
+
352
+        // Splitting evenly the string
353
+        foreach($hashChars as  $i => $char) {
354
+            $result[$i % $modulo] .= intval($char, 16);
355
+        }
356
+
357
+        // Converting our data into a usable rgb format
358
+        // Start at 1 because 16%3=1 but 15%3=0 and makes the repartition even
359
+        for($count = 1; $count < $modulo; $count++) {
360
+            $rgb[$count%3] += (int)$result[$count];
361
+        }
362
+
363
+        // Reduce values bigger than rgb requirements
364
+        $rgb[0] %= 255;
365
+        $rgb[1] %= 255;
366
+        $rgb[2] %= 255;
367
+
368
+        $hsl = $this->rgbToHsl($rgb[0], $rgb[1], $rgb[2]);
369
+
370
+        // Classic formula to check the brightness for our eye
371
+        // If too bright, lower the sat
372
+        $bright = sqrt(0.299 * ($rgb[0] ** 2) + 0.587 * ($rgb[1] ** 2) + 0.114 * ($rgb[2] ** 2));
373
+        if ($bright >= 200) {
374
+            $sat = 0.60;
375
+        }
376
+
377
+        return $this->hslToRgb($hsl[0], $sat, $lum);
378
+    }
379
+
380
+    /**
381
+     * @param double $h Hue in range [0, 1]
382
+     * @param double $s Saturation in range [0, 1]
383
+     * @param double $l Lightness in range [0, 1]
384
+     * @return int[] Array containing r g b in the range [0, 255]
385
+     */
386
+    private function hslToRgb($h, $s, $l){
387
+        $hue2rgb = function ($p, $q, $t){
388
+            if($t < 0) {
389
+                $t += 1;
390
+            }
391
+            if($t > 1) {
392
+                $t -= 1;
393
+            }
394
+            if($t < 1/6) {
395
+                return $p + ($q - $p) * 6 * $t;
396
+            }
397
+            if($t < 1/2) {
398
+                return $q;
399
+            }
400
+            if($t < 2/3) {
401
+                return $p + ($q - $p) * (2/3 - $t) * 6;
402
+            }
403
+            return $p;
404
+        };
405
+
406
+        if($s === 0){
407
+            $r = $l;
408
+            $g = $l;
409
+            $b = $l; // achromatic
410
+        }else{
411
+            $q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
412
+            $p = 2 * $l - $q;
413
+            $r = $hue2rgb($p, $q, $h + 1/3);
414
+            $g = $hue2rgb($p, $q, $h);
415
+            $b = $hue2rgb($p, $q, $h - 1/3);
416
+        }
417
+
418
+        return array(round($r * 255), round($g * 255), round($b * 255));
419
+    }
420
+
421
+    public function userChanged($feature, $oldValue, $newValue) {
422
+        // We only change the avatar on display name changes
423
+        if ($feature !== 'displayName') {
424
+            return;
425
+        }
426
+
427
+        // If the avatar is not generated (so an uploaded image) we skip this
428
+        if (!$this->folder->fileExists('generated')) {
429
+            return;
430
+        }
431
+
432
+        $this->remove();
433
+    }
434 434
 
435 435
 }
Please login to merge, or discard this patch.
lib/private/legacy/image.php 2 patches
Indentation   +1062 added lines, -1062 removed lines patch added patch discarded remove patch
@@ -43,556 +43,556 @@  discard block
 block discarded – undo
43 43
  * Class for basic image manipulation
44 44
  */
45 45
 class OC_Image implements \OCP\IImage {
46
-	/** @var false|resource */
47
-	protected $resource = false; // tmp resource.
48
-	/** @var int */
49
-	protected $imageType = IMAGETYPE_PNG; // Default to png if file type isn't evident.
50
-	/** @var string */
51
-	protected $mimeType = 'image/png'; // Default to png
52
-	/** @var int */
53
-	protected $bitDepth = 24;
54
-	/** @var null|string */
55
-	protected $filePath = null;
56
-	/** @var finfo */
57
-	private $fileInfo;
58
-	/** @var \OCP\ILogger */
59
-	private $logger;
60
-	/** @var \OCP\IConfig */
61
-	private $config;
62
-	/** @var array */
63
-	private $exif;
46
+    /** @var false|resource */
47
+    protected $resource = false; // tmp resource.
48
+    /** @var int */
49
+    protected $imageType = IMAGETYPE_PNG; // Default to png if file type isn't evident.
50
+    /** @var string */
51
+    protected $mimeType = 'image/png'; // Default to png
52
+    /** @var int */
53
+    protected $bitDepth = 24;
54
+    /** @var null|string */
55
+    protected $filePath = null;
56
+    /** @var finfo */
57
+    private $fileInfo;
58
+    /** @var \OCP\ILogger */
59
+    private $logger;
60
+    /** @var \OCP\IConfig */
61
+    private $config;
62
+    /** @var array */
63
+    private $exif;
64 64
 
65
-	/**
66
-	 * Constructor.
67
-	 *
68
-	 * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by
69
-	 * an imagecreate* function.
70
-	 * @param \OCP\ILogger $logger
71
-	 * @param \OCP\IConfig $config
72
-	 * @throws \InvalidArgumentException in case the $imageRef parameter is not null
73
-	 */
74
-	public function __construct($imageRef = null, \OCP\ILogger $logger = null, \OCP\IConfig $config = null) {
75
-		$this->logger = $logger;
76
-		if ($logger === null) {
77
-			$this->logger = \OC::$server->getLogger();
78
-		}
79
-		$this->config = $config;
80
-		if ($config === null) {
81
-			$this->config = \OC::$server->getConfig();
82
-		}
65
+    /**
66
+     * Constructor.
67
+     *
68
+     * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by
69
+     * an imagecreate* function.
70
+     * @param \OCP\ILogger $logger
71
+     * @param \OCP\IConfig $config
72
+     * @throws \InvalidArgumentException in case the $imageRef parameter is not null
73
+     */
74
+    public function __construct($imageRef = null, \OCP\ILogger $logger = null, \OCP\IConfig $config = null) {
75
+        $this->logger = $logger;
76
+        if ($logger === null) {
77
+            $this->logger = \OC::$server->getLogger();
78
+        }
79
+        $this->config = $config;
80
+        if ($config === null) {
81
+            $this->config = \OC::$server->getConfig();
82
+        }
83 83
 
84
-		if (\OC_Util::fileInfoLoaded()) {
85
-			$this->fileInfo = new finfo(FILEINFO_MIME_TYPE);
86
-		}
84
+        if (\OC_Util::fileInfoLoaded()) {
85
+            $this->fileInfo = new finfo(FILEINFO_MIME_TYPE);
86
+        }
87 87
 
88
-		if ($imageRef !== null) {
89
-			throw new \InvalidArgumentException('The first parameter in the constructor is not supported anymore. Please use any of the load* methods of the image object to load an image.');
90
-		}
91
-	}
88
+        if ($imageRef !== null) {
89
+            throw new \InvalidArgumentException('The first parameter in the constructor is not supported anymore. Please use any of the load* methods of the image object to load an image.');
90
+        }
91
+    }
92 92
 
93
-	/**
94
-	 * Determine whether the object contains an image resource.
95
-	 *
96
-	 * @return bool
97
-	 */
98
-	public function valid() { // apparently you can't name a method 'empty'...
99
-		return is_resource($this->resource);
100
-	}
93
+    /**
94
+     * Determine whether the object contains an image resource.
95
+     *
96
+     * @return bool
97
+     */
98
+    public function valid() { // apparently you can't name a method 'empty'...
99
+        return is_resource($this->resource);
100
+    }
101 101
 
102
-	/**
103
-	 * Returns the MIME type of the image or an empty string if no image is loaded.
104
-	 *
105
-	 * @return string
106
-	 */
107
-	public function mimeType() {
108
-		return $this->valid() ? $this->mimeType : '';
109
-	}
102
+    /**
103
+     * Returns the MIME type of the image or an empty string if no image is loaded.
104
+     *
105
+     * @return string
106
+     */
107
+    public function mimeType() {
108
+        return $this->valid() ? $this->mimeType : '';
109
+    }
110 110
 
111
-	/**
112
-	 * Returns the width of the image or -1 if no image is loaded.
113
-	 *
114
-	 * @return int
115
-	 */
116
-	public function width() {
117
-		return $this->valid() ? imagesx($this->resource) : -1;
118
-	}
111
+    /**
112
+     * Returns the width of the image or -1 if no image is loaded.
113
+     *
114
+     * @return int
115
+     */
116
+    public function width() {
117
+        return $this->valid() ? imagesx($this->resource) : -1;
118
+    }
119 119
 
120
-	/**
121
-	 * Returns the height of the image or -1 if no image is loaded.
122
-	 *
123
-	 * @return int
124
-	 */
125
-	public function height() {
126
-		return $this->valid() ? imagesy($this->resource) : -1;
127
-	}
120
+    /**
121
+     * Returns the height of the image or -1 if no image is loaded.
122
+     *
123
+     * @return int
124
+     */
125
+    public function height() {
126
+        return $this->valid() ? imagesy($this->resource) : -1;
127
+    }
128 128
 
129
-	/**
130
-	 * Returns the width when the image orientation is top-left.
131
-	 *
132
-	 * @return int
133
-	 */
134
-	public function widthTopLeft() {
135
-		$o = $this->getOrientation();
136
-		$this->logger->debug('OC_Image->widthTopLeft() Orientation: ' . $o, array('app' => 'core'));
137
-		switch ($o) {
138
-			case -1:
139
-			case 1:
140
-			case 2: // Not tested
141
-			case 3:
142
-			case 4: // Not tested
143
-				return $this->width();
144
-			case 5: // Not tested
145
-			case 6:
146
-			case 7: // Not tested
147
-			case 8:
148
-				return $this->height();
149
-		}
150
-		return $this->width();
151
-	}
129
+    /**
130
+     * Returns the width when the image orientation is top-left.
131
+     *
132
+     * @return int
133
+     */
134
+    public function widthTopLeft() {
135
+        $o = $this->getOrientation();
136
+        $this->logger->debug('OC_Image->widthTopLeft() Orientation: ' . $o, array('app' => 'core'));
137
+        switch ($o) {
138
+            case -1:
139
+            case 1:
140
+            case 2: // Not tested
141
+            case 3:
142
+            case 4: // Not tested
143
+                return $this->width();
144
+            case 5: // Not tested
145
+            case 6:
146
+            case 7: // Not tested
147
+            case 8:
148
+                return $this->height();
149
+        }
150
+        return $this->width();
151
+    }
152 152
 
153
-	/**
154
-	 * Returns the height when the image orientation is top-left.
155
-	 *
156
-	 * @return int
157
-	 */
158
-	public function heightTopLeft() {
159
-		$o = $this->getOrientation();
160
-		$this->logger->debug('OC_Image->heightTopLeft() Orientation: ' . $o, array('app' => 'core'));
161
-		switch ($o) {
162
-			case -1:
163
-			case 1:
164
-			case 2: // Not tested
165
-			case 3:
166
-			case 4: // Not tested
167
-				return $this->height();
168
-			case 5: // Not tested
169
-			case 6:
170
-			case 7: // Not tested
171
-			case 8:
172
-				return $this->width();
173
-		}
174
-		return $this->height();
175
-	}
153
+    /**
154
+     * Returns the height when the image orientation is top-left.
155
+     *
156
+     * @return int
157
+     */
158
+    public function heightTopLeft() {
159
+        $o = $this->getOrientation();
160
+        $this->logger->debug('OC_Image->heightTopLeft() Orientation: ' . $o, array('app' => 'core'));
161
+        switch ($o) {
162
+            case -1:
163
+            case 1:
164
+            case 2: // Not tested
165
+            case 3:
166
+            case 4: // Not tested
167
+                return $this->height();
168
+            case 5: // Not tested
169
+            case 6:
170
+            case 7: // Not tested
171
+            case 8:
172
+                return $this->width();
173
+        }
174
+        return $this->height();
175
+    }
176 176
 
177
-	/**
178
-	 * Outputs the image.
179
-	 *
180
-	 * @param string $mimeType
181
-	 * @return bool
182
-	 */
183
-	public function show($mimeType = null) {
184
-		if ($mimeType === null) {
185
-			$mimeType = $this->mimeType();
186
-		}
187
-		header('Content-Type: ' . $mimeType);
188
-		return $this->_output(null, $mimeType);
189
-	}
177
+    /**
178
+     * Outputs the image.
179
+     *
180
+     * @param string $mimeType
181
+     * @return bool
182
+     */
183
+    public function show($mimeType = null) {
184
+        if ($mimeType === null) {
185
+            $mimeType = $this->mimeType();
186
+        }
187
+        header('Content-Type: ' . $mimeType);
188
+        return $this->_output(null, $mimeType);
189
+    }
190 190
 
191
-	/**
192
-	 * Saves the image.
193
-	 *
194
-	 * @param string $filePath
195
-	 * @param string $mimeType
196
-	 * @return bool
197
-	 */
191
+    /**
192
+     * Saves the image.
193
+     *
194
+     * @param string $filePath
195
+     * @param string $mimeType
196
+     * @return bool
197
+     */
198 198
 
199
-	public function save($filePath = null, $mimeType = null) {
200
-		if ($mimeType === null) {
201
-			$mimeType = $this->mimeType();
202
-		}
203
-		if ($filePath === null) {
204
-			if ($this->filePath === null) {
205
-				$this->logger->error(__METHOD__ . '(): called with no path.', array('app' => 'core'));
206
-				return false;
207
-			} else {
208
-				$filePath = $this->filePath;
209
-			}
210
-		}
211
-		return $this->_output($filePath, $mimeType);
212
-	}
199
+    public function save($filePath = null, $mimeType = null) {
200
+        if ($mimeType === null) {
201
+            $mimeType = $this->mimeType();
202
+        }
203
+        if ($filePath === null) {
204
+            if ($this->filePath === null) {
205
+                $this->logger->error(__METHOD__ . '(): called with no path.', array('app' => 'core'));
206
+                return false;
207
+            } else {
208
+                $filePath = $this->filePath;
209
+            }
210
+        }
211
+        return $this->_output($filePath, $mimeType);
212
+    }
213 213
 
214
-	/**
215
-	 * Outputs/saves the image.
216
-	 *
217
-	 * @param string $filePath
218
-	 * @param string $mimeType
219
-	 * @return bool
220
-	 * @throws Exception
221
-	 */
222
-	private function _output($filePath = null, $mimeType = null) {
223
-		if ($filePath) {
224
-			if (!file_exists(dirname($filePath))) {
225
-				mkdir(dirname($filePath), 0777, true);
226
-			}
227
-			$isWritable = is_writable(dirname($filePath));
228
-			if (!$isWritable) {
229
-				$this->logger->error(__METHOD__ . '(): Directory \'' . dirname($filePath) . '\' is not writable.', array('app' => 'core'));
230
-				return false;
231
-			} elseif ($isWritable && file_exists($filePath) && !is_writable($filePath)) {
232
-				$this->logger->error(__METHOD__ . '(): File \'' . $filePath . '\' is not writable.', array('app' => 'core'));
233
-				return false;
234
-			}
235
-		}
236
-		if (!$this->valid()) {
237
-			return false;
238
-		}
214
+    /**
215
+     * Outputs/saves the image.
216
+     *
217
+     * @param string $filePath
218
+     * @param string $mimeType
219
+     * @return bool
220
+     * @throws Exception
221
+     */
222
+    private function _output($filePath = null, $mimeType = null) {
223
+        if ($filePath) {
224
+            if (!file_exists(dirname($filePath))) {
225
+                mkdir(dirname($filePath), 0777, true);
226
+            }
227
+            $isWritable = is_writable(dirname($filePath));
228
+            if (!$isWritable) {
229
+                $this->logger->error(__METHOD__ . '(): Directory \'' . dirname($filePath) . '\' is not writable.', array('app' => 'core'));
230
+                return false;
231
+            } elseif ($isWritable && file_exists($filePath) && !is_writable($filePath)) {
232
+                $this->logger->error(__METHOD__ . '(): File \'' . $filePath . '\' is not writable.', array('app' => 'core'));
233
+                return false;
234
+            }
235
+        }
236
+        if (!$this->valid()) {
237
+            return false;
238
+        }
239 239
 
240
-		$imageType = $this->imageType;
241
-		if ($mimeType !== null) {
242
-			switch ($mimeType) {
243
-				case 'image/gif':
244
-					$imageType = IMAGETYPE_GIF;
245
-					break;
246
-				case 'image/jpeg':
247
-					$imageType = IMAGETYPE_JPEG;
248
-					break;
249
-				case 'image/png':
250
-					$imageType = IMAGETYPE_PNG;
251
-					break;
252
-				case 'image/x-xbitmap':
253
-					$imageType = IMAGETYPE_XBM;
254
-					break;
255
-				case 'image/bmp':
256
-				case 'image/x-ms-bmp':
257
-					$imageType = IMAGETYPE_BMP;
258
-					break;
259
-				default:
260
-					throw new Exception('\OC_Image::_output(): "' . $mimeType . '" is not supported when forcing a specific output format');
261
-			}
262
-		}
240
+        $imageType = $this->imageType;
241
+        if ($mimeType !== null) {
242
+            switch ($mimeType) {
243
+                case 'image/gif':
244
+                    $imageType = IMAGETYPE_GIF;
245
+                    break;
246
+                case 'image/jpeg':
247
+                    $imageType = IMAGETYPE_JPEG;
248
+                    break;
249
+                case 'image/png':
250
+                    $imageType = IMAGETYPE_PNG;
251
+                    break;
252
+                case 'image/x-xbitmap':
253
+                    $imageType = IMAGETYPE_XBM;
254
+                    break;
255
+                case 'image/bmp':
256
+                case 'image/x-ms-bmp':
257
+                    $imageType = IMAGETYPE_BMP;
258
+                    break;
259
+                default:
260
+                    throw new Exception('\OC_Image::_output(): "' . $mimeType . '" is not supported when forcing a specific output format');
261
+            }
262
+        }
263 263
 
264
-		switch ($imageType) {
265
-			case IMAGETYPE_GIF:
266
-				$retVal = imagegif($this->resource, $filePath);
267
-				break;
268
-			case IMAGETYPE_JPEG:
269
-				$retVal = imagejpeg($this->resource, $filePath, $this->getJpegQuality());
270
-				break;
271
-			case IMAGETYPE_PNG:
272
-				$retVal = imagepng($this->resource, $filePath);
273
-				break;
274
-			case IMAGETYPE_XBM:
275
-				if (function_exists('imagexbm')) {
276
-					$retVal = imagexbm($this->resource, $filePath);
277
-				} else {
278
-					throw new Exception('\OC_Image::_output(): imagexbm() is not supported.');
279
-				}
264
+        switch ($imageType) {
265
+            case IMAGETYPE_GIF:
266
+                $retVal = imagegif($this->resource, $filePath);
267
+                break;
268
+            case IMAGETYPE_JPEG:
269
+                $retVal = imagejpeg($this->resource, $filePath, $this->getJpegQuality());
270
+                break;
271
+            case IMAGETYPE_PNG:
272
+                $retVal = imagepng($this->resource, $filePath);
273
+                break;
274
+            case IMAGETYPE_XBM:
275
+                if (function_exists('imagexbm')) {
276
+                    $retVal = imagexbm($this->resource, $filePath);
277
+                } else {
278
+                    throw new Exception('\OC_Image::_output(): imagexbm() is not supported.');
279
+                }
280 280
 
281
-				break;
282
-			case IMAGETYPE_WBMP:
283
-				$retVal = imagewbmp($this->resource, $filePath);
284
-				break;
285
-			case IMAGETYPE_BMP:
286
-				$retVal = imagebmp($this->resource, $filePath, $this->bitDepth);
287
-				break;
288
-			default:
289
-				$retVal = imagepng($this->resource, $filePath);
290
-		}
291
-		return $retVal;
292
-	}
281
+                break;
282
+            case IMAGETYPE_WBMP:
283
+                $retVal = imagewbmp($this->resource, $filePath);
284
+                break;
285
+            case IMAGETYPE_BMP:
286
+                $retVal = imagebmp($this->resource, $filePath, $this->bitDepth);
287
+                break;
288
+            default:
289
+                $retVal = imagepng($this->resource, $filePath);
290
+        }
291
+        return $retVal;
292
+    }
293 293
 
294
-	/**
295
-	 * Prints the image when called as $image().
296
-	 */
297
-	public function __invoke() {
298
-		return $this->show();
299
-	}
294
+    /**
295
+     * Prints the image when called as $image().
296
+     */
297
+    public function __invoke() {
298
+        return $this->show();
299
+    }
300 300
 
301
-	/**
302
-	 * @param resource Returns the image resource in any.
303
-	 * @throws \InvalidArgumentException in case the supplied resource does not have the type "gd"
304
-	 */
305
-	public function setResource($resource) {
306
-		if (get_resource_type($resource) === 'gd') {
307
-			$this->resource = $resource;
308
-			return;
309
-		}
310
-		throw new \InvalidArgumentException('Supplied resource is not of type "gd".');
311
-	}
301
+    /**
302
+     * @param resource Returns the image resource in any.
303
+     * @throws \InvalidArgumentException in case the supplied resource does not have the type "gd"
304
+     */
305
+    public function setResource($resource) {
306
+        if (get_resource_type($resource) === 'gd') {
307
+            $this->resource = $resource;
308
+            return;
309
+        }
310
+        throw new \InvalidArgumentException('Supplied resource is not of type "gd".');
311
+    }
312 312
 
313
-	/**
314
-	 * @return resource Returns the image resource in any.
315
-	 */
316
-	public function resource() {
317
-		return $this->resource;
318
-	}
313
+    /**
314
+     * @return resource Returns the image resource in any.
315
+     */
316
+    public function resource() {
317
+        return $this->resource;
318
+    }
319 319
 
320
-	/**
321
-	 * @return string Returns the mimetype of the data. Returns the empty string
322
-	 * if the data is not valid.
323
-	 */
324
-	public function dataMimeType() {
325
-		if (!$this->valid()) {
326
-			return '';
327
-		}
320
+    /**
321
+     * @return string Returns the mimetype of the data. Returns the empty string
322
+     * if the data is not valid.
323
+     */
324
+    public function dataMimeType() {
325
+        if (!$this->valid()) {
326
+            return '';
327
+        }
328 328
 
329
-		switch ($this->mimeType) {
330
-			case 'image/png':
331
-			case 'image/jpeg':
332
-			case 'image/gif':
333
-				return $this->mimeType;
334
-			default:
335
-				return 'image/png';
336
-		}
337
-	}
329
+        switch ($this->mimeType) {
330
+            case 'image/png':
331
+            case 'image/jpeg':
332
+            case 'image/gif':
333
+                return $this->mimeType;
334
+            default:
335
+                return 'image/png';
336
+        }
337
+    }
338 338
 
339
-	/**
340
-	 * @return null|string Returns the raw image data.
341
-	 */
342
-	public function data() {
343
-		if (!$this->valid()) {
344
-			return null;
345
-		}
346
-		ob_start();
347
-		switch ($this->mimeType) {
348
-			case "image/png":
349
-				$res = imagepng($this->resource);
350
-				break;
351
-			case "image/jpeg":
352
-				$quality = $this->getJpegQuality();
353
-				if ($quality !== null) {
354
-					$res = imagejpeg($this->resource, null, $quality);
355
-				} else {
356
-					$res = imagejpeg($this->resource);
357
-				}
358
-				break;
359
-			case "image/gif":
360
-				$res = imagegif($this->resource);
361
-				break;
362
-			default:
363
-				$res = imagepng($this->resource);
364
-				$this->logger->info('OC_Image->data. Could not guess mime-type, defaulting to png', array('app' => 'core'));
365
-				break;
366
-		}
367
-		if (!$res) {
368
-			$this->logger->error('OC_Image->data. Error getting image data.', array('app' => 'core'));
369
-		}
370
-		return ob_get_clean();
371
-	}
339
+    /**
340
+     * @return null|string Returns the raw image data.
341
+     */
342
+    public function data() {
343
+        if (!$this->valid()) {
344
+            return null;
345
+        }
346
+        ob_start();
347
+        switch ($this->mimeType) {
348
+            case "image/png":
349
+                $res = imagepng($this->resource);
350
+                break;
351
+            case "image/jpeg":
352
+                $quality = $this->getJpegQuality();
353
+                if ($quality !== null) {
354
+                    $res = imagejpeg($this->resource, null, $quality);
355
+                } else {
356
+                    $res = imagejpeg($this->resource);
357
+                }
358
+                break;
359
+            case "image/gif":
360
+                $res = imagegif($this->resource);
361
+                break;
362
+            default:
363
+                $res = imagepng($this->resource);
364
+                $this->logger->info('OC_Image->data. Could not guess mime-type, defaulting to png', array('app' => 'core'));
365
+                break;
366
+        }
367
+        if (!$res) {
368
+            $this->logger->error('OC_Image->data. Error getting image data.', array('app' => 'core'));
369
+        }
370
+        return ob_get_clean();
371
+    }
372 372
 
373
-	/**
374
-	 * @return string - base64 encoded, which is suitable for embedding in a VCard.
375
-	 */
376
-	public function __toString() {
377
-		return base64_encode($this->data());
378
-	}
373
+    /**
374
+     * @return string - base64 encoded, which is suitable for embedding in a VCard.
375
+     */
376
+    public function __toString() {
377
+        return base64_encode($this->data());
378
+    }
379 379
 
380
-	/**
381
-	 * @return int|null
382
-	 */
383
-	protected function getJpegQuality() {
384
-		$quality = $this->config->getAppValue('preview', 'jpeg_quality', 90);
385
-		if ($quality !== null) {
386
-			$quality = min(100, max(10, (int) $quality));
387
-		}
388
-		return $quality;
389
-	}
380
+    /**
381
+     * @return int|null
382
+     */
383
+    protected function getJpegQuality() {
384
+        $quality = $this->config->getAppValue('preview', 'jpeg_quality', 90);
385
+        if ($quality !== null) {
386
+            $quality = min(100, max(10, (int) $quality));
387
+        }
388
+        return $quality;
389
+    }
390 390
 
391
-	/**
392
-	 * (I'm open for suggestions on better method name ;)
393
-	 * Get the orientation based on EXIF data.
394
-	 *
395
-	 * @return int The orientation or -1 if no EXIF data is available.
396
-	 */
397
-	public function getOrientation() {
398
-		if ($this->exif !== null) {
399
-			return $this->exif['Orientation'];
400
-		}
391
+    /**
392
+     * (I'm open for suggestions on better method name ;)
393
+     * Get the orientation based on EXIF data.
394
+     *
395
+     * @return int The orientation or -1 if no EXIF data is available.
396
+     */
397
+    public function getOrientation() {
398
+        if ($this->exif !== null) {
399
+            return $this->exif['Orientation'];
400
+        }
401 401
 
402
-		if ($this->imageType !== IMAGETYPE_JPEG) {
403
-			$this->logger->debug('OC_Image->fixOrientation() Image is not a JPEG.', array('app' => 'core'));
404
-			return -1;
405
-		}
406
-		if (!is_callable('exif_read_data')) {
407
-			$this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', array('app' => 'core'));
408
-			return -1;
409
-		}
410
-		if (!$this->valid()) {
411
-			$this->logger->debug('OC_Image->fixOrientation() No image loaded.', array('app' => 'core'));
412
-			return -1;
413
-		}
414
-		if (is_null($this->filePath) || !is_readable($this->filePath)) {
415
-			$this->logger->debug('OC_Image->fixOrientation() No readable file path set.', array('app' => 'core'));
416
-			return -1;
417
-		}
418
-		$exif = @exif_read_data($this->filePath, 'IFD0');
419
-		if (!$exif) {
420
-			return -1;
421
-		}
422
-		if (!isset($exif['Orientation'])) {
423
-			return -1;
424
-		}
425
-		$this->exif = $exif;
426
-		return $exif['Orientation'];
427
-	}
402
+        if ($this->imageType !== IMAGETYPE_JPEG) {
403
+            $this->logger->debug('OC_Image->fixOrientation() Image is not a JPEG.', array('app' => 'core'));
404
+            return -1;
405
+        }
406
+        if (!is_callable('exif_read_data')) {
407
+            $this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', array('app' => 'core'));
408
+            return -1;
409
+        }
410
+        if (!$this->valid()) {
411
+            $this->logger->debug('OC_Image->fixOrientation() No image loaded.', array('app' => 'core'));
412
+            return -1;
413
+        }
414
+        if (is_null($this->filePath) || !is_readable($this->filePath)) {
415
+            $this->logger->debug('OC_Image->fixOrientation() No readable file path set.', array('app' => 'core'));
416
+            return -1;
417
+        }
418
+        $exif = @exif_read_data($this->filePath, 'IFD0');
419
+        if (!$exif) {
420
+            return -1;
421
+        }
422
+        if (!isset($exif['Orientation'])) {
423
+            return -1;
424
+        }
425
+        $this->exif = $exif;
426
+        return $exif['Orientation'];
427
+    }
428 428
 
429
-	public function readExif($data) {
430
-		if (!is_callable('exif_read_data')) {
431
-			$this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', array('app' => 'core'));
432
-			return;
433
-		}
434
-		if (!$this->valid()) {
435
-			$this->logger->debug('OC_Image->fixOrientation() No image loaded.', array('app' => 'core'));
436
-			return;
437
-		}
429
+    public function readExif($data) {
430
+        if (!is_callable('exif_read_data')) {
431
+            $this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', array('app' => 'core'));
432
+            return;
433
+        }
434
+        if (!$this->valid()) {
435
+            $this->logger->debug('OC_Image->fixOrientation() No image loaded.', array('app' => 'core'));
436
+            return;
437
+        }
438 438
 
439
-		$exif = @exif_read_data('data://image/jpeg;base64,' . base64_encode($data));
440
-		if (!$exif) {
441
-			return;
442
-		}
443
-		if (!isset($exif['Orientation'])) {
444
-			return;
445
-		}
446
-		$this->exif = $exif;
447
-	}
439
+        $exif = @exif_read_data('data://image/jpeg;base64,' . base64_encode($data));
440
+        if (!$exif) {
441
+            return;
442
+        }
443
+        if (!isset($exif['Orientation'])) {
444
+            return;
445
+        }
446
+        $this->exif = $exif;
447
+    }
448 448
 
449
-	/**
450
-	 * (I'm open for suggestions on better method name ;)
451
-	 * Fixes orientation based on EXIF data.
452
-	 *
453
-	 * @return bool
454
-	 */
455
-	public function fixOrientation() {
456
-		$o = $this->getOrientation();
457
-		$this->logger->debug('OC_Image->fixOrientation() Orientation: ' . $o, array('app' => 'core'));
458
-		$rotate = 0;
459
-		$flip = false;
460
-		switch ($o) {
461
-			case -1:
462
-				return false; //Nothing to fix
463
-			case 1:
464
-				$rotate = 0;
465
-				break;
466
-			case 2:
467
-				$rotate = 0;
468
-				$flip = true;
469
-				break;
470
-			case 3:
471
-				$rotate = 180;
472
-				break;
473
-			case 4:
474
-				$rotate = 180;
475
-				$flip = true;
476
-				break;
477
-			case 5:
478
-				$rotate = 90;
479
-				$flip = true;
480
-				break;
481
-			case 6:
482
-				$rotate = 270;
483
-				break;
484
-			case 7:
485
-				$rotate = 270;
486
-				$flip = true;
487
-				break;
488
-			case 8:
489
-				$rotate = 90;
490
-				break;
491
-		}
492
-		if($flip && function_exists('imageflip')) {
493
-			imageflip($this->resource, IMG_FLIP_HORIZONTAL);
494
-		}
495
-		if ($rotate) {
496
-			$res = imagerotate($this->resource, $rotate, 0);
497
-			if ($res) {
498
-				if (imagealphablending($res, true)) {
499
-					if (imagesavealpha($res, true)) {
500
-						imagedestroy($this->resource);
501
-						$this->resource = $res;
502
-						return true;
503
-					} else {
504
-						$this->logger->debug('OC_Image->fixOrientation() Error during alpha-saving', array('app' => 'core'));
505
-						return false;
506
-					}
507
-				} else {
508
-					$this->logger->debug('OC_Image->fixOrientation() Error during alpha-blending', array('app' => 'core'));
509
-					return false;
510
-				}
511
-			} else {
512
-				$this->logger->debug('OC_Image->fixOrientation() Error during orientation fixing', array('app' => 'core'));
513
-				return false;
514
-			}
515
-		}
516
-		return false;
517
-	}
449
+    /**
450
+     * (I'm open for suggestions on better method name ;)
451
+     * Fixes orientation based on EXIF data.
452
+     *
453
+     * @return bool
454
+     */
455
+    public function fixOrientation() {
456
+        $o = $this->getOrientation();
457
+        $this->logger->debug('OC_Image->fixOrientation() Orientation: ' . $o, array('app' => 'core'));
458
+        $rotate = 0;
459
+        $flip = false;
460
+        switch ($o) {
461
+            case -1:
462
+                return false; //Nothing to fix
463
+            case 1:
464
+                $rotate = 0;
465
+                break;
466
+            case 2:
467
+                $rotate = 0;
468
+                $flip = true;
469
+                break;
470
+            case 3:
471
+                $rotate = 180;
472
+                break;
473
+            case 4:
474
+                $rotate = 180;
475
+                $flip = true;
476
+                break;
477
+            case 5:
478
+                $rotate = 90;
479
+                $flip = true;
480
+                break;
481
+            case 6:
482
+                $rotate = 270;
483
+                break;
484
+            case 7:
485
+                $rotate = 270;
486
+                $flip = true;
487
+                break;
488
+            case 8:
489
+                $rotate = 90;
490
+                break;
491
+        }
492
+        if($flip && function_exists('imageflip')) {
493
+            imageflip($this->resource, IMG_FLIP_HORIZONTAL);
494
+        }
495
+        if ($rotate) {
496
+            $res = imagerotate($this->resource, $rotate, 0);
497
+            if ($res) {
498
+                if (imagealphablending($res, true)) {
499
+                    if (imagesavealpha($res, true)) {
500
+                        imagedestroy($this->resource);
501
+                        $this->resource = $res;
502
+                        return true;
503
+                    } else {
504
+                        $this->logger->debug('OC_Image->fixOrientation() Error during alpha-saving', array('app' => 'core'));
505
+                        return false;
506
+                    }
507
+                } else {
508
+                    $this->logger->debug('OC_Image->fixOrientation() Error during alpha-blending', array('app' => 'core'));
509
+                    return false;
510
+                }
511
+            } else {
512
+                $this->logger->debug('OC_Image->fixOrientation() Error during orientation fixing', array('app' => 'core'));
513
+                return false;
514
+            }
515
+        }
516
+        return false;
517
+    }
518 518
 
519
-	/**
520
-	 * Loads an image from an open file handle.
521
-	 * It is the responsibility of the caller to position the pointer at the correct place and to close the handle again.
522
-	 *
523
-	 * @param resource $handle
524
-	 * @return resource|false An image resource or false on error
525
-	 */
526
-	public function loadFromFileHandle($handle) {
527
-		$contents = stream_get_contents($handle);
528
-		if ($this->loadFromData($contents)) {
529
-			return $this->resource;
530
-		}
531
-		return false;
532
-	}
519
+    /**
520
+     * Loads an image from an open file handle.
521
+     * It is the responsibility of the caller to position the pointer at the correct place and to close the handle again.
522
+     *
523
+     * @param resource $handle
524
+     * @return resource|false An image resource or false on error
525
+     */
526
+    public function loadFromFileHandle($handle) {
527
+        $contents = stream_get_contents($handle);
528
+        if ($this->loadFromData($contents)) {
529
+            return $this->resource;
530
+        }
531
+        return false;
532
+    }
533 533
 
534
-	/**
535
-	 * Loads an image from a local file.
536
-	 *
537
-	 * @param bool|string $imagePath The path to a local file.
538
-	 * @return bool|resource An image resource or false on error
539
-	 */
540
-	public function loadFromFile($imagePath = false) {
541
-		// exif_imagetype throws "read error!" if file is less than 12 byte
542
-		if (!@is_file($imagePath) || !file_exists($imagePath) || filesize($imagePath) < 12 || !is_readable($imagePath)) {
543
-			return false;
544
-		}
545
-		$iType = exif_imagetype($imagePath);
546
-		switch ($iType) {
547
-			case IMAGETYPE_GIF:
548
-				if (imagetypes() & IMG_GIF) {
549
-					$this->resource = imagecreatefromgif($imagePath);
550
-					// Preserve transparency
551
-					imagealphablending($this->resource, true);
552
-					imagesavealpha($this->resource, true);
553
-				} else {
554
-					$this->logger->debug('OC_Image->loadFromFile, GIF images not supported: ' . $imagePath, array('app' => 'core'));
555
-				}
556
-				break;
557
-			case IMAGETYPE_JPEG:
558
-				if (imagetypes() & IMG_JPG) {
559
-					if (getimagesize($imagePath) !== false) {
560
-						$this->resource = @imagecreatefromjpeg($imagePath);
561
-					} else {
562
-						$this->logger->debug('OC_Image->loadFromFile, JPG image not valid: ' . $imagePath, array('app' => 'core'));
563
-					}
564
-				} else {
565
-					$this->logger->debug('OC_Image->loadFromFile, JPG images not supported: ' . $imagePath, array('app' => 'core'));
566
-				}
567
-				break;
568
-			case IMAGETYPE_PNG:
569
-				if (imagetypes() & IMG_PNG) {
570
-					$this->resource = @imagecreatefrompng($imagePath);
571
-					// Preserve transparency
572
-					imagealphablending($this->resource, true);
573
-					imagesavealpha($this->resource, true);
574
-				} else {
575
-					$this->logger->debug('OC_Image->loadFromFile, PNG images not supported: ' . $imagePath, array('app' => 'core'));
576
-				}
577
-				break;
578
-			case IMAGETYPE_XBM:
579
-				if (imagetypes() & IMG_XPM) {
580
-					$this->resource = @imagecreatefromxbm($imagePath);
581
-				} else {
582
-					$this->logger->debug('OC_Image->loadFromFile, XBM/XPM images not supported: ' . $imagePath, array('app' => 'core'));
583
-				}
584
-				break;
585
-			case IMAGETYPE_WBMP:
586
-				if (imagetypes() & IMG_WBMP) {
587
-					$this->resource = @imagecreatefromwbmp($imagePath);
588
-				} else {
589
-					$this->logger->debug('OC_Image->loadFromFile, WBMP images not supported: ' . $imagePath, array('app' => 'core'));
590
-				}
591
-				break;
592
-			case IMAGETYPE_BMP:
593
-				$this->resource = $this->imagecreatefrombmp($imagePath);
594
-				break;
595
-			/*
534
+    /**
535
+     * Loads an image from a local file.
536
+     *
537
+     * @param bool|string $imagePath The path to a local file.
538
+     * @return bool|resource An image resource or false on error
539
+     */
540
+    public function loadFromFile($imagePath = false) {
541
+        // exif_imagetype throws "read error!" if file is less than 12 byte
542
+        if (!@is_file($imagePath) || !file_exists($imagePath) || filesize($imagePath) < 12 || !is_readable($imagePath)) {
543
+            return false;
544
+        }
545
+        $iType = exif_imagetype($imagePath);
546
+        switch ($iType) {
547
+            case IMAGETYPE_GIF:
548
+                if (imagetypes() & IMG_GIF) {
549
+                    $this->resource = imagecreatefromgif($imagePath);
550
+                    // Preserve transparency
551
+                    imagealphablending($this->resource, true);
552
+                    imagesavealpha($this->resource, true);
553
+                } else {
554
+                    $this->logger->debug('OC_Image->loadFromFile, GIF images not supported: ' . $imagePath, array('app' => 'core'));
555
+                }
556
+                break;
557
+            case IMAGETYPE_JPEG:
558
+                if (imagetypes() & IMG_JPG) {
559
+                    if (getimagesize($imagePath) !== false) {
560
+                        $this->resource = @imagecreatefromjpeg($imagePath);
561
+                    } else {
562
+                        $this->logger->debug('OC_Image->loadFromFile, JPG image not valid: ' . $imagePath, array('app' => 'core'));
563
+                    }
564
+                } else {
565
+                    $this->logger->debug('OC_Image->loadFromFile, JPG images not supported: ' . $imagePath, array('app' => 'core'));
566
+                }
567
+                break;
568
+            case IMAGETYPE_PNG:
569
+                if (imagetypes() & IMG_PNG) {
570
+                    $this->resource = @imagecreatefrompng($imagePath);
571
+                    // Preserve transparency
572
+                    imagealphablending($this->resource, true);
573
+                    imagesavealpha($this->resource, true);
574
+                } else {
575
+                    $this->logger->debug('OC_Image->loadFromFile, PNG images not supported: ' . $imagePath, array('app' => 'core'));
576
+                }
577
+                break;
578
+            case IMAGETYPE_XBM:
579
+                if (imagetypes() & IMG_XPM) {
580
+                    $this->resource = @imagecreatefromxbm($imagePath);
581
+                } else {
582
+                    $this->logger->debug('OC_Image->loadFromFile, XBM/XPM images not supported: ' . $imagePath, array('app' => 'core'));
583
+                }
584
+                break;
585
+            case IMAGETYPE_WBMP:
586
+                if (imagetypes() & IMG_WBMP) {
587
+                    $this->resource = @imagecreatefromwbmp($imagePath);
588
+                } else {
589
+                    $this->logger->debug('OC_Image->loadFromFile, WBMP images not supported: ' . $imagePath, array('app' => 'core'));
590
+                }
591
+                break;
592
+            case IMAGETYPE_BMP:
593
+                $this->resource = $this->imagecreatefrombmp($imagePath);
594
+                break;
595
+            /*
596 596
 			case IMAGETYPE_TIFF_II: // (intel byte order)
597 597
 				break;
598 598
 			case IMAGETYPE_TIFF_MM: // (motorola byte order)
@@ -616,581 +616,581 @@  discard block
 block discarded – undo
616 616
 			case IMAGETYPE_PSD:
617 617
 				break;
618 618
 			*/
619
-			default:
619
+            default:
620 620
 
621
-				// this is mostly file created from encrypted file
622
-				$this->resource = imagecreatefromstring(\OC\Files\Filesystem::file_get_contents(\OC\Files\Filesystem::getLocalPath($imagePath)));
623
-				$iType = IMAGETYPE_PNG;
624
-				$this->logger->debug('OC_Image->loadFromFile, Default', array('app' => 'core'));
625
-				break;
626
-		}
627
-		if ($this->valid()) {
628
-			$this->imageType = $iType;
629
-			$this->mimeType = image_type_to_mime_type($iType);
630
-			$this->filePath = $imagePath;
631
-		}
632
-		return $this->resource;
633
-	}
621
+                // this is mostly file created from encrypted file
622
+                $this->resource = imagecreatefromstring(\OC\Files\Filesystem::file_get_contents(\OC\Files\Filesystem::getLocalPath($imagePath)));
623
+                $iType = IMAGETYPE_PNG;
624
+                $this->logger->debug('OC_Image->loadFromFile, Default', array('app' => 'core'));
625
+                break;
626
+        }
627
+        if ($this->valid()) {
628
+            $this->imageType = $iType;
629
+            $this->mimeType = image_type_to_mime_type($iType);
630
+            $this->filePath = $imagePath;
631
+        }
632
+        return $this->resource;
633
+    }
634 634
 
635
-	/**
636
-	 * Loads an image from a string of data.
637
-	 *
638
-	 * @param string $str A string of image data as read from a file.
639
-	 * @return bool|resource An image resource or false on error
640
-	 */
641
-	public function loadFromData($str) {
642
-		if (is_resource($str)) {
643
-			return false;
644
-		}
645
-		$this->resource = @imagecreatefromstring($str);
646
-		if ($this->fileInfo) {
647
-			$this->mimeType = $this->fileInfo->buffer($str);
648
-		}
649
-		if (is_resource($this->resource)) {
650
-			imagealphablending($this->resource, false);
651
-			imagesavealpha($this->resource, true);
652
-		}
635
+    /**
636
+     * Loads an image from a string of data.
637
+     *
638
+     * @param string $str A string of image data as read from a file.
639
+     * @return bool|resource An image resource or false on error
640
+     */
641
+    public function loadFromData($str) {
642
+        if (is_resource($str)) {
643
+            return false;
644
+        }
645
+        $this->resource = @imagecreatefromstring($str);
646
+        if ($this->fileInfo) {
647
+            $this->mimeType = $this->fileInfo->buffer($str);
648
+        }
649
+        if (is_resource($this->resource)) {
650
+            imagealphablending($this->resource, false);
651
+            imagesavealpha($this->resource, true);
652
+        }
653 653
 
654
-		if (!$this->resource) {
655
-			$this->logger->debug('OC_Image->loadFromFile, could not load', array('app' => 'core'));
656
-			return false;
657
-		}
658
-		return $this->resource;
659
-	}
654
+        if (!$this->resource) {
655
+            $this->logger->debug('OC_Image->loadFromFile, could not load', array('app' => 'core'));
656
+            return false;
657
+        }
658
+        return $this->resource;
659
+    }
660 660
 
661
-	/**
662
-	 * Loads an image from a base64 encoded string.
663
-	 *
664
-	 * @param string $str A string base64 encoded string of image data.
665
-	 * @return bool|resource An image resource or false on error
666
-	 */
667
-	public function loadFromBase64($str) {
668
-		if (!is_string($str)) {
669
-			return false;
670
-		}
671
-		$data = base64_decode($str);
672
-		if ($data) { // try to load from string data
673
-			$this->resource = @imagecreatefromstring($data);
674
-			if ($this->fileInfo) {
675
-				$this->mimeType = $this->fileInfo->buffer($data);
676
-			}
677
-			if (!$this->resource) {
678
-				$this->logger->debug('OC_Image->loadFromBase64, could not load', array('app' => 'core'));
679
-				return false;
680
-			}
681
-			return $this->resource;
682
-		} else {
683
-			return false;
684
-		}
685
-	}
661
+    /**
662
+     * Loads an image from a base64 encoded string.
663
+     *
664
+     * @param string $str A string base64 encoded string of image data.
665
+     * @return bool|resource An image resource or false on error
666
+     */
667
+    public function loadFromBase64($str) {
668
+        if (!is_string($str)) {
669
+            return false;
670
+        }
671
+        $data = base64_decode($str);
672
+        if ($data) { // try to load from string data
673
+            $this->resource = @imagecreatefromstring($data);
674
+            if ($this->fileInfo) {
675
+                $this->mimeType = $this->fileInfo->buffer($data);
676
+            }
677
+            if (!$this->resource) {
678
+                $this->logger->debug('OC_Image->loadFromBase64, could not load', array('app' => 'core'));
679
+                return false;
680
+            }
681
+            return $this->resource;
682
+        } else {
683
+            return false;
684
+        }
685
+    }
686 686
 
687
-	/**
688
-	 * Create a new image from file or URL
689
-	 *
690
-	 * @link http://www.programmierer-forum.de/function-imagecreatefrombmp-laeuft-mit-allen-bitraten-t143137.htm
691
-	 * @version 1.00
692
-	 * @param string $fileName <p>
693
-	 * Path to the BMP image.
694
-	 * </p>
695
-	 * @return bool|resource an image resource identifier on success, <b>FALSE</b> on errors.
696
-	 */
697
-	private function imagecreatefrombmp($fileName) {
698
-		if (!($fh = fopen($fileName, 'rb'))) {
699
-			$this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName, array('app' => 'core'));
700
-			return false;
701
-		}
702
-		// read file header
703
-		$meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14));
704
-		// check for bitmap
705
-		if ($meta['type'] != 19778) {
706
-			fclose($fh);
707
-			$this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName . ' is not a bitmap!', array('app' => 'core'));
708
-			return false;
709
-		}
710
-		// read image header
711
-		$meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40));
712
-		// read additional 16bit header
713
-		if ($meta['bits'] == 16) {
714
-			$meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12));
715
-		}
716
-		// set bytes and padding
717
-		$meta['bytes'] = $meta['bits'] / 8;
718
-		$this->bitDepth = $meta['bits']; //remember the bit depth for the imagebmp call
719
-		$meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4) - floor($meta['width'] * $meta['bytes'] / 4)));
720
-		if ($meta['decal'] == 4) {
721
-			$meta['decal'] = 0;
722
-		}
723
-		// obtain imagesize
724
-		if ($meta['imagesize'] < 1) {
725
-			$meta['imagesize'] = $meta['filesize'] - $meta['offset'];
726
-			// in rare cases filesize is equal to offset so we need to read physical size
727
-			if ($meta['imagesize'] < 1) {
728
-				$meta['imagesize'] = @filesize($fileName) - $meta['offset'];
729
-				if ($meta['imagesize'] < 1) {
730
-					fclose($fh);
731
-					$this->logger->warning('imagecreatefrombmp: Can not obtain file size of ' . $fileName . ' is not a bitmap!', array('app' => 'core'));
732
-					return false;
733
-				}
734
-			}
735
-		}
736
-		// calculate colors
737
-		$meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors'];
738
-		// read color palette
739
-		$palette = array();
740
-		if ($meta['bits'] < 16) {
741
-			$palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4));
742
-			// in rare cases the color value is signed
743
-			if ($palette[1] < 0) {
744
-				foreach ($palette as $i => $color) {
745
-					$palette[$i] = $color + 16777216;
746
-				}
747
-			}
748
-		}
749
-		// create gd image
750
-		$im = imagecreatetruecolor($meta['width'], $meta['height']);
751
-		if ($im == false) {
752
-			fclose($fh);
753
-			$this->logger->warning(
754
-				'imagecreatefrombmp: imagecreatetruecolor failed for file "' . $fileName . '" with dimensions ' . $meta['width'] . 'x' . $meta['height'],
755
-				array('app' => 'core'));
756
-			return false;
757
-		}
687
+    /**
688
+     * Create a new image from file or URL
689
+     *
690
+     * @link http://www.programmierer-forum.de/function-imagecreatefrombmp-laeuft-mit-allen-bitraten-t143137.htm
691
+     * @version 1.00
692
+     * @param string $fileName <p>
693
+     * Path to the BMP image.
694
+     * </p>
695
+     * @return bool|resource an image resource identifier on success, <b>FALSE</b> on errors.
696
+     */
697
+    private function imagecreatefrombmp($fileName) {
698
+        if (!($fh = fopen($fileName, 'rb'))) {
699
+            $this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName, array('app' => 'core'));
700
+            return false;
701
+        }
702
+        // read file header
703
+        $meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14));
704
+        // check for bitmap
705
+        if ($meta['type'] != 19778) {
706
+            fclose($fh);
707
+            $this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName . ' is not a bitmap!', array('app' => 'core'));
708
+            return false;
709
+        }
710
+        // read image header
711
+        $meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40));
712
+        // read additional 16bit header
713
+        if ($meta['bits'] == 16) {
714
+            $meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12));
715
+        }
716
+        // set bytes and padding
717
+        $meta['bytes'] = $meta['bits'] / 8;
718
+        $this->bitDepth = $meta['bits']; //remember the bit depth for the imagebmp call
719
+        $meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4) - floor($meta['width'] * $meta['bytes'] / 4)));
720
+        if ($meta['decal'] == 4) {
721
+            $meta['decal'] = 0;
722
+        }
723
+        // obtain imagesize
724
+        if ($meta['imagesize'] < 1) {
725
+            $meta['imagesize'] = $meta['filesize'] - $meta['offset'];
726
+            // in rare cases filesize is equal to offset so we need to read physical size
727
+            if ($meta['imagesize'] < 1) {
728
+                $meta['imagesize'] = @filesize($fileName) - $meta['offset'];
729
+                if ($meta['imagesize'] < 1) {
730
+                    fclose($fh);
731
+                    $this->logger->warning('imagecreatefrombmp: Can not obtain file size of ' . $fileName . ' is not a bitmap!', array('app' => 'core'));
732
+                    return false;
733
+                }
734
+            }
735
+        }
736
+        // calculate colors
737
+        $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors'];
738
+        // read color palette
739
+        $palette = array();
740
+        if ($meta['bits'] < 16) {
741
+            $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4));
742
+            // in rare cases the color value is signed
743
+            if ($palette[1] < 0) {
744
+                foreach ($palette as $i => $color) {
745
+                    $palette[$i] = $color + 16777216;
746
+                }
747
+            }
748
+        }
749
+        // create gd image
750
+        $im = imagecreatetruecolor($meta['width'], $meta['height']);
751
+        if ($im == false) {
752
+            fclose($fh);
753
+            $this->logger->warning(
754
+                'imagecreatefrombmp: imagecreatetruecolor failed for file "' . $fileName . '" with dimensions ' . $meta['width'] . 'x' . $meta['height'],
755
+                array('app' => 'core'));
756
+            return false;
757
+        }
758 758
 
759
-		$data = fread($fh, $meta['imagesize']);
760
-		$p = 0;
761
-		$vide = chr(0);
762
-		$y = $meta['height'] - 1;
763
-		$error = 'imagecreatefrombmp: ' . $fileName . ' has not enough data!';
764
-		// loop through the image data beginning with the lower left corner
765
-		while ($y >= 0) {
766
-			$x = 0;
767
-			while ($x < $meta['width']) {
768
-				switch ($meta['bits']) {
769
-					case 32:
770
-					case 24:
771
-						if (!($part = substr($data, $p, 3))) {
772
-							$this->logger->warning($error, array('app' => 'core'));
773
-							return $im;
774
-						}
775
-						$color = @unpack('V', $part . $vide);
776
-						break;
777
-					case 16:
778
-						if (!($part = substr($data, $p, 2))) {
779
-							fclose($fh);
780
-							$this->logger->warning($error, array('app' => 'core'));
781
-							return $im;
782
-						}
783
-						$color = @unpack('v', $part);
784
-						$color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3);
785
-						break;
786
-					case 8:
787
-						$color = @unpack('n', $vide . ($data[$p] ?? ''));
788
-						$color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1];
789
-						break;
790
-					case 4:
791
-						$color = @unpack('n', $vide . ($data[floor($p)] ?? ''));
792
-						$color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F;
793
-						$color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1];
794
-						break;
795
-					case 1:
796
-						$color = @unpack('n', $vide . ($data[floor($p)] ?? ''));
797
-						switch (($p * 8) % 8) {
798
-							case 0:
799
-								$color[1] = $color[1] >> 7;
800
-								break;
801
-							case 1:
802
-								$color[1] = ($color[1] & 0x40) >> 6;
803
-								break;
804
-							case 2:
805
-								$color[1] = ($color[1] & 0x20) >> 5;
806
-								break;
807
-							case 3:
808
-								$color[1] = ($color[1] & 0x10) >> 4;
809
-								break;
810
-							case 4:
811
-								$color[1] = ($color[1] & 0x8) >> 3;
812
-								break;
813
-							case 5:
814
-								$color[1] = ($color[1] & 0x4) >> 2;
815
-								break;
816
-							case 6:
817
-								$color[1] = ($color[1] & 0x2) >> 1;
818
-								break;
819
-							case 7:
820
-								$color[1] = ($color[1] & 0x1);
821
-								break;
822
-						}
823
-						$color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1];
824
-						break;
825
-					default:
826
-						fclose($fh);
827
-						$this->logger->warning('imagecreatefrombmp: ' . $fileName . ' has ' . $meta['bits'] . ' bits and this is not supported!', array('app' => 'core'));
828
-						return false;
829
-				}
830
-				imagesetpixel($im, $x, $y, $color[1]);
831
-				$x++;
832
-				$p += $meta['bytes'];
833
-			}
834
-			$y--;
835
-			$p += $meta['decal'];
836
-		}
837
-		fclose($fh);
838
-		return $im;
839
-	}
759
+        $data = fread($fh, $meta['imagesize']);
760
+        $p = 0;
761
+        $vide = chr(0);
762
+        $y = $meta['height'] - 1;
763
+        $error = 'imagecreatefrombmp: ' . $fileName . ' has not enough data!';
764
+        // loop through the image data beginning with the lower left corner
765
+        while ($y >= 0) {
766
+            $x = 0;
767
+            while ($x < $meta['width']) {
768
+                switch ($meta['bits']) {
769
+                    case 32:
770
+                    case 24:
771
+                        if (!($part = substr($data, $p, 3))) {
772
+                            $this->logger->warning($error, array('app' => 'core'));
773
+                            return $im;
774
+                        }
775
+                        $color = @unpack('V', $part . $vide);
776
+                        break;
777
+                    case 16:
778
+                        if (!($part = substr($data, $p, 2))) {
779
+                            fclose($fh);
780
+                            $this->logger->warning($error, array('app' => 'core'));
781
+                            return $im;
782
+                        }
783
+                        $color = @unpack('v', $part);
784
+                        $color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3);
785
+                        break;
786
+                    case 8:
787
+                        $color = @unpack('n', $vide . ($data[$p] ?? ''));
788
+                        $color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1];
789
+                        break;
790
+                    case 4:
791
+                        $color = @unpack('n', $vide . ($data[floor($p)] ?? ''));
792
+                        $color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F;
793
+                        $color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1];
794
+                        break;
795
+                    case 1:
796
+                        $color = @unpack('n', $vide . ($data[floor($p)] ?? ''));
797
+                        switch (($p * 8) % 8) {
798
+                            case 0:
799
+                                $color[1] = $color[1] >> 7;
800
+                                break;
801
+                            case 1:
802
+                                $color[1] = ($color[1] & 0x40) >> 6;
803
+                                break;
804
+                            case 2:
805
+                                $color[1] = ($color[1] & 0x20) >> 5;
806
+                                break;
807
+                            case 3:
808
+                                $color[1] = ($color[1] & 0x10) >> 4;
809
+                                break;
810
+                            case 4:
811
+                                $color[1] = ($color[1] & 0x8) >> 3;
812
+                                break;
813
+                            case 5:
814
+                                $color[1] = ($color[1] & 0x4) >> 2;
815
+                                break;
816
+                            case 6:
817
+                                $color[1] = ($color[1] & 0x2) >> 1;
818
+                                break;
819
+                            case 7:
820
+                                $color[1] = ($color[1] & 0x1);
821
+                                break;
822
+                        }
823
+                        $color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1];
824
+                        break;
825
+                    default:
826
+                        fclose($fh);
827
+                        $this->logger->warning('imagecreatefrombmp: ' . $fileName . ' has ' . $meta['bits'] . ' bits and this is not supported!', array('app' => 'core'));
828
+                        return false;
829
+                }
830
+                imagesetpixel($im, $x, $y, $color[1]);
831
+                $x++;
832
+                $p += $meta['bytes'];
833
+            }
834
+            $y--;
835
+            $p += $meta['decal'];
836
+        }
837
+        fclose($fh);
838
+        return $im;
839
+    }
840 840
 
841
-	/**
842
-	 * Resizes the image preserving ratio.
843
-	 *
844
-	 * @param integer $maxSize The maximum size of either the width or height.
845
-	 * @return bool
846
-	 */
847
-	public function resize($maxSize) {
848
-		if (!$this->valid()) {
849
-			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
850
-			return false;
851
-		}
852
-		$widthOrig = imagesx($this->resource);
853
-		$heightOrig = imagesy($this->resource);
854
-		$ratioOrig = $widthOrig / $heightOrig;
841
+    /**
842
+     * Resizes the image preserving ratio.
843
+     *
844
+     * @param integer $maxSize The maximum size of either the width or height.
845
+     * @return bool
846
+     */
847
+    public function resize($maxSize) {
848
+        if (!$this->valid()) {
849
+            $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
850
+            return false;
851
+        }
852
+        $widthOrig = imagesx($this->resource);
853
+        $heightOrig = imagesy($this->resource);
854
+        $ratioOrig = $widthOrig / $heightOrig;
855 855
 
856
-		if ($ratioOrig > 1) {
857
-			$newHeight = round($maxSize / $ratioOrig);
858
-			$newWidth = $maxSize;
859
-		} else {
860
-			$newWidth = round($maxSize * $ratioOrig);
861
-			$newHeight = $maxSize;
862
-		}
856
+        if ($ratioOrig > 1) {
857
+            $newHeight = round($maxSize / $ratioOrig);
858
+            $newWidth = $maxSize;
859
+        } else {
860
+            $newWidth = round($maxSize * $ratioOrig);
861
+            $newHeight = $maxSize;
862
+        }
863 863
 
864
-		$this->preciseResize((int)round($newWidth), (int)round($newHeight));
865
-		return true;
866
-	}
864
+        $this->preciseResize((int)round($newWidth), (int)round($newHeight));
865
+        return true;
866
+    }
867 867
 
868
-	/**
869
-	 * @param int $width
870
-	 * @param int $height
871
-	 * @return bool
872
-	 */
873
-	public function preciseResize(int $width, int $height): bool {
874
-		if (!$this->valid()) {
875
-			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
876
-			return false;
877
-		}
878
-		$widthOrig = imagesx($this->resource);
879
-		$heightOrig = imagesy($this->resource);
880
-		$process = imagecreatetruecolor($width, $height);
868
+    /**
869
+     * @param int $width
870
+     * @param int $height
871
+     * @return bool
872
+     */
873
+    public function preciseResize(int $width, int $height): bool {
874
+        if (!$this->valid()) {
875
+            $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
876
+            return false;
877
+        }
878
+        $widthOrig = imagesx($this->resource);
879
+        $heightOrig = imagesy($this->resource);
880
+        $process = imagecreatetruecolor($width, $height);
881 881
 
882
-		if ($process == false) {
883
-			$this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core'));
884
-			imagedestroy($process);
885
-			return false;
886
-		}
882
+        if ($process == false) {
883
+            $this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core'));
884
+            imagedestroy($process);
885
+            return false;
886
+        }
887 887
 
888
-		// preserve transparency
889
-		if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
890
-			imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
891
-			imagealphablending($process, false);
892
-			imagesavealpha($process, true);
893
-		}
888
+        // preserve transparency
889
+        if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
890
+            imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
891
+            imagealphablending($process, false);
892
+            imagesavealpha($process, true);
893
+        }
894 894
 
895
-		imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig);
896
-		if ($process == false) {
897
-			$this->logger->error(__METHOD__ . '(): Error re-sampling process image', array('app' => 'core'));
898
-			imagedestroy($process);
899
-			return false;
900
-		}
901
-		imagedestroy($this->resource);
902
-		$this->resource = $process;
903
-		return true;
904
-	}
895
+        imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig);
896
+        if ($process == false) {
897
+            $this->logger->error(__METHOD__ . '(): Error re-sampling process image', array('app' => 'core'));
898
+            imagedestroy($process);
899
+            return false;
900
+        }
901
+        imagedestroy($this->resource);
902
+        $this->resource = $process;
903
+        return true;
904
+    }
905 905
 
906
-	/**
907
-	 * Crops the image to the middle square. If the image is already square it just returns.
908
-	 *
909
-	 * @param int $size maximum size for the result (optional)
910
-	 * @return bool for success or failure
911
-	 */
912
-	public function centerCrop($size = 0) {
913
-		if (!$this->valid()) {
914
-			$this->logger->error('OC_Image->centerCrop, No image loaded', array('app' => 'core'));
915
-			return false;
916
-		}
917
-		$widthOrig = imagesx($this->resource);
918
-		$heightOrig = imagesy($this->resource);
919
-		if ($widthOrig === $heightOrig and $size == 0) {
920
-			return true;
921
-		}
922
-		$ratioOrig = $widthOrig / $heightOrig;
923
-		$width = $height = min($widthOrig, $heightOrig);
906
+    /**
907
+     * Crops the image to the middle square. If the image is already square it just returns.
908
+     *
909
+     * @param int $size maximum size for the result (optional)
910
+     * @return bool for success or failure
911
+     */
912
+    public function centerCrop($size = 0) {
913
+        if (!$this->valid()) {
914
+            $this->logger->error('OC_Image->centerCrop, No image loaded', array('app' => 'core'));
915
+            return false;
916
+        }
917
+        $widthOrig = imagesx($this->resource);
918
+        $heightOrig = imagesy($this->resource);
919
+        if ($widthOrig === $heightOrig and $size == 0) {
920
+            return true;
921
+        }
922
+        $ratioOrig = $widthOrig / $heightOrig;
923
+        $width = $height = min($widthOrig, $heightOrig);
924 924
 
925
-		if ($ratioOrig > 1) {
926
-			$x = ($widthOrig / 2) - ($width / 2);
927
-			$y = 0;
928
-		} else {
929
-			$y = ($heightOrig / 2) - ($height / 2);
930
-			$x = 0;
931
-		}
932
-		if ($size > 0) {
933
-			$targetWidth = $size;
934
-			$targetHeight = $size;
935
-		} else {
936
-			$targetWidth = $width;
937
-			$targetHeight = $height;
938
-		}
939
-		$process = imagecreatetruecolor($targetWidth, $targetHeight);
940
-		if ($process == false) {
941
-			$this->logger->error('OC_Image->centerCrop, Error creating true color image', array('app' => 'core'));
942
-			imagedestroy($process);
943
-			return false;
944
-		}
925
+        if ($ratioOrig > 1) {
926
+            $x = ($widthOrig / 2) - ($width / 2);
927
+            $y = 0;
928
+        } else {
929
+            $y = ($heightOrig / 2) - ($height / 2);
930
+            $x = 0;
931
+        }
932
+        if ($size > 0) {
933
+            $targetWidth = $size;
934
+            $targetHeight = $size;
935
+        } else {
936
+            $targetWidth = $width;
937
+            $targetHeight = $height;
938
+        }
939
+        $process = imagecreatetruecolor($targetWidth, $targetHeight);
940
+        if ($process == false) {
941
+            $this->logger->error('OC_Image->centerCrop, Error creating true color image', array('app' => 'core'));
942
+            imagedestroy($process);
943
+            return false;
944
+        }
945 945
 
946
-		// preserve transparency
947
-		if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
948
-			imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
949
-			imagealphablending($process, false);
950
-			imagesavealpha($process, true);
951
-		}
946
+        // preserve transparency
947
+        if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
948
+            imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
949
+            imagealphablending($process, false);
950
+            imagesavealpha($process, true);
951
+        }
952 952
 
953
-		imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height);
954
-		if ($process == false) {
955
-			$this->logger->error('OC_Image->centerCrop, Error re-sampling process image ' . $width . 'x' . $height, array('app' => 'core'));
956
-			imagedestroy($process);
957
-			return false;
958
-		}
959
-		imagedestroy($this->resource);
960
-		$this->resource = $process;
961
-		return true;
962
-	}
953
+        imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height);
954
+        if ($process == false) {
955
+            $this->logger->error('OC_Image->centerCrop, Error re-sampling process image ' . $width . 'x' . $height, array('app' => 'core'));
956
+            imagedestroy($process);
957
+            return false;
958
+        }
959
+        imagedestroy($this->resource);
960
+        $this->resource = $process;
961
+        return true;
962
+    }
963 963
 
964
-	/**
965
-	 * Crops the image from point $x$y with dimension $wx$h.
966
-	 *
967
-	 * @param int $x Horizontal position
968
-	 * @param int $y Vertical position
969
-	 * @param int $w Width
970
-	 * @param int $h Height
971
-	 * @return bool for success or failure
972
-	 */
973
-	public function crop(int $x, int $y, int $w, int $h): bool {
974
-		if (!$this->valid()) {
975
-			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
976
-			return false;
977
-		}
978
-		$process = imagecreatetruecolor($w, $h);
979
-		if ($process == false) {
980
-			$this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core'));
981
-			imagedestroy($process);
982
-			return false;
983
-		}
964
+    /**
965
+     * Crops the image from point $x$y with dimension $wx$h.
966
+     *
967
+     * @param int $x Horizontal position
968
+     * @param int $y Vertical position
969
+     * @param int $w Width
970
+     * @param int $h Height
971
+     * @return bool for success or failure
972
+     */
973
+    public function crop(int $x, int $y, int $w, int $h): bool {
974
+        if (!$this->valid()) {
975
+            $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
976
+            return false;
977
+        }
978
+        $process = imagecreatetruecolor($w, $h);
979
+        if ($process == false) {
980
+            $this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core'));
981
+            imagedestroy($process);
982
+            return false;
983
+        }
984 984
 
985
-		// preserve transparency
986
-		if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
987
-			imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
988
-			imagealphablending($process, false);
989
-			imagesavealpha($process, true);
990
-		}
985
+        // preserve transparency
986
+        if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
987
+            imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
988
+            imagealphablending($process, false);
989
+            imagesavealpha($process, true);
990
+        }
991 991
 
992
-		imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h);
993
-		if ($process == false) {
994
-			$this->logger->error(__METHOD__ . '(): Error re-sampling process image ' . $w . 'x' . $h, array('app' => 'core'));
995
-			imagedestroy($process);
996
-			return false;
997
-		}
998
-		imagedestroy($this->resource);
999
-		$this->resource = $process;
1000
-		return true;
1001
-	}
992
+        imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h);
993
+        if ($process == false) {
994
+            $this->logger->error(__METHOD__ . '(): Error re-sampling process image ' . $w . 'x' . $h, array('app' => 'core'));
995
+            imagedestroy($process);
996
+            return false;
997
+        }
998
+        imagedestroy($this->resource);
999
+        $this->resource = $process;
1000
+        return true;
1001
+    }
1002 1002
 
1003
-	/**
1004
-	 * Resizes the image to fit within a boundary while preserving ratio.
1005
-	 *
1006
-	 * Warning: Images smaller than $maxWidth x $maxHeight will end up being scaled up
1007
-	 *
1008
-	 * @param integer $maxWidth
1009
-	 * @param integer $maxHeight
1010
-	 * @return bool
1011
-	 */
1012
-	public function fitIn($maxWidth, $maxHeight) {
1013
-		if (!$this->valid()) {
1014
-			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
1015
-			return false;
1016
-		}
1017
-		$widthOrig = imagesx($this->resource);
1018
-		$heightOrig = imagesy($this->resource);
1019
-		$ratio = $widthOrig / $heightOrig;
1003
+    /**
1004
+     * Resizes the image to fit within a boundary while preserving ratio.
1005
+     *
1006
+     * Warning: Images smaller than $maxWidth x $maxHeight will end up being scaled up
1007
+     *
1008
+     * @param integer $maxWidth
1009
+     * @param integer $maxHeight
1010
+     * @return bool
1011
+     */
1012
+    public function fitIn($maxWidth, $maxHeight) {
1013
+        if (!$this->valid()) {
1014
+            $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
1015
+            return false;
1016
+        }
1017
+        $widthOrig = imagesx($this->resource);
1018
+        $heightOrig = imagesy($this->resource);
1019
+        $ratio = $widthOrig / $heightOrig;
1020 1020
 
1021
-		$newWidth = min($maxWidth, $ratio * $maxHeight);
1022
-		$newHeight = min($maxHeight, $maxWidth / $ratio);
1021
+        $newWidth = min($maxWidth, $ratio * $maxHeight);
1022
+        $newHeight = min($maxHeight, $maxWidth / $ratio);
1023 1023
 
1024
-		$this->preciseResize((int)round($newWidth), (int)round($newHeight));
1025
-		return true;
1026
-	}
1024
+        $this->preciseResize((int)round($newWidth), (int)round($newHeight));
1025
+        return true;
1026
+    }
1027 1027
 
1028
-	/**
1029
-	 * Shrinks larger images to fit within specified boundaries while preserving ratio.
1030
-	 *
1031
-	 * @param integer $maxWidth
1032
-	 * @param integer $maxHeight
1033
-	 * @return bool
1034
-	 */
1035
-	public function scaleDownToFit($maxWidth, $maxHeight) {
1036
-		if (!$this->valid()) {
1037
-			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
1038
-			return false;
1039
-		}
1040
-		$widthOrig = imagesx($this->resource);
1041
-		$heightOrig = imagesy($this->resource);
1028
+    /**
1029
+     * Shrinks larger images to fit within specified boundaries while preserving ratio.
1030
+     *
1031
+     * @param integer $maxWidth
1032
+     * @param integer $maxHeight
1033
+     * @return bool
1034
+     */
1035
+    public function scaleDownToFit($maxWidth, $maxHeight) {
1036
+        if (!$this->valid()) {
1037
+            $this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
1038
+            return false;
1039
+        }
1040
+        $widthOrig = imagesx($this->resource);
1041
+        $heightOrig = imagesy($this->resource);
1042 1042
 
1043
-		if ($widthOrig > $maxWidth || $heightOrig > $maxHeight) {
1044
-			return $this->fitIn($maxWidth, $maxHeight);
1045
-		}
1043
+        if ($widthOrig > $maxWidth || $heightOrig > $maxHeight) {
1044
+            return $this->fitIn($maxWidth, $maxHeight);
1045
+        }
1046 1046
 
1047
-		return false;
1048
-	}
1047
+        return false;
1048
+    }
1049 1049
 
1050
-	/**
1051
-	 * Destroys the current image and resets the object
1052
-	 */
1053
-	public function destroy() {
1054
-		if ($this->valid()) {
1055
-			imagedestroy($this->resource);
1056
-		}
1057
-		$this->resource = null;
1058
-	}
1050
+    /**
1051
+     * Destroys the current image and resets the object
1052
+     */
1053
+    public function destroy() {
1054
+        if ($this->valid()) {
1055
+            imagedestroy($this->resource);
1056
+        }
1057
+        $this->resource = null;
1058
+    }
1059 1059
 
1060
-	public function __destruct() {
1061
-		$this->destroy();
1062
-	}
1060
+    public function __destruct() {
1061
+        $this->destroy();
1062
+    }
1063 1063
 }
1064 1064
 
1065 1065
 if (!function_exists('imagebmp')) {
1066
-	/**
1067
-	 * Output a BMP image to either the browser or a file
1068
-	 *
1069
-	 * @link http://www.ugia.cn/wp-data/imagebmp.php
1070
-	 * @author legend <[email protected]>
1071
-	 * @link http://www.programmierer-forum.de/imagebmp-gute-funktion-gefunden-t143716.htm
1072
-	 * @author mgutt <[email protected]>
1073
-	 * @version 1.00
1074
-	 * @param resource $im
1075
-	 * @param string $fileName [optional] <p>The path to save the file to.</p>
1076
-	 * @param int $bit [optional] <p>Bit depth, (default is 24).</p>
1077
-	 * @param int $compression [optional]
1078
-	 * @return bool <b>TRUE</b> on success or <b>FALSE</b> on failure.
1079
-	 */
1080
-	function imagebmp($im, $fileName = '', $bit = 24, $compression = 0) {
1081
-		if (!in_array($bit, array(1, 4, 8, 16, 24, 32))) {
1082
-			$bit = 24;
1083
-		} else if ($bit == 32) {
1084
-			$bit = 24;
1085
-		}
1086
-		$bits = pow(2, $bit);
1087
-		imagetruecolortopalette($im, true, $bits);
1088
-		$width = imagesx($im);
1089
-		$height = imagesy($im);
1090
-		$colorsNum = imagecolorstotal($im);
1091
-		$rgbQuad = '';
1092
-		if ($bit <= 8) {
1093
-			for ($i = 0; $i < $colorsNum; $i++) {
1094
-				$colors = imagecolorsforindex($im, $i);
1095
-				$rgbQuad .= chr($colors['blue']) . chr($colors['green']) . chr($colors['red']) . "\0";
1096
-			}
1097
-			$bmpData = '';
1098
-			if ($compression == 0 || $bit < 8) {
1099
-				$compression = 0;
1100
-				$extra = '';
1101
-				$padding = 4 - ceil($width / (8 / $bit)) % 4;
1102
-				if ($padding % 4 != 0) {
1103
-					$extra = str_repeat("\0", $padding);
1104
-				}
1105
-				for ($j = $height - 1; $j >= 0; $j--) {
1106
-					$i = 0;
1107
-					while ($i < $width) {
1108
-						$bin = 0;
1109
-						$limit = $width - $i < 8 / $bit ? (8 / $bit - $width + $i) * $bit : 0;
1110
-						for ($k = 8 - $bit; $k >= $limit; $k -= $bit) {
1111
-							$index = imagecolorat($im, $i, $j);
1112
-							$bin |= $index << $k;
1113
-							$i++;
1114
-						}
1115
-						$bmpData .= chr($bin);
1116
-					}
1117
-					$bmpData .= $extra;
1118
-				}
1119
-			} // RLE8
1120
-			else if ($compression == 1 && $bit == 8) {
1121
-				for ($j = $height - 1; $j >= 0; $j--) {
1122
-					$lastIndex = "\0";
1123
-					$sameNum = 0;
1124
-					for ($i = 0; $i <= $width; $i++) {
1125
-						$index = imagecolorat($im, $i, $j);
1126
-						if ($index !== $lastIndex || $sameNum > 255) {
1127
-							if ($sameNum != 0) {
1128
-								$bmpData .= chr($sameNum) . chr($lastIndex);
1129
-							}
1130
-							$lastIndex = $index;
1131
-							$sameNum = 1;
1132
-						} else {
1133
-							$sameNum++;
1134
-						}
1135
-					}
1136
-					$bmpData .= "\0\0";
1137
-				}
1138
-				$bmpData .= "\0\1";
1139
-			}
1140
-			$sizeQuad = strlen($rgbQuad);
1141
-			$sizeData = strlen($bmpData);
1142
-		} else {
1143
-			$extra = '';
1144
-			$padding = 4 - ($width * ($bit / 8)) % 4;
1145
-			if ($padding % 4 != 0) {
1146
-				$extra = str_repeat("\0", $padding);
1147
-			}
1148
-			$bmpData = '';
1149
-			for ($j = $height - 1; $j >= 0; $j--) {
1150
-				for ($i = 0; $i < $width; $i++) {
1151
-					$index = imagecolorat($im, $i, $j);
1152
-					$colors = imagecolorsforindex($im, $index);
1153
-					if ($bit == 16) {
1154
-						$bin = 0 << $bit;
1155
-						$bin |= ($colors['red'] >> 3) << 10;
1156
-						$bin |= ($colors['green'] >> 3) << 5;
1157
-						$bin |= $colors['blue'] >> 3;
1158
-						$bmpData .= pack("v", $bin);
1159
-					} else {
1160
-						$bmpData .= pack("c*", $colors['blue'], $colors['green'], $colors['red']);
1161
-					}
1162
-				}
1163
-				$bmpData .= $extra;
1164
-			}
1165
-			$sizeQuad = 0;
1166
-			$sizeData = strlen($bmpData);
1167
-			$colorsNum = 0;
1168
-		}
1169
-		$fileHeader = 'BM' . pack('V3', 54 + $sizeQuad + $sizeData, 0, 54 + $sizeQuad);
1170
-		$infoHeader = pack('V3v2V*', 0x28, $width, $height, 1, $bit, $compression, $sizeData, 0, 0, $colorsNum, 0);
1171
-		if ($fileName != '') {
1172
-			$fp = fopen($fileName, 'wb');
1173
-			fwrite($fp, $fileHeader . $infoHeader . $rgbQuad . $bmpData);
1174
-			fclose($fp);
1175
-			return true;
1176
-		}
1177
-		echo $fileHeader . $infoHeader . $rgbQuad . $bmpData;
1178
-		return true;
1179
-	}
1066
+    /**
1067
+     * Output a BMP image to either the browser or a file
1068
+     *
1069
+     * @link http://www.ugia.cn/wp-data/imagebmp.php
1070
+     * @author legend <[email protected]>
1071
+     * @link http://www.programmierer-forum.de/imagebmp-gute-funktion-gefunden-t143716.htm
1072
+     * @author mgutt <[email protected]>
1073
+     * @version 1.00
1074
+     * @param resource $im
1075
+     * @param string $fileName [optional] <p>The path to save the file to.</p>
1076
+     * @param int $bit [optional] <p>Bit depth, (default is 24).</p>
1077
+     * @param int $compression [optional]
1078
+     * @return bool <b>TRUE</b> on success or <b>FALSE</b> on failure.
1079
+     */
1080
+    function imagebmp($im, $fileName = '', $bit = 24, $compression = 0) {
1081
+        if (!in_array($bit, array(1, 4, 8, 16, 24, 32))) {
1082
+            $bit = 24;
1083
+        } else if ($bit == 32) {
1084
+            $bit = 24;
1085
+        }
1086
+        $bits = pow(2, $bit);
1087
+        imagetruecolortopalette($im, true, $bits);
1088
+        $width = imagesx($im);
1089
+        $height = imagesy($im);
1090
+        $colorsNum = imagecolorstotal($im);
1091
+        $rgbQuad = '';
1092
+        if ($bit <= 8) {
1093
+            for ($i = 0; $i < $colorsNum; $i++) {
1094
+                $colors = imagecolorsforindex($im, $i);
1095
+                $rgbQuad .= chr($colors['blue']) . chr($colors['green']) . chr($colors['red']) . "\0";
1096
+            }
1097
+            $bmpData = '';
1098
+            if ($compression == 0 || $bit < 8) {
1099
+                $compression = 0;
1100
+                $extra = '';
1101
+                $padding = 4 - ceil($width / (8 / $bit)) % 4;
1102
+                if ($padding % 4 != 0) {
1103
+                    $extra = str_repeat("\0", $padding);
1104
+                }
1105
+                for ($j = $height - 1; $j >= 0; $j--) {
1106
+                    $i = 0;
1107
+                    while ($i < $width) {
1108
+                        $bin = 0;
1109
+                        $limit = $width - $i < 8 / $bit ? (8 / $bit - $width + $i) * $bit : 0;
1110
+                        for ($k = 8 - $bit; $k >= $limit; $k -= $bit) {
1111
+                            $index = imagecolorat($im, $i, $j);
1112
+                            $bin |= $index << $k;
1113
+                            $i++;
1114
+                        }
1115
+                        $bmpData .= chr($bin);
1116
+                    }
1117
+                    $bmpData .= $extra;
1118
+                }
1119
+            } // RLE8
1120
+            else if ($compression == 1 && $bit == 8) {
1121
+                for ($j = $height - 1; $j >= 0; $j--) {
1122
+                    $lastIndex = "\0";
1123
+                    $sameNum = 0;
1124
+                    for ($i = 0; $i <= $width; $i++) {
1125
+                        $index = imagecolorat($im, $i, $j);
1126
+                        if ($index !== $lastIndex || $sameNum > 255) {
1127
+                            if ($sameNum != 0) {
1128
+                                $bmpData .= chr($sameNum) . chr($lastIndex);
1129
+                            }
1130
+                            $lastIndex = $index;
1131
+                            $sameNum = 1;
1132
+                        } else {
1133
+                            $sameNum++;
1134
+                        }
1135
+                    }
1136
+                    $bmpData .= "\0\0";
1137
+                }
1138
+                $bmpData .= "\0\1";
1139
+            }
1140
+            $sizeQuad = strlen($rgbQuad);
1141
+            $sizeData = strlen($bmpData);
1142
+        } else {
1143
+            $extra = '';
1144
+            $padding = 4 - ($width * ($bit / 8)) % 4;
1145
+            if ($padding % 4 != 0) {
1146
+                $extra = str_repeat("\0", $padding);
1147
+            }
1148
+            $bmpData = '';
1149
+            for ($j = $height - 1; $j >= 0; $j--) {
1150
+                for ($i = 0; $i < $width; $i++) {
1151
+                    $index = imagecolorat($im, $i, $j);
1152
+                    $colors = imagecolorsforindex($im, $index);
1153
+                    if ($bit == 16) {
1154
+                        $bin = 0 << $bit;
1155
+                        $bin |= ($colors['red'] >> 3) << 10;
1156
+                        $bin |= ($colors['green'] >> 3) << 5;
1157
+                        $bin |= $colors['blue'] >> 3;
1158
+                        $bmpData .= pack("v", $bin);
1159
+                    } else {
1160
+                        $bmpData .= pack("c*", $colors['blue'], $colors['green'], $colors['red']);
1161
+                    }
1162
+                }
1163
+                $bmpData .= $extra;
1164
+            }
1165
+            $sizeQuad = 0;
1166
+            $sizeData = strlen($bmpData);
1167
+            $colorsNum = 0;
1168
+        }
1169
+        $fileHeader = 'BM' . pack('V3', 54 + $sizeQuad + $sizeData, 0, 54 + $sizeQuad);
1170
+        $infoHeader = pack('V3v2V*', 0x28, $width, $height, 1, $bit, $compression, $sizeData, 0, 0, $colorsNum, 0);
1171
+        if ($fileName != '') {
1172
+            $fp = fopen($fileName, 'wb');
1173
+            fwrite($fp, $fileHeader . $infoHeader . $rgbQuad . $bmpData);
1174
+            fclose($fp);
1175
+            return true;
1176
+        }
1177
+        echo $fileHeader . $infoHeader . $rgbQuad . $bmpData;
1178
+        return true;
1179
+    }
1180 1180
 }
1181 1181
 
1182 1182
 if (!function_exists('exif_imagetype')) {
1183
-	/**
1184
-	 * Workaround if exif_imagetype does not exist
1185
-	 *
1186
-	 * @link http://www.php.net/manual/en/function.exif-imagetype.php#80383
1187
-	 * @param string $fileName
1188
-	 * @return string|boolean
1189
-	 */
1190
-	function exif_imagetype($fileName) {
1191
-		if (($info = getimagesize($fileName)) !== false) {
1192
-			return $info[2];
1193
-		}
1194
-		return false;
1195
-	}
1183
+    /**
1184
+     * Workaround if exif_imagetype does not exist
1185
+     *
1186
+     * @link http://www.php.net/manual/en/function.exif-imagetype.php#80383
1187
+     * @param string $fileName
1188
+     * @return string|boolean
1189
+     */
1190
+    function exif_imagetype($fileName) {
1191
+        if (($info = getimagesize($fileName)) !== false) {
1192
+            return $info[2];
1193
+        }
1194
+        return false;
1195
+    }
1196 1196
 }
Please login to merge, or discard this patch.
Spacing   +44 added lines, -44 removed lines patch added patch discarded remove patch
@@ -133,7 +133,7 @@  discard block
 block discarded – undo
133 133
 	 */
134 134
 	public function widthTopLeft() {
135 135
 		$o = $this->getOrientation();
136
-		$this->logger->debug('OC_Image->widthTopLeft() Orientation: ' . $o, array('app' => 'core'));
136
+		$this->logger->debug('OC_Image->widthTopLeft() Orientation: '.$o, array('app' => 'core'));
137 137
 		switch ($o) {
138 138
 			case -1:
139 139
 			case 1:
@@ -157,7 +157,7 @@  discard block
 block discarded – undo
157 157
 	 */
158 158
 	public function heightTopLeft() {
159 159
 		$o = $this->getOrientation();
160
-		$this->logger->debug('OC_Image->heightTopLeft() Orientation: ' . $o, array('app' => 'core'));
160
+		$this->logger->debug('OC_Image->heightTopLeft() Orientation: '.$o, array('app' => 'core'));
161 161
 		switch ($o) {
162 162
 			case -1:
163 163
 			case 1:
@@ -184,7 +184,7 @@  discard block
 block discarded – undo
184 184
 		if ($mimeType === null) {
185 185
 			$mimeType = $this->mimeType();
186 186
 		}
187
-		header('Content-Type: ' . $mimeType);
187
+		header('Content-Type: '.$mimeType);
188 188
 		return $this->_output(null, $mimeType);
189 189
 	}
190 190
 
@@ -202,7 +202,7 @@  discard block
 block discarded – undo
202 202
 		}
203 203
 		if ($filePath === null) {
204 204
 			if ($this->filePath === null) {
205
-				$this->logger->error(__METHOD__ . '(): called with no path.', array('app' => 'core'));
205
+				$this->logger->error(__METHOD__.'(): called with no path.', array('app' => 'core'));
206 206
 				return false;
207 207
 			} else {
208 208
 				$filePath = $this->filePath;
@@ -226,10 +226,10 @@  discard block
 block discarded – undo
226 226
 			}
227 227
 			$isWritable = is_writable(dirname($filePath));
228 228
 			if (!$isWritable) {
229
-				$this->logger->error(__METHOD__ . '(): Directory \'' . dirname($filePath) . '\' is not writable.', array('app' => 'core'));
229
+				$this->logger->error(__METHOD__.'(): Directory \''.dirname($filePath).'\' is not writable.', array('app' => 'core'));
230 230
 				return false;
231 231
 			} elseif ($isWritable && file_exists($filePath) && !is_writable($filePath)) {
232
-				$this->logger->error(__METHOD__ . '(): File \'' . $filePath . '\' is not writable.', array('app' => 'core'));
232
+				$this->logger->error(__METHOD__.'(): File \''.$filePath.'\' is not writable.', array('app' => 'core'));
233 233
 				return false;
234 234
 			}
235 235
 		}
@@ -257,7 +257,7 @@  discard block
 block discarded – undo
257 257
 					$imageType = IMAGETYPE_BMP;
258 258
 					break;
259 259
 				default:
260
-					throw new Exception('\OC_Image::_output(): "' . $mimeType . '" is not supported when forcing a specific output format');
260
+					throw new Exception('\OC_Image::_output(): "'.$mimeType.'" is not supported when forcing a specific output format');
261 261
 			}
262 262
 		}
263 263
 
@@ -436,7 +436,7 @@  discard block
 block discarded – undo
436 436
 			return;
437 437
 		}
438 438
 
439
-		$exif = @exif_read_data('data://image/jpeg;base64,' . base64_encode($data));
439
+		$exif = @exif_read_data('data://image/jpeg;base64,'.base64_encode($data));
440 440
 		if (!$exif) {
441 441
 			return;
442 442
 		}
@@ -454,7 +454,7 @@  discard block
 block discarded – undo
454 454
 	 */
455 455
 	public function fixOrientation() {
456 456
 		$o = $this->getOrientation();
457
-		$this->logger->debug('OC_Image->fixOrientation() Orientation: ' . $o, array('app' => 'core'));
457
+		$this->logger->debug('OC_Image->fixOrientation() Orientation: '.$o, array('app' => 'core'));
458 458
 		$rotate = 0;
459 459
 		$flip = false;
460 460
 		switch ($o) {
@@ -489,7 +489,7 @@  discard block
 block discarded – undo
489 489
 				$rotate = 90;
490 490
 				break;
491 491
 		}
492
-		if($flip && function_exists('imageflip')) {
492
+		if ($flip && function_exists('imageflip')) {
493 493
 			imageflip($this->resource, IMG_FLIP_HORIZONTAL);
494 494
 		}
495 495
 		if ($rotate) {
@@ -551,7 +551,7 @@  discard block
 block discarded – undo
551 551
 					imagealphablending($this->resource, true);
552 552
 					imagesavealpha($this->resource, true);
553 553
 				} else {
554
-					$this->logger->debug('OC_Image->loadFromFile, GIF images not supported: ' . $imagePath, array('app' => 'core'));
554
+					$this->logger->debug('OC_Image->loadFromFile, GIF images not supported: '.$imagePath, array('app' => 'core'));
555 555
 				}
556 556
 				break;
557 557
 			case IMAGETYPE_JPEG:
@@ -559,10 +559,10 @@  discard block
 block discarded – undo
559 559
 					if (getimagesize($imagePath) !== false) {
560 560
 						$this->resource = @imagecreatefromjpeg($imagePath);
561 561
 					} else {
562
-						$this->logger->debug('OC_Image->loadFromFile, JPG image not valid: ' . $imagePath, array('app' => 'core'));
562
+						$this->logger->debug('OC_Image->loadFromFile, JPG image not valid: '.$imagePath, array('app' => 'core'));
563 563
 					}
564 564
 				} else {
565
-					$this->logger->debug('OC_Image->loadFromFile, JPG images not supported: ' . $imagePath, array('app' => 'core'));
565
+					$this->logger->debug('OC_Image->loadFromFile, JPG images not supported: '.$imagePath, array('app' => 'core'));
566 566
 				}
567 567
 				break;
568 568
 			case IMAGETYPE_PNG:
@@ -572,21 +572,21 @@  discard block
 block discarded – undo
572 572
 					imagealphablending($this->resource, true);
573 573
 					imagesavealpha($this->resource, true);
574 574
 				} else {
575
-					$this->logger->debug('OC_Image->loadFromFile, PNG images not supported: ' . $imagePath, array('app' => 'core'));
575
+					$this->logger->debug('OC_Image->loadFromFile, PNG images not supported: '.$imagePath, array('app' => 'core'));
576 576
 				}
577 577
 				break;
578 578
 			case IMAGETYPE_XBM:
579 579
 				if (imagetypes() & IMG_XPM) {
580 580
 					$this->resource = @imagecreatefromxbm($imagePath);
581 581
 				} else {
582
-					$this->logger->debug('OC_Image->loadFromFile, XBM/XPM images not supported: ' . $imagePath, array('app' => 'core'));
582
+					$this->logger->debug('OC_Image->loadFromFile, XBM/XPM images not supported: '.$imagePath, array('app' => 'core'));
583 583
 				}
584 584
 				break;
585 585
 			case IMAGETYPE_WBMP:
586 586
 				if (imagetypes() & IMG_WBMP) {
587 587
 					$this->resource = @imagecreatefromwbmp($imagePath);
588 588
 				} else {
589
-					$this->logger->debug('OC_Image->loadFromFile, WBMP images not supported: ' . $imagePath, array('app' => 'core'));
589
+					$this->logger->debug('OC_Image->loadFromFile, WBMP images not supported: '.$imagePath, array('app' => 'core'));
590 590
 				}
591 591
 				break;
592 592
 			case IMAGETYPE_BMP:
@@ -696,7 +696,7 @@  discard block
 block discarded – undo
696 696
 	 */
697 697
 	private function imagecreatefrombmp($fileName) {
698 698
 		if (!($fh = fopen($fileName, 'rb'))) {
699
-			$this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName, array('app' => 'core'));
699
+			$this->logger->warning('imagecreatefrombmp: Can not open '.$fileName, array('app' => 'core'));
700 700
 			return false;
701 701
 		}
702 702
 		// read file header
@@ -704,7 +704,7 @@  discard block
 block discarded – undo
704 704
 		// check for bitmap
705 705
 		if ($meta['type'] != 19778) {
706 706
 			fclose($fh);
707
-			$this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName . ' is not a bitmap!', array('app' => 'core'));
707
+			$this->logger->warning('imagecreatefrombmp: Can not open '.$fileName.' is not a bitmap!', array('app' => 'core'));
708 708
 			return false;
709 709
 		}
710 710
 		// read image header
@@ -728,7 +728,7 @@  discard block
 block discarded – undo
728 728
 				$meta['imagesize'] = @filesize($fileName) - $meta['offset'];
729 729
 				if ($meta['imagesize'] < 1) {
730 730
 					fclose($fh);
731
-					$this->logger->warning('imagecreatefrombmp: Can not obtain file size of ' . $fileName . ' is not a bitmap!', array('app' => 'core'));
731
+					$this->logger->warning('imagecreatefrombmp: Can not obtain file size of '.$fileName.' is not a bitmap!', array('app' => 'core'));
732 732
 					return false;
733 733
 				}
734 734
 			}
@@ -738,7 +738,7 @@  discard block
 block discarded – undo
738 738
 		// read color palette
739 739
 		$palette = array();
740 740
 		if ($meta['bits'] < 16) {
741
-			$palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4));
741
+			$palette = unpack('l'.$meta['colors'], fread($fh, $meta['colors'] * 4));
742 742
 			// in rare cases the color value is signed
743 743
 			if ($palette[1] < 0) {
744 744
 				foreach ($palette as $i => $color) {
@@ -751,7 +751,7 @@  discard block
 block discarded – undo
751 751
 		if ($im == false) {
752 752
 			fclose($fh);
753 753
 			$this->logger->warning(
754
-				'imagecreatefrombmp: imagecreatetruecolor failed for file "' . $fileName . '" with dimensions ' . $meta['width'] . 'x' . $meta['height'],
754
+				'imagecreatefrombmp: imagecreatetruecolor failed for file "'.$fileName.'" with dimensions '.$meta['width'].'x'.$meta['height'],
755 755
 				array('app' => 'core'));
756 756
 			return false;
757 757
 		}
@@ -760,7 +760,7 @@  discard block
 block discarded – undo
760 760
 		$p = 0;
761 761
 		$vide = chr(0);
762 762
 		$y = $meta['height'] - 1;
763
-		$error = 'imagecreatefrombmp: ' . $fileName . ' has not enough data!';
763
+		$error = 'imagecreatefrombmp: '.$fileName.' has not enough data!';
764 764
 		// loop through the image data beginning with the lower left corner
765 765
 		while ($y >= 0) {
766 766
 			$x = 0;
@@ -772,7 +772,7 @@  discard block
 block discarded – undo
772 772
 							$this->logger->warning($error, array('app' => 'core'));
773 773
 							return $im;
774 774
 						}
775
-						$color = @unpack('V', $part . $vide);
775
+						$color = @unpack('V', $part.$vide);
776 776
 						break;
777 777
 					case 16:
778 778
 						if (!($part = substr($data, $p, 2))) {
@@ -784,16 +784,16 @@  discard block
 block discarded – undo
784 784
 						$color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3);
785 785
 						break;
786 786
 					case 8:
787
-						$color = @unpack('n', $vide . ($data[$p] ?? ''));
787
+						$color = @unpack('n', $vide.($data[$p] ?? ''));
788 788
 						$color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1];
789 789
 						break;
790 790
 					case 4:
791
-						$color = @unpack('n', $vide . ($data[floor($p)] ?? ''));
791
+						$color = @unpack('n', $vide.($data[floor($p)] ?? ''));
792 792
 						$color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F;
793 793
 						$color[1] = (isset($palette[$color[1] + 1])) ? $palette[$color[1] + 1] : $palette[1];
794 794
 						break;
795 795
 					case 1:
796
-						$color = @unpack('n', $vide . ($data[floor($p)] ?? ''));
796
+						$color = @unpack('n', $vide.($data[floor($p)] ?? ''));
797 797
 						switch (($p * 8) % 8) {
798 798
 							case 0:
799 799
 								$color[1] = $color[1] >> 7;
@@ -824,7 +824,7 @@  discard block
 block discarded – undo
824 824
 						break;
825 825
 					default:
826 826
 						fclose($fh);
827
-						$this->logger->warning('imagecreatefrombmp: ' . $fileName . ' has ' . $meta['bits'] . ' bits and this is not supported!', array('app' => 'core'));
827
+						$this->logger->warning('imagecreatefrombmp: '.$fileName.' has '.$meta['bits'].' bits and this is not supported!', array('app' => 'core'));
828 828
 						return false;
829 829
 				}
830 830
 				imagesetpixel($im, $x, $y, $color[1]);
@@ -846,7 +846,7 @@  discard block
 block discarded – undo
846 846
 	 */
847 847
 	public function resize($maxSize) {
848 848
 		if (!$this->valid()) {
849
-			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
849
+			$this->logger->error(__METHOD__.'(): No image loaded', array('app' => 'core'));
850 850
 			return false;
851 851
 		}
852 852
 		$widthOrig = imagesx($this->resource);
@@ -861,7 +861,7 @@  discard block
 block discarded – undo
861 861
 			$newHeight = $maxSize;
862 862
 		}
863 863
 
864
-		$this->preciseResize((int)round($newWidth), (int)round($newHeight));
864
+		$this->preciseResize((int) round($newWidth), (int) round($newHeight));
865 865
 		return true;
866 866
 	}
867 867
 
@@ -872,7 +872,7 @@  discard block
 block discarded – undo
872 872
 	 */
873 873
 	public function preciseResize(int $width, int $height): bool {
874 874
 		if (!$this->valid()) {
875
-			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
875
+			$this->logger->error(__METHOD__.'(): No image loaded', array('app' => 'core'));
876 876
 			return false;
877 877
 		}
878 878
 		$widthOrig = imagesx($this->resource);
@@ -880,7 +880,7 @@  discard block
 block discarded – undo
880 880
 		$process = imagecreatetruecolor($width, $height);
881 881
 
882 882
 		if ($process == false) {
883
-			$this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core'));
883
+			$this->logger->error(__METHOD__.'(): Error creating true color image', array('app' => 'core'));
884 884
 			imagedestroy($process);
885 885
 			return false;
886 886
 		}
@@ -894,7 +894,7 @@  discard block
 block discarded – undo
894 894
 
895 895
 		imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig);
896 896
 		if ($process == false) {
897
-			$this->logger->error(__METHOD__ . '(): Error re-sampling process image', array('app' => 'core'));
897
+			$this->logger->error(__METHOD__.'(): Error re-sampling process image', array('app' => 'core'));
898 898
 			imagedestroy($process);
899 899
 			return false;
900 900
 		}
@@ -952,7 +952,7 @@  discard block
 block discarded – undo
952 952
 
953 953
 		imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height);
954 954
 		if ($process == false) {
955
-			$this->logger->error('OC_Image->centerCrop, Error re-sampling process image ' . $width . 'x' . $height, array('app' => 'core'));
955
+			$this->logger->error('OC_Image->centerCrop, Error re-sampling process image '.$width.'x'.$height, array('app' => 'core'));
956 956
 			imagedestroy($process);
957 957
 			return false;
958 958
 		}
@@ -972,12 +972,12 @@  discard block
 block discarded – undo
972 972
 	 */
973 973
 	public function crop(int $x, int $y, int $w, int $h): bool {
974 974
 		if (!$this->valid()) {
975
-			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
975
+			$this->logger->error(__METHOD__.'(): No image loaded', array('app' => 'core'));
976 976
 			return false;
977 977
 		}
978 978
 		$process = imagecreatetruecolor($w, $h);
979 979
 		if ($process == false) {
980
-			$this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core'));
980
+			$this->logger->error(__METHOD__.'(): Error creating true color image', array('app' => 'core'));
981 981
 			imagedestroy($process);
982 982
 			return false;
983 983
 		}
@@ -991,7 +991,7 @@  discard block
 block discarded – undo
991 991
 
992 992
 		imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h);
993 993
 		if ($process == false) {
994
-			$this->logger->error(__METHOD__ . '(): Error re-sampling process image ' . $w . 'x' . $h, array('app' => 'core'));
994
+			$this->logger->error(__METHOD__.'(): Error re-sampling process image '.$w.'x'.$h, array('app' => 'core'));
995 995
 			imagedestroy($process);
996 996
 			return false;
997 997
 		}
@@ -1011,7 +1011,7 @@  discard block
 block discarded – undo
1011 1011
 	 */
1012 1012
 	public function fitIn($maxWidth, $maxHeight) {
1013 1013
 		if (!$this->valid()) {
1014
-			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
1014
+			$this->logger->error(__METHOD__.'(): No image loaded', array('app' => 'core'));
1015 1015
 			return false;
1016 1016
 		}
1017 1017
 		$widthOrig = imagesx($this->resource);
@@ -1021,7 +1021,7 @@  discard block
 block discarded – undo
1021 1021
 		$newWidth = min($maxWidth, $ratio * $maxHeight);
1022 1022
 		$newHeight = min($maxHeight, $maxWidth / $ratio);
1023 1023
 
1024
-		$this->preciseResize((int)round($newWidth), (int)round($newHeight));
1024
+		$this->preciseResize((int) round($newWidth), (int) round($newHeight));
1025 1025
 		return true;
1026 1026
 	}
1027 1027
 
@@ -1034,7 +1034,7 @@  discard block
 block discarded – undo
1034 1034
 	 */
1035 1035
 	public function scaleDownToFit($maxWidth, $maxHeight) {
1036 1036
 		if (!$this->valid()) {
1037
-			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
1037
+			$this->logger->error(__METHOD__.'(): No image loaded', array('app' => 'core'));
1038 1038
 			return false;
1039 1039
 		}
1040 1040
 		$widthOrig = imagesx($this->resource);
@@ -1092,7 +1092,7 @@  discard block
 block discarded – undo
1092 1092
 		if ($bit <= 8) {
1093 1093
 			for ($i = 0; $i < $colorsNum; $i++) {
1094 1094
 				$colors = imagecolorsforindex($im, $i);
1095
-				$rgbQuad .= chr($colors['blue']) . chr($colors['green']) . chr($colors['red']) . "\0";
1095
+				$rgbQuad .= chr($colors['blue']).chr($colors['green']).chr($colors['red'])."\0";
1096 1096
 			}
1097 1097
 			$bmpData = '';
1098 1098
 			if ($compression == 0 || $bit < 8) {
@@ -1125,7 +1125,7 @@  discard block
 block discarded – undo
1125 1125
 						$index = imagecolorat($im, $i, $j);
1126 1126
 						if ($index !== $lastIndex || $sameNum > 255) {
1127 1127
 							if ($sameNum != 0) {
1128
-								$bmpData .= chr($sameNum) . chr($lastIndex);
1128
+								$bmpData .= chr($sameNum).chr($lastIndex);
1129 1129
 							}
1130 1130
 							$lastIndex = $index;
1131 1131
 							$sameNum = 1;
@@ -1166,15 +1166,15 @@  discard block
 block discarded – undo
1166 1166
 			$sizeData = strlen($bmpData);
1167 1167
 			$colorsNum = 0;
1168 1168
 		}
1169
-		$fileHeader = 'BM' . pack('V3', 54 + $sizeQuad + $sizeData, 0, 54 + $sizeQuad);
1169
+		$fileHeader = 'BM'.pack('V3', 54 + $sizeQuad + $sizeData, 0, 54 + $sizeQuad);
1170 1170
 		$infoHeader = pack('V3v2V*', 0x28, $width, $height, 1, $bit, $compression, $sizeData, 0, 0, $colorsNum, 0);
1171 1171
 		if ($fileName != '') {
1172 1172
 			$fp = fopen($fileName, 'wb');
1173
-			fwrite($fp, $fileHeader . $infoHeader . $rgbQuad . $bmpData);
1173
+			fwrite($fp, $fileHeader.$infoHeader.$rgbQuad.$bmpData);
1174 1174
 			fclose($fp);
1175 1175
 			return true;
1176 1176
 		}
1177
-		echo $fileHeader . $infoHeader . $rgbQuad . $bmpData;
1177
+		echo $fileHeader.$infoHeader.$rgbQuad.$bmpData;
1178 1178
 		return true;
1179 1179
 	}
1180 1180
 }
Please login to merge, or discard this patch.
lib/private/Files/Storage/DAV.php 2 patches
Indentation   +800 added lines, -800 removed lines patch added patch discarded remove patch
@@ -57,805 +57,805 @@
 block discarded – undo
57 57
  * @package OC\Files\Storage
58 58
  */
59 59
 class DAV extends Common {
60
-	/** @var string */
61
-	protected $password;
62
-	/** @var string */
63
-	protected $user;
64
-	/** @var string */
65
-	protected $authType;
66
-	/** @var string */
67
-	protected $host;
68
-	/** @var bool */
69
-	protected $secure;
70
-	/** @var string */
71
-	protected $root;
72
-	/** @var string */
73
-	protected $certPath;
74
-	/** @var bool */
75
-	protected $ready;
76
-	/** @var Client */
77
-	protected $client;
78
-	/** @var ArrayCache */
79
-	protected $statCache;
80
-	/** @var \OCP\Http\Client\IClientService */
81
-	protected $httpClientService;
82
-
83
-	/**
84
-	 * @param array $params
85
-	 * @throws \Exception
86
-	 */
87
-	public function __construct($params) {
88
-		$this->statCache = new ArrayCache();
89
-		$this->httpClientService = \OC::$server->getHTTPClientService();
90
-		if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
91
-			$host = $params['host'];
92
-			//remove leading http[s], will be generated in createBaseUri()
93
-			if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
94
-			else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
95
-			$this->host = $host;
96
-			$this->user = $params['user'];
97
-			$this->password = $params['password'];
98
-			if (isset($params['authType'])) {
99
-				$this->authType = $params['authType'];
100
-			}
101
-			if (isset($params['secure'])) {
102
-				if (is_string($params['secure'])) {
103
-					$this->secure = ($params['secure'] === 'true');
104
-				} else {
105
-					$this->secure = (bool)$params['secure'];
106
-				}
107
-			} else {
108
-				$this->secure = false;
109
-			}
110
-			if ($this->secure === true) {
111
-				// inject mock for testing
112
-				$certManager = \OC::$server->getCertificateManager();
113
-				if (is_null($certManager)) { //no user
114
-					$certManager = \OC::$server->getCertificateManager(null);
115
-				}
116
-				$certPath = $certManager->getAbsoluteBundlePath();
117
-				if (file_exists($certPath)) {
118
-					$this->certPath = $certPath;
119
-				}
120
-			}
121
-			$this->root = isset($params['root']) ? $params['root'] : '/';
122
-			if (!$this->root || $this->root[0] != '/') {
123
-				$this->root = '/' . $this->root;
124
-			}
125
-			if ($this->root[strlen($this->root) - 1] != '/') {
126
-				$this->root .= '/';
127
-			}
128
-		} else {
129
-			throw new \Exception('Invalid webdav storage configuration');
130
-		}
131
-	}
132
-
133
-	protected function init() {
134
-		if ($this->ready) {
135
-			return;
136
-		}
137
-		$this->ready = true;
138
-
139
-		$settings = [
140
-			'baseUri' => $this->createBaseUri(),
141
-			'userName' => $this->user,
142
-			'password' => $this->password,
143
-		];
144
-		if (isset($this->authType)) {
145
-			$settings['authType'] = $this->authType;
146
-		}
147
-
148
-		$proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
149
-		if ($proxy !== '') {
150
-			$settings['proxy'] = $proxy;
151
-		}
152
-
153
-		$this->client = new Client($settings);
154
-		$this->client->setThrowExceptions(true);
155
-		if ($this->secure === true && $this->certPath) {
156
-			$this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
157
-		}
158
-	}
159
-
160
-	/**
161
-	 * Clear the stat cache
162
-	 */
163
-	public function clearStatCache() {
164
-		$this->statCache->clear();
165
-	}
166
-
167
-	/** {@inheritdoc} */
168
-	public function getId() {
169
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
170
-	}
171
-
172
-	/** {@inheritdoc} */
173
-	public function createBaseUri() {
174
-		$baseUri = 'http';
175
-		if ($this->secure) {
176
-			$baseUri .= 's';
177
-		}
178
-		$baseUri .= '://' . $this->host . $this->root;
179
-		return $baseUri;
180
-	}
181
-
182
-	/** {@inheritdoc} */
183
-	public function mkdir($path) {
184
-		$this->init();
185
-		$path = $this->cleanPath($path);
186
-		$result = $this->simpleResponse('MKCOL', $path, null, 201);
187
-		if ($result) {
188
-			$this->statCache->set($path, true);
189
-		}
190
-		return $result;
191
-	}
192
-
193
-	/** {@inheritdoc} */
194
-	public function rmdir($path) {
195
-		$this->init();
196
-		$path = $this->cleanPath($path);
197
-		// FIXME: some WebDAV impl return 403 when trying to DELETE
198
-		// a non-empty folder
199
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
200
-		$this->statCache->clear($path . '/');
201
-		$this->statCache->remove($path);
202
-		return $result;
203
-	}
204
-
205
-	/** {@inheritdoc} */
206
-	public function opendir($path) {
207
-		$this->init();
208
-		$path = $this->cleanPath($path);
209
-		try {
210
-			$response = $this->client->propFind(
211
-				$this->encodePath($path),
212
-				['{DAV:}href'],
213
-				1
214
-			);
215
-			if ($response === false) {
216
-				return false;
217
-			}
218
-			$content = [];
219
-			$files = array_keys($response);
220
-			array_shift($files); //the first entry is the current directory
221
-
222
-			if (!$this->statCache->hasKey($path)) {
223
-				$this->statCache->set($path, true);
224
-			}
225
-			foreach ($files as $file) {
226
-				$file = urldecode($file);
227
-				// do not store the real entry, we might not have all properties
228
-				if (!$this->statCache->hasKey($path)) {
229
-					$this->statCache->set($file, true);
230
-				}
231
-				$file = basename($file);
232
-				$content[] = $file;
233
-			}
234
-			return IteratorDirectory::wrap($content);
235
-		} catch (\Exception $e) {
236
-			$this->convertException($e, $path);
237
-		}
238
-		return false;
239
-	}
240
-
241
-	/**
242
-	 * Propfind call with cache handling.
243
-	 *
244
-	 * First checks if information is cached.
245
-	 * If not, request it from the server then store to cache.
246
-	 *
247
-	 * @param string $path path to propfind
248
-	 *
249
-	 * @return array|boolean propfind response or false if the entry was not found
250
-	 *
251
-	 * @throws ClientHttpException
252
-	 */
253
-	protected function propfind($path) {
254
-		$path = $this->cleanPath($path);
255
-		$cachedResponse = $this->statCache->get($path);
256
-		// we either don't know it, or we know it exists but need more details
257
-		if (is_null($cachedResponse) || $cachedResponse === true) {
258
-			$this->init();
259
-			try {
260
-				$response = $this->client->propFind(
261
-					$this->encodePath($path),
262
-					array(
263
-						'{DAV:}getlastmodified',
264
-						'{DAV:}getcontentlength',
265
-						'{DAV:}getcontenttype',
266
-						'{http://owncloud.org/ns}permissions',
267
-						'{http://open-collaboration-services.org/ns}share-permissions',
268
-						'{DAV:}resourcetype',
269
-						'{DAV:}getetag',
270
-					)
271
-				);
272
-				$this->statCache->set($path, $response);
273
-			} catch (ClientHttpException $e) {
274
-				if ($e->getHttpStatus() === 404) {
275
-					$this->statCache->clear($path . '/');
276
-					$this->statCache->set($path, false);
277
-					return false;
278
-				}
279
-				$this->convertException($e, $path);
280
-			} catch (\Exception $e) {
281
-				$this->convertException($e, $path);
282
-			}
283
-		} else {
284
-			$response = $cachedResponse;
285
-		}
286
-		return $response;
287
-	}
288
-
289
-	/** {@inheritdoc} */
290
-	public function filetype($path) {
291
-		try {
292
-			$response = $this->propfind($path);
293
-			if ($response === false) {
294
-				return false;
295
-			}
296
-			$responseType = [];
297
-			if (isset($response["{DAV:}resourcetype"])) {
298
-				/** @var ResourceType[] $response */
299
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
300
-			}
301
-			return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
302
-		} catch (\Exception $e) {
303
-			$this->convertException($e, $path);
304
-		}
305
-		return false;
306
-	}
307
-
308
-	/** {@inheritdoc} */
309
-	public function file_exists($path) {
310
-		try {
311
-			$path = $this->cleanPath($path);
312
-			$cachedState = $this->statCache->get($path);
313
-			if ($cachedState === false) {
314
-				// we know the file doesn't exist
315
-				return false;
316
-			} else if (!is_null($cachedState)) {
317
-				return true;
318
-			}
319
-			// need to get from server
320
-			return ($this->propfind($path) !== false);
321
-		} catch (\Exception $e) {
322
-			$this->convertException($e, $path);
323
-		}
324
-		return false;
325
-	}
326
-
327
-	/** {@inheritdoc} */
328
-	public function unlink($path) {
329
-		$this->init();
330
-		$path = $this->cleanPath($path);
331
-		$result = $this->simpleResponse('DELETE', $path, null, 204);
332
-		$this->statCache->clear($path . '/');
333
-		$this->statCache->remove($path);
334
-		return $result;
335
-	}
336
-
337
-	/** {@inheritdoc} */
338
-	public function fopen($path, $mode) {
339
-		$this->init();
340
-		$path = $this->cleanPath($path);
341
-		switch ($mode) {
342
-			case 'r':
343
-			case 'rb':
344
-				try {
345
-					$response = $this->httpClientService
346
-						->newClient()
347
-						->get($this->createBaseUri() . $this->encodePath($path), [
348
-							'auth' => [$this->user, $this->password],
349
-							'stream' => true
350
-						]);
351
-				} catch (RequestException $e) {
352
-					if ($e->getResponse() instanceof ResponseInterface
353
-						&& $e->getResponse()->getStatusCode() === 404) {
354
-						return false;
355
-					} else {
356
-						throw $e;
357
-					}
358
-				}
359
-
360
-				if ($response->getStatusCode() !== Http::STATUS_OK) {
361
-					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
362
-						throw new \OCP\Lock\LockedException($path);
363
-					} else {
364
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
365
-					}
366
-				}
367
-
368
-				return $response->getBody();
369
-			case 'w':
370
-			case 'wb':
371
-			case 'a':
372
-			case 'ab':
373
-			case 'r+':
374
-			case 'w+':
375
-			case 'wb+':
376
-			case 'a+':
377
-			case 'x':
378
-			case 'x+':
379
-			case 'c':
380
-			case 'c+':
381
-				//emulate these
382
-				$tempManager = \OC::$server->getTempManager();
383
-				if (strrpos($path, '.') !== false) {
384
-					$ext = substr($path, strrpos($path, '.'));
385
-				} else {
386
-					$ext = '';
387
-				}
388
-				if ($this->file_exists($path)) {
389
-					if (!$this->isUpdatable($path)) {
390
-						return false;
391
-					}
392
-					if ($mode === 'w' or $mode === 'w+') {
393
-						$tmpFile = $tempManager->getTemporaryFile($ext);
394
-					} else {
395
-						$tmpFile = $this->getCachedFile($path);
396
-					}
397
-				} else {
398
-					if (!$this->isCreatable(dirname($path))) {
399
-						return false;
400
-					}
401
-					$tmpFile = $tempManager->getTemporaryFile($ext);
402
-				}
403
-				$handle = fopen($tmpFile, $mode);
404
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
405
-					$this->writeBack($tmpFile, $path);
406
-				});
407
-		}
408
-	}
409
-
410
-	/**
411
-	 * @param string $tmpFile
412
-	 */
413
-	public function writeBack($tmpFile, $path) {
414
-		$this->uploadFile($tmpFile, $path);
415
-		unlink($tmpFile);
416
-	}
417
-
418
-	/** {@inheritdoc} */
419
-	public function free_space($path) {
420
-		$this->init();
421
-		$path = $this->cleanPath($path);
422
-		try {
423
-			// TODO: cacheable ?
424
-			$response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
425
-			if ($response === false) {
426
-				return FileInfo::SPACE_UNKNOWN;
427
-			}
428
-			if (isset($response['{DAV:}quota-available-bytes'])) {
429
-				return (int)$response['{DAV:}quota-available-bytes'];
430
-			} else {
431
-				return FileInfo::SPACE_UNKNOWN;
432
-			}
433
-		} catch (\Exception $e) {
434
-			return FileInfo::SPACE_UNKNOWN;
435
-		}
436
-	}
437
-
438
-	/** {@inheritdoc} */
439
-	public function touch($path, $mtime = null) {
440
-		$this->init();
441
-		if (is_null($mtime)) {
442
-			$mtime = time();
443
-		}
444
-		$path = $this->cleanPath($path);
445
-
446
-		// if file exists, update the mtime, else create a new empty file
447
-		if ($this->file_exists($path)) {
448
-			try {
449
-				$this->statCache->remove($path);
450
-				$this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
451
-				// non-owncloud clients might not have accepted the property, need to recheck it
452
-				$response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
453
-				if ($response === false) {
454
-					return false;
455
-				}
456
-				if (isset($response['{DAV:}getlastmodified'])) {
457
-					$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
458
-					if ($remoteMtime !== $mtime) {
459
-						// server has not accepted the mtime
460
-						return false;
461
-					}
462
-				}
463
-			} catch (ClientHttpException $e) {
464
-				if ($e->getHttpStatus() === 501) {
465
-					return false;
466
-				}
467
-				$this->convertException($e, $path);
468
-				return false;
469
-			} catch (\Exception $e) {
470
-				$this->convertException($e, $path);
471
-				return false;
472
-			}
473
-		} else {
474
-			$this->file_put_contents($path, '');
475
-		}
476
-		return true;
477
-	}
478
-
479
-	/**
480
-	 * @param string $path
481
-	 * @param string $data
482
-	 * @return int
483
-	 */
484
-	public function file_put_contents($path, $data) {
485
-		$path = $this->cleanPath($path);
486
-		$result = parent::file_put_contents($path, $data);
487
-		$this->statCache->remove($path);
488
-		return $result;
489
-	}
490
-
491
-	/**
492
-	 * @param string $path
493
-	 * @param string $target
494
-	 */
495
-	protected function uploadFile($path, $target) {
496
-		$this->init();
497
-
498
-		// invalidate
499
-		$target = $this->cleanPath($target);
500
-		$this->statCache->remove($target);
501
-		$source = fopen($path, 'r');
502
-
503
-		$this->httpClientService
504
-			->newClient()
505
-			->put($this->createBaseUri() . $this->encodePath($target), [
506
-				'body' => $source,
507
-				'auth' => [$this->user, $this->password]
508
-			]);
509
-
510
-		$this->removeCachedFile($target);
511
-	}
512
-
513
-	/** {@inheritdoc} */
514
-	public function rename($path1, $path2) {
515
-		$this->init();
516
-		$path1 = $this->cleanPath($path1);
517
-		$path2 = $this->cleanPath($path2);
518
-		try {
519
-			// overwrite directory ?
520
-			if ($this->is_dir($path2)) {
521
-				// needs trailing slash in destination
522
-				$path2 = rtrim($path2, '/') . '/';
523
-			}
524
-			$this->client->request(
525
-				'MOVE',
526
-				$this->encodePath($path1),
527
-				null,
528
-				[
529
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
530
-				]
531
-			);
532
-			$this->statCache->clear($path1 . '/');
533
-			$this->statCache->clear($path2 . '/');
534
-			$this->statCache->set($path1, false);
535
-			$this->statCache->set($path2, true);
536
-			$this->removeCachedFile($path1);
537
-			$this->removeCachedFile($path2);
538
-			return true;
539
-		} catch (\Exception $e) {
540
-			$this->convertException($e);
541
-		}
542
-		return false;
543
-	}
544
-
545
-	/** {@inheritdoc} */
546
-	public function copy($path1, $path2) {
547
-		$this->init();
548
-		$path1 = $this->cleanPath($path1);
549
-		$path2 = $this->cleanPath($path2);
550
-		try {
551
-			// overwrite directory ?
552
-			if ($this->is_dir($path2)) {
553
-				// needs trailing slash in destination
554
-				$path2 = rtrim($path2, '/') . '/';
555
-			}
556
-			$this->client->request(
557
-				'COPY',
558
-				$this->encodePath($path1),
559
-				null,
560
-				[
561
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
562
-				]
563
-			);
564
-			$this->statCache->clear($path2 . '/');
565
-			$this->statCache->set($path2, true);
566
-			$this->removeCachedFile($path2);
567
-			return true;
568
-		} catch (\Exception $e) {
569
-			$this->convertException($e);
570
-		}
571
-		return false;
572
-	}
573
-
574
-	/** {@inheritdoc} */
575
-	public function stat($path) {
576
-		try {
577
-			$response = $this->propfind($path);
578
-			if (!$response) {
579
-				return false;
580
-			}
581
-			return [
582
-				'mtime' => strtotime($response['{DAV:}getlastmodified']),
583
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
584
-			];
585
-		} catch (\Exception $e) {
586
-			$this->convertException($e, $path);
587
-		}
588
-		return array();
589
-	}
590
-
591
-	/** {@inheritdoc} */
592
-	public function getMimeType($path) {
593
-		$remoteMimetype = $this->getMimeTypeFromRemote($path);
594
-		if ($remoteMimetype === 'application/octet-stream') {
595
-			return \OC::$server->getMimeTypeDetector()->detectPath($path);
596
-		} else {
597
-			return $remoteMimetype;
598
-		}
599
-	}
600
-
601
-	public function getMimeTypeFromRemote($path) {
602
-		try {
603
-			$response = $this->propfind($path);
604
-			if ($response === false) {
605
-				return false;
606
-			}
607
-			$responseType = [];
608
-			if (isset($response["{DAV:}resourcetype"])) {
609
-				/** @var ResourceType[] $response */
610
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
611
-			}
612
-			$type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
613
-			if ($type == 'dir') {
614
-				return 'httpd/unix-directory';
615
-			} elseif (isset($response['{DAV:}getcontenttype'])) {
616
-				return $response['{DAV:}getcontenttype'];
617
-			} else {
618
-				return 'application/octet-stream';
619
-			}
620
-		} catch (\Exception $e) {
621
-			return false;
622
-		}
623
-	}
624
-
625
-	/**
626
-	 * @param string $path
627
-	 * @return string
628
-	 */
629
-	public function cleanPath($path) {
630
-		if ($path === '') {
631
-			return $path;
632
-		}
633
-		$path = Filesystem::normalizePath($path);
634
-		// remove leading slash
635
-		return substr($path, 1);
636
-	}
637
-
638
-	/**
639
-	 * URL encodes the given path but keeps the slashes
640
-	 *
641
-	 * @param string $path to encode
642
-	 * @return string encoded path
643
-	 */
644
-	protected function encodePath($path) {
645
-		// slashes need to stay
646
-		return str_replace('%2F', '/', rawurlencode($path));
647
-	}
648
-
649
-	/**
650
-	 * @param string $method
651
-	 * @param string $path
652
-	 * @param string|resource|null $body
653
-	 * @param int $expected
654
-	 * @return bool
655
-	 * @throws StorageInvalidException
656
-	 * @throws StorageNotAvailableException
657
-	 */
658
-	protected function simpleResponse($method, $path, $body, $expected) {
659
-		$path = $this->cleanPath($path);
660
-		try {
661
-			$response = $this->client->request($method, $this->encodePath($path), $body);
662
-			return $response['statusCode'] == $expected;
663
-		} catch (ClientHttpException $e) {
664
-			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
665
-				$this->statCache->clear($path . '/');
666
-				$this->statCache->set($path, false);
667
-				return false;
668
-			}
669
-
670
-			$this->convertException($e, $path);
671
-		} catch (\Exception $e) {
672
-			$this->convertException($e, $path);
673
-		}
674
-		return false;
675
-	}
676
-
677
-	/**
678
-	 * check if curl is installed
679
-	 */
680
-	public static function checkDependencies() {
681
-		return true;
682
-	}
683
-
684
-	/** {@inheritdoc} */
685
-	public function isUpdatable($path) {
686
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
687
-	}
688
-
689
-	/** {@inheritdoc} */
690
-	public function isCreatable($path) {
691
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
692
-	}
693
-
694
-	/** {@inheritdoc} */
695
-	public function isSharable($path) {
696
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
697
-	}
698
-
699
-	/** {@inheritdoc} */
700
-	public function isDeletable($path) {
701
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
702
-	}
703
-
704
-	/** {@inheritdoc} */
705
-	public function getPermissions($path) {
706
-		$this->init();
707
-		$path = $this->cleanPath($path);
708
-		$response = $this->propfind($path);
709
-		if ($response === false) {
710
-			return 0;
711
-		}
712
-		if (isset($response['{http://owncloud.org/ns}permissions'])) {
713
-			return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
714
-		} else if ($this->is_dir($path)) {
715
-			return Constants::PERMISSION_ALL;
716
-		} else if ($this->file_exists($path)) {
717
-			return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
718
-		} else {
719
-			return 0;
720
-		}
721
-	}
722
-
723
-	/** {@inheritdoc} */
724
-	public function getETag($path) {
725
-		$this->init();
726
-		$path = $this->cleanPath($path);
727
-		$response = $this->propfind($path);
728
-		if ($response === false) {
729
-			return null;
730
-		}
731
-		if (isset($response['{DAV:}getetag'])) {
732
-			return trim($response['{DAV:}getetag'], '"');
733
-		}
734
-		return parent::getEtag($path);
735
-	}
736
-
737
-	/**
738
-	 * @param string $permissionsString
739
-	 * @return int
740
-	 */
741
-	protected function parsePermissions($permissionsString) {
742
-		$permissions = Constants::PERMISSION_READ;
743
-		if (strpos($permissionsString, 'R') !== false) {
744
-			$permissions |= Constants::PERMISSION_SHARE;
745
-		}
746
-		if (strpos($permissionsString, 'D') !== false) {
747
-			$permissions |= Constants::PERMISSION_DELETE;
748
-		}
749
-		if (strpos($permissionsString, 'W') !== false) {
750
-			$permissions |= Constants::PERMISSION_UPDATE;
751
-		}
752
-		if (strpos($permissionsString, 'CK') !== false) {
753
-			$permissions |= Constants::PERMISSION_CREATE;
754
-			$permissions |= Constants::PERMISSION_UPDATE;
755
-		}
756
-		return $permissions;
757
-	}
758
-
759
-	/**
760
-	 * check if a file or folder has been updated since $time
761
-	 *
762
-	 * @param string $path
763
-	 * @param int $time
764
-	 * @throws \OCP\Files\StorageNotAvailableException
765
-	 * @return bool
766
-	 */
767
-	public function hasUpdated($path, $time) {
768
-		$this->init();
769
-		$path = $this->cleanPath($path);
770
-		try {
771
-			// force refresh for $path
772
-			$this->statCache->remove($path);
773
-			$response = $this->propfind($path);
774
-			if ($response === false) {
775
-				if ($path === '') {
776
-					// if root is gone it means the storage is not available
777
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
778
-				}
779
-				return false;
780
-			}
781
-			if (isset($response['{DAV:}getetag'])) {
782
-				$cachedData = $this->getCache()->get($path);
783
-				$etag = null;
784
-				if (isset($response['{DAV:}getetag'])) {
785
-					$etag = trim($response['{DAV:}getetag'], '"');
786
-				}
787
-				if (!empty($etag) && $cachedData['etag'] !== $etag) {
788
-					return true;
789
-				} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
790
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
791
-					return $sharePermissions !== $cachedData['permissions'];
792
-				} else if (isset($response['{http://owncloud.org/ns}permissions'])) {
793
-					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
794
-					return $permissions !== $cachedData['permissions'];
795
-				} else {
796
-					return false;
797
-				}
798
-			} else {
799
-				$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
800
-				return $remoteMtime > $time;
801
-			}
802
-		} catch (ClientHttpException $e) {
803
-			if ($e->getHttpStatus() === 405) {
804
-				if ($path === '') {
805
-					// if root is gone it means the storage is not available
806
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
807
-				}
808
-				return false;
809
-			}
810
-			$this->convertException($e, $path);
811
-			return false;
812
-		} catch (\Exception $e) {
813
-			$this->convertException($e, $path);
814
-			return false;
815
-		}
816
-	}
817
-
818
-	/**
819
-	 * Interpret the given exception and decide whether it is due to an
820
-	 * unavailable storage, invalid storage or other.
821
-	 * This will either throw StorageInvalidException, StorageNotAvailableException
822
-	 * or do nothing.
823
-	 *
824
-	 * @param Exception $e sabre exception
825
-	 * @param string $path optional path from the operation
826
-	 *
827
-	 * @throws StorageInvalidException if the storage is invalid, for example
828
-	 * when the authentication expired or is invalid
829
-	 * @throws StorageNotAvailableException if the storage is not available,
830
-	 * which might be temporary
831
-	 */
832
-	protected function convertException(Exception $e, $path = '') {
833
-		\OC::$server->getLogger()->logException($e, ['app' => 'files_external']);
834
-		if ($e instanceof ClientHttpException) {
835
-			if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
836
-				throw new \OCP\Lock\LockedException($path);
837
-			}
838
-			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
839
-				// either password was changed or was invalid all along
840
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
841
-			} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
842
-				// ignore exception for MethodNotAllowed, false will be returned
843
-				return;
844
-			}
845
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
846
-		} else if ($e instanceof ClientException) {
847
-			// connection timeout or refused, server could be temporarily down
848
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
849
-		} else if ($e instanceof \InvalidArgumentException) {
850
-			// parse error because the server returned HTML instead of XML,
851
-			// possibly temporarily down
852
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
853
-		} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
854
-			// rethrow
855
-			throw $e;
856
-		}
857
-
858
-		// TODO: only log for now, but in the future need to wrap/rethrow exception
859
-	}
60
+    /** @var string */
61
+    protected $password;
62
+    /** @var string */
63
+    protected $user;
64
+    /** @var string */
65
+    protected $authType;
66
+    /** @var string */
67
+    protected $host;
68
+    /** @var bool */
69
+    protected $secure;
70
+    /** @var string */
71
+    protected $root;
72
+    /** @var string */
73
+    protected $certPath;
74
+    /** @var bool */
75
+    protected $ready;
76
+    /** @var Client */
77
+    protected $client;
78
+    /** @var ArrayCache */
79
+    protected $statCache;
80
+    /** @var \OCP\Http\Client\IClientService */
81
+    protected $httpClientService;
82
+
83
+    /**
84
+     * @param array $params
85
+     * @throws \Exception
86
+     */
87
+    public function __construct($params) {
88
+        $this->statCache = new ArrayCache();
89
+        $this->httpClientService = \OC::$server->getHTTPClientService();
90
+        if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
91
+            $host = $params['host'];
92
+            //remove leading http[s], will be generated in createBaseUri()
93
+            if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
94
+            else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
95
+            $this->host = $host;
96
+            $this->user = $params['user'];
97
+            $this->password = $params['password'];
98
+            if (isset($params['authType'])) {
99
+                $this->authType = $params['authType'];
100
+            }
101
+            if (isset($params['secure'])) {
102
+                if (is_string($params['secure'])) {
103
+                    $this->secure = ($params['secure'] === 'true');
104
+                } else {
105
+                    $this->secure = (bool)$params['secure'];
106
+                }
107
+            } else {
108
+                $this->secure = false;
109
+            }
110
+            if ($this->secure === true) {
111
+                // inject mock for testing
112
+                $certManager = \OC::$server->getCertificateManager();
113
+                if (is_null($certManager)) { //no user
114
+                    $certManager = \OC::$server->getCertificateManager(null);
115
+                }
116
+                $certPath = $certManager->getAbsoluteBundlePath();
117
+                if (file_exists($certPath)) {
118
+                    $this->certPath = $certPath;
119
+                }
120
+            }
121
+            $this->root = isset($params['root']) ? $params['root'] : '/';
122
+            if (!$this->root || $this->root[0] != '/') {
123
+                $this->root = '/' . $this->root;
124
+            }
125
+            if ($this->root[strlen($this->root) - 1] != '/') {
126
+                $this->root .= '/';
127
+            }
128
+        } else {
129
+            throw new \Exception('Invalid webdav storage configuration');
130
+        }
131
+    }
132
+
133
+    protected function init() {
134
+        if ($this->ready) {
135
+            return;
136
+        }
137
+        $this->ready = true;
138
+
139
+        $settings = [
140
+            'baseUri' => $this->createBaseUri(),
141
+            'userName' => $this->user,
142
+            'password' => $this->password,
143
+        ];
144
+        if (isset($this->authType)) {
145
+            $settings['authType'] = $this->authType;
146
+        }
147
+
148
+        $proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
149
+        if ($proxy !== '') {
150
+            $settings['proxy'] = $proxy;
151
+        }
152
+
153
+        $this->client = new Client($settings);
154
+        $this->client->setThrowExceptions(true);
155
+        if ($this->secure === true && $this->certPath) {
156
+            $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
157
+        }
158
+    }
159
+
160
+    /**
161
+     * Clear the stat cache
162
+     */
163
+    public function clearStatCache() {
164
+        $this->statCache->clear();
165
+    }
166
+
167
+    /** {@inheritdoc} */
168
+    public function getId() {
169
+        return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
170
+    }
171
+
172
+    /** {@inheritdoc} */
173
+    public function createBaseUri() {
174
+        $baseUri = 'http';
175
+        if ($this->secure) {
176
+            $baseUri .= 's';
177
+        }
178
+        $baseUri .= '://' . $this->host . $this->root;
179
+        return $baseUri;
180
+    }
181
+
182
+    /** {@inheritdoc} */
183
+    public function mkdir($path) {
184
+        $this->init();
185
+        $path = $this->cleanPath($path);
186
+        $result = $this->simpleResponse('MKCOL', $path, null, 201);
187
+        if ($result) {
188
+            $this->statCache->set($path, true);
189
+        }
190
+        return $result;
191
+    }
192
+
193
+    /** {@inheritdoc} */
194
+    public function rmdir($path) {
195
+        $this->init();
196
+        $path = $this->cleanPath($path);
197
+        // FIXME: some WebDAV impl return 403 when trying to DELETE
198
+        // a non-empty folder
199
+        $result = $this->simpleResponse('DELETE', $path . '/', null, 204);
200
+        $this->statCache->clear($path . '/');
201
+        $this->statCache->remove($path);
202
+        return $result;
203
+    }
204
+
205
+    /** {@inheritdoc} */
206
+    public function opendir($path) {
207
+        $this->init();
208
+        $path = $this->cleanPath($path);
209
+        try {
210
+            $response = $this->client->propFind(
211
+                $this->encodePath($path),
212
+                ['{DAV:}href'],
213
+                1
214
+            );
215
+            if ($response === false) {
216
+                return false;
217
+            }
218
+            $content = [];
219
+            $files = array_keys($response);
220
+            array_shift($files); //the first entry is the current directory
221
+
222
+            if (!$this->statCache->hasKey($path)) {
223
+                $this->statCache->set($path, true);
224
+            }
225
+            foreach ($files as $file) {
226
+                $file = urldecode($file);
227
+                // do not store the real entry, we might not have all properties
228
+                if (!$this->statCache->hasKey($path)) {
229
+                    $this->statCache->set($file, true);
230
+                }
231
+                $file = basename($file);
232
+                $content[] = $file;
233
+            }
234
+            return IteratorDirectory::wrap($content);
235
+        } catch (\Exception $e) {
236
+            $this->convertException($e, $path);
237
+        }
238
+        return false;
239
+    }
240
+
241
+    /**
242
+     * Propfind call with cache handling.
243
+     *
244
+     * First checks if information is cached.
245
+     * If not, request it from the server then store to cache.
246
+     *
247
+     * @param string $path path to propfind
248
+     *
249
+     * @return array|boolean propfind response or false if the entry was not found
250
+     *
251
+     * @throws ClientHttpException
252
+     */
253
+    protected function propfind($path) {
254
+        $path = $this->cleanPath($path);
255
+        $cachedResponse = $this->statCache->get($path);
256
+        // we either don't know it, or we know it exists but need more details
257
+        if (is_null($cachedResponse) || $cachedResponse === true) {
258
+            $this->init();
259
+            try {
260
+                $response = $this->client->propFind(
261
+                    $this->encodePath($path),
262
+                    array(
263
+                        '{DAV:}getlastmodified',
264
+                        '{DAV:}getcontentlength',
265
+                        '{DAV:}getcontenttype',
266
+                        '{http://owncloud.org/ns}permissions',
267
+                        '{http://open-collaboration-services.org/ns}share-permissions',
268
+                        '{DAV:}resourcetype',
269
+                        '{DAV:}getetag',
270
+                    )
271
+                );
272
+                $this->statCache->set($path, $response);
273
+            } catch (ClientHttpException $e) {
274
+                if ($e->getHttpStatus() === 404) {
275
+                    $this->statCache->clear($path . '/');
276
+                    $this->statCache->set($path, false);
277
+                    return false;
278
+                }
279
+                $this->convertException($e, $path);
280
+            } catch (\Exception $e) {
281
+                $this->convertException($e, $path);
282
+            }
283
+        } else {
284
+            $response = $cachedResponse;
285
+        }
286
+        return $response;
287
+    }
288
+
289
+    /** {@inheritdoc} */
290
+    public function filetype($path) {
291
+        try {
292
+            $response = $this->propfind($path);
293
+            if ($response === false) {
294
+                return false;
295
+            }
296
+            $responseType = [];
297
+            if (isset($response["{DAV:}resourcetype"])) {
298
+                /** @var ResourceType[] $response */
299
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
300
+            }
301
+            return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
302
+        } catch (\Exception $e) {
303
+            $this->convertException($e, $path);
304
+        }
305
+        return false;
306
+    }
307
+
308
+    /** {@inheritdoc} */
309
+    public function file_exists($path) {
310
+        try {
311
+            $path = $this->cleanPath($path);
312
+            $cachedState = $this->statCache->get($path);
313
+            if ($cachedState === false) {
314
+                // we know the file doesn't exist
315
+                return false;
316
+            } else if (!is_null($cachedState)) {
317
+                return true;
318
+            }
319
+            // need to get from server
320
+            return ($this->propfind($path) !== false);
321
+        } catch (\Exception $e) {
322
+            $this->convertException($e, $path);
323
+        }
324
+        return false;
325
+    }
326
+
327
+    /** {@inheritdoc} */
328
+    public function unlink($path) {
329
+        $this->init();
330
+        $path = $this->cleanPath($path);
331
+        $result = $this->simpleResponse('DELETE', $path, null, 204);
332
+        $this->statCache->clear($path . '/');
333
+        $this->statCache->remove($path);
334
+        return $result;
335
+    }
336
+
337
+    /** {@inheritdoc} */
338
+    public function fopen($path, $mode) {
339
+        $this->init();
340
+        $path = $this->cleanPath($path);
341
+        switch ($mode) {
342
+            case 'r':
343
+            case 'rb':
344
+                try {
345
+                    $response = $this->httpClientService
346
+                        ->newClient()
347
+                        ->get($this->createBaseUri() . $this->encodePath($path), [
348
+                            'auth' => [$this->user, $this->password],
349
+                            'stream' => true
350
+                        ]);
351
+                } catch (RequestException $e) {
352
+                    if ($e->getResponse() instanceof ResponseInterface
353
+                        && $e->getResponse()->getStatusCode() === 404) {
354
+                        return false;
355
+                    } else {
356
+                        throw $e;
357
+                    }
358
+                }
359
+
360
+                if ($response->getStatusCode() !== Http::STATUS_OK) {
361
+                    if ($response->getStatusCode() === Http::STATUS_LOCKED) {
362
+                        throw new \OCP\Lock\LockedException($path);
363
+                    } else {
364
+                        Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
365
+                    }
366
+                }
367
+
368
+                return $response->getBody();
369
+            case 'w':
370
+            case 'wb':
371
+            case 'a':
372
+            case 'ab':
373
+            case 'r+':
374
+            case 'w+':
375
+            case 'wb+':
376
+            case 'a+':
377
+            case 'x':
378
+            case 'x+':
379
+            case 'c':
380
+            case 'c+':
381
+                //emulate these
382
+                $tempManager = \OC::$server->getTempManager();
383
+                if (strrpos($path, '.') !== false) {
384
+                    $ext = substr($path, strrpos($path, '.'));
385
+                } else {
386
+                    $ext = '';
387
+                }
388
+                if ($this->file_exists($path)) {
389
+                    if (!$this->isUpdatable($path)) {
390
+                        return false;
391
+                    }
392
+                    if ($mode === 'w' or $mode === 'w+') {
393
+                        $tmpFile = $tempManager->getTemporaryFile($ext);
394
+                    } else {
395
+                        $tmpFile = $this->getCachedFile($path);
396
+                    }
397
+                } else {
398
+                    if (!$this->isCreatable(dirname($path))) {
399
+                        return false;
400
+                    }
401
+                    $tmpFile = $tempManager->getTemporaryFile($ext);
402
+                }
403
+                $handle = fopen($tmpFile, $mode);
404
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
405
+                    $this->writeBack($tmpFile, $path);
406
+                });
407
+        }
408
+    }
409
+
410
+    /**
411
+     * @param string $tmpFile
412
+     */
413
+    public function writeBack($tmpFile, $path) {
414
+        $this->uploadFile($tmpFile, $path);
415
+        unlink($tmpFile);
416
+    }
417
+
418
+    /** {@inheritdoc} */
419
+    public function free_space($path) {
420
+        $this->init();
421
+        $path = $this->cleanPath($path);
422
+        try {
423
+            // TODO: cacheable ?
424
+            $response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
425
+            if ($response === false) {
426
+                return FileInfo::SPACE_UNKNOWN;
427
+            }
428
+            if (isset($response['{DAV:}quota-available-bytes'])) {
429
+                return (int)$response['{DAV:}quota-available-bytes'];
430
+            } else {
431
+                return FileInfo::SPACE_UNKNOWN;
432
+            }
433
+        } catch (\Exception $e) {
434
+            return FileInfo::SPACE_UNKNOWN;
435
+        }
436
+    }
437
+
438
+    /** {@inheritdoc} */
439
+    public function touch($path, $mtime = null) {
440
+        $this->init();
441
+        if (is_null($mtime)) {
442
+            $mtime = time();
443
+        }
444
+        $path = $this->cleanPath($path);
445
+
446
+        // if file exists, update the mtime, else create a new empty file
447
+        if ($this->file_exists($path)) {
448
+            try {
449
+                $this->statCache->remove($path);
450
+                $this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
451
+                // non-owncloud clients might not have accepted the property, need to recheck it
452
+                $response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
453
+                if ($response === false) {
454
+                    return false;
455
+                }
456
+                if (isset($response['{DAV:}getlastmodified'])) {
457
+                    $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
458
+                    if ($remoteMtime !== $mtime) {
459
+                        // server has not accepted the mtime
460
+                        return false;
461
+                    }
462
+                }
463
+            } catch (ClientHttpException $e) {
464
+                if ($e->getHttpStatus() === 501) {
465
+                    return false;
466
+                }
467
+                $this->convertException($e, $path);
468
+                return false;
469
+            } catch (\Exception $e) {
470
+                $this->convertException($e, $path);
471
+                return false;
472
+            }
473
+        } else {
474
+            $this->file_put_contents($path, '');
475
+        }
476
+        return true;
477
+    }
478
+
479
+    /**
480
+     * @param string $path
481
+     * @param string $data
482
+     * @return int
483
+     */
484
+    public function file_put_contents($path, $data) {
485
+        $path = $this->cleanPath($path);
486
+        $result = parent::file_put_contents($path, $data);
487
+        $this->statCache->remove($path);
488
+        return $result;
489
+    }
490
+
491
+    /**
492
+     * @param string $path
493
+     * @param string $target
494
+     */
495
+    protected function uploadFile($path, $target) {
496
+        $this->init();
497
+
498
+        // invalidate
499
+        $target = $this->cleanPath($target);
500
+        $this->statCache->remove($target);
501
+        $source = fopen($path, 'r');
502
+
503
+        $this->httpClientService
504
+            ->newClient()
505
+            ->put($this->createBaseUri() . $this->encodePath($target), [
506
+                'body' => $source,
507
+                'auth' => [$this->user, $this->password]
508
+            ]);
509
+
510
+        $this->removeCachedFile($target);
511
+    }
512
+
513
+    /** {@inheritdoc} */
514
+    public function rename($path1, $path2) {
515
+        $this->init();
516
+        $path1 = $this->cleanPath($path1);
517
+        $path2 = $this->cleanPath($path2);
518
+        try {
519
+            // overwrite directory ?
520
+            if ($this->is_dir($path2)) {
521
+                // needs trailing slash in destination
522
+                $path2 = rtrim($path2, '/') . '/';
523
+            }
524
+            $this->client->request(
525
+                'MOVE',
526
+                $this->encodePath($path1),
527
+                null,
528
+                [
529
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
530
+                ]
531
+            );
532
+            $this->statCache->clear($path1 . '/');
533
+            $this->statCache->clear($path2 . '/');
534
+            $this->statCache->set($path1, false);
535
+            $this->statCache->set($path2, true);
536
+            $this->removeCachedFile($path1);
537
+            $this->removeCachedFile($path2);
538
+            return true;
539
+        } catch (\Exception $e) {
540
+            $this->convertException($e);
541
+        }
542
+        return false;
543
+    }
544
+
545
+    /** {@inheritdoc} */
546
+    public function copy($path1, $path2) {
547
+        $this->init();
548
+        $path1 = $this->cleanPath($path1);
549
+        $path2 = $this->cleanPath($path2);
550
+        try {
551
+            // overwrite directory ?
552
+            if ($this->is_dir($path2)) {
553
+                // needs trailing slash in destination
554
+                $path2 = rtrim($path2, '/') . '/';
555
+            }
556
+            $this->client->request(
557
+                'COPY',
558
+                $this->encodePath($path1),
559
+                null,
560
+                [
561
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
562
+                ]
563
+            );
564
+            $this->statCache->clear($path2 . '/');
565
+            $this->statCache->set($path2, true);
566
+            $this->removeCachedFile($path2);
567
+            return true;
568
+        } catch (\Exception $e) {
569
+            $this->convertException($e);
570
+        }
571
+        return false;
572
+    }
573
+
574
+    /** {@inheritdoc} */
575
+    public function stat($path) {
576
+        try {
577
+            $response = $this->propfind($path);
578
+            if (!$response) {
579
+                return false;
580
+            }
581
+            return [
582
+                'mtime' => strtotime($response['{DAV:}getlastmodified']),
583
+                'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
584
+            ];
585
+        } catch (\Exception $e) {
586
+            $this->convertException($e, $path);
587
+        }
588
+        return array();
589
+    }
590
+
591
+    /** {@inheritdoc} */
592
+    public function getMimeType($path) {
593
+        $remoteMimetype = $this->getMimeTypeFromRemote($path);
594
+        if ($remoteMimetype === 'application/octet-stream') {
595
+            return \OC::$server->getMimeTypeDetector()->detectPath($path);
596
+        } else {
597
+            return $remoteMimetype;
598
+        }
599
+    }
600
+
601
+    public function getMimeTypeFromRemote($path) {
602
+        try {
603
+            $response = $this->propfind($path);
604
+            if ($response === false) {
605
+                return false;
606
+            }
607
+            $responseType = [];
608
+            if (isset($response["{DAV:}resourcetype"])) {
609
+                /** @var ResourceType[] $response */
610
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
611
+            }
612
+            $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
613
+            if ($type == 'dir') {
614
+                return 'httpd/unix-directory';
615
+            } elseif (isset($response['{DAV:}getcontenttype'])) {
616
+                return $response['{DAV:}getcontenttype'];
617
+            } else {
618
+                return 'application/octet-stream';
619
+            }
620
+        } catch (\Exception $e) {
621
+            return false;
622
+        }
623
+    }
624
+
625
+    /**
626
+     * @param string $path
627
+     * @return string
628
+     */
629
+    public function cleanPath($path) {
630
+        if ($path === '') {
631
+            return $path;
632
+        }
633
+        $path = Filesystem::normalizePath($path);
634
+        // remove leading slash
635
+        return substr($path, 1);
636
+    }
637
+
638
+    /**
639
+     * URL encodes the given path but keeps the slashes
640
+     *
641
+     * @param string $path to encode
642
+     * @return string encoded path
643
+     */
644
+    protected function encodePath($path) {
645
+        // slashes need to stay
646
+        return str_replace('%2F', '/', rawurlencode($path));
647
+    }
648
+
649
+    /**
650
+     * @param string $method
651
+     * @param string $path
652
+     * @param string|resource|null $body
653
+     * @param int $expected
654
+     * @return bool
655
+     * @throws StorageInvalidException
656
+     * @throws StorageNotAvailableException
657
+     */
658
+    protected function simpleResponse($method, $path, $body, $expected) {
659
+        $path = $this->cleanPath($path);
660
+        try {
661
+            $response = $this->client->request($method, $this->encodePath($path), $body);
662
+            return $response['statusCode'] == $expected;
663
+        } catch (ClientHttpException $e) {
664
+            if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
665
+                $this->statCache->clear($path . '/');
666
+                $this->statCache->set($path, false);
667
+                return false;
668
+            }
669
+
670
+            $this->convertException($e, $path);
671
+        } catch (\Exception $e) {
672
+            $this->convertException($e, $path);
673
+        }
674
+        return false;
675
+    }
676
+
677
+    /**
678
+     * check if curl is installed
679
+     */
680
+    public static function checkDependencies() {
681
+        return true;
682
+    }
683
+
684
+    /** {@inheritdoc} */
685
+    public function isUpdatable($path) {
686
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
687
+    }
688
+
689
+    /** {@inheritdoc} */
690
+    public function isCreatable($path) {
691
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
692
+    }
693
+
694
+    /** {@inheritdoc} */
695
+    public function isSharable($path) {
696
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
697
+    }
698
+
699
+    /** {@inheritdoc} */
700
+    public function isDeletable($path) {
701
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
702
+    }
703
+
704
+    /** {@inheritdoc} */
705
+    public function getPermissions($path) {
706
+        $this->init();
707
+        $path = $this->cleanPath($path);
708
+        $response = $this->propfind($path);
709
+        if ($response === false) {
710
+            return 0;
711
+        }
712
+        if (isset($response['{http://owncloud.org/ns}permissions'])) {
713
+            return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
714
+        } else if ($this->is_dir($path)) {
715
+            return Constants::PERMISSION_ALL;
716
+        } else if ($this->file_exists($path)) {
717
+            return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
718
+        } else {
719
+            return 0;
720
+        }
721
+    }
722
+
723
+    /** {@inheritdoc} */
724
+    public function getETag($path) {
725
+        $this->init();
726
+        $path = $this->cleanPath($path);
727
+        $response = $this->propfind($path);
728
+        if ($response === false) {
729
+            return null;
730
+        }
731
+        if (isset($response['{DAV:}getetag'])) {
732
+            return trim($response['{DAV:}getetag'], '"');
733
+        }
734
+        return parent::getEtag($path);
735
+    }
736
+
737
+    /**
738
+     * @param string $permissionsString
739
+     * @return int
740
+     */
741
+    protected function parsePermissions($permissionsString) {
742
+        $permissions = Constants::PERMISSION_READ;
743
+        if (strpos($permissionsString, 'R') !== false) {
744
+            $permissions |= Constants::PERMISSION_SHARE;
745
+        }
746
+        if (strpos($permissionsString, 'D') !== false) {
747
+            $permissions |= Constants::PERMISSION_DELETE;
748
+        }
749
+        if (strpos($permissionsString, 'W') !== false) {
750
+            $permissions |= Constants::PERMISSION_UPDATE;
751
+        }
752
+        if (strpos($permissionsString, 'CK') !== false) {
753
+            $permissions |= Constants::PERMISSION_CREATE;
754
+            $permissions |= Constants::PERMISSION_UPDATE;
755
+        }
756
+        return $permissions;
757
+    }
758
+
759
+    /**
760
+     * check if a file or folder has been updated since $time
761
+     *
762
+     * @param string $path
763
+     * @param int $time
764
+     * @throws \OCP\Files\StorageNotAvailableException
765
+     * @return bool
766
+     */
767
+    public function hasUpdated($path, $time) {
768
+        $this->init();
769
+        $path = $this->cleanPath($path);
770
+        try {
771
+            // force refresh for $path
772
+            $this->statCache->remove($path);
773
+            $response = $this->propfind($path);
774
+            if ($response === false) {
775
+                if ($path === '') {
776
+                    // if root is gone it means the storage is not available
777
+                    throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
778
+                }
779
+                return false;
780
+            }
781
+            if (isset($response['{DAV:}getetag'])) {
782
+                $cachedData = $this->getCache()->get($path);
783
+                $etag = null;
784
+                if (isset($response['{DAV:}getetag'])) {
785
+                    $etag = trim($response['{DAV:}getetag'], '"');
786
+                }
787
+                if (!empty($etag) && $cachedData['etag'] !== $etag) {
788
+                    return true;
789
+                } else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
790
+                    $sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
791
+                    return $sharePermissions !== $cachedData['permissions'];
792
+                } else if (isset($response['{http://owncloud.org/ns}permissions'])) {
793
+                    $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
794
+                    return $permissions !== $cachedData['permissions'];
795
+                } else {
796
+                    return false;
797
+                }
798
+            } else {
799
+                $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
800
+                return $remoteMtime > $time;
801
+            }
802
+        } catch (ClientHttpException $e) {
803
+            if ($e->getHttpStatus() === 405) {
804
+                if ($path === '') {
805
+                    // if root is gone it means the storage is not available
806
+                    throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
807
+                }
808
+                return false;
809
+            }
810
+            $this->convertException($e, $path);
811
+            return false;
812
+        } catch (\Exception $e) {
813
+            $this->convertException($e, $path);
814
+            return false;
815
+        }
816
+    }
817
+
818
+    /**
819
+     * Interpret the given exception and decide whether it is due to an
820
+     * unavailable storage, invalid storage or other.
821
+     * This will either throw StorageInvalidException, StorageNotAvailableException
822
+     * or do nothing.
823
+     *
824
+     * @param Exception $e sabre exception
825
+     * @param string $path optional path from the operation
826
+     *
827
+     * @throws StorageInvalidException if the storage is invalid, for example
828
+     * when the authentication expired or is invalid
829
+     * @throws StorageNotAvailableException if the storage is not available,
830
+     * which might be temporary
831
+     */
832
+    protected function convertException(Exception $e, $path = '') {
833
+        \OC::$server->getLogger()->logException($e, ['app' => 'files_external']);
834
+        if ($e instanceof ClientHttpException) {
835
+            if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
836
+                throw new \OCP\Lock\LockedException($path);
837
+            }
838
+            if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
839
+                // either password was changed or was invalid all along
840
+                throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
841
+            } else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
842
+                // ignore exception for MethodNotAllowed, false will be returned
843
+                return;
844
+            }
845
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
846
+        } else if ($e instanceof ClientException) {
847
+            // connection timeout or refused, server could be temporarily down
848
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
849
+        } else if ($e instanceof \InvalidArgumentException) {
850
+            // parse error because the server returned HTML instead of XML,
851
+            // possibly temporarily down
852
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
853
+        } else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
854
+            // rethrow
855
+            throw $e;
856
+        }
857
+
858
+        // TODO: only log for now, but in the future need to wrap/rethrow exception
859
+    }
860 860
 }
861 861
 
Please login to merge, or discard this patch.
Spacing   +33 added lines, -33 removed lines patch added patch discarded remove patch
@@ -102,7 +102,7 @@  discard block
 block discarded – undo
102 102
 				if (is_string($params['secure'])) {
103 103
 					$this->secure = ($params['secure'] === 'true');
104 104
 				} else {
105
-					$this->secure = (bool)$params['secure'];
105
+					$this->secure = (bool) $params['secure'];
106 106
 				}
107 107
 			} else {
108 108
 				$this->secure = false;
@@ -120,7 +120,7 @@  discard block
 block discarded – undo
120 120
 			}
121 121
 			$this->root = isset($params['root']) ? $params['root'] : '/';
122 122
 			if (!$this->root || $this->root[0] != '/') {
123
-				$this->root = '/' . $this->root;
123
+				$this->root = '/'.$this->root;
124 124
 			}
125 125
 			if ($this->root[strlen($this->root) - 1] != '/') {
126 126
 				$this->root .= '/';
@@ -166,7 +166,7 @@  discard block
 block discarded – undo
166 166
 
167 167
 	/** {@inheritdoc} */
168 168
 	public function getId() {
169
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
169
+		return 'webdav::'.$this->user.'@'.$this->host.'/'.$this->root;
170 170
 	}
171 171
 
172 172
 	/** {@inheritdoc} */
@@ -175,7 +175,7 @@  discard block
 block discarded – undo
175 175
 		if ($this->secure) {
176 176
 			$baseUri .= 's';
177 177
 		}
178
-		$baseUri .= '://' . $this->host . $this->root;
178
+		$baseUri .= '://'.$this->host.$this->root;
179 179
 		return $baseUri;
180 180
 	}
181 181
 
@@ -196,8 +196,8 @@  discard block
 block discarded – undo
196 196
 		$path = $this->cleanPath($path);
197 197
 		// FIXME: some WebDAV impl return 403 when trying to DELETE
198 198
 		// a non-empty folder
199
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
200
-		$this->statCache->clear($path . '/');
199
+		$result = $this->simpleResponse('DELETE', $path.'/', null, 204);
200
+		$this->statCache->clear($path.'/');
201 201
 		$this->statCache->remove($path);
202 202
 		return $result;
203 203
 	}
@@ -272,7 +272,7 @@  discard block
 block discarded – undo
272 272
 				$this->statCache->set($path, $response);
273 273
 			} catch (ClientHttpException $e) {
274 274
 				if ($e->getHttpStatus() === 404) {
275
-					$this->statCache->clear($path . '/');
275
+					$this->statCache->clear($path.'/');
276 276
 					$this->statCache->set($path, false);
277 277
 					return false;
278 278
 				}
@@ -329,7 +329,7 @@  discard block
 block discarded – undo
329 329
 		$this->init();
330 330
 		$path = $this->cleanPath($path);
331 331
 		$result = $this->simpleResponse('DELETE', $path, null, 204);
332
-		$this->statCache->clear($path . '/');
332
+		$this->statCache->clear($path.'/');
333 333
 		$this->statCache->remove($path);
334 334
 		return $result;
335 335
 	}
@@ -344,7 +344,7 @@  discard block
 block discarded – undo
344 344
 				try {
345 345
 					$response = $this->httpClientService
346 346
 						->newClient()
347
-						->get($this->createBaseUri() . $this->encodePath($path), [
347
+						->get($this->createBaseUri().$this->encodePath($path), [
348 348
 							'auth' => [$this->user, $this->password],
349 349
 							'stream' => true
350 350
 						]);
@@ -361,7 +361,7 @@  discard block
 block discarded – undo
361 361
 					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
362 362
 						throw new \OCP\Lock\LockedException($path);
363 363
 					} else {
364
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
364
+						Util::writeLog("webdav client", 'Guzzle get returned status code '.$response->getStatusCode(), Util::ERROR);
365 365
 					}
366 366
 				}
367 367
 
@@ -401,7 +401,7 @@  discard block
 block discarded – undo
401 401
 					$tmpFile = $tempManager->getTemporaryFile($ext);
402 402
 				}
403 403
 				$handle = fopen($tmpFile, $mode);
404
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
404
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
405 405
 					$this->writeBack($tmpFile, $path);
406 406
 				});
407 407
 		}
@@ -426,7 +426,7 @@  discard block
 block discarded – undo
426 426
 				return FileInfo::SPACE_UNKNOWN;
427 427
 			}
428 428
 			if (isset($response['{DAV:}quota-available-bytes'])) {
429
-				return (int)$response['{DAV:}quota-available-bytes'];
429
+				return (int) $response['{DAV:}quota-available-bytes'];
430 430
 			} else {
431 431
 				return FileInfo::SPACE_UNKNOWN;
432 432
 			}
@@ -502,7 +502,7 @@  discard block
 block discarded – undo
502 502
 
503 503
 		$this->httpClientService
504 504
 			->newClient()
505
-			->put($this->createBaseUri() . $this->encodePath($target), [
505
+			->put($this->createBaseUri().$this->encodePath($target), [
506 506
 				'body' => $source,
507 507
 				'auth' => [$this->user, $this->password]
508 508
 			]);
@@ -519,18 +519,18 @@  discard block
 block discarded – undo
519 519
 			// overwrite directory ?
520 520
 			if ($this->is_dir($path2)) {
521 521
 				// needs trailing slash in destination
522
-				$path2 = rtrim($path2, '/') . '/';
522
+				$path2 = rtrim($path2, '/').'/';
523 523
 			}
524 524
 			$this->client->request(
525 525
 				'MOVE',
526 526
 				$this->encodePath($path1),
527 527
 				null,
528 528
 				[
529
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
529
+					'Destination' => $this->createBaseUri().$this->encodePath($path2),
530 530
 				]
531 531
 			);
532
-			$this->statCache->clear($path1 . '/');
533
-			$this->statCache->clear($path2 . '/');
532
+			$this->statCache->clear($path1.'/');
533
+			$this->statCache->clear($path2.'/');
534 534
 			$this->statCache->set($path1, false);
535 535
 			$this->statCache->set($path2, true);
536 536
 			$this->removeCachedFile($path1);
@@ -551,17 +551,17 @@  discard block
 block discarded – undo
551 551
 			// overwrite directory ?
552 552
 			if ($this->is_dir($path2)) {
553 553
 				// needs trailing slash in destination
554
-				$path2 = rtrim($path2, '/') . '/';
554
+				$path2 = rtrim($path2, '/').'/';
555 555
 			}
556 556
 			$this->client->request(
557 557
 				'COPY',
558 558
 				$this->encodePath($path1),
559 559
 				null,
560 560
 				[
561
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
561
+					'Destination' => $this->createBaseUri().$this->encodePath($path2),
562 562
 				]
563 563
 			);
564
-			$this->statCache->clear($path2 . '/');
564
+			$this->statCache->clear($path2.'/');
565 565
 			$this->statCache->set($path2, true);
566 566
 			$this->removeCachedFile($path2);
567 567
 			return true;
@@ -580,7 +580,7 @@  discard block
 block discarded – undo
580 580
 			}
581 581
 			return [
582 582
 				'mtime' => strtotime($response['{DAV:}getlastmodified']),
583
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
583
+				'size' => (int) isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
584 584
 			];
585 585
 		} catch (\Exception $e) {
586 586
 			$this->convertException($e, $path);
@@ -662,7 +662,7 @@  discard block
 block discarded – undo
662 662
 			return $response['statusCode'] == $expected;
663 663
 		} catch (ClientHttpException $e) {
664 664
 			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
665
-				$this->statCache->clear($path . '/');
665
+				$this->statCache->clear($path.'/');
666 666
 				$this->statCache->set($path, false);
667 667
 				return false;
668 668
 			}
@@ -683,22 +683,22 @@  discard block
 block discarded – undo
683 683
 
684 684
 	/** {@inheritdoc} */
685 685
 	public function isUpdatable($path) {
686
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
686
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
687 687
 	}
688 688
 
689 689
 	/** {@inheritdoc} */
690 690
 	public function isCreatable($path) {
691
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
691
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_CREATE);
692 692
 	}
693 693
 
694 694
 	/** {@inheritdoc} */
695 695
 	public function isSharable($path) {
696
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
696
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_SHARE);
697 697
 	}
698 698
 
699 699
 	/** {@inheritdoc} */
700 700
 	public function isDeletable($path) {
701
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
701
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_DELETE);
702 702
 	}
703 703
 
704 704
 	/** {@inheritdoc} */
@@ -774,7 +774,7 @@  discard block
 block discarded – undo
774 774
 			if ($response === false) {
775 775
 				if ($path === '') {
776 776
 					// if root is gone it means the storage is not available
777
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
777
+					throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
778 778
 				}
779 779
 				return false;
780 780
 			}
@@ -787,7 +787,7 @@  discard block
 block discarded – undo
787 787
 				if (!empty($etag) && $cachedData['etag'] !== $etag) {
788 788
 					return true;
789 789
 				} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
790
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
790
+					$sharePermissions = (int) $response['{http://open-collaboration-services.org/ns}share-permissions'];
791 791
 					return $sharePermissions !== $cachedData['permissions'];
792 792
 				} else if (isset($response['{http://owncloud.org/ns}permissions'])) {
793 793
 					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
@@ -803,7 +803,7 @@  discard block
 block discarded – undo
803 803
 			if ($e->getHttpStatus() === 405) {
804 804
 				if ($path === '') {
805 805
 					// if root is gone it means the storage is not available
806
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
806
+					throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
807 807
 				}
808 808
 				return false;
809 809
 			}
@@ -837,19 +837,19 @@  discard block
 block discarded – undo
837 837
 			}
838 838
 			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
839 839
 				// either password was changed or was invalid all along
840
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
840
+				throw new StorageInvalidException(get_class($e).': '.$e->getMessage());
841 841
 			} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
842 842
 				// ignore exception for MethodNotAllowed, false will be returned
843 843
 				return;
844 844
 			}
845
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
845
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
846 846
 		} else if ($e instanceof ClientException) {
847 847
 			// connection timeout or refused, server could be temporarily down
848
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
848
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
849 849
 		} else if ($e instanceof \InvalidArgumentException) {
850 850
 			// parse error because the server returned HTML instead of XML,
851 851
 			// possibly temporarily down
852
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
852
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
853 853
 		} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
854 854
 			// rethrow
855 855
 			throw $e;
Please login to merge, or discard this patch.
lib/private/Files/Filesystem.php 1 patch
Indentation   +853 added lines, -853 removed lines patch added patch discarded remove patch
@@ -69,857 +69,857 @@
 block discarded – undo
69 69
 
70 70
 class Filesystem {
71 71
 
72
-	/**
73
-	 * @var Mount\Manager $mounts
74
-	 */
75
-	private static $mounts;
76
-
77
-	public static $loaded = false;
78
-	/**
79
-	 * @var \OC\Files\View $defaultInstance
80
-	 */
81
-	static private $defaultInstance;
82
-
83
-	static private $usersSetup = array();
84
-
85
-	static private $normalizedPathCache = null;
86
-
87
-	static private $listeningForProviders = false;
88
-
89
-	/**
90
-	 * classname which used for hooks handling
91
-	 * used as signalclass in OC_Hooks::emit()
92
-	 */
93
-	const CLASSNAME = 'OC_Filesystem';
94
-
95
-	/**
96
-	 * signalname emitted before file renaming
97
-	 *
98
-	 * @param string $oldpath
99
-	 * @param string $newpath
100
-	 */
101
-	const signal_rename = 'rename';
102
-
103
-	/**
104
-	 * signal emitted after file renaming
105
-	 *
106
-	 * @param string $oldpath
107
-	 * @param string $newpath
108
-	 */
109
-	const signal_post_rename = 'post_rename';
110
-
111
-	/**
112
-	 * signal emitted before file/dir creation
113
-	 *
114
-	 * @param string $path
115
-	 * @param bool $run changing this flag to false in hook handler will cancel event
116
-	 */
117
-	const signal_create = 'create';
118
-
119
-	/**
120
-	 * signal emitted after file/dir creation
121
-	 *
122
-	 * @param string $path
123
-	 * @param bool $run changing this flag to false in hook handler will cancel event
124
-	 */
125
-	const signal_post_create = 'post_create';
126
-
127
-	/**
128
-	 * signal emits before file/dir copy
129
-	 *
130
-	 * @param string $oldpath
131
-	 * @param string $newpath
132
-	 * @param bool $run changing this flag to false in hook handler will cancel event
133
-	 */
134
-	const signal_copy = 'copy';
135
-
136
-	/**
137
-	 * signal emits after file/dir copy
138
-	 *
139
-	 * @param string $oldpath
140
-	 * @param string $newpath
141
-	 */
142
-	const signal_post_copy = 'post_copy';
143
-
144
-	/**
145
-	 * signal emits before file/dir save
146
-	 *
147
-	 * @param string $path
148
-	 * @param bool $run changing this flag to false in hook handler will cancel event
149
-	 */
150
-	const signal_write = 'write';
151
-
152
-	/**
153
-	 * signal emits after file/dir save
154
-	 *
155
-	 * @param string $path
156
-	 */
157
-	const signal_post_write = 'post_write';
158
-
159
-	/**
160
-	 * signal emitted before file/dir update
161
-	 *
162
-	 * @param string $path
163
-	 * @param bool $run changing this flag to false in hook handler will cancel event
164
-	 */
165
-	const signal_update = 'update';
166
-
167
-	/**
168
-	 * signal emitted after file/dir update
169
-	 *
170
-	 * @param string $path
171
-	 * @param bool $run changing this flag to false in hook handler will cancel event
172
-	 */
173
-	const signal_post_update = 'post_update';
174
-
175
-	/**
176
-	 * signal emits when reading file/dir
177
-	 *
178
-	 * @param string $path
179
-	 */
180
-	const signal_read = 'read';
181
-
182
-	/**
183
-	 * signal emits when removing file/dir
184
-	 *
185
-	 * @param string $path
186
-	 */
187
-	const signal_delete = 'delete';
188
-
189
-	/**
190
-	 * parameters definitions for signals
191
-	 */
192
-	const signal_param_path = 'path';
193
-	const signal_param_oldpath = 'oldpath';
194
-	const signal_param_newpath = 'newpath';
195
-
196
-	/**
197
-	 * run - changing this flag to false in hook handler will cancel event
198
-	 */
199
-	const signal_param_run = 'run';
200
-
201
-	const signal_create_mount = 'create_mount';
202
-	const signal_delete_mount = 'delete_mount';
203
-	const signal_param_mount_type = 'mounttype';
204
-	const signal_param_users = 'users';
205
-
206
-	/**
207
-	 * @var \OC\Files\Storage\StorageFactory $loader
208
-	 */
209
-	private static $loader;
210
-
211
-	/** @var bool */
212
-	private static $logWarningWhenAddingStorageWrapper = true;
213
-
214
-	/**
215
-	 * @param bool $shouldLog
216
-	 * @return bool previous value
217
-	 * @internal
218
-	 */
219
-	public static function logWarningWhenAddingStorageWrapper($shouldLog) {
220
-		$previousValue = self::$logWarningWhenAddingStorageWrapper;
221
-		self::$logWarningWhenAddingStorageWrapper = (bool) $shouldLog;
222
-		return $previousValue;
223
-	}
224
-
225
-	/**
226
-	 * @param string $wrapperName
227
-	 * @param callable $wrapper
228
-	 * @param int $priority
229
-	 */
230
-	public static function addStorageWrapper($wrapperName, $wrapper, $priority = 50) {
231
-		if (self::$logWarningWhenAddingStorageWrapper) {
232
-			\OC::$server->getLogger()->warning("Storage wrapper '{wrapper}' was not registered via the 'OC_Filesystem - preSetup' hook which could cause potential problems.", [
233
-				'wrapper' => $wrapperName,
234
-				'app' => 'filesystem',
235
-			]);
236
-		}
237
-
238
-		$mounts = self::getMountManager()->getAll();
239
-		if (!self::getLoader()->addStorageWrapper($wrapperName, $wrapper, $priority, $mounts)) {
240
-			// do not re-wrap if storage with this name already existed
241
-			return;
242
-		}
243
-	}
244
-
245
-	/**
246
-	 * Returns the storage factory
247
-	 *
248
-	 * @return \OCP\Files\Storage\IStorageFactory
249
-	 */
250
-	public static function getLoader() {
251
-		if (!self::$loader) {
252
-			self::$loader = new StorageFactory();
253
-		}
254
-		return self::$loader;
255
-	}
256
-
257
-	/**
258
-	 * Returns the mount manager
259
-	 *
260
-	 * @return \OC\Files\Mount\Manager
261
-	 */
262
-	public static function getMountManager($user = '') {
263
-		if (!self::$mounts) {
264
-			\OC_Util::setupFS($user);
265
-		}
266
-		return self::$mounts;
267
-	}
268
-
269
-	/**
270
-	 * get the mountpoint of the storage object for a path
271
-	 * ( note: because a storage is not always mounted inside the fakeroot, the
272
-	 * returned mountpoint is relative to the absolute root of the filesystem
273
-	 * and doesn't take the chroot into account )
274
-	 *
275
-	 * @param string $path
276
-	 * @return string
277
-	 */
278
-	static public function getMountPoint($path) {
279
-		if (!self::$mounts) {
280
-			\OC_Util::setupFS();
281
-		}
282
-		$mount = self::$mounts->find($path);
283
-		if ($mount) {
284
-			return $mount->getMountPoint();
285
-		} else {
286
-			return '';
287
-		}
288
-	}
289
-
290
-	/**
291
-	 * get a list of all mount points in a directory
292
-	 *
293
-	 * @param string $path
294
-	 * @return string[]
295
-	 */
296
-	static public function getMountPoints($path) {
297
-		if (!self::$mounts) {
298
-			\OC_Util::setupFS();
299
-		}
300
-		$result = array();
301
-		$mounts = self::$mounts->findIn($path);
302
-		foreach ($mounts as $mount) {
303
-			$result[] = $mount->getMountPoint();
304
-		}
305
-		return $result;
306
-	}
307
-
308
-	/**
309
-	 * get the storage mounted at $mountPoint
310
-	 *
311
-	 * @param string $mountPoint
312
-	 * @return \OC\Files\Storage\Storage
313
-	 */
314
-	public static function getStorage($mountPoint) {
315
-		if (!self::$mounts) {
316
-			\OC_Util::setupFS();
317
-		}
318
-		$mount = self::$mounts->find($mountPoint);
319
-		return $mount->getStorage();
320
-	}
321
-
322
-	/**
323
-	 * @param string $id
324
-	 * @return Mount\MountPoint[]
325
-	 */
326
-	public static function getMountByStorageId($id) {
327
-		if (!self::$mounts) {
328
-			\OC_Util::setupFS();
329
-		}
330
-		return self::$mounts->findByStorageId($id);
331
-	}
332
-
333
-	/**
334
-	 * @param int $id
335
-	 * @return Mount\MountPoint[]
336
-	 */
337
-	public static function getMountByNumericId($id) {
338
-		if (!self::$mounts) {
339
-			\OC_Util::setupFS();
340
-		}
341
-		return self::$mounts->findByNumericId($id);
342
-	}
343
-
344
-	/**
345
-	 * resolve a path to a storage and internal path
346
-	 *
347
-	 * @param string $path
348
-	 * @return array an array consisting of the storage and the internal path
349
-	 */
350
-	static public function resolvePath($path) {
351
-		if (!self::$mounts) {
352
-			\OC_Util::setupFS();
353
-		}
354
-		$mount = self::$mounts->find($path);
355
-		if ($mount) {
356
-			return array($mount->getStorage(), rtrim($mount->getInternalPath($path), '/'));
357
-		} else {
358
-			return array(null, null);
359
-		}
360
-	}
361
-
362
-	static public function init($user, $root) {
363
-		if (self::$defaultInstance) {
364
-			return false;
365
-		}
366
-		self::getLoader();
367
-		self::$defaultInstance = new View($root);
368
-
369
-		if (!self::$mounts) {
370
-			self::$mounts = \OC::$server->getMountManager();
371
-		}
372
-
373
-		//load custom mount config
374
-		self::initMountPoints($user);
375
-
376
-		self::$loaded = true;
377
-
378
-		return true;
379
-	}
380
-
381
-	static public function initMountManager() {
382
-		if (!self::$mounts) {
383
-			self::$mounts = \OC::$server->getMountManager();
384
-		}
385
-	}
386
-
387
-	/**
388
-	 * Initialize system and personal mount points for a user
389
-	 *
390
-	 * @param string $user
391
-	 * @throws \OC\User\NoUserException if the user is not available
392
-	 */
393
-	public static function initMountPoints($user = '') {
394
-		if ($user == '') {
395
-			$user = \OC_User::getUser();
396
-		}
397
-		if ($user === null || $user === false || $user === '') {
398
-			throw new \OC\User\NoUserException('Attempted to initialize mount points for null user and no user in session');
399
-		}
400
-
401
-		if (isset(self::$usersSetup[$user])) {
402
-			return;
403
-		}
404
-
405
-		self::$usersSetup[$user] = true;
406
-
407
-		$userManager = \OC::$server->getUserManager();
408
-		$userObject = $userManager->get($user);
409
-
410
-		if (is_null($userObject)) {
411
-			\OCP\Util::writeLog('files', ' Backends provided no user object for ' . $user, \OCP\Util::ERROR);
412
-			// reset flag, this will make it possible to rethrow the exception if called again
413
-			unset(self::$usersSetup[$user]);
414
-			throw new \OC\User\NoUserException('Backends provided no user object for ' . $user);
415
-		}
416
-
417
-		$realUid = $userObject->getUID();
418
-		// workaround in case of different casings
419
-		if ($user !== $realUid) {
420
-			$stack = json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 50));
421
-			\OCP\Util::writeLog('files', 'initMountPoints() called with wrong user casing. This could be a bug. Expected: "' . $realUid . '" got "' . $user . '". Stack: ' . $stack, \OCP\Util::WARN);
422
-			$user = $realUid;
423
-
424
-			// again with the correct casing
425
-			if (isset(self::$usersSetup[$user])) {
426
-				return;
427
-			}
428
-
429
-			self::$usersSetup[$user] = true;
430
-		}
431
-
432
-		if (\OC::$server->getLockdownManager()->canAccessFilesystem()) {
433
-			/** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */
434
-			$mountConfigManager = \OC::$server->getMountProviderCollection();
435
-
436
-			// home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers
437
-			$homeMount = $mountConfigManager->getHomeMountForUser($userObject);
438
-
439
-			self::getMountManager()->addMount($homeMount);
440
-
441
-			\OC\Files\Filesystem::getStorage($user);
442
-
443
-			// Chance to mount for other storages
444
-			if ($userObject) {
445
-				$mounts = $mountConfigManager->addMountForUser($userObject, self::getMountManager());
446
-				$mounts[] = $homeMount;
447
-				$mountConfigManager->registerMounts($userObject, $mounts);
448
-			}
449
-
450
-			self::listenForNewMountProviders($mountConfigManager, $userManager);
451
-		} else {
452
-			self::getMountManager()->addMount(new MountPoint(
453
-				new NullStorage([]),
454
-				'/' . $user
455
-			));
456
-			self::getMountManager()->addMount(new MountPoint(
457
-				new NullStorage([]),
458
-				'/' . $user . '/files'
459
-			));
460
-		}
461
-		\OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user));
462
-	}
463
-
464
-	/**
465
-	 * Get mounts from mount providers that are registered after setup
466
-	 *
467
-	 * @param MountProviderCollection $mountConfigManager
468
-	 * @param IUserManager $userManager
469
-	 */
470
-	private static function listenForNewMountProviders(MountProviderCollection $mountConfigManager, IUserManager $userManager) {
471
-		if (!self::$listeningForProviders) {
472
-			self::$listeningForProviders = true;
473
-			$mountConfigManager->listen('\OC\Files\Config', 'registerMountProvider', function (IMountProvider $provider) use ($userManager) {
474
-				foreach (Filesystem::$usersSetup as $user => $setup) {
475
-					$userObject = $userManager->get($user);
476
-					if ($userObject) {
477
-						$mounts = $provider->getMountsForUser($userObject, Filesystem::getLoader());
478
-						array_walk($mounts, array(self::$mounts, 'addMount'));
479
-					}
480
-				}
481
-			});
482
-		}
483
-	}
484
-
485
-	/**
486
-	 * get the default filesystem view
487
-	 *
488
-	 * @return View
489
-	 */
490
-	static public function getView() {
491
-		return self::$defaultInstance;
492
-	}
493
-
494
-	/**
495
-	 * tear down the filesystem, removing all storage providers
496
-	 */
497
-	static public function tearDown() {
498
-		self::clearMounts();
499
-		self::$defaultInstance = null;
500
-	}
501
-
502
-	/**
503
-	 * get the relative path of the root data directory for the current user
504
-	 *
505
-	 * @return string
506
-	 *
507
-	 * Returns path like /admin/files
508
-	 */
509
-	static public function getRoot() {
510
-		if (!self::$defaultInstance) {
511
-			return null;
512
-		}
513
-		return self::$defaultInstance->getRoot();
514
-	}
515
-
516
-	/**
517
-	 * clear all mounts and storage backends
518
-	 */
519
-	public static function clearMounts() {
520
-		if (self::$mounts) {
521
-			self::$usersSetup = array();
522
-			self::$mounts->clear();
523
-		}
524
-	}
525
-
526
-	/**
527
-	 * mount an \OC\Files\Storage\Storage in our virtual filesystem
528
-	 *
529
-	 * @param \OC\Files\Storage\Storage|string $class
530
-	 * @param array $arguments
531
-	 * @param string $mountpoint
532
-	 */
533
-	static public function mount($class, $arguments, $mountpoint) {
534
-		if (!self::$mounts) {
535
-			\OC_Util::setupFS();
536
-		}
537
-		$mount = new Mount\MountPoint($class, $mountpoint, $arguments, self::getLoader());
538
-		self::$mounts->addMount($mount);
539
-	}
540
-
541
-	/**
542
-	 * return the path to a local version of the file
543
-	 * we need this because we can't know if a file is stored local or not from
544
-	 * outside the filestorage and for some purposes a local file is needed
545
-	 *
546
-	 * @param string $path
547
-	 * @return string
548
-	 */
549
-	static public function getLocalFile($path) {
550
-		return self::$defaultInstance->getLocalFile($path);
551
-	}
552
-
553
-	/**
554
-	 * @param string $path
555
-	 * @return string
556
-	 */
557
-	static public function getLocalFolder($path) {
558
-		return self::$defaultInstance->getLocalFolder($path);
559
-	}
560
-
561
-	/**
562
-	 * return path to file which reflects one visible in browser
563
-	 *
564
-	 * @param string $path
565
-	 * @return string
566
-	 */
567
-	static public function getLocalPath($path) {
568
-		$datadir = \OC_User::getHome(\OC_User::getUser()) . '/files';
569
-		$newpath = $path;
570
-		if (strncmp($newpath, $datadir, strlen($datadir)) == 0) {
571
-			$newpath = substr($path, strlen($datadir));
572
-		}
573
-		return $newpath;
574
-	}
575
-
576
-	/**
577
-	 * check if the requested path is valid
578
-	 *
579
-	 * @param string $path
580
-	 * @return bool
581
-	 */
582
-	static public function isValidPath($path) {
583
-		$path = self::normalizePath($path);
584
-		if (!$path || $path[0] !== '/') {
585
-			$path = '/' . $path;
586
-		}
587
-		if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') {
588
-			return false;
589
-		}
590
-		return true;
591
-	}
592
-
593
-	/**
594
-	 * checks if a file is blacklisted for storage in the filesystem
595
-	 * Listens to write and rename hooks
596
-	 *
597
-	 * @param array $data from hook
598
-	 */
599
-	static public function isBlacklisted($data) {
600
-		if (isset($data['path'])) {
601
-			$path = $data['path'];
602
-		} else if (isset($data['newpath'])) {
603
-			$path = $data['newpath'];
604
-		}
605
-		if (isset($path)) {
606
-			if (self::isFileBlacklisted($path)) {
607
-				$data['run'] = false;
608
-			}
609
-		}
610
-	}
611
-
612
-	/**
613
-	 * @param string $filename
614
-	 * @return bool
615
-	 */
616
-	static public function isFileBlacklisted($filename) {
617
-		$filename = self::normalizePath($filename);
618
-
619
-		$blacklist = \OC::$server->getConfig()->getSystemValue('blacklisted_files', array('.htaccess'));
620
-		$filename = strtolower(basename($filename));
621
-		return in_array($filename, $blacklist);
622
-	}
623
-
624
-	/**
625
-	 * check if the directory should be ignored when scanning
626
-	 * NOTE: the special directories . and .. would cause never ending recursion
627
-	 *
628
-	 * @param String $dir
629
-	 * @return boolean
630
-	 */
631
-	static public function isIgnoredDir($dir) {
632
-		if ($dir === '.' || $dir === '..') {
633
-			return true;
634
-		}
635
-		return false;
636
-	}
637
-
638
-	/**
639
-	 * following functions are equivalent to their php builtin equivalents for arguments/return values.
640
-	 */
641
-	static public function mkdir($path) {
642
-		return self::$defaultInstance->mkdir($path);
643
-	}
644
-
645
-	static public function rmdir($path) {
646
-		return self::$defaultInstance->rmdir($path);
647
-	}
648
-
649
-	static public function is_dir($path) {
650
-		return self::$defaultInstance->is_dir($path);
651
-	}
652
-
653
-	static public function is_file($path) {
654
-		return self::$defaultInstance->is_file($path);
655
-	}
656
-
657
-	static public function stat($path) {
658
-		return self::$defaultInstance->stat($path);
659
-	}
660
-
661
-	static public function filetype($path) {
662
-		return self::$defaultInstance->filetype($path);
663
-	}
664
-
665
-	static public function filesize($path) {
666
-		return self::$defaultInstance->filesize($path);
667
-	}
668
-
669
-	static public function readfile($path) {
670
-		return self::$defaultInstance->readfile($path);
671
-	}
672
-
673
-	static public function isCreatable($path) {
674
-		return self::$defaultInstance->isCreatable($path);
675
-	}
676
-
677
-	static public function isReadable($path) {
678
-		return self::$defaultInstance->isReadable($path);
679
-	}
680
-
681
-	static public function isUpdatable($path) {
682
-		return self::$defaultInstance->isUpdatable($path);
683
-	}
684
-
685
-	static public function isDeletable($path) {
686
-		return self::$defaultInstance->isDeletable($path);
687
-	}
688
-
689
-	static public function isSharable($path) {
690
-		return self::$defaultInstance->isSharable($path);
691
-	}
692
-
693
-	static public function file_exists($path) {
694
-		return self::$defaultInstance->file_exists($path);
695
-	}
696
-
697
-	static public function filemtime($path) {
698
-		return self::$defaultInstance->filemtime($path);
699
-	}
700
-
701
-	static public function touch($path, $mtime = null) {
702
-		return self::$defaultInstance->touch($path, $mtime);
703
-	}
704
-
705
-	/**
706
-	 * @return string
707
-	 */
708
-	static public function file_get_contents($path) {
709
-		return self::$defaultInstance->file_get_contents($path);
710
-	}
711
-
712
-	static public function file_put_contents($path, $data) {
713
-		return self::$defaultInstance->file_put_contents($path, $data);
714
-	}
715
-
716
-	static public function unlink($path) {
717
-		return self::$defaultInstance->unlink($path);
718
-	}
719
-
720
-	static public function rename($path1, $path2) {
721
-		return self::$defaultInstance->rename($path1, $path2);
722
-	}
723
-
724
-	static public function copy($path1, $path2) {
725
-		return self::$defaultInstance->copy($path1, $path2);
726
-	}
727
-
728
-	static public function fopen($path, $mode) {
729
-		return self::$defaultInstance->fopen($path, $mode);
730
-	}
731
-
732
-	/**
733
-	 * @return string
734
-	 */
735
-	static public function toTmpFile($path) {
736
-		return self::$defaultInstance->toTmpFile($path);
737
-	}
738
-
739
-	static public function fromTmpFile($tmpFile, $path) {
740
-		return self::$defaultInstance->fromTmpFile($tmpFile, $path);
741
-	}
742
-
743
-	static public function getMimeType($path) {
744
-		return self::$defaultInstance->getMimeType($path);
745
-	}
746
-
747
-	static public function hash($type, $path, $raw = false) {
748
-		return self::$defaultInstance->hash($type, $path, $raw);
749
-	}
750
-
751
-	static public function free_space($path = '/') {
752
-		return self::$defaultInstance->free_space($path);
753
-	}
754
-
755
-	static public function search($query) {
756
-		return self::$defaultInstance->search($query);
757
-	}
758
-
759
-	/**
760
-	 * @param string $query
761
-	 */
762
-	static public function searchByMime($query) {
763
-		return self::$defaultInstance->searchByMime($query);
764
-	}
765
-
766
-	/**
767
-	 * @param string|int $tag name or tag id
768
-	 * @param string $userId owner of the tags
769
-	 * @return FileInfo[] array or file info
770
-	 */
771
-	static public function searchByTag($tag, $userId) {
772
-		return self::$defaultInstance->searchByTag($tag, $userId);
773
-	}
774
-
775
-	/**
776
-	 * check if a file or folder has been updated since $time
777
-	 *
778
-	 * @param string $path
779
-	 * @param int $time
780
-	 * @return bool
781
-	 */
782
-	static public function hasUpdated($path, $time) {
783
-		return self::$defaultInstance->hasUpdated($path, $time);
784
-	}
785
-
786
-	/**
787
-	 * Fix common problems with a file path
788
-	 *
789
-	 * @param string $path
790
-	 * @param bool $stripTrailingSlash whether to strip the trailing slash
791
-	 * @param bool $isAbsolutePath whether the given path is absolute
792
-	 * @param bool $keepUnicode true to disable unicode normalization
793
-	 * @return string
794
-	 */
795
-	public static function normalizePath($path, $stripTrailingSlash = true, $isAbsolutePath = false, $keepUnicode = false) {
796
-		if (is_null(self::$normalizedPathCache)) {
797
-			self::$normalizedPathCache = new CappedMemoryCache();
798
-		}
799
-
800
-		/**
801
-		 * FIXME: This is a workaround for existing classes and files which call
802
-		 *        this function with another type than a valid string. This
803
-		 *        conversion should get removed as soon as all existing
804
-		 *        function calls have been fixed.
805
-		 */
806
-		$path = (string)$path;
807
-
808
-		$cacheKey = json_encode([$path, $stripTrailingSlash, $isAbsolutePath, $keepUnicode]);
809
-
810
-		if (isset(self::$normalizedPathCache[$cacheKey])) {
811
-			return self::$normalizedPathCache[$cacheKey];
812
-		}
813
-
814
-		if ($path == '') {
815
-			return '/';
816
-		}
817
-
818
-		//normalize unicode if possible
819
-		if (!$keepUnicode) {
820
-			$path = \OC_Util::normalizeUnicode($path);
821
-		}
822
-
823
-		//no windows style slashes
824
-		$path = str_replace('\\', '/', $path);
825
-
826
-		//add leading slash
827
-		if ($path[0] !== '/') {
828
-			$path = '/' . $path;
829
-		}
830
-
831
-		// remove '/./'
832
-		// ugly, but str_replace() can't replace them all in one go
833
-		// as the replacement itself is part of the search string
834
-		// which will only be found during the next iteration
835
-		while (strpos($path, '/./') !== false) {
836
-			$path = str_replace('/./', '/', $path);
837
-		}
838
-		// remove sequences of slashes
839
-		$path = preg_replace('#/{2,}#', '/', $path);
840
-
841
-		//remove trailing slash
842
-		if ($stripTrailingSlash and strlen($path) > 1 and $path[strlen($path) - 1] === '/') {
843
-			$path = substr($path, 0, -1);
844
-		}
845
-
846
-		// remove trailing '/.'
847
-		if (substr($path, -2) == '/.') {
848
-			$path = substr($path, 0, -2);
849
-		}
850
-
851
-		$normalizedPath = $path;
852
-		self::$normalizedPathCache[$cacheKey] = $normalizedPath;
853
-
854
-		return $normalizedPath;
855
-	}
856
-
857
-	/**
858
-	 * get the filesystem info
859
-	 *
860
-	 * @param string $path
861
-	 * @param boolean $includeMountPoints whether to add mountpoint sizes,
862
-	 * defaults to true
863
-	 * @return \OC\Files\FileInfo|bool False if file does not exist
864
-	 */
865
-	public static function getFileInfo($path, $includeMountPoints = true) {
866
-		return self::$defaultInstance->getFileInfo($path, $includeMountPoints);
867
-	}
868
-
869
-	/**
870
-	 * change file metadata
871
-	 *
872
-	 * @param string $path
873
-	 * @param array $data
874
-	 * @return int
875
-	 *
876
-	 * returns the fileid of the updated file
877
-	 */
878
-	public static function putFileInfo($path, $data) {
879
-		return self::$defaultInstance->putFileInfo($path, $data);
880
-	}
881
-
882
-	/**
883
-	 * get the content of a directory
884
-	 *
885
-	 * @param string $directory path under datadirectory
886
-	 * @param string $mimetype_filter limit returned content to this mimetype or mimepart
887
-	 * @return \OC\Files\FileInfo[]
888
-	 */
889
-	public static function getDirectoryContent($directory, $mimetype_filter = '') {
890
-		return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter);
891
-	}
892
-
893
-	/**
894
-	 * Get the path of a file by id
895
-	 *
896
-	 * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file
897
-	 *
898
-	 * @param int $id
899
-	 * @throws NotFoundException
900
-	 * @return string
901
-	 */
902
-	public static function getPath($id) {
903
-		return self::$defaultInstance->getPath($id);
904
-	}
905
-
906
-	/**
907
-	 * Get the owner for a file or folder
908
-	 *
909
-	 * @param string $path
910
-	 * @return string
911
-	 */
912
-	public static function getOwner($path) {
913
-		return self::$defaultInstance->getOwner($path);
914
-	}
915
-
916
-	/**
917
-	 * get the ETag for a file or folder
918
-	 *
919
-	 * @param string $path
920
-	 * @return string
921
-	 */
922
-	static public function getETag($path) {
923
-		return self::$defaultInstance->getETag($path);
924
-	}
72
+    /**
73
+     * @var Mount\Manager $mounts
74
+     */
75
+    private static $mounts;
76
+
77
+    public static $loaded = false;
78
+    /**
79
+     * @var \OC\Files\View $defaultInstance
80
+     */
81
+    static private $defaultInstance;
82
+
83
+    static private $usersSetup = array();
84
+
85
+    static private $normalizedPathCache = null;
86
+
87
+    static private $listeningForProviders = false;
88
+
89
+    /**
90
+     * classname which used for hooks handling
91
+     * used as signalclass in OC_Hooks::emit()
92
+     */
93
+    const CLASSNAME = 'OC_Filesystem';
94
+
95
+    /**
96
+     * signalname emitted before file renaming
97
+     *
98
+     * @param string $oldpath
99
+     * @param string $newpath
100
+     */
101
+    const signal_rename = 'rename';
102
+
103
+    /**
104
+     * signal emitted after file renaming
105
+     *
106
+     * @param string $oldpath
107
+     * @param string $newpath
108
+     */
109
+    const signal_post_rename = 'post_rename';
110
+
111
+    /**
112
+     * signal emitted before file/dir creation
113
+     *
114
+     * @param string $path
115
+     * @param bool $run changing this flag to false in hook handler will cancel event
116
+     */
117
+    const signal_create = 'create';
118
+
119
+    /**
120
+     * signal emitted after file/dir creation
121
+     *
122
+     * @param string $path
123
+     * @param bool $run changing this flag to false in hook handler will cancel event
124
+     */
125
+    const signal_post_create = 'post_create';
126
+
127
+    /**
128
+     * signal emits before file/dir copy
129
+     *
130
+     * @param string $oldpath
131
+     * @param string $newpath
132
+     * @param bool $run changing this flag to false in hook handler will cancel event
133
+     */
134
+    const signal_copy = 'copy';
135
+
136
+    /**
137
+     * signal emits after file/dir copy
138
+     *
139
+     * @param string $oldpath
140
+     * @param string $newpath
141
+     */
142
+    const signal_post_copy = 'post_copy';
143
+
144
+    /**
145
+     * signal emits before file/dir save
146
+     *
147
+     * @param string $path
148
+     * @param bool $run changing this flag to false in hook handler will cancel event
149
+     */
150
+    const signal_write = 'write';
151
+
152
+    /**
153
+     * signal emits after file/dir save
154
+     *
155
+     * @param string $path
156
+     */
157
+    const signal_post_write = 'post_write';
158
+
159
+    /**
160
+     * signal emitted before file/dir update
161
+     *
162
+     * @param string $path
163
+     * @param bool $run changing this flag to false in hook handler will cancel event
164
+     */
165
+    const signal_update = 'update';
166
+
167
+    /**
168
+     * signal emitted after file/dir update
169
+     *
170
+     * @param string $path
171
+     * @param bool $run changing this flag to false in hook handler will cancel event
172
+     */
173
+    const signal_post_update = 'post_update';
174
+
175
+    /**
176
+     * signal emits when reading file/dir
177
+     *
178
+     * @param string $path
179
+     */
180
+    const signal_read = 'read';
181
+
182
+    /**
183
+     * signal emits when removing file/dir
184
+     *
185
+     * @param string $path
186
+     */
187
+    const signal_delete = 'delete';
188
+
189
+    /**
190
+     * parameters definitions for signals
191
+     */
192
+    const signal_param_path = 'path';
193
+    const signal_param_oldpath = 'oldpath';
194
+    const signal_param_newpath = 'newpath';
195
+
196
+    /**
197
+     * run - changing this flag to false in hook handler will cancel event
198
+     */
199
+    const signal_param_run = 'run';
200
+
201
+    const signal_create_mount = 'create_mount';
202
+    const signal_delete_mount = 'delete_mount';
203
+    const signal_param_mount_type = 'mounttype';
204
+    const signal_param_users = 'users';
205
+
206
+    /**
207
+     * @var \OC\Files\Storage\StorageFactory $loader
208
+     */
209
+    private static $loader;
210
+
211
+    /** @var bool */
212
+    private static $logWarningWhenAddingStorageWrapper = true;
213
+
214
+    /**
215
+     * @param bool $shouldLog
216
+     * @return bool previous value
217
+     * @internal
218
+     */
219
+    public static function logWarningWhenAddingStorageWrapper($shouldLog) {
220
+        $previousValue = self::$logWarningWhenAddingStorageWrapper;
221
+        self::$logWarningWhenAddingStorageWrapper = (bool) $shouldLog;
222
+        return $previousValue;
223
+    }
224
+
225
+    /**
226
+     * @param string $wrapperName
227
+     * @param callable $wrapper
228
+     * @param int $priority
229
+     */
230
+    public static function addStorageWrapper($wrapperName, $wrapper, $priority = 50) {
231
+        if (self::$logWarningWhenAddingStorageWrapper) {
232
+            \OC::$server->getLogger()->warning("Storage wrapper '{wrapper}' was not registered via the 'OC_Filesystem - preSetup' hook which could cause potential problems.", [
233
+                'wrapper' => $wrapperName,
234
+                'app' => 'filesystem',
235
+            ]);
236
+        }
237
+
238
+        $mounts = self::getMountManager()->getAll();
239
+        if (!self::getLoader()->addStorageWrapper($wrapperName, $wrapper, $priority, $mounts)) {
240
+            // do not re-wrap if storage with this name already existed
241
+            return;
242
+        }
243
+    }
244
+
245
+    /**
246
+     * Returns the storage factory
247
+     *
248
+     * @return \OCP\Files\Storage\IStorageFactory
249
+     */
250
+    public static function getLoader() {
251
+        if (!self::$loader) {
252
+            self::$loader = new StorageFactory();
253
+        }
254
+        return self::$loader;
255
+    }
256
+
257
+    /**
258
+     * Returns the mount manager
259
+     *
260
+     * @return \OC\Files\Mount\Manager
261
+     */
262
+    public static function getMountManager($user = '') {
263
+        if (!self::$mounts) {
264
+            \OC_Util::setupFS($user);
265
+        }
266
+        return self::$mounts;
267
+    }
268
+
269
+    /**
270
+     * get the mountpoint of the storage object for a path
271
+     * ( note: because a storage is not always mounted inside the fakeroot, the
272
+     * returned mountpoint is relative to the absolute root of the filesystem
273
+     * and doesn't take the chroot into account )
274
+     *
275
+     * @param string $path
276
+     * @return string
277
+     */
278
+    static public function getMountPoint($path) {
279
+        if (!self::$mounts) {
280
+            \OC_Util::setupFS();
281
+        }
282
+        $mount = self::$mounts->find($path);
283
+        if ($mount) {
284
+            return $mount->getMountPoint();
285
+        } else {
286
+            return '';
287
+        }
288
+    }
289
+
290
+    /**
291
+     * get a list of all mount points in a directory
292
+     *
293
+     * @param string $path
294
+     * @return string[]
295
+     */
296
+    static public function getMountPoints($path) {
297
+        if (!self::$mounts) {
298
+            \OC_Util::setupFS();
299
+        }
300
+        $result = array();
301
+        $mounts = self::$mounts->findIn($path);
302
+        foreach ($mounts as $mount) {
303
+            $result[] = $mount->getMountPoint();
304
+        }
305
+        return $result;
306
+    }
307
+
308
+    /**
309
+     * get the storage mounted at $mountPoint
310
+     *
311
+     * @param string $mountPoint
312
+     * @return \OC\Files\Storage\Storage
313
+     */
314
+    public static function getStorage($mountPoint) {
315
+        if (!self::$mounts) {
316
+            \OC_Util::setupFS();
317
+        }
318
+        $mount = self::$mounts->find($mountPoint);
319
+        return $mount->getStorage();
320
+    }
321
+
322
+    /**
323
+     * @param string $id
324
+     * @return Mount\MountPoint[]
325
+     */
326
+    public static function getMountByStorageId($id) {
327
+        if (!self::$mounts) {
328
+            \OC_Util::setupFS();
329
+        }
330
+        return self::$mounts->findByStorageId($id);
331
+    }
332
+
333
+    /**
334
+     * @param int $id
335
+     * @return Mount\MountPoint[]
336
+     */
337
+    public static function getMountByNumericId($id) {
338
+        if (!self::$mounts) {
339
+            \OC_Util::setupFS();
340
+        }
341
+        return self::$mounts->findByNumericId($id);
342
+    }
343
+
344
+    /**
345
+     * resolve a path to a storage and internal path
346
+     *
347
+     * @param string $path
348
+     * @return array an array consisting of the storage and the internal path
349
+     */
350
+    static public function resolvePath($path) {
351
+        if (!self::$mounts) {
352
+            \OC_Util::setupFS();
353
+        }
354
+        $mount = self::$mounts->find($path);
355
+        if ($mount) {
356
+            return array($mount->getStorage(), rtrim($mount->getInternalPath($path), '/'));
357
+        } else {
358
+            return array(null, null);
359
+        }
360
+    }
361
+
362
+    static public function init($user, $root) {
363
+        if (self::$defaultInstance) {
364
+            return false;
365
+        }
366
+        self::getLoader();
367
+        self::$defaultInstance = new View($root);
368
+
369
+        if (!self::$mounts) {
370
+            self::$mounts = \OC::$server->getMountManager();
371
+        }
372
+
373
+        //load custom mount config
374
+        self::initMountPoints($user);
375
+
376
+        self::$loaded = true;
377
+
378
+        return true;
379
+    }
380
+
381
+    static public function initMountManager() {
382
+        if (!self::$mounts) {
383
+            self::$mounts = \OC::$server->getMountManager();
384
+        }
385
+    }
386
+
387
+    /**
388
+     * Initialize system and personal mount points for a user
389
+     *
390
+     * @param string $user
391
+     * @throws \OC\User\NoUserException if the user is not available
392
+     */
393
+    public static function initMountPoints($user = '') {
394
+        if ($user == '') {
395
+            $user = \OC_User::getUser();
396
+        }
397
+        if ($user === null || $user === false || $user === '') {
398
+            throw new \OC\User\NoUserException('Attempted to initialize mount points for null user and no user in session');
399
+        }
400
+
401
+        if (isset(self::$usersSetup[$user])) {
402
+            return;
403
+        }
404
+
405
+        self::$usersSetup[$user] = true;
406
+
407
+        $userManager = \OC::$server->getUserManager();
408
+        $userObject = $userManager->get($user);
409
+
410
+        if (is_null($userObject)) {
411
+            \OCP\Util::writeLog('files', ' Backends provided no user object for ' . $user, \OCP\Util::ERROR);
412
+            // reset flag, this will make it possible to rethrow the exception if called again
413
+            unset(self::$usersSetup[$user]);
414
+            throw new \OC\User\NoUserException('Backends provided no user object for ' . $user);
415
+        }
416
+
417
+        $realUid = $userObject->getUID();
418
+        // workaround in case of different casings
419
+        if ($user !== $realUid) {
420
+            $stack = json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 50));
421
+            \OCP\Util::writeLog('files', 'initMountPoints() called with wrong user casing. This could be a bug. Expected: "' . $realUid . '" got "' . $user . '". Stack: ' . $stack, \OCP\Util::WARN);
422
+            $user = $realUid;
423
+
424
+            // again with the correct casing
425
+            if (isset(self::$usersSetup[$user])) {
426
+                return;
427
+            }
428
+
429
+            self::$usersSetup[$user] = true;
430
+        }
431
+
432
+        if (\OC::$server->getLockdownManager()->canAccessFilesystem()) {
433
+            /** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */
434
+            $mountConfigManager = \OC::$server->getMountProviderCollection();
435
+
436
+            // home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers
437
+            $homeMount = $mountConfigManager->getHomeMountForUser($userObject);
438
+
439
+            self::getMountManager()->addMount($homeMount);
440
+
441
+            \OC\Files\Filesystem::getStorage($user);
442
+
443
+            // Chance to mount for other storages
444
+            if ($userObject) {
445
+                $mounts = $mountConfigManager->addMountForUser($userObject, self::getMountManager());
446
+                $mounts[] = $homeMount;
447
+                $mountConfigManager->registerMounts($userObject, $mounts);
448
+            }
449
+
450
+            self::listenForNewMountProviders($mountConfigManager, $userManager);
451
+        } else {
452
+            self::getMountManager()->addMount(new MountPoint(
453
+                new NullStorage([]),
454
+                '/' . $user
455
+            ));
456
+            self::getMountManager()->addMount(new MountPoint(
457
+                new NullStorage([]),
458
+                '/' . $user . '/files'
459
+            ));
460
+        }
461
+        \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user));
462
+    }
463
+
464
+    /**
465
+     * Get mounts from mount providers that are registered after setup
466
+     *
467
+     * @param MountProviderCollection $mountConfigManager
468
+     * @param IUserManager $userManager
469
+     */
470
+    private static function listenForNewMountProviders(MountProviderCollection $mountConfigManager, IUserManager $userManager) {
471
+        if (!self::$listeningForProviders) {
472
+            self::$listeningForProviders = true;
473
+            $mountConfigManager->listen('\OC\Files\Config', 'registerMountProvider', function (IMountProvider $provider) use ($userManager) {
474
+                foreach (Filesystem::$usersSetup as $user => $setup) {
475
+                    $userObject = $userManager->get($user);
476
+                    if ($userObject) {
477
+                        $mounts = $provider->getMountsForUser($userObject, Filesystem::getLoader());
478
+                        array_walk($mounts, array(self::$mounts, 'addMount'));
479
+                    }
480
+                }
481
+            });
482
+        }
483
+    }
484
+
485
+    /**
486
+     * get the default filesystem view
487
+     *
488
+     * @return View
489
+     */
490
+    static public function getView() {
491
+        return self::$defaultInstance;
492
+    }
493
+
494
+    /**
495
+     * tear down the filesystem, removing all storage providers
496
+     */
497
+    static public function tearDown() {
498
+        self::clearMounts();
499
+        self::$defaultInstance = null;
500
+    }
501
+
502
+    /**
503
+     * get the relative path of the root data directory for the current user
504
+     *
505
+     * @return string
506
+     *
507
+     * Returns path like /admin/files
508
+     */
509
+    static public function getRoot() {
510
+        if (!self::$defaultInstance) {
511
+            return null;
512
+        }
513
+        return self::$defaultInstance->getRoot();
514
+    }
515
+
516
+    /**
517
+     * clear all mounts and storage backends
518
+     */
519
+    public static function clearMounts() {
520
+        if (self::$mounts) {
521
+            self::$usersSetup = array();
522
+            self::$mounts->clear();
523
+        }
524
+    }
525
+
526
+    /**
527
+     * mount an \OC\Files\Storage\Storage in our virtual filesystem
528
+     *
529
+     * @param \OC\Files\Storage\Storage|string $class
530
+     * @param array $arguments
531
+     * @param string $mountpoint
532
+     */
533
+    static public function mount($class, $arguments, $mountpoint) {
534
+        if (!self::$mounts) {
535
+            \OC_Util::setupFS();
536
+        }
537
+        $mount = new Mount\MountPoint($class, $mountpoint, $arguments, self::getLoader());
538
+        self::$mounts->addMount($mount);
539
+    }
540
+
541
+    /**
542
+     * return the path to a local version of the file
543
+     * we need this because we can't know if a file is stored local or not from
544
+     * outside the filestorage and for some purposes a local file is needed
545
+     *
546
+     * @param string $path
547
+     * @return string
548
+     */
549
+    static public function getLocalFile($path) {
550
+        return self::$defaultInstance->getLocalFile($path);
551
+    }
552
+
553
+    /**
554
+     * @param string $path
555
+     * @return string
556
+     */
557
+    static public function getLocalFolder($path) {
558
+        return self::$defaultInstance->getLocalFolder($path);
559
+    }
560
+
561
+    /**
562
+     * return path to file which reflects one visible in browser
563
+     *
564
+     * @param string $path
565
+     * @return string
566
+     */
567
+    static public function getLocalPath($path) {
568
+        $datadir = \OC_User::getHome(\OC_User::getUser()) . '/files';
569
+        $newpath = $path;
570
+        if (strncmp($newpath, $datadir, strlen($datadir)) == 0) {
571
+            $newpath = substr($path, strlen($datadir));
572
+        }
573
+        return $newpath;
574
+    }
575
+
576
+    /**
577
+     * check if the requested path is valid
578
+     *
579
+     * @param string $path
580
+     * @return bool
581
+     */
582
+    static public function isValidPath($path) {
583
+        $path = self::normalizePath($path);
584
+        if (!$path || $path[0] !== '/') {
585
+            $path = '/' . $path;
586
+        }
587
+        if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') {
588
+            return false;
589
+        }
590
+        return true;
591
+    }
592
+
593
+    /**
594
+     * checks if a file is blacklisted for storage in the filesystem
595
+     * Listens to write and rename hooks
596
+     *
597
+     * @param array $data from hook
598
+     */
599
+    static public function isBlacklisted($data) {
600
+        if (isset($data['path'])) {
601
+            $path = $data['path'];
602
+        } else if (isset($data['newpath'])) {
603
+            $path = $data['newpath'];
604
+        }
605
+        if (isset($path)) {
606
+            if (self::isFileBlacklisted($path)) {
607
+                $data['run'] = false;
608
+            }
609
+        }
610
+    }
611
+
612
+    /**
613
+     * @param string $filename
614
+     * @return bool
615
+     */
616
+    static public function isFileBlacklisted($filename) {
617
+        $filename = self::normalizePath($filename);
618
+
619
+        $blacklist = \OC::$server->getConfig()->getSystemValue('blacklisted_files', array('.htaccess'));
620
+        $filename = strtolower(basename($filename));
621
+        return in_array($filename, $blacklist);
622
+    }
623
+
624
+    /**
625
+     * check if the directory should be ignored when scanning
626
+     * NOTE: the special directories . and .. would cause never ending recursion
627
+     *
628
+     * @param String $dir
629
+     * @return boolean
630
+     */
631
+    static public function isIgnoredDir($dir) {
632
+        if ($dir === '.' || $dir === '..') {
633
+            return true;
634
+        }
635
+        return false;
636
+    }
637
+
638
+    /**
639
+     * following functions are equivalent to their php builtin equivalents for arguments/return values.
640
+     */
641
+    static public function mkdir($path) {
642
+        return self::$defaultInstance->mkdir($path);
643
+    }
644
+
645
+    static public function rmdir($path) {
646
+        return self::$defaultInstance->rmdir($path);
647
+    }
648
+
649
+    static public function is_dir($path) {
650
+        return self::$defaultInstance->is_dir($path);
651
+    }
652
+
653
+    static public function is_file($path) {
654
+        return self::$defaultInstance->is_file($path);
655
+    }
656
+
657
+    static public function stat($path) {
658
+        return self::$defaultInstance->stat($path);
659
+    }
660
+
661
+    static public function filetype($path) {
662
+        return self::$defaultInstance->filetype($path);
663
+    }
664
+
665
+    static public function filesize($path) {
666
+        return self::$defaultInstance->filesize($path);
667
+    }
668
+
669
+    static public function readfile($path) {
670
+        return self::$defaultInstance->readfile($path);
671
+    }
672
+
673
+    static public function isCreatable($path) {
674
+        return self::$defaultInstance->isCreatable($path);
675
+    }
676
+
677
+    static public function isReadable($path) {
678
+        return self::$defaultInstance->isReadable($path);
679
+    }
680
+
681
+    static public function isUpdatable($path) {
682
+        return self::$defaultInstance->isUpdatable($path);
683
+    }
684
+
685
+    static public function isDeletable($path) {
686
+        return self::$defaultInstance->isDeletable($path);
687
+    }
688
+
689
+    static public function isSharable($path) {
690
+        return self::$defaultInstance->isSharable($path);
691
+    }
692
+
693
+    static public function file_exists($path) {
694
+        return self::$defaultInstance->file_exists($path);
695
+    }
696
+
697
+    static public function filemtime($path) {
698
+        return self::$defaultInstance->filemtime($path);
699
+    }
700
+
701
+    static public function touch($path, $mtime = null) {
702
+        return self::$defaultInstance->touch($path, $mtime);
703
+    }
704
+
705
+    /**
706
+     * @return string
707
+     */
708
+    static public function file_get_contents($path) {
709
+        return self::$defaultInstance->file_get_contents($path);
710
+    }
711
+
712
+    static public function file_put_contents($path, $data) {
713
+        return self::$defaultInstance->file_put_contents($path, $data);
714
+    }
715
+
716
+    static public function unlink($path) {
717
+        return self::$defaultInstance->unlink($path);
718
+    }
719
+
720
+    static public function rename($path1, $path2) {
721
+        return self::$defaultInstance->rename($path1, $path2);
722
+    }
723
+
724
+    static public function copy($path1, $path2) {
725
+        return self::$defaultInstance->copy($path1, $path2);
726
+    }
727
+
728
+    static public function fopen($path, $mode) {
729
+        return self::$defaultInstance->fopen($path, $mode);
730
+    }
731
+
732
+    /**
733
+     * @return string
734
+     */
735
+    static public function toTmpFile($path) {
736
+        return self::$defaultInstance->toTmpFile($path);
737
+    }
738
+
739
+    static public function fromTmpFile($tmpFile, $path) {
740
+        return self::$defaultInstance->fromTmpFile($tmpFile, $path);
741
+    }
742
+
743
+    static public function getMimeType($path) {
744
+        return self::$defaultInstance->getMimeType($path);
745
+    }
746
+
747
+    static public function hash($type, $path, $raw = false) {
748
+        return self::$defaultInstance->hash($type, $path, $raw);
749
+    }
750
+
751
+    static public function free_space($path = '/') {
752
+        return self::$defaultInstance->free_space($path);
753
+    }
754
+
755
+    static public function search($query) {
756
+        return self::$defaultInstance->search($query);
757
+    }
758
+
759
+    /**
760
+     * @param string $query
761
+     */
762
+    static public function searchByMime($query) {
763
+        return self::$defaultInstance->searchByMime($query);
764
+    }
765
+
766
+    /**
767
+     * @param string|int $tag name or tag id
768
+     * @param string $userId owner of the tags
769
+     * @return FileInfo[] array or file info
770
+     */
771
+    static public function searchByTag($tag, $userId) {
772
+        return self::$defaultInstance->searchByTag($tag, $userId);
773
+    }
774
+
775
+    /**
776
+     * check if a file or folder has been updated since $time
777
+     *
778
+     * @param string $path
779
+     * @param int $time
780
+     * @return bool
781
+     */
782
+    static public function hasUpdated($path, $time) {
783
+        return self::$defaultInstance->hasUpdated($path, $time);
784
+    }
785
+
786
+    /**
787
+     * Fix common problems with a file path
788
+     *
789
+     * @param string $path
790
+     * @param bool $stripTrailingSlash whether to strip the trailing slash
791
+     * @param bool $isAbsolutePath whether the given path is absolute
792
+     * @param bool $keepUnicode true to disable unicode normalization
793
+     * @return string
794
+     */
795
+    public static function normalizePath($path, $stripTrailingSlash = true, $isAbsolutePath = false, $keepUnicode = false) {
796
+        if (is_null(self::$normalizedPathCache)) {
797
+            self::$normalizedPathCache = new CappedMemoryCache();
798
+        }
799
+
800
+        /**
801
+         * FIXME: This is a workaround for existing classes and files which call
802
+         *        this function with another type than a valid string. This
803
+         *        conversion should get removed as soon as all existing
804
+         *        function calls have been fixed.
805
+         */
806
+        $path = (string)$path;
807
+
808
+        $cacheKey = json_encode([$path, $stripTrailingSlash, $isAbsolutePath, $keepUnicode]);
809
+
810
+        if (isset(self::$normalizedPathCache[$cacheKey])) {
811
+            return self::$normalizedPathCache[$cacheKey];
812
+        }
813
+
814
+        if ($path == '') {
815
+            return '/';
816
+        }
817
+
818
+        //normalize unicode if possible
819
+        if (!$keepUnicode) {
820
+            $path = \OC_Util::normalizeUnicode($path);
821
+        }
822
+
823
+        //no windows style slashes
824
+        $path = str_replace('\\', '/', $path);
825
+
826
+        //add leading slash
827
+        if ($path[0] !== '/') {
828
+            $path = '/' . $path;
829
+        }
830
+
831
+        // remove '/./'
832
+        // ugly, but str_replace() can't replace them all in one go
833
+        // as the replacement itself is part of the search string
834
+        // which will only be found during the next iteration
835
+        while (strpos($path, '/./') !== false) {
836
+            $path = str_replace('/./', '/', $path);
837
+        }
838
+        // remove sequences of slashes
839
+        $path = preg_replace('#/{2,}#', '/', $path);
840
+
841
+        //remove trailing slash
842
+        if ($stripTrailingSlash and strlen($path) > 1 and $path[strlen($path) - 1] === '/') {
843
+            $path = substr($path, 0, -1);
844
+        }
845
+
846
+        // remove trailing '/.'
847
+        if (substr($path, -2) == '/.') {
848
+            $path = substr($path, 0, -2);
849
+        }
850
+
851
+        $normalizedPath = $path;
852
+        self::$normalizedPathCache[$cacheKey] = $normalizedPath;
853
+
854
+        return $normalizedPath;
855
+    }
856
+
857
+    /**
858
+     * get the filesystem info
859
+     *
860
+     * @param string $path
861
+     * @param boolean $includeMountPoints whether to add mountpoint sizes,
862
+     * defaults to true
863
+     * @return \OC\Files\FileInfo|bool False if file does not exist
864
+     */
865
+    public static function getFileInfo($path, $includeMountPoints = true) {
866
+        return self::$defaultInstance->getFileInfo($path, $includeMountPoints);
867
+    }
868
+
869
+    /**
870
+     * change file metadata
871
+     *
872
+     * @param string $path
873
+     * @param array $data
874
+     * @return int
875
+     *
876
+     * returns the fileid of the updated file
877
+     */
878
+    public static function putFileInfo($path, $data) {
879
+        return self::$defaultInstance->putFileInfo($path, $data);
880
+    }
881
+
882
+    /**
883
+     * get the content of a directory
884
+     *
885
+     * @param string $directory path under datadirectory
886
+     * @param string $mimetype_filter limit returned content to this mimetype or mimepart
887
+     * @return \OC\Files\FileInfo[]
888
+     */
889
+    public static function getDirectoryContent($directory, $mimetype_filter = '') {
890
+        return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter);
891
+    }
892
+
893
+    /**
894
+     * Get the path of a file by id
895
+     *
896
+     * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file
897
+     *
898
+     * @param int $id
899
+     * @throws NotFoundException
900
+     * @return string
901
+     */
902
+    public static function getPath($id) {
903
+        return self::$defaultInstance->getPath($id);
904
+    }
905
+
906
+    /**
907
+     * Get the owner for a file or folder
908
+     *
909
+     * @param string $path
910
+     * @return string
911
+     */
912
+    public static function getOwner($path) {
913
+        return self::$defaultInstance->getOwner($path);
914
+    }
915
+
916
+    /**
917
+     * get the ETag for a file or folder
918
+     *
919
+     * @param string $path
920
+     * @return string
921
+     */
922
+    static public function getETag($path) {
923
+        return self::$defaultInstance->getETag($path);
924
+    }
925 925
 }
Please login to merge, or discard this patch.
lib/private/Files/View.php 2 patches
Indentation   +2083 added lines, -2083 removed lines patch added patch discarded remove patch
@@ -80,2087 +80,2087 @@
 block discarded – undo
80 80
  * \OC\Files\Storage\Storage object
81 81
  */
82 82
 class View {
83
-	/** @var string */
84
-	private $fakeRoot = '';
85
-
86
-	/**
87
-	 * @var \OCP\Lock\ILockingProvider
88
-	 */
89
-	protected $lockingProvider;
90
-
91
-	private $lockingEnabled;
92
-
93
-	private $updaterEnabled = true;
94
-
95
-	/** @var \OC\User\Manager */
96
-	private $userManager;
97
-
98
-	/** @var \OCP\ILogger */
99
-	private $logger;
100
-
101
-	/**
102
-	 * @param string $root
103
-	 * @throws \Exception If $root contains an invalid path
104
-	 */
105
-	public function __construct($root = '') {
106
-		if (is_null($root)) {
107
-			throw new \InvalidArgumentException('Root can\'t be null');
108
-		}
109
-		if (!Filesystem::isValidPath($root)) {
110
-			throw new \Exception();
111
-		}
112
-
113
-		$this->fakeRoot = $root;
114
-		$this->lockingProvider = \OC::$server->getLockingProvider();
115
-		$this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider);
116
-		$this->userManager = \OC::$server->getUserManager();
117
-		$this->logger = \OC::$server->getLogger();
118
-	}
119
-
120
-	public function getAbsolutePath($path = '/') {
121
-		if ($path === null) {
122
-			return null;
123
-		}
124
-		$this->assertPathLength($path);
125
-		if ($path === '') {
126
-			$path = '/';
127
-		}
128
-		if ($path[0] !== '/') {
129
-			$path = '/' . $path;
130
-		}
131
-		return $this->fakeRoot . $path;
132
-	}
133
-
134
-	/**
135
-	 * change the root to a fake root
136
-	 *
137
-	 * @param string $fakeRoot
138
-	 * @return boolean|null
139
-	 */
140
-	public function chroot($fakeRoot) {
141
-		if (!$fakeRoot == '') {
142
-			if ($fakeRoot[0] !== '/') {
143
-				$fakeRoot = '/' . $fakeRoot;
144
-			}
145
-		}
146
-		$this->fakeRoot = $fakeRoot;
147
-	}
148
-
149
-	/**
150
-	 * get the fake root
151
-	 *
152
-	 * @return string
153
-	 */
154
-	public function getRoot() {
155
-		return $this->fakeRoot;
156
-	}
157
-
158
-	/**
159
-	 * get path relative to the root of the view
160
-	 *
161
-	 * @param string $path
162
-	 * @return string
163
-	 */
164
-	public function getRelativePath($path) {
165
-		$this->assertPathLength($path);
166
-		if ($this->fakeRoot == '') {
167
-			return $path;
168
-		}
169
-
170
-		if (rtrim($path, '/') === rtrim($this->fakeRoot, '/')) {
171
-			return '/';
172
-		}
173
-
174
-		// missing slashes can cause wrong matches!
175
-		$root = rtrim($this->fakeRoot, '/') . '/';
176
-
177
-		if (strpos($path, $root) !== 0) {
178
-			return null;
179
-		} else {
180
-			$path = substr($path, strlen($this->fakeRoot));
181
-			if (strlen($path) === 0) {
182
-				return '/';
183
-			} else {
184
-				return $path;
185
-			}
186
-		}
187
-	}
188
-
189
-	/**
190
-	 * get the mountpoint of the storage object for a path
191
-	 * ( note: because a storage is not always mounted inside the fakeroot, the
192
-	 * returned mountpoint is relative to the absolute root of the filesystem
193
-	 * and does not take the chroot into account )
194
-	 *
195
-	 * @param string $path
196
-	 * @return string
197
-	 */
198
-	public function getMountPoint($path) {
199
-		return Filesystem::getMountPoint($this->getAbsolutePath($path));
200
-	}
201
-
202
-	/**
203
-	 * get the mountpoint of the storage object for a path
204
-	 * ( note: because a storage is not always mounted inside the fakeroot, the
205
-	 * returned mountpoint is relative to the absolute root of the filesystem
206
-	 * and does not take the chroot into account )
207
-	 *
208
-	 * @param string $path
209
-	 * @return \OCP\Files\Mount\IMountPoint
210
-	 */
211
-	public function getMount($path) {
212
-		return Filesystem::getMountManager()->find($this->getAbsolutePath($path));
213
-	}
214
-
215
-	/**
216
-	 * resolve a path to a storage and internal path
217
-	 *
218
-	 * @param string $path
219
-	 * @return array an array consisting of the storage and the internal path
220
-	 */
221
-	public function resolvePath($path) {
222
-		$a = $this->getAbsolutePath($path);
223
-		$p = Filesystem::normalizePath($a);
224
-		return Filesystem::resolvePath($p);
225
-	}
226
-
227
-	/**
228
-	 * return the path to a local version of the file
229
-	 * we need this because we can't know if a file is stored local or not from
230
-	 * outside the filestorage and for some purposes a local file is needed
231
-	 *
232
-	 * @param string $path
233
-	 * @return string
234
-	 */
235
-	public function getLocalFile($path) {
236
-		$parent = substr($path, 0, strrpos($path, '/'));
237
-		$path = $this->getAbsolutePath($path);
238
-		list($storage, $internalPath) = Filesystem::resolvePath($path);
239
-		if (Filesystem::isValidPath($parent) and $storage) {
240
-			return $storage->getLocalFile($internalPath);
241
-		} else {
242
-			return null;
243
-		}
244
-	}
245
-
246
-	/**
247
-	 * @param string $path
248
-	 * @return string
249
-	 */
250
-	public function getLocalFolder($path) {
251
-		$parent = substr($path, 0, strrpos($path, '/'));
252
-		$path = $this->getAbsolutePath($path);
253
-		list($storage, $internalPath) = Filesystem::resolvePath($path);
254
-		if (Filesystem::isValidPath($parent) and $storage) {
255
-			return $storage->getLocalFolder($internalPath);
256
-		} else {
257
-			return null;
258
-		}
259
-	}
260
-
261
-	/**
262
-	 * the following functions operate with arguments and return values identical
263
-	 * to those of their PHP built-in equivalents. Mostly they are merely wrappers
264
-	 * for \OC\Files\Storage\Storage via basicOperation().
265
-	 */
266
-	public function mkdir($path) {
267
-		return $this->basicOperation('mkdir', $path, array('create', 'write'));
268
-	}
269
-
270
-	/**
271
-	 * remove mount point
272
-	 *
273
-	 * @param \OC\Files\Mount\MoveableMount $mount
274
-	 * @param string $path relative to data/
275
-	 * @return boolean
276
-	 */
277
-	protected function removeMount($mount, $path) {
278
-		if ($mount instanceof MoveableMount) {
279
-			// cut of /user/files to get the relative path to data/user/files
280
-			$pathParts = explode('/', $path, 4);
281
-			$relPath = '/' . $pathParts[3];
282
-			$this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true);
283
-			\OC_Hook::emit(
284
-				Filesystem::CLASSNAME, "umount",
285
-				array(Filesystem::signal_param_path => $relPath)
286
-			);
287
-			$this->changeLock($relPath, ILockingProvider::LOCK_EXCLUSIVE, true);
288
-			$result = $mount->removeMount();
289
-			$this->changeLock($relPath, ILockingProvider::LOCK_SHARED, true);
290
-			if ($result) {
291
-				\OC_Hook::emit(
292
-					Filesystem::CLASSNAME, "post_umount",
293
-					array(Filesystem::signal_param_path => $relPath)
294
-				);
295
-			}
296
-			$this->unlockFile($relPath, ILockingProvider::LOCK_SHARED, true);
297
-			return $result;
298
-		} else {
299
-			// do not allow deleting the storage's root / the mount point
300
-			// because for some storages it might delete the whole contents
301
-			// but isn't supposed to work that way
302
-			return false;
303
-		}
304
-	}
305
-
306
-	public function disableCacheUpdate() {
307
-		$this->updaterEnabled = false;
308
-	}
309
-
310
-	public function enableCacheUpdate() {
311
-		$this->updaterEnabled = true;
312
-	}
313
-
314
-	protected function writeUpdate(Storage $storage, $internalPath, $time = null) {
315
-		if ($this->updaterEnabled) {
316
-			if (is_null($time)) {
317
-				$time = time();
318
-			}
319
-			$storage->getUpdater()->update($internalPath, $time);
320
-		}
321
-	}
322
-
323
-	protected function removeUpdate(Storage $storage, $internalPath) {
324
-		if ($this->updaterEnabled) {
325
-			$storage->getUpdater()->remove($internalPath);
326
-		}
327
-	}
328
-
329
-	protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) {
330
-		if ($this->updaterEnabled) {
331
-			$targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
332
-		}
333
-	}
334
-
335
-	/**
336
-	 * @param string $path
337
-	 * @return bool|mixed
338
-	 */
339
-	public function rmdir($path) {
340
-		$absolutePath = $this->getAbsolutePath($path);
341
-		$mount = Filesystem::getMountManager()->find($absolutePath);
342
-		if ($mount->getInternalPath($absolutePath) === '') {
343
-			return $this->removeMount($mount, $absolutePath);
344
-		}
345
-		if ($this->is_dir($path)) {
346
-			$result = $this->basicOperation('rmdir', $path, array('delete'));
347
-		} else {
348
-			$result = false;
349
-		}
350
-
351
-		if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
352
-			$storage = $mount->getStorage();
353
-			$internalPath = $mount->getInternalPath($absolutePath);
354
-			$storage->getUpdater()->remove($internalPath);
355
-		}
356
-		return $result;
357
-	}
358
-
359
-	/**
360
-	 * @param string $path
361
-	 * @return resource
362
-	 */
363
-	public function opendir($path) {
364
-		return $this->basicOperation('opendir', $path, array('read'));
365
-	}
366
-
367
-	/**
368
-	 * @param string $path
369
-	 * @return bool|mixed
370
-	 */
371
-	public function is_dir($path) {
372
-		if ($path == '/') {
373
-			return true;
374
-		}
375
-		return $this->basicOperation('is_dir', $path);
376
-	}
377
-
378
-	/**
379
-	 * @param string $path
380
-	 * @return bool|mixed
381
-	 */
382
-	public function is_file($path) {
383
-		if ($path == '/') {
384
-			return false;
385
-		}
386
-		return $this->basicOperation('is_file', $path);
387
-	}
388
-
389
-	/**
390
-	 * @param string $path
391
-	 * @return mixed
392
-	 */
393
-	public function stat($path) {
394
-		return $this->basicOperation('stat', $path);
395
-	}
396
-
397
-	/**
398
-	 * @param string $path
399
-	 * @return mixed
400
-	 */
401
-	public function filetype($path) {
402
-		return $this->basicOperation('filetype', $path);
403
-	}
404
-
405
-	/**
406
-	 * @param string $path
407
-	 * @return mixed
408
-	 */
409
-	public function filesize($path) {
410
-		return $this->basicOperation('filesize', $path);
411
-	}
412
-
413
-	/**
414
-	 * @param string $path
415
-	 * @return bool|mixed
416
-	 * @throws \OCP\Files\InvalidPathException
417
-	 */
418
-	public function readfile($path) {
419
-		$this->assertPathLength($path);
420
-		@ob_end_clean();
421
-		$handle = $this->fopen($path, 'rb');
422
-		if ($handle) {
423
-			$chunkSize = 8192; // 8 kB chunks
424
-			while (!feof($handle)) {
425
-				echo fread($handle, $chunkSize);
426
-				flush();
427
-			}
428
-			fclose($handle);
429
-			$size = $this->filesize($path);
430
-			return $size;
431
-		}
432
-		return false;
433
-	}
434
-
435
-	/**
436
-	 * @param string $path
437
-	 * @param int $from
438
-	 * @param int $to
439
-	 * @return bool|mixed
440
-	 * @throws \OCP\Files\InvalidPathException
441
-	 * @throws \OCP\Files\UnseekableException
442
-	 */
443
-	public function readfilePart($path, $from, $to) {
444
-		$this->assertPathLength($path);
445
-		@ob_end_clean();
446
-		$handle = $this->fopen($path, 'rb');
447
-		if ($handle) {
448
-			$chunkSize = 8192; // 8 kB chunks
449
-			$startReading = true;
450
-
451
-			if ($from !== 0 && $from !== '0' && fseek($handle, $from) !== 0) {
452
-				// forward file handle via chunked fread because fseek seem to have failed
453
-
454
-				$end = $from + 1;
455
-				while (!feof($handle) && ftell($handle) < $end) {
456
-					$len = $from - ftell($handle);
457
-					if ($len > $chunkSize) {
458
-						$len = $chunkSize;
459
-					}
460
-					$result = fread($handle, $len);
461
-
462
-					if ($result === false) {
463
-						$startReading = false;
464
-						break;
465
-					}
466
-				}
467
-			}
468
-
469
-			if ($startReading) {
470
-				$end = $to + 1;
471
-				while (!feof($handle) && ftell($handle) < $end) {
472
-					$len = $end - ftell($handle);
473
-					if ($len > $chunkSize) {
474
-						$len = $chunkSize;
475
-					}
476
-					echo fread($handle, $len);
477
-					flush();
478
-				}
479
-				$size = ftell($handle) - $from;
480
-				return $size;
481
-			}
482
-
483
-			throw new \OCP\Files\UnseekableException('fseek error');
484
-		}
485
-		return false;
486
-	}
487
-
488
-	/**
489
-	 * @param string $path
490
-	 * @return mixed
491
-	 */
492
-	public function isCreatable($path) {
493
-		return $this->basicOperation('isCreatable', $path);
494
-	}
495
-
496
-	/**
497
-	 * @param string $path
498
-	 * @return mixed
499
-	 */
500
-	public function isReadable($path) {
501
-		return $this->basicOperation('isReadable', $path);
502
-	}
503
-
504
-	/**
505
-	 * @param string $path
506
-	 * @return mixed
507
-	 */
508
-	public function isUpdatable($path) {
509
-		return $this->basicOperation('isUpdatable', $path);
510
-	}
511
-
512
-	/**
513
-	 * @param string $path
514
-	 * @return bool|mixed
515
-	 */
516
-	public function isDeletable($path) {
517
-		$absolutePath = $this->getAbsolutePath($path);
518
-		$mount = Filesystem::getMountManager()->find($absolutePath);
519
-		if ($mount->getInternalPath($absolutePath) === '') {
520
-			return $mount instanceof MoveableMount;
521
-		}
522
-		return $this->basicOperation('isDeletable', $path);
523
-	}
524
-
525
-	/**
526
-	 * @param string $path
527
-	 * @return mixed
528
-	 */
529
-	public function isSharable($path) {
530
-		return $this->basicOperation('isSharable', $path);
531
-	}
532
-
533
-	/**
534
-	 * @param string $path
535
-	 * @return bool|mixed
536
-	 */
537
-	public function file_exists($path) {
538
-		if ($path == '/') {
539
-			return true;
540
-		}
541
-		return $this->basicOperation('file_exists', $path);
542
-	}
543
-
544
-	/**
545
-	 * @param string $path
546
-	 * @return mixed
547
-	 */
548
-	public function filemtime($path) {
549
-		return $this->basicOperation('filemtime', $path);
550
-	}
551
-
552
-	/**
553
-	 * @param string $path
554
-	 * @param int|string $mtime
555
-	 * @return bool
556
-	 */
557
-	public function touch($path, $mtime = null) {
558
-		if (!is_null($mtime) and !is_numeric($mtime)) {
559
-			$mtime = strtotime($mtime);
560
-		}
561
-
562
-		$hooks = array('touch');
563
-
564
-		if (!$this->file_exists($path)) {
565
-			$hooks[] = 'create';
566
-			$hooks[] = 'write';
567
-		}
568
-		$result = $this->basicOperation('touch', $path, $hooks, $mtime);
569
-		if (!$result) {
570
-			// If create file fails because of permissions on external storage like SMB folders,
571
-			// check file exists and return false if not.
572
-			if (!$this->file_exists($path)) {
573
-				return false;
574
-			}
575
-			if (is_null($mtime)) {
576
-				$mtime = time();
577
-			}
578
-			//if native touch fails, we emulate it by changing the mtime in the cache
579
-			$this->putFileInfo($path, array('mtime' => floor($mtime)));
580
-		}
581
-		return true;
582
-	}
583
-
584
-	/**
585
-	 * @param string $path
586
-	 * @return mixed
587
-	 */
588
-	public function file_get_contents($path) {
589
-		return $this->basicOperation('file_get_contents', $path, array('read'));
590
-	}
591
-
592
-	/**
593
-	 * @param bool $exists
594
-	 * @param string $path
595
-	 * @param bool $run
596
-	 */
597
-	protected function emit_file_hooks_pre($exists, $path, &$run) {
598
-		if (!$exists) {
599
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array(
600
-				Filesystem::signal_param_path => $this->getHookPath($path),
601
-				Filesystem::signal_param_run => &$run,
602
-			));
603
-		} else {
604
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array(
605
-				Filesystem::signal_param_path => $this->getHookPath($path),
606
-				Filesystem::signal_param_run => &$run,
607
-			));
608
-		}
609
-		\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array(
610
-			Filesystem::signal_param_path => $this->getHookPath($path),
611
-			Filesystem::signal_param_run => &$run,
612
-		));
613
-	}
614
-
615
-	/**
616
-	 * @param bool $exists
617
-	 * @param string $path
618
-	 */
619
-	protected function emit_file_hooks_post($exists, $path) {
620
-		if (!$exists) {
621
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array(
622
-				Filesystem::signal_param_path => $this->getHookPath($path),
623
-			));
624
-		} else {
625
-			\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array(
626
-				Filesystem::signal_param_path => $this->getHookPath($path),
627
-			));
628
-		}
629
-		\OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array(
630
-			Filesystem::signal_param_path => $this->getHookPath($path),
631
-		));
632
-	}
633
-
634
-	/**
635
-	 * @param string $path
636
-	 * @param mixed $data
637
-	 * @return bool|mixed
638
-	 * @throws \Exception
639
-	 */
640
-	public function file_put_contents($path, $data) {
641
-		if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier
642
-			$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
643
-			if (Filesystem::isValidPath($path)
644
-				and !Filesystem::isFileBlacklisted($path)
645
-			) {
646
-				$path = $this->getRelativePath($absolutePath);
647
-
648
-				$this->lockFile($path, ILockingProvider::LOCK_SHARED);
649
-
650
-				$exists = $this->file_exists($path);
651
-				$run = true;
652
-				if ($this->shouldEmitHooks($path)) {
653
-					$this->emit_file_hooks_pre($exists, $path, $run);
654
-				}
655
-				if (!$run) {
656
-					$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
657
-					return false;
658
-				}
659
-
660
-				$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
661
-
662
-				/** @var \OC\Files\Storage\Storage $storage */
663
-				list($storage, $internalPath) = $this->resolvePath($path);
664
-				$target = $storage->fopen($internalPath, 'w');
665
-				if ($target) {
666
-					list (, $result) = \OC_Helper::streamCopy($data, $target);
667
-					fclose($target);
668
-					fclose($data);
669
-
670
-					$this->writeUpdate($storage, $internalPath);
671
-
672
-					$this->changeLock($path, ILockingProvider::LOCK_SHARED);
673
-
674
-					if ($this->shouldEmitHooks($path) && $result !== false) {
675
-						$this->emit_file_hooks_post($exists, $path);
676
-					}
677
-					$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
678
-					return $result;
679
-				} else {
680
-					$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
681
-					return false;
682
-				}
683
-			} else {
684
-				return false;
685
-			}
686
-		} else {
687
-			$hooks = ($this->file_exists($path)) ? array('update', 'write') : array('create', 'write');
688
-			return $this->basicOperation('file_put_contents', $path, $hooks, $data);
689
-		}
690
-	}
691
-
692
-	/**
693
-	 * @param string $path
694
-	 * @return bool|mixed
695
-	 */
696
-	public function unlink($path) {
697
-		if ($path === '' || $path === '/') {
698
-			// do not allow deleting the root
699
-			return false;
700
-		}
701
-		$postFix = ($path[strlen($path) - 1] === '/') ? '/' : '';
702
-		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
703
-		$mount = Filesystem::getMountManager()->find($absolutePath . $postFix);
704
-		if ($mount and $mount->getInternalPath($absolutePath) === '') {
705
-			return $this->removeMount($mount, $absolutePath);
706
-		}
707
-		if ($this->is_dir($path)) {
708
-			$result = $this->basicOperation('rmdir', $path, ['delete']);
709
-		} else {
710
-			$result = $this->basicOperation('unlink', $path, ['delete']);
711
-		}
712
-		if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
713
-			$storage = $mount->getStorage();
714
-			$internalPath = $mount->getInternalPath($absolutePath);
715
-			$storage->getUpdater()->remove($internalPath);
716
-			return true;
717
-		} else {
718
-			return $result;
719
-		}
720
-	}
721
-
722
-	/**
723
-	 * @param string $directory
724
-	 * @return bool|mixed
725
-	 */
726
-	public function deleteAll($directory) {
727
-		return $this->rmdir($directory);
728
-	}
729
-
730
-	/**
731
-	 * Rename/move a file or folder from the source path to target path.
732
-	 *
733
-	 * @param string $path1 source path
734
-	 * @param string $path2 target path
735
-	 *
736
-	 * @return bool|mixed
737
-	 */
738
-	public function rename($path1, $path2) {
739
-		$absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
740
-		$absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
741
-		$result = false;
742
-		if (
743
-			Filesystem::isValidPath($path2)
744
-			and Filesystem::isValidPath($path1)
745
-			and !Filesystem::isFileBlacklisted($path2)
746
-		) {
747
-			$path1 = $this->getRelativePath($absolutePath1);
748
-			$path2 = $this->getRelativePath($absolutePath2);
749
-			$exists = $this->file_exists($path2);
750
-
751
-			if ($path1 == null or $path2 == null) {
752
-				return false;
753
-			}
754
-
755
-			$this->lockFile($path1, ILockingProvider::LOCK_SHARED, true);
756
-			try {
757
-				$this->lockFile($path2, ILockingProvider::LOCK_SHARED, true);
758
-
759
-				$run = true;
760
-				if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) {
761
-					// if it was a rename from a part file to a regular file it was a write and not a rename operation
762
-					$this->emit_file_hooks_pre($exists, $path2, $run);
763
-				} elseif ($this->shouldEmitHooks($path1)) {
764
-					\OC_Hook::emit(
765
-						Filesystem::CLASSNAME, Filesystem::signal_rename,
766
-						array(
767
-							Filesystem::signal_param_oldpath => $this->getHookPath($path1),
768
-							Filesystem::signal_param_newpath => $this->getHookPath($path2),
769
-							Filesystem::signal_param_run => &$run
770
-						)
771
-					);
772
-				}
773
-				if ($run) {
774
-					$this->verifyPath(dirname($path2), basename($path2));
775
-
776
-					$manager = Filesystem::getMountManager();
777
-					$mount1 = $this->getMount($path1);
778
-					$mount2 = $this->getMount($path2);
779
-					$storage1 = $mount1->getStorage();
780
-					$storage2 = $mount2->getStorage();
781
-					$internalPath1 = $mount1->getInternalPath($absolutePath1);
782
-					$internalPath2 = $mount2->getInternalPath($absolutePath2);
783
-
784
-					$this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true);
785
-					try {
786
-						$this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true);
787
-
788
-						if ($internalPath1 === '') {
789
-							if ($mount1 instanceof MoveableMount) {
790
-								if ($this->isTargetAllowed($absolutePath2)) {
791
-									/**
792
-									 * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1
793
-									 */
794
-									$sourceMountPoint = $mount1->getMountPoint();
795
-									$result = $mount1->moveMount($absolutePath2);
796
-									$manager->moveMount($sourceMountPoint, $mount1->getMountPoint());
797
-								} else {
798
-									$result = false;
799
-								}
800
-							} else {
801
-								$result = false;
802
-							}
803
-							// moving a file/folder within the same mount point
804
-						} elseif ($storage1 === $storage2) {
805
-							if ($storage1) {
806
-								$result = $storage1->rename($internalPath1, $internalPath2);
807
-							} else {
808
-								$result = false;
809
-							}
810
-							// moving a file/folder between storages (from $storage1 to $storage2)
811
-						} else {
812
-							$result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
813
-						}
814
-
815
-						if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
816
-							// if it was a rename from a part file to a regular file it was a write and not a rename operation
817
-							$this->writeUpdate($storage2, $internalPath2);
818
-						} else if ($result) {
819
-							if ($internalPath1 !== '') { // don't do a cache update for moved mounts
820
-								$this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2);
821
-							}
822
-						}
823
-					} catch(\Exception $e) {
824
-						throw $e;
825
-					} finally {
826
-						$this->changeLock($path1, ILockingProvider::LOCK_SHARED, true);
827
-						$this->changeLock($path2, ILockingProvider::LOCK_SHARED, true);
828
-					}
829
-
830
-					if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
831
-						if ($this->shouldEmitHooks()) {
832
-							$this->emit_file_hooks_post($exists, $path2);
833
-						}
834
-					} elseif ($result) {
835
-						if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) {
836
-							\OC_Hook::emit(
837
-								Filesystem::CLASSNAME,
838
-								Filesystem::signal_post_rename,
839
-								array(
840
-									Filesystem::signal_param_oldpath => $this->getHookPath($path1),
841
-									Filesystem::signal_param_newpath => $this->getHookPath($path2)
842
-								)
843
-							);
844
-						}
845
-					}
846
-				}
847
-			} catch(\Exception $e) {
848
-				throw $e;
849
-			} finally {
850
-				$this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true);
851
-				$this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true);
852
-			}
853
-		}
854
-		return $result;
855
-	}
856
-
857
-	/**
858
-	 * Copy a file/folder from the source path to target path
859
-	 *
860
-	 * @param string $path1 source path
861
-	 * @param string $path2 target path
862
-	 * @param bool $preserveMtime whether to preserve mtime on the copy
863
-	 *
864
-	 * @return bool|mixed
865
-	 */
866
-	public function copy($path1, $path2, $preserveMtime = false) {
867
-		$absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
868
-		$absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
869
-		$result = false;
870
-		if (
871
-			Filesystem::isValidPath($path2)
872
-			and Filesystem::isValidPath($path1)
873
-			and !Filesystem::isFileBlacklisted($path2)
874
-		) {
875
-			$path1 = $this->getRelativePath($absolutePath1);
876
-			$path2 = $this->getRelativePath($absolutePath2);
877
-
878
-			if ($path1 == null or $path2 == null) {
879
-				return false;
880
-			}
881
-			$run = true;
882
-
883
-			$this->lockFile($path2, ILockingProvider::LOCK_SHARED);
884
-			$this->lockFile($path1, ILockingProvider::LOCK_SHARED);
885
-			$lockTypePath1 = ILockingProvider::LOCK_SHARED;
886
-			$lockTypePath2 = ILockingProvider::LOCK_SHARED;
887
-
888
-			try {
889
-
890
-				$exists = $this->file_exists($path2);
891
-				if ($this->shouldEmitHooks()) {
892
-					\OC_Hook::emit(
893
-						Filesystem::CLASSNAME,
894
-						Filesystem::signal_copy,
895
-						array(
896
-							Filesystem::signal_param_oldpath => $this->getHookPath($path1),
897
-							Filesystem::signal_param_newpath => $this->getHookPath($path2),
898
-							Filesystem::signal_param_run => &$run
899
-						)
900
-					);
901
-					$this->emit_file_hooks_pre($exists, $path2, $run);
902
-				}
903
-				if ($run) {
904
-					$mount1 = $this->getMount($path1);
905
-					$mount2 = $this->getMount($path2);
906
-					$storage1 = $mount1->getStorage();
907
-					$internalPath1 = $mount1->getInternalPath($absolutePath1);
908
-					$storage2 = $mount2->getStorage();
909
-					$internalPath2 = $mount2->getInternalPath($absolutePath2);
910
-
911
-					$this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE);
912
-					$lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE;
913
-
914
-					if ($mount1->getMountPoint() == $mount2->getMountPoint()) {
915
-						if ($storage1) {
916
-							$result = $storage1->copy($internalPath1, $internalPath2);
917
-						} else {
918
-							$result = false;
919
-						}
920
-					} else {
921
-						$result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2);
922
-					}
923
-
924
-					$this->writeUpdate($storage2, $internalPath2);
925
-
926
-					$this->changeLock($path2, ILockingProvider::LOCK_SHARED);
927
-					$lockTypePath2 = ILockingProvider::LOCK_SHARED;
928
-
929
-					if ($this->shouldEmitHooks() && $result !== false) {
930
-						\OC_Hook::emit(
931
-							Filesystem::CLASSNAME,
932
-							Filesystem::signal_post_copy,
933
-							array(
934
-								Filesystem::signal_param_oldpath => $this->getHookPath($path1),
935
-								Filesystem::signal_param_newpath => $this->getHookPath($path2)
936
-							)
937
-						);
938
-						$this->emit_file_hooks_post($exists, $path2);
939
-					}
940
-
941
-				}
942
-			} catch (\Exception $e) {
943
-				$this->unlockFile($path2, $lockTypePath2);
944
-				$this->unlockFile($path1, $lockTypePath1);
945
-				throw $e;
946
-			}
947
-
948
-			$this->unlockFile($path2, $lockTypePath2);
949
-			$this->unlockFile($path1, $lockTypePath1);
950
-
951
-		}
952
-		return $result;
953
-	}
954
-
955
-	/**
956
-	 * @param string $path
957
-	 * @param string $mode 'r' or 'w'
958
-	 * @return resource
959
-	 */
960
-	public function fopen($path, $mode) {
961
-		$mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support
962
-		$hooks = array();
963
-		switch ($mode) {
964
-			case 'r':
965
-				$hooks[] = 'read';
966
-				break;
967
-			case 'r+':
968
-			case 'w+':
969
-			case 'x+':
970
-			case 'a+':
971
-				$hooks[] = 'read';
972
-				$hooks[] = 'write';
973
-				break;
974
-			case 'w':
975
-			case 'x':
976
-			case 'a':
977
-				$hooks[] = 'write';
978
-				break;
979
-			default:
980
-				\OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR);
981
-		}
982
-
983
-		if ($mode !== 'r' && $mode !== 'w') {
984
-			\OC::$server->getLogger()->info('Trying to open a file with a mode other than "r" or "w" can cause severe performance issues with some backends');
985
-		}
986
-
987
-		return $this->basicOperation('fopen', $path, $hooks, $mode);
988
-	}
989
-
990
-	/**
991
-	 * @param string $path
992
-	 * @return bool|string
993
-	 * @throws \OCP\Files\InvalidPathException
994
-	 */
995
-	public function toTmpFile($path) {
996
-		$this->assertPathLength($path);
997
-		if (Filesystem::isValidPath($path)) {
998
-			$source = $this->fopen($path, 'r');
999
-			if ($source) {
1000
-				$extension = pathinfo($path, PATHINFO_EXTENSION);
1001
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension);
1002
-				file_put_contents($tmpFile, $source);
1003
-				return $tmpFile;
1004
-			} else {
1005
-				return false;
1006
-			}
1007
-		} else {
1008
-			return false;
1009
-		}
1010
-	}
1011
-
1012
-	/**
1013
-	 * @param string $tmpFile
1014
-	 * @param string $path
1015
-	 * @return bool|mixed
1016
-	 * @throws \OCP\Files\InvalidPathException
1017
-	 */
1018
-	public function fromTmpFile($tmpFile, $path) {
1019
-		$this->assertPathLength($path);
1020
-		if (Filesystem::isValidPath($path)) {
1021
-
1022
-			// Get directory that the file is going into
1023
-			$filePath = dirname($path);
1024
-
1025
-			// Create the directories if any
1026
-			if (!$this->file_exists($filePath)) {
1027
-				$result = $this->createParentDirectories($filePath);
1028
-				if ($result === false) {
1029
-					return false;
1030
-				}
1031
-			}
1032
-
1033
-			$source = fopen($tmpFile, 'r');
1034
-			if ($source) {
1035
-				$result = $this->file_put_contents($path, $source);
1036
-				// $this->file_put_contents() might have already closed
1037
-				// the resource, so we check it, before trying to close it
1038
-				// to avoid messages in the error log.
1039
-				if (is_resource($source)) {
1040
-					fclose($source);
1041
-				}
1042
-				unlink($tmpFile);
1043
-				return $result;
1044
-			} else {
1045
-				return false;
1046
-			}
1047
-		} else {
1048
-			return false;
1049
-		}
1050
-	}
1051
-
1052
-
1053
-	/**
1054
-	 * @param string $path
1055
-	 * @return mixed
1056
-	 * @throws \OCP\Files\InvalidPathException
1057
-	 */
1058
-	public function getMimeType($path) {
1059
-		$this->assertPathLength($path);
1060
-		return $this->basicOperation('getMimeType', $path);
1061
-	}
1062
-
1063
-	/**
1064
-	 * @param string $type
1065
-	 * @param string $path
1066
-	 * @param bool $raw
1067
-	 * @return bool|null|string
1068
-	 */
1069
-	public function hash($type, $path, $raw = false) {
1070
-		$postFix = ($path[strlen($path) - 1] === '/') ? '/' : '';
1071
-		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1072
-		if (Filesystem::isValidPath($path)) {
1073
-			$path = $this->getRelativePath($absolutePath);
1074
-			if ($path == null) {
1075
-				return false;
1076
-			}
1077
-			if ($this->shouldEmitHooks($path)) {
1078
-				\OC_Hook::emit(
1079
-					Filesystem::CLASSNAME,
1080
-					Filesystem::signal_read,
1081
-					array(Filesystem::signal_param_path => $this->getHookPath($path))
1082
-				);
1083
-			}
1084
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1085
-			if ($storage) {
1086
-				$result = $storage->hash($type, $internalPath, $raw);
1087
-				return $result;
1088
-			}
1089
-		}
1090
-		return null;
1091
-	}
1092
-
1093
-	/**
1094
-	 * @param string $path
1095
-	 * @return mixed
1096
-	 * @throws \OCP\Files\InvalidPathException
1097
-	 */
1098
-	public function free_space($path = '/') {
1099
-		$this->assertPathLength($path);
1100
-		$result = $this->basicOperation('free_space', $path);
1101
-		if ($result === null) {
1102
-			throw new InvalidPathException();
1103
-		}
1104
-		return $result;
1105
-	}
1106
-
1107
-	/**
1108
-	 * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage
1109
-	 *
1110
-	 * @param string $operation
1111
-	 * @param string $path
1112
-	 * @param array $hooks (optional)
1113
-	 * @param mixed $extraParam (optional)
1114
-	 * @return mixed
1115
-	 * @throws \Exception
1116
-	 *
1117
-	 * This method takes requests for basic filesystem functions (e.g. reading & writing
1118
-	 * files), processes hooks and proxies, sanitises paths, and finally passes them on to
1119
-	 * \OC\Files\Storage\Storage for delegation to a storage backend for execution
1120
-	 */
1121
-	private function basicOperation($operation, $path, $hooks = [], $extraParam = null) {
1122
-		$postFix = ($path[strlen($path) - 1] === '/') ? '/' : '';
1123
-		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1124
-		if (Filesystem::isValidPath($path)
1125
-			and !Filesystem::isFileBlacklisted($path)
1126
-		) {
1127
-			$path = $this->getRelativePath($absolutePath);
1128
-			if ($path == null) {
1129
-				return false;
1130
-			}
1131
-
1132
-			if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) {
1133
-				// always a shared lock during pre-hooks so the hook can read the file
1134
-				$this->lockFile($path, ILockingProvider::LOCK_SHARED);
1135
-			}
1136
-
1137
-			$run = $this->runHooks($hooks, $path);
1138
-			/** @var \OC\Files\Storage\Storage $storage */
1139
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1140
-			if ($run and $storage) {
1141
-				if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1142
-					$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
1143
-				}
1144
-				try {
1145
-					if (!is_null($extraParam)) {
1146
-						$result = $storage->$operation($internalPath, $extraParam);
1147
-					} else {
1148
-						$result = $storage->$operation($internalPath);
1149
-					}
1150
-				} catch (\Exception $e) {
1151
-					if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1152
-						$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1153
-					} else if (in_array('read', $hooks)) {
1154
-						$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1155
-					}
1156
-					throw $e;
1157
-				}
1158
-
1159
-				if ($result && in_array('delete', $hooks) and $result) {
1160
-					$this->removeUpdate($storage, $internalPath);
1161
-				}
1162
-				if ($result && in_array('write', $hooks) and $operation !== 'fopen') {
1163
-					$this->writeUpdate($storage, $internalPath);
1164
-				}
1165
-				if ($result && in_array('touch', $hooks)) {
1166
-					$this->writeUpdate($storage, $internalPath, $extraParam);
1167
-				}
1168
-
1169
-				if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) {
1170
-					$this->changeLock($path, ILockingProvider::LOCK_SHARED);
1171
-				}
1172
-
1173
-				$unlockLater = false;
1174
-				if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) {
1175
-					$unlockLater = true;
1176
-					// make sure our unlocking callback will still be called if connection is aborted
1177
-					ignore_user_abort(true);
1178
-					$result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) {
1179
-						if (in_array('write', $hooks)) {
1180
-							$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1181
-						} else if (in_array('read', $hooks)) {
1182
-							$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1183
-						}
1184
-					});
1185
-				}
1186
-
1187
-				if ($this->shouldEmitHooks($path) && $result !== false) {
1188
-					if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open
1189
-						$this->runHooks($hooks, $path, true);
1190
-					}
1191
-				}
1192
-
1193
-				if (!$unlockLater
1194
-					&& (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks))
1195
-				) {
1196
-					$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1197
-				}
1198
-				return $result;
1199
-			} else {
1200
-				$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1201
-			}
1202
-		}
1203
-		return null;
1204
-	}
1205
-
1206
-	/**
1207
-	 * get the path relative to the default root for hook usage
1208
-	 *
1209
-	 * @param string $path
1210
-	 * @return string
1211
-	 */
1212
-	private function getHookPath($path) {
1213
-		if (!Filesystem::getView()) {
1214
-			return $path;
1215
-		}
1216
-		return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path));
1217
-	}
1218
-
1219
-	private function shouldEmitHooks($path = '') {
1220
-		if ($path && Cache\Scanner::isPartialFile($path)) {
1221
-			return false;
1222
-		}
1223
-		if (!Filesystem::$loaded) {
1224
-			return false;
1225
-		}
1226
-		$defaultRoot = Filesystem::getRoot();
1227
-		if ($defaultRoot === null) {
1228
-			return false;
1229
-		}
1230
-		if ($this->fakeRoot === $defaultRoot) {
1231
-			return true;
1232
-		}
1233
-		$fullPath = $this->getAbsolutePath($path);
1234
-
1235
-		if ($fullPath === $defaultRoot) {
1236
-			return true;
1237
-		}
1238
-
1239
-		return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
1240
-	}
1241
-
1242
-	/**
1243
-	 * @param string[] $hooks
1244
-	 * @param string $path
1245
-	 * @param bool $post
1246
-	 * @return bool
1247
-	 */
1248
-	private function runHooks($hooks, $path, $post = false) {
1249
-		$relativePath = $path;
1250
-		$path = $this->getHookPath($path);
1251
-		$prefix = ($post) ? 'post_' : '';
1252
-		$run = true;
1253
-		if ($this->shouldEmitHooks($relativePath)) {
1254
-			foreach ($hooks as $hook) {
1255
-				if ($hook != 'read') {
1256
-					\OC_Hook::emit(
1257
-						Filesystem::CLASSNAME,
1258
-						$prefix . $hook,
1259
-						array(
1260
-							Filesystem::signal_param_run => &$run,
1261
-							Filesystem::signal_param_path => $path
1262
-						)
1263
-					);
1264
-				} elseif (!$post) {
1265
-					\OC_Hook::emit(
1266
-						Filesystem::CLASSNAME,
1267
-						$prefix . $hook,
1268
-						array(
1269
-							Filesystem::signal_param_path => $path
1270
-						)
1271
-					);
1272
-				}
1273
-			}
1274
-		}
1275
-		return $run;
1276
-	}
1277
-
1278
-	/**
1279
-	 * check if a file or folder has been updated since $time
1280
-	 *
1281
-	 * @param string $path
1282
-	 * @param int $time
1283
-	 * @return bool
1284
-	 */
1285
-	public function hasUpdated($path, $time) {
1286
-		return $this->basicOperation('hasUpdated', $path, array(), $time);
1287
-	}
1288
-
1289
-	/**
1290
-	 * @param string $ownerId
1291
-	 * @return \OC\User\User
1292
-	 */
1293
-	private function getUserObjectForOwner($ownerId) {
1294
-		$owner = $this->userManager->get($ownerId);
1295
-		if ($owner instanceof IUser) {
1296
-			return $owner;
1297
-		} else {
1298
-			return new User($ownerId, null);
1299
-		}
1300
-	}
1301
-
1302
-	/**
1303
-	 * Get file info from cache
1304
-	 *
1305
-	 * If the file is not in cached it will be scanned
1306
-	 * If the file has changed on storage the cache will be updated
1307
-	 *
1308
-	 * @param \OC\Files\Storage\Storage $storage
1309
-	 * @param string $internalPath
1310
-	 * @param string $relativePath
1311
-	 * @return ICacheEntry|bool
1312
-	 */
1313
-	private function getCacheEntry($storage, $internalPath, $relativePath) {
1314
-		$cache = $storage->getCache($internalPath);
1315
-		$data = $cache->get($internalPath);
1316
-		$watcher = $storage->getWatcher($internalPath);
1317
-
1318
-		try {
1319
-			// if the file is not in the cache or needs to be updated, trigger the scanner and reload the data
1320
-			if (!$data || $data['size'] === -1) {
1321
-				$this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1322
-				if (!$storage->file_exists($internalPath)) {
1323
-					$this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1324
-					return false;
1325
-				}
1326
-				$scanner = $storage->getScanner($internalPath);
1327
-				$scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1328
-				$data = $cache->get($internalPath);
1329
-				$this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1330
-			} else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) {
1331
-				$this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1332
-				$watcher->update($internalPath, $data);
1333
-				$storage->getPropagator()->propagateChange($internalPath, time());
1334
-				$data = $cache->get($internalPath);
1335
-				$this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1336
-			}
1337
-		} catch (LockedException $e) {
1338
-			// if the file is locked we just use the old cache info
1339
-		}
1340
-
1341
-		return $data;
1342
-	}
1343
-
1344
-	/**
1345
-	 * get the filesystem info
1346
-	 *
1347
-	 * @param string $path
1348
-	 * @param boolean|string $includeMountPoints true to add mountpoint sizes,
1349
-	 * 'ext' to add only ext storage mount point sizes. Defaults to true.
1350
-	 * defaults to true
1351
-	 * @return \OC\Files\FileInfo|false False if file does not exist
1352
-	 */
1353
-	public function getFileInfo($path, $includeMountPoints = true) {
1354
-		$this->assertPathLength($path);
1355
-		if (!Filesystem::isValidPath($path)) {
1356
-			return false;
1357
-		}
1358
-		if (Cache\Scanner::isPartialFile($path)) {
1359
-			return $this->getPartFileInfo($path);
1360
-		}
1361
-		$relativePath = $path;
1362
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1363
-
1364
-		$mount = Filesystem::getMountManager()->find($path);
1365
-		if (!$mount) {
1366
-			return false;
1367
-		}
1368
-		$storage = $mount->getStorage();
1369
-		$internalPath = $mount->getInternalPath($path);
1370
-		if ($storage) {
1371
-			$data = $this->getCacheEntry($storage, $internalPath, $relativePath);
1372
-
1373
-			if (!$data instanceof ICacheEntry) {
1374
-				return false;
1375
-			}
1376
-
1377
-			if ($mount instanceof MoveableMount && $internalPath === '') {
1378
-				$data['permissions'] |= \OCP\Constants::PERMISSION_DELETE;
1379
-			}
1380
-
1381
-			$owner = $this->getUserObjectForOwner($storage->getOwner($internalPath));
1382
-			$info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner);
1383
-
1384
-			if ($data and isset($data['fileid'])) {
1385
-				if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') {
1386
-					//add the sizes of other mount points to the folder
1387
-					$extOnly = ($includeMountPoints === 'ext');
1388
-					$mounts = Filesystem::getMountManager()->findIn($path);
1389
-					$info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
1390
-						$subStorage = $mount->getStorage();
1391
-						return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
1392
-					}));
1393
-				}
1394
-			}
1395
-
1396
-			return $info;
1397
-		}
1398
-
1399
-		return false;
1400
-	}
1401
-
1402
-	/**
1403
-	 * get the content of a directory
1404
-	 *
1405
-	 * @param string $directory path under datadirectory
1406
-	 * @param string $mimetype_filter limit returned content to this mimetype or mimepart
1407
-	 * @return FileInfo[]
1408
-	 */
1409
-	public function getDirectoryContent($directory, $mimetype_filter = '') {
1410
-		$this->assertPathLength($directory);
1411
-		if (!Filesystem::isValidPath($directory)) {
1412
-			return [];
1413
-		}
1414
-		$path = $this->getAbsolutePath($directory);
1415
-		$path = Filesystem::normalizePath($path);
1416
-		$mount = $this->getMount($directory);
1417
-		if (!$mount) {
1418
-			return [];
1419
-		}
1420
-		$storage = $mount->getStorage();
1421
-		$internalPath = $mount->getInternalPath($path);
1422
-		if ($storage) {
1423
-			$cache = $storage->getCache($internalPath);
1424
-			$user = \OC_User::getUser();
1425
-
1426
-			$data = $this->getCacheEntry($storage, $internalPath, $directory);
1427
-
1428
-			if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) {
1429
-				return [];
1430
-			}
1431
-
1432
-			$folderId = $data['fileid'];
1433
-			$contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter
1434
-
1435
-			$sharingDisabled = \OCP\Util::isSharingDisabledForUser();
1436
-			/**
1437
-			 * @var \OC\Files\FileInfo[] $files
1438
-			 */
1439
-			$files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1440
-				if ($sharingDisabled) {
1441
-					$content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1442
-				}
1443
-				$owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
1444
-				return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
1445
-			}, $contents);
1446
-
1447
-			//add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
1448
-			$mounts = Filesystem::getMountManager()->findIn($path);
1449
-			$dirLength = strlen($path);
1450
-			foreach ($mounts as $mount) {
1451
-				$mountPoint = $mount->getMountPoint();
1452
-				$subStorage = $mount->getStorage();
1453
-				if ($subStorage) {
1454
-					$subCache = $subStorage->getCache('');
1455
-
1456
-					$rootEntry = $subCache->get('');
1457
-					if (!$rootEntry) {
1458
-						$subScanner = $subStorage->getScanner('');
1459
-						try {
1460
-							$subScanner->scanFile('');
1461
-						} catch (\OCP\Files\StorageNotAvailableException $e) {
1462
-							continue;
1463
-						} catch (\OCP\Files\StorageInvalidException $e) {
1464
-							continue;
1465
-						} catch (\Exception $e) {
1466
-							// sometimes when the storage is not available it can be any exception
1467
-							\OC::$server->getLogger()->logException($e, [
1468
-								'message' => 'Exception while scanning storage "' . $subStorage->getId() . '"',
1469
-								'level' => \OCP\Util::ERROR,
1470
-								'app' => 'lib',
1471
-							]);
1472
-							continue;
1473
-						}
1474
-						$rootEntry = $subCache->get('');
1475
-					}
1476
-
1477
-					if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) {
1478
-						$relativePath = trim(substr($mountPoint, $dirLength), '/');
1479
-						if ($pos = strpos($relativePath, '/')) {
1480
-							//mountpoint inside subfolder add size to the correct folder
1481
-							$entryName = substr($relativePath, 0, $pos);
1482
-							foreach ($files as &$entry) {
1483
-								if ($entry->getName() === $entryName) {
1484
-									$entry->addSubEntry($rootEntry, $mountPoint);
1485
-								}
1486
-							}
1487
-						} else { //mountpoint in this folder, add an entry for it
1488
-							$rootEntry['name'] = $relativePath;
1489
-							$rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
1490
-							$permissions = $rootEntry['permissions'];
1491
-							// do not allow renaming/deleting the mount point if they are not shared files/folders
1492
-							// for shared files/folders we use the permissions given by the owner
1493
-							if ($mount instanceof MoveableMount) {
1494
-								$rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
1495
-							} else {
1496
-								$rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE));
1497
-							}
1498
-
1499
-							//remove any existing entry with the same name
1500
-							foreach ($files as $i => $file) {
1501
-								if ($file['name'] === $rootEntry['name']) {
1502
-									unset($files[$i]);
1503
-									break;
1504
-								}
1505
-							}
1506
-							$rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
1507
-
1508
-							// if sharing was disabled for the user we remove the share permissions
1509
-							if (\OCP\Util::isSharingDisabledForUser()) {
1510
-								$rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1511
-							}
1512
-
1513
-							$owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
1514
-							$files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1515
-						}
1516
-					}
1517
-				}
1518
-			}
1519
-
1520
-			if ($mimetype_filter) {
1521
-				$files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
1522
-					if (strpos($mimetype_filter, '/')) {
1523
-						return $file->getMimetype() === $mimetype_filter;
1524
-					} else {
1525
-						return $file->getMimePart() === $mimetype_filter;
1526
-					}
1527
-				});
1528
-			}
1529
-
1530
-			return $files;
1531
-		} else {
1532
-			return [];
1533
-		}
1534
-	}
1535
-
1536
-	/**
1537
-	 * change file metadata
1538
-	 *
1539
-	 * @param string $path
1540
-	 * @param array|\OCP\Files\FileInfo $data
1541
-	 * @return int
1542
-	 *
1543
-	 * returns the fileid of the updated file
1544
-	 */
1545
-	public function putFileInfo($path, $data) {
1546
-		$this->assertPathLength($path);
1547
-		if ($data instanceof FileInfo) {
1548
-			$data = $data->getData();
1549
-		}
1550
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1551
-		/**
1552
-		 * @var \OC\Files\Storage\Storage $storage
1553
-		 * @var string $internalPath
1554
-		 */
1555
-		list($storage, $internalPath) = Filesystem::resolvePath($path);
1556
-		if ($storage) {
1557
-			$cache = $storage->getCache($path);
1558
-
1559
-			if (!$cache->inCache($internalPath)) {
1560
-				$scanner = $storage->getScanner($internalPath);
1561
-				$scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1562
-			}
1563
-
1564
-			return $cache->put($internalPath, $data);
1565
-		} else {
1566
-			return -1;
1567
-		}
1568
-	}
1569
-
1570
-	/**
1571
-	 * search for files with the name matching $query
1572
-	 *
1573
-	 * @param string $query
1574
-	 * @return FileInfo[]
1575
-	 */
1576
-	public function search($query) {
1577
-		return $this->searchCommon('search', array('%' . $query . '%'));
1578
-	}
1579
-
1580
-	/**
1581
-	 * search for files with the name matching $query
1582
-	 *
1583
-	 * @param string $query
1584
-	 * @return FileInfo[]
1585
-	 */
1586
-	public function searchRaw($query) {
1587
-		return $this->searchCommon('search', array($query));
1588
-	}
1589
-
1590
-	/**
1591
-	 * search for files by mimetype
1592
-	 *
1593
-	 * @param string $mimetype
1594
-	 * @return FileInfo[]
1595
-	 */
1596
-	public function searchByMime($mimetype) {
1597
-		return $this->searchCommon('searchByMime', array($mimetype));
1598
-	}
1599
-
1600
-	/**
1601
-	 * search for files by tag
1602
-	 *
1603
-	 * @param string|int $tag name or tag id
1604
-	 * @param string $userId owner of the tags
1605
-	 * @return FileInfo[]
1606
-	 */
1607
-	public function searchByTag($tag, $userId) {
1608
-		return $this->searchCommon('searchByTag', array($tag, $userId));
1609
-	}
1610
-
1611
-	/**
1612
-	 * @param string $method cache method
1613
-	 * @param array $args
1614
-	 * @return FileInfo[]
1615
-	 */
1616
-	private function searchCommon($method, $args) {
1617
-		$files = array();
1618
-		$rootLength = strlen($this->fakeRoot);
1619
-
1620
-		$mount = $this->getMount('');
1621
-		$mountPoint = $mount->getMountPoint();
1622
-		$storage = $mount->getStorage();
1623
-		if ($storage) {
1624
-			$cache = $storage->getCache('');
1625
-
1626
-			$results = call_user_func_array(array($cache, $method), $args);
1627
-			foreach ($results as $result) {
1628
-				if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') {
1629
-					$internalPath = $result['path'];
1630
-					$path = $mountPoint . $result['path'];
1631
-					$result['path'] = substr($mountPoint . $result['path'], $rootLength);
1632
-					$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1633
-					$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1634
-				}
1635
-			}
1636
-
1637
-			$mounts = Filesystem::getMountManager()->findIn($this->fakeRoot);
1638
-			foreach ($mounts as $mount) {
1639
-				$mountPoint = $mount->getMountPoint();
1640
-				$storage = $mount->getStorage();
1641
-				if ($storage) {
1642
-					$cache = $storage->getCache('');
1643
-
1644
-					$relativeMountPoint = substr($mountPoint, $rootLength);
1645
-					$results = call_user_func_array(array($cache, $method), $args);
1646
-					if ($results) {
1647
-						foreach ($results as $result) {
1648
-							$internalPath = $result['path'];
1649
-							$result['path'] = rtrim($relativeMountPoint . $result['path'], '/');
1650
-							$path = rtrim($mountPoint . $internalPath, '/');
1651
-							$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1652
-							$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1653
-						}
1654
-					}
1655
-				}
1656
-			}
1657
-		}
1658
-		return $files;
1659
-	}
1660
-
1661
-	/**
1662
-	 * Get the owner for a file or folder
1663
-	 *
1664
-	 * @param string $path
1665
-	 * @return string the user id of the owner
1666
-	 * @throws NotFoundException
1667
-	 */
1668
-	public function getOwner($path) {
1669
-		$info = $this->getFileInfo($path);
1670
-		if (!$info) {
1671
-			throw new NotFoundException($path . ' not found while trying to get owner');
1672
-		}
1673
-		return $info->getOwner()->getUID();
1674
-	}
1675
-
1676
-	/**
1677
-	 * get the ETag for a file or folder
1678
-	 *
1679
-	 * @param string $path
1680
-	 * @return string
1681
-	 */
1682
-	public function getETag($path) {
1683
-		/**
1684
-		 * @var Storage\Storage $storage
1685
-		 * @var string $internalPath
1686
-		 */
1687
-		list($storage, $internalPath) = $this->resolvePath($path);
1688
-		if ($storage) {
1689
-			return $storage->getETag($internalPath);
1690
-		} else {
1691
-			return null;
1692
-		}
1693
-	}
1694
-
1695
-	/**
1696
-	 * Get the path of a file by id, relative to the view
1697
-	 *
1698
-	 * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file
1699
-	 *
1700
-	 * @param int $id
1701
-	 * @throws NotFoundException
1702
-	 * @return string
1703
-	 */
1704
-	public function getPath($id) {
1705
-		$id = (int)$id;
1706
-		$manager = Filesystem::getMountManager();
1707
-		$mounts = $manager->findIn($this->fakeRoot);
1708
-		$mounts[] = $manager->find($this->fakeRoot);
1709
-		// reverse the array so we start with the storage this view is in
1710
-		// which is the most likely to contain the file we're looking for
1711
-		$mounts = array_reverse($mounts);
1712
-		foreach ($mounts as $mount) {
1713
-			/**
1714
-			 * @var \OC\Files\Mount\MountPoint $mount
1715
-			 */
1716
-			if ($mount->getStorage()) {
1717
-				$cache = $mount->getStorage()->getCache();
1718
-				$internalPath = $cache->getPathById($id);
1719
-				if (is_string($internalPath)) {
1720
-					$fullPath = $mount->getMountPoint() . $internalPath;
1721
-					if (!is_null($path = $this->getRelativePath($fullPath))) {
1722
-						return $path;
1723
-					}
1724
-				}
1725
-			}
1726
-		}
1727
-		throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id));
1728
-	}
1729
-
1730
-	/**
1731
-	 * @param string $path
1732
-	 * @throws InvalidPathException
1733
-	 */
1734
-	private function assertPathLength($path) {
1735
-		$maxLen = min(PHP_MAXPATHLEN, 4000);
1736
-		// Check for the string length - performed using isset() instead of strlen()
1737
-		// because isset() is about 5x-40x faster.
1738
-		if (isset($path[$maxLen])) {
1739
-			$pathLen = strlen($path);
1740
-			throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path");
1741
-		}
1742
-	}
1743
-
1744
-	/**
1745
-	 * check if it is allowed to move a mount point to a given target.
1746
-	 * It is not allowed to move a mount point into a different mount point or
1747
-	 * into an already shared folder
1748
-	 *
1749
-	 * @param string $target path
1750
-	 * @return boolean
1751
-	 */
1752
-	private function isTargetAllowed($target) {
1753
-
1754
-		list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target);
1755
-		if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) {
1756
-			\OCP\Util::writeLog('files',
1757
-				'It is not allowed to move one mount point into another one',
1758
-				\OCP\Util::DEBUG);
1759
-			return false;
1760
-		}
1761
-
1762
-		// note: cannot use the view because the target is already locked
1763
-		$fileId = (int)$targetStorage->getCache()->getId($targetInternalPath);
1764
-		if ($fileId === -1) {
1765
-			// target might not exist, need to check parent instead
1766
-			$fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath));
1767
-		}
1768
-
1769
-		// check if any of the parents were shared by the current owner (include collections)
1770
-		$shares = \OCP\Share::getItemShared(
1771
-			'folder',
1772
-			$fileId,
1773
-			\OCP\Share::FORMAT_NONE,
1774
-			null,
1775
-			true
1776
-		);
1777
-
1778
-		if (count($shares) > 0) {
1779
-			\OCP\Util::writeLog('files',
1780
-				'It is not allowed to move one mount point into a shared folder',
1781
-				\OCP\Util::DEBUG);
1782
-			return false;
1783
-		}
1784
-
1785
-		return true;
1786
-	}
1787
-
1788
-	/**
1789
-	 * Get a fileinfo object for files that are ignored in the cache (part files)
1790
-	 *
1791
-	 * @param string $path
1792
-	 * @return \OCP\Files\FileInfo
1793
-	 */
1794
-	private function getPartFileInfo($path) {
1795
-		$mount = $this->getMount($path);
1796
-		$storage = $mount->getStorage();
1797
-		$internalPath = $mount->getInternalPath($this->getAbsolutePath($path));
1798
-		$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1799
-		return new FileInfo(
1800
-			$this->getAbsolutePath($path),
1801
-			$storage,
1802
-			$internalPath,
1803
-			[
1804
-				'fileid' => null,
1805
-				'mimetype' => $storage->getMimeType($internalPath),
1806
-				'name' => basename($path),
1807
-				'etag' => null,
1808
-				'size' => $storage->filesize($internalPath),
1809
-				'mtime' => $storage->filemtime($internalPath),
1810
-				'encrypted' => false,
1811
-				'permissions' => \OCP\Constants::PERMISSION_ALL
1812
-			],
1813
-			$mount,
1814
-			$owner
1815
-		);
1816
-	}
1817
-
1818
-	/**
1819
-	 * @param string $path
1820
-	 * @param string $fileName
1821
-	 * @throws InvalidPathException
1822
-	 */
1823
-	public function verifyPath($path, $fileName) {
1824
-		try {
1825
-			/** @type \OCP\Files\Storage $storage */
1826
-			list($storage, $internalPath) = $this->resolvePath($path);
1827
-			$storage->verifyPath($internalPath, $fileName);
1828
-		} catch (ReservedWordException $ex) {
1829
-			$l = \OC::$server->getL10N('lib');
1830
-			throw new InvalidPathException($l->t('File name is a reserved word'));
1831
-		} catch (InvalidCharacterInPathException $ex) {
1832
-			$l = \OC::$server->getL10N('lib');
1833
-			throw new InvalidPathException($l->t('File name contains at least one invalid character'));
1834
-		} catch (FileNameTooLongException $ex) {
1835
-			$l = \OC::$server->getL10N('lib');
1836
-			throw new InvalidPathException($l->t('File name is too long'));
1837
-		} catch (InvalidDirectoryException $ex) {
1838
-			$l = \OC::$server->getL10N('lib');
1839
-			throw new InvalidPathException($l->t('Dot files are not allowed'));
1840
-		} catch (EmptyFileNameException $ex) {
1841
-			$l = \OC::$server->getL10N('lib');
1842
-			throw new InvalidPathException($l->t('Empty filename is not allowed'));
1843
-		}
1844
-	}
1845
-
1846
-	/**
1847
-	 * get all parent folders of $path
1848
-	 *
1849
-	 * @param string $path
1850
-	 * @return string[]
1851
-	 */
1852
-	private function getParents($path) {
1853
-		$path = trim($path, '/');
1854
-		if (!$path) {
1855
-			return [];
1856
-		}
1857
-
1858
-		$parts = explode('/', $path);
1859
-
1860
-		// remove the single file
1861
-		array_pop($parts);
1862
-		$result = array('/');
1863
-		$resultPath = '';
1864
-		foreach ($parts as $part) {
1865
-			if ($part) {
1866
-				$resultPath .= '/' . $part;
1867
-				$result[] = $resultPath;
1868
-			}
1869
-		}
1870
-		return $result;
1871
-	}
1872
-
1873
-	/**
1874
-	 * Returns the mount point for which to lock
1875
-	 *
1876
-	 * @param string $absolutePath absolute path
1877
-	 * @param bool $useParentMount true to return parent mount instead of whatever
1878
-	 * is mounted directly on the given path, false otherwise
1879
-	 * @return \OC\Files\Mount\MountPoint mount point for which to apply locks
1880
-	 */
1881
-	private function getMountForLock($absolutePath, $useParentMount = false) {
1882
-		$results = [];
1883
-		$mount = Filesystem::getMountManager()->find($absolutePath);
1884
-		if (!$mount) {
1885
-			return $results;
1886
-		}
1887
-
1888
-		if ($useParentMount) {
1889
-			// find out if something is mounted directly on the path
1890
-			$internalPath = $mount->getInternalPath($absolutePath);
1891
-			if ($internalPath === '') {
1892
-				// resolve the parent mount instead
1893
-				$mount = Filesystem::getMountManager()->find(dirname($absolutePath));
1894
-			}
1895
-		}
1896
-
1897
-		return $mount;
1898
-	}
1899
-
1900
-	/**
1901
-	 * Lock the given path
1902
-	 *
1903
-	 * @param string $path the path of the file to lock, relative to the view
1904
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1905
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1906
-	 *
1907
-	 * @return bool False if the path is excluded from locking, true otherwise
1908
-	 * @throws \OCP\Lock\LockedException if the path is already locked
1909
-	 */
1910
-	private function lockPath($path, $type, $lockMountPoint = false) {
1911
-		$absolutePath = $this->getAbsolutePath($path);
1912
-		$absolutePath = Filesystem::normalizePath($absolutePath);
1913
-		if (!$this->shouldLockFile($absolutePath)) {
1914
-			return false;
1915
-		}
1916
-
1917
-		$mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1918
-		if ($mount) {
1919
-			try {
1920
-				$storage = $mount->getStorage();
1921
-				if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1922
-					$storage->acquireLock(
1923
-						$mount->getInternalPath($absolutePath),
1924
-						$type,
1925
-						$this->lockingProvider
1926
-					);
1927
-				}
1928
-			} catch (\OCP\Lock\LockedException $e) {
1929
-				// rethrow with the a human-readable path
1930
-				throw new \OCP\Lock\LockedException(
1931
-					$this->getPathRelativeToFiles($absolutePath),
1932
-					$e
1933
-				);
1934
-			}
1935
-		}
1936
-
1937
-		return true;
1938
-	}
1939
-
1940
-	/**
1941
-	 * Change the lock type
1942
-	 *
1943
-	 * @param string $path the path of the file to lock, relative to the view
1944
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1945
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1946
-	 *
1947
-	 * @return bool False if the path is excluded from locking, true otherwise
1948
-	 * @throws \OCP\Lock\LockedException if the path is already locked
1949
-	 */
1950
-	public function changeLock($path, $type, $lockMountPoint = false) {
1951
-		$path = Filesystem::normalizePath($path);
1952
-		$absolutePath = $this->getAbsolutePath($path);
1953
-		$absolutePath = Filesystem::normalizePath($absolutePath);
1954
-		if (!$this->shouldLockFile($absolutePath)) {
1955
-			return false;
1956
-		}
1957
-
1958
-		$mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1959
-		if ($mount) {
1960
-			try {
1961
-				$storage = $mount->getStorage();
1962
-				if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1963
-					$storage->changeLock(
1964
-						$mount->getInternalPath($absolutePath),
1965
-						$type,
1966
-						$this->lockingProvider
1967
-					);
1968
-				}
1969
-			} catch (\OCP\Lock\LockedException $e) {
1970
-				try {
1971
-					// rethrow with the a human-readable path
1972
-					throw new \OCP\Lock\LockedException(
1973
-						$this->getPathRelativeToFiles($absolutePath),
1974
-						$e
1975
-					);
1976
-				} catch (\InvalidArgumentException $e) {
1977
-					throw new \OCP\Lock\LockedException(
1978
-						$absolutePath,
1979
-						$e
1980
-					);
1981
-				}
1982
-			}
1983
-		}
1984
-
1985
-		return true;
1986
-	}
1987
-
1988
-	/**
1989
-	 * Unlock the given path
1990
-	 *
1991
-	 * @param string $path the path of the file to unlock, relative to the view
1992
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1993
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1994
-	 *
1995
-	 * @return bool False if the path is excluded from locking, true otherwise
1996
-	 */
1997
-	private function unlockPath($path, $type, $lockMountPoint = false) {
1998
-		$absolutePath = $this->getAbsolutePath($path);
1999
-		$absolutePath = Filesystem::normalizePath($absolutePath);
2000
-		if (!$this->shouldLockFile($absolutePath)) {
2001
-			return false;
2002
-		}
2003
-
2004
-		$mount = $this->getMountForLock($absolutePath, $lockMountPoint);
2005
-		if ($mount) {
2006
-			$storage = $mount->getStorage();
2007
-			if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
2008
-				$storage->releaseLock(
2009
-					$mount->getInternalPath($absolutePath),
2010
-					$type,
2011
-					$this->lockingProvider
2012
-				);
2013
-			}
2014
-		}
2015
-
2016
-		return true;
2017
-	}
2018
-
2019
-	/**
2020
-	 * Lock a path and all its parents up to the root of the view
2021
-	 *
2022
-	 * @param string $path the path of the file to lock relative to the view
2023
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2024
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2025
-	 *
2026
-	 * @return bool False if the path is excluded from locking, true otherwise
2027
-	 */
2028
-	public function lockFile($path, $type, $lockMountPoint = false) {
2029
-		$absolutePath = $this->getAbsolutePath($path);
2030
-		$absolutePath = Filesystem::normalizePath($absolutePath);
2031
-		if (!$this->shouldLockFile($absolutePath)) {
2032
-			return false;
2033
-		}
2034
-
2035
-		$this->lockPath($path, $type, $lockMountPoint);
2036
-
2037
-		$parents = $this->getParents($path);
2038
-		foreach ($parents as $parent) {
2039
-			$this->lockPath($parent, ILockingProvider::LOCK_SHARED);
2040
-		}
2041
-
2042
-		return true;
2043
-	}
2044
-
2045
-	/**
2046
-	 * Unlock a path and all its parents up to the root of the view
2047
-	 *
2048
-	 * @param string $path the path of the file to lock relative to the view
2049
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2050
-	 * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2051
-	 *
2052
-	 * @return bool False if the path is excluded from locking, true otherwise
2053
-	 */
2054
-	public function unlockFile($path, $type, $lockMountPoint = false) {
2055
-		$absolutePath = $this->getAbsolutePath($path);
2056
-		$absolutePath = Filesystem::normalizePath($absolutePath);
2057
-		if (!$this->shouldLockFile($absolutePath)) {
2058
-			return false;
2059
-		}
2060
-
2061
-		$this->unlockPath($path, $type, $lockMountPoint);
2062
-
2063
-		$parents = $this->getParents($path);
2064
-		foreach ($parents as $parent) {
2065
-			$this->unlockPath($parent, ILockingProvider::LOCK_SHARED);
2066
-		}
2067
-
2068
-		return true;
2069
-	}
2070
-
2071
-	/**
2072
-	 * Only lock files in data/user/files/
2073
-	 *
2074
-	 * @param string $path Absolute path to the file/folder we try to (un)lock
2075
-	 * @return bool
2076
-	 */
2077
-	protected function shouldLockFile($path) {
2078
-		$path = Filesystem::normalizePath($path);
2079
-
2080
-		$pathSegments = explode('/', $path);
2081
-		if (isset($pathSegments[2])) {
2082
-			// E.g.: /username/files/path-to-file
2083
-			return ($pathSegments[2] === 'files') && (count($pathSegments) > 3);
2084
-		}
2085
-
2086
-		return strpos($path, '/appdata_') !== 0;
2087
-	}
2088
-
2089
-	/**
2090
-	 * Shortens the given absolute path to be relative to
2091
-	 * "$user/files".
2092
-	 *
2093
-	 * @param string $absolutePath absolute path which is under "files"
2094
-	 *
2095
-	 * @return string path relative to "files" with trimmed slashes or null
2096
-	 * if the path was NOT relative to files
2097
-	 *
2098
-	 * @throws \InvalidArgumentException if the given path was not under "files"
2099
-	 * @since 8.1.0
2100
-	 */
2101
-	public function getPathRelativeToFiles($absolutePath) {
2102
-		$path = Filesystem::normalizePath($absolutePath);
2103
-		$parts = explode('/', trim($path, '/'), 3);
2104
-		// "$user", "files", "path/to/dir"
2105
-		if (!isset($parts[1]) || $parts[1] !== 'files') {
2106
-			$this->logger->error(
2107
-				'$absolutePath must be relative to "files", value is "%s"',
2108
-				[
2109
-					$absolutePath
2110
-				]
2111
-			);
2112
-			throw new \InvalidArgumentException('$absolutePath must be relative to "files"');
2113
-		}
2114
-		if (isset($parts[2])) {
2115
-			return $parts[2];
2116
-		}
2117
-		return '';
2118
-	}
2119
-
2120
-	/**
2121
-	 * @param string $filename
2122
-	 * @return array
2123
-	 * @throws \OC\User\NoUserException
2124
-	 * @throws NotFoundException
2125
-	 */
2126
-	public function getUidAndFilename($filename) {
2127
-		$info = $this->getFileInfo($filename);
2128
-		if (!$info instanceof \OCP\Files\FileInfo) {
2129
-			throw new NotFoundException($this->getAbsolutePath($filename) . ' not found');
2130
-		}
2131
-		$uid = $info->getOwner()->getUID();
2132
-		if ($uid != \OCP\User::getUser()) {
2133
-			Filesystem::initMountPoints($uid);
2134
-			$ownerView = new View('/' . $uid . '/files');
2135
-			try {
2136
-				$filename = $ownerView->getPath($info['fileid']);
2137
-			} catch (NotFoundException $e) {
2138
-				throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid);
2139
-			}
2140
-		}
2141
-		return [$uid, $filename];
2142
-	}
2143
-
2144
-	/**
2145
-	 * Creates parent non-existing folders
2146
-	 *
2147
-	 * @param string $filePath
2148
-	 * @return bool
2149
-	 */
2150
-	private function createParentDirectories($filePath) {
2151
-		$directoryParts = explode('/', $filePath);
2152
-		$directoryParts = array_filter($directoryParts);
2153
-		foreach ($directoryParts as $key => $part) {
2154
-			$currentPathElements = array_slice($directoryParts, 0, $key);
2155
-			$currentPath = '/' . implode('/', $currentPathElements);
2156
-			if ($this->is_file($currentPath)) {
2157
-				return false;
2158
-			}
2159
-			if (!$this->file_exists($currentPath)) {
2160
-				$this->mkdir($currentPath);
2161
-			}
2162
-		}
2163
-
2164
-		return true;
2165
-	}
83
+    /** @var string */
84
+    private $fakeRoot = '';
85
+
86
+    /**
87
+     * @var \OCP\Lock\ILockingProvider
88
+     */
89
+    protected $lockingProvider;
90
+
91
+    private $lockingEnabled;
92
+
93
+    private $updaterEnabled = true;
94
+
95
+    /** @var \OC\User\Manager */
96
+    private $userManager;
97
+
98
+    /** @var \OCP\ILogger */
99
+    private $logger;
100
+
101
+    /**
102
+     * @param string $root
103
+     * @throws \Exception If $root contains an invalid path
104
+     */
105
+    public function __construct($root = '') {
106
+        if (is_null($root)) {
107
+            throw new \InvalidArgumentException('Root can\'t be null');
108
+        }
109
+        if (!Filesystem::isValidPath($root)) {
110
+            throw new \Exception();
111
+        }
112
+
113
+        $this->fakeRoot = $root;
114
+        $this->lockingProvider = \OC::$server->getLockingProvider();
115
+        $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider);
116
+        $this->userManager = \OC::$server->getUserManager();
117
+        $this->logger = \OC::$server->getLogger();
118
+    }
119
+
120
+    public function getAbsolutePath($path = '/') {
121
+        if ($path === null) {
122
+            return null;
123
+        }
124
+        $this->assertPathLength($path);
125
+        if ($path === '') {
126
+            $path = '/';
127
+        }
128
+        if ($path[0] !== '/') {
129
+            $path = '/' . $path;
130
+        }
131
+        return $this->fakeRoot . $path;
132
+    }
133
+
134
+    /**
135
+     * change the root to a fake root
136
+     *
137
+     * @param string $fakeRoot
138
+     * @return boolean|null
139
+     */
140
+    public function chroot($fakeRoot) {
141
+        if (!$fakeRoot == '') {
142
+            if ($fakeRoot[0] !== '/') {
143
+                $fakeRoot = '/' . $fakeRoot;
144
+            }
145
+        }
146
+        $this->fakeRoot = $fakeRoot;
147
+    }
148
+
149
+    /**
150
+     * get the fake root
151
+     *
152
+     * @return string
153
+     */
154
+    public function getRoot() {
155
+        return $this->fakeRoot;
156
+    }
157
+
158
+    /**
159
+     * get path relative to the root of the view
160
+     *
161
+     * @param string $path
162
+     * @return string
163
+     */
164
+    public function getRelativePath($path) {
165
+        $this->assertPathLength($path);
166
+        if ($this->fakeRoot == '') {
167
+            return $path;
168
+        }
169
+
170
+        if (rtrim($path, '/') === rtrim($this->fakeRoot, '/')) {
171
+            return '/';
172
+        }
173
+
174
+        // missing slashes can cause wrong matches!
175
+        $root = rtrim($this->fakeRoot, '/') . '/';
176
+
177
+        if (strpos($path, $root) !== 0) {
178
+            return null;
179
+        } else {
180
+            $path = substr($path, strlen($this->fakeRoot));
181
+            if (strlen($path) === 0) {
182
+                return '/';
183
+            } else {
184
+                return $path;
185
+            }
186
+        }
187
+    }
188
+
189
+    /**
190
+     * get the mountpoint of the storage object for a path
191
+     * ( note: because a storage is not always mounted inside the fakeroot, the
192
+     * returned mountpoint is relative to the absolute root of the filesystem
193
+     * and does not take the chroot into account )
194
+     *
195
+     * @param string $path
196
+     * @return string
197
+     */
198
+    public function getMountPoint($path) {
199
+        return Filesystem::getMountPoint($this->getAbsolutePath($path));
200
+    }
201
+
202
+    /**
203
+     * get the mountpoint of the storage object for a path
204
+     * ( note: because a storage is not always mounted inside the fakeroot, the
205
+     * returned mountpoint is relative to the absolute root of the filesystem
206
+     * and does not take the chroot into account )
207
+     *
208
+     * @param string $path
209
+     * @return \OCP\Files\Mount\IMountPoint
210
+     */
211
+    public function getMount($path) {
212
+        return Filesystem::getMountManager()->find($this->getAbsolutePath($path));
213
+    }
214
+
215
+    /**
216
+     * resolve a path to a storage and internal path
217
+     *
218
+     * @param string $path
219
+     * @return array an array consisting of the storage and the internal path
220
+     */
221
+    public function resolvePath($path) {
222
+        $a = $this->getAbsolutePath($path);
223
+        $p = Filesystem::normalizePath($a);
224
+        return Filesystem::resolvePath($p);
225
+    }
226
+
227
+    /**
228
+     * return the path to a local version of the file
229
+     * we need this because we can't know if a file is stored local or not from
230
+     * outside the filestorage and for some purposes a local file is needed
231
+     *
232
+     * @param string $path
233
+     * @return string
234
+     */
235
+    public function getLocalFile($path) {
236
+        $parent = substr($path, 0, strrpos($path, '/'));
237
+        $path = $this->getAbsolutePath($path);
238
+        list($storage, $internalPath) = Filesystem::resolvePath($path);
239
+        if (Filesystem::isValidPath($parent) and $storage) {
240
+            return $storage->getLocalFile($internalPath);
241
+        } else {
242
+            return null;
243
+        }
244
+    }
245
+
246
+    /**
247
+     * @param string $path
248
+     * @return string
249
+     */
250
+    public function getLocalFolder($path) {
251
+        $parent = substr($path, 0, strrpos($path, '/'));
252
+        $path = $this->getAbsolutePath($path);
253
+        list($storage, $internalPath) = Filesystem::resolvePath($path);
254
+        if (Filesystem::isValidPath($parent) and $storage) {
255
+            return $storage->getLocalFolder($internalPath);
256
+        } else {
257
+            return null;
258
+        }
259
+    }
260
+
261
+    /**
262
+     * the following functions operate with arguments and return values identical
263
+     * to those of their PHP built-in equivalents. Mostly they are merely wrappers
264
+     * for \OC\Files\Storage\Storage via basicOperation().
265
+     */
266
+    public function mkdir($path) {
267
+        return $this->basicOperation('mkdir', $path, array('create', 'write'));
268
+    }
269
+
270
+    /**
271
+     * remove mount point
272
+     *
273
+     * @param \OC\Files\Mount\MoveableMount $mount
274
+     * @param string $path relative to data/
275
+     * @return boolean
276
+     */
277
+    protected function removeMount($mount, $path) {
278
+        if ($mount instanceof MoveableMount) {
279
+            // cut of /user/files to get the relative path to data/user/files
280
+            $pathParts = explode('/', $path, 4);
281
+            $relPath = '/' . $pathParts[3];
282
+            $this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true);
283
+            \OC_Hook::emit(
284
+                Filesystem::CLASSNAME, "umount",
285
+                array(Filesystem::signal_param_path => $relPath)
286
+            );
287
+            $this->changeLock($relPath, ILockingProvider::LOCK_EXCLUSIVE, true);
288
+            $result = $mount->removeMount();
289
+            $this->changeLock($relPath, ILockingProvider::LOCK_SHARED, true);
290
+            if ($result) {
291
+                \OC_Hook::emit(
292
+                    Filesystem::CLASSNAME, "post_umount",
293
+                    array(Filesystem::signal_param_path => $relPath)
294
+                );
295
+            }
296
+            $this->unlockFile($relPath, ILockingProvider::LOCK_SHARED, true);
297
+            return $result;
298
+        } else {
299
+            // do not allow deleting the storage's root / the mount point
300
+            // because for some storages it might delete the whole contents
301
+            // but isn't supposed to work that way
302
+            return false;
303
+        }
304
+    }
305
+
306
+    public function disableCacheUpdate() {
307
+        $this->updaterEnabled = false;
308
+    }
309
+
310
+    public function enableCacheUpdate() {
311
+        $this->updaterEnabled = true;
312
+    }
313
+
314
+    protected function writeUpdate(Storage $storage, $internalPath, $time = null) {
315
+        if ($this->updaterEnabled) {
316
+            if (is_null($time)) {
317
+                $time = time();
318
+            }
319
+            $storage->getUpdater()->update($internalPath, $time);
320
+        }
321
+    }
322
+
323
+    protected function removeUpdate(Storage $storage, $internalPath) {
324
+        if ($this->updaterEnabled) {
325
+            $storage->getUpdater()->remove($internalPath);
326
+        }
327
+    }
328
+
329
+    protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) {
330
+        if ($this->updaterEnabled) {
331
+            $targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
332
+        }
333
+    }
334
+
335
+    /**
336
+     * @param string $path
337
+     * @return bool|mixed
338
+     */
339
+    public function rmdir($path) {
340
+        $absolutePath = $this->getAbsolutePath($path);
341
+        $mount = Filesystem::getMountManager()->find($absolutePath);
342
+        if ($mount->getInternalPath($absolutePath) === '') {
343
+            return $this->removeMount($mount, $absolutePath);
344
+        }
345
+        if ($this->is_dir($path)) {
346
+            $result = $this->basicOperation('rmdir', $path, array('delete'));
347
+        } else {
348
+            $result = false;
349
+        }
350
+
351
+        if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
352
+            $storage = $mount->getStorage();
353
+            $internalPath = $mount->getInternalPath($absolutePath);
354
+            $storage->getUpdater()->remove($internalPath);
355
+        }
356
+        return $result;
357
+    }
358
+
359
+    /**
360
+     * @param string $path
361
+     * @return resource
362
+     */
363
+    public function opendir($path) {
364
+        return $this->basicOperation('opendir', $path, array('read'));
365
+    }
366
+
367
+    /**
368
+     * @param string $path
369
+     * @return bool|mixed
370
+     */
371
+    public function is_dir($path) {
372
+        if ($path == '/') {
373
+            return true;
374
+        }
375
+        return $this->basicOperation('is_dir', $path);
376
+    }
377
+
378
+    /**
379
+     * @param string $path
380
+     * @return bool|mixed
381
+     */
382
+    public function is_file($path) {
383
+        if ($path == '/') {
384
+            return false;
385
+        }
386
+        return $this->basicOperation('is_file', $path);
387
+    }
388
+
389
+    /**
390
+     * @param string $path
391
+     * @return mixed
392
+     */
393
+    public function stat($path) {
394
+        return $this->basicOperation('stat', $path);
395
+    }
396
+
397
+    /**
398
+     * @param string $path
399
+     * @return mixed
400
+     */
401
+    public function filetype($path) {
402
+        return $this->basicOperation('filetype', $path);
403
+    }
404
+
405
+    /**
406
+     * @param string $path
407
+     * @return mixed
408
+     */
409
+    public function filesize($path) {
410
+        return $this->basicOperation('filesize', $path);
411
+    }
412
+
413
+    /**
414
+     * @param string $path
415
+     * @return bool|mixed
416
+     * @throws \OCP\Files\InvalidPathException
417
+     */
418
+    public function readfile($path) {
419
+        $this->assertPathLength($path);
420
+        @ob_end_clean();
421
+        $handle = $this->fopen($path, 'rb');
422
+        if ($handle) {
423
+            $chunkSize = 8192; // 8 kB chunks
424
+            while (!feof($handle)) {
425
+                echo fread($handle, $chunkSize);
426
+                flush();
427
+            }
428
+            fclose($handle);
429
+            $size = $this->filesize($path);
430
+            return $size;
431
+        }
432
+        return false;
433
+    }
434
+
435
+    /**
436
+     * @param string $path
437
+     * @param int $from
438
+     * @param int $to
439
+     * @return bool|mixed
440
+     * @throws \OCP\Files\InvalidPathException
441
+     * @throws \OCP\Files\UnseekableException
442
+     */
443
+    public function readfilePart($path, $from, $to) {
444
+        $this->assertPathLength($path);
445
+        @ob_end_clean();
446
+        $handle = $this->fopen($path, 'rb');
447
+        if ($handle) {
448
+            $chunkSize = 8192; // 8 kB chunks
449
+            $startReading = true;
450
+
451
+            if ($from !== 0 && $from !== '0' && fseek($handle, $from) !== 0) {
452
+                // forward file handle via chunked fread because fseek seem to have failed
453
+
454
+                $end = $from + 1;
455
+                while (!feof($handle) && ftell($handle) < $end) {
456
+                    $len = $from - ftell($handle);
457
+                    if ($len > $chunkSize) {
458
+                        $len = $chunkSize;
459
+                    }
460
+                    $result = fread($handle, $len);
461
+
462
+                    if ($result === false) {
463
+                        $startReading = false;
464
+                        break;
465
+                    }
466
+                }
467
+            }
468
+
469
+            if ($startReading) {
470
+                $end = $to + 1;
471
+                while (!feof($handle) && ftell($handle) < $end) {
472
+                    $len = $end - ftell($handle);
473
+                    if ($len > $chunkSize) {
474
+                        $len = $chunkSize;
475
+                    }
476
+                    echo fread($handle, $len);
477
+                    flush();
478
+                }
479
+                $size = ftell($handle) - $from;
480
+                return $size;
481
+            }
482
+
483
+            throw new \OCP\Files\UnseekableException('fseek error');
484
+        }
485
+        return false;
486
+    }
487
+
488
+    /**
489
+     * @param string $path
490
+     * @return mixed
491
+     */
492
+    public function isCreatable($path) {
493
+        return $this->basicOperation('isCreatable', $path);
494
+    }
495
+
496
+    /**
497
+     * @param string $path
498
+     * @return mixed
499
+     */
500
+    public function isReadable($path) {
501
+        return $this->basicOperation('isReadable', $path);
502
+    }
503
+
504
+    /**
505
+     * @param string $path
506
+     * @return mixed
507
+     */
508
+    public function isUpdatable($path) {
509
+        return $this->basicOperation('isUpdatable', $path);
510
+    }
511
+
512
+    /**
513
+     * @param string $path
514
+     * @return bool|mixed
515
+     */
516
+    public function isDeletable($path) {
517
+        $absolutePath = $this->getAbsolutePath($path);
518
+        $mount = Filesystem::getMountManager()->find($absolutePath);
519
+        if ($mount->getInternalPath($absolutePath) === '') {
520
+            return $mount instanceof MoveableMount;
521
+        }
522
+        return $this->basicOperation('isDeletable', $path);
523
+    }
524
+
525
+    /**
526
+     * @param string $path
527
+     * @return mixed
528
+     */
529
+    public function isSharable($path) {
530
+        return $this->basicOperation('isSharable', $path);
531
+    }
532
+
533
+    /**
534
+     * @param string $path
535
+     * @return bool|mixed
536
+     */
537
+    public function file_exists($path) {
538
+        if ($path == '/') {
539
+            return true;
540
+        }
541
+        return $this->basicOperation('file_exists', $path);
542
+    }
543
+
544
+    /**
545
+     * @param string $path
546
+     * @return mixed
547
+     */
548
+    public function filemtime($path) {
549
+        return $this->basicOperation('filemtime', $path);
550
+    }
551
+
552
+    /**
553
+     * @param string $path
554
+     * @param int|string $mtime
555
+     * @return bool
556
+     */
557
+    public function touch($path, $mtime = null) {
558
+        if (!is_null($mtime) and !is_numeric($mtime)) {
559
+            $mtime = strtotime($mtime);
560
+        }
561
+
562
+        $hooks = array('touch');
563
+
564
+        if (!$this->file_exists($path)) {
565
+            $hooks[] = 'create';
566
+            $hooks[] = 'write';
567
+        }
568
+        $result = $this->basicOperation('touch', $path, $hooks, $mtime);
569
+        if (!$result) {
570
+            // If create file fails because of permissions on external storage like SMB folders,
571
+            // check file exists and return false if not.
572
+            if (!$this->file_exists($path)) {
573
+                return false;
574
+            }
575
+            if (is_null($mtime)) {
576
+                $mtime = time();
577
+            }
578
+            //if native touch fails, we emulate it by changing the mtime in the cache
579
+            $this->putFileInfo($path, array('mtime' => floor($mtime)));
580
+        }
581
+        return true;
582
+    }
583
+
584
+    /**
585
+     * @param string $path
586
+     * @return mixed
587
+     */
588
+    public function file_get_contents($path) {
589
+        return $this->basicOperation('file_get_contents', $path, array('read'));
590
+    }
591
+
592
+    /**
593
+     * @param bool $exists
594
+     * @param string $path
595
+     * @param bool $run
596
+     */
597
+    protected function emit_file_hooks_pre($exists, $path, &$run) {
598
+        if (!$exists) {
599
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array(
600
+                Filesystem::signal_param_path => $this->getHookPath($path),
601
+                Filesystem::signal_param_run => &$run,
602
+            ));
603
+        } else {
604
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array(
605
+                Filesystem::signal_param_path => $this->getHookPath($path),
606
+                Filesystem::signal_param_run => &$run,
607
+            ));
608
+        }
609
+        \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array(
610
+            Filesystem::signal_param_path => $this->getHookPath($path),
611
+            Filesystem::signal_param_run => &$run,
612
+        ));
613
+    }
614
+
615
+    /**
616
+     * @param bool $exists
617
+     * @param string $path
618
+     */
619
+    protected function emit_file_hooks_post($exists, $path) {
620
+        if (!$exists) {
621
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array(
622
+                Filesystem::signal_param_path => $this->getHookPath($path),
623
+            ));
624
+        } else {
625
+            \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array(
626
+                Filesystem::signal_param_path => $this->getHookPath($path),
627
+            ));
628
+        }
629
+        \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array(
630
+            Filesystem::signal_param_path => $this->getHookPath($path),
631
+        ));
632
+    }
633
+
634
+    /**
635
+     * @param string $path
636
+     * @param mixed $data
637
+     * @return bool|mixed
638
+     * @throws \Exception
639
+     */
640
+    public function file_put_contents($path, $data) {
641
+        if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier
642
+            $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
643
+            if (Filesystem::isValidPath($path)
644
+                and !Filesystem::isFileBlacklisted($path)
645
+            ) {
646
+                $path = $this->getRelativePath($absolutePath);
647
+
648
+                $this->lockFile($path, ILockingProvider::LOCK_SHARED);
649
+
650
+                $exists = $this->file_exists($path);
651
+                $run = true;
652
+                if ($this->shouldEmitHooks($path)) {
653
+                    $this->emit_file_hooks_pre($exists, $path, $run);
654
+                }
655
+                if (!$run) {
656
+                    $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
657
+                    return false;
658
+                }
659
+
660
+                $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
661
+
662
+                /** @var \OC\Files\Storage\Storage $storage */
663
+                list($storage, $internalPath) = $this->resolvePath($path);
664
+                $target = $storage->fopen($internalPath, 'w');
665
+                if ($target) {
666
+                    list (, $result) = \OC_Helper::streamCopy($data, $target);
667
+                    fclose($target);
668
+                    fclose($data);
669
+
670
+                    $this->writeUpdate($storage, $internalPath);
671
+
672
+                    $this->changeLock($path, ILockingProvider::LOCK_SHARED);
673
+
674
+                    if ($this->shouldEmitHooks($path) && $result !== false) {
675
+                        $this->emit_file_hooks_post($exists, $path);
676
+                    }
677
+                    $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
678
+                    return $result;
679
+                } else {
680
+                    $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
681
+                    return false;
682
+                }
683
+            } else {
684
+                return false;
685
+            }
686
+        } else {
687
+            $hooks = ($this->file_exists($path)) ? array('update', 'write') : array('create', 'write');
688
+            return $this->basicOperation('file_put_contents', $path, $hooks, $data);
689
+        }
690
+    }
691
+
692
+    /**
693
+     * @param string $path
694
+     * @return bool|mixed
695
+     */
696
+    public function unlink($path) {
697
+        if ($path === '' || $path === '/') {
698
+            // do not allow deleting the root
699
+            return false;
700
+        }
701
+        $postFix = ($path[strlen($path) - 1] === '/') ? '/' : '';
702
+        $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
703
+        $mount = Filesystem::getMountManager()->find($absolutePath . $postFix);
704
+        if ($mount and $mount->getInternalPath($absolutePath) === '') {
705
+            return $this->removeMount($mount, $absolutePath);
706
+        }
707
+        if ($this->is_dir($path)) {
708
+            $result = $this->basicOperation('rmdir', $path, ['delete']);
709
+        } else {
710
+            $result = $this->basicOperation('unlink', $path, ['delete']);
711
+        }
712
+        if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
713
+            $storage = $mount->getStorage();
714
+            $internalPath = $mount->getInternalPath($absolutePath);
715
+            $storage->getUpdater()->remove($internalPath);
716
+            return true;
717
+        } else {
718
+            return $result;
719
+        }
720
+    }
721
+
722
+    /**
723
+     * @param string $directory
724
+     * @return bool|mixed
725
+     */
726
+    public function deleteAll($directory) {
727
+        return $this->rmdir($directory);
728
+    }
729
+
730
+    /**
731
+     * Rename/move a file or folder from the source path to target path.
732
+     *
733
+     * @param string $path1 source path
734
+     * @param string $path2 target path
735
+     *
736
+     * @return bool|mixed
737
+     */
738
+    public function rename($path1, $path2) {
739
+        $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
740
+        $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
741
+        $result = false;
742
+        if (
743
+            Filesystem::isValidPath($path2)
744
+            and Filesystem::isValidPath($path1)
745
+            and !Filesystem::isFileBlacklisted($path2)
746
+        ) {
747
+            $path1 = $this->getRelativePath($absolutePath1);
748
+            $path2 = $this->getRelativePath($absolutePath2);
749
+            $exists = $this->file_exists($path2);
750
+
751
+            if ($path1 == null or $path2 == null) {
752
+                return false;
753
+            }
754
+
755
+            $this->lockFile($path1, ILockingProvider::LOCK_SHARED, true);
756
+            try {
757
+                $this->lockFile($path2, ILockingProvider::LOCK_SHARED, true);
758
+
759
+                $run = true;
760
+                if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) {
761
+                    // if it was a rename from a part file to a regular file it was a write and not a rename operation
762
+                    $this->emit_file_hooks_pre($exists, $path2, $run);
763
+                } elseif ($this->shouldEmitHooks($path1)) {
764
+                    \OC_Hook::emit(
765
+                        Filesystem::CLASSNAME, Filesystem::signal_rename,
766
+                        array(
767
+                            Filesystem::signal_param_oldpath => $this->getHookPath($path1),
768
+                            Filesystem::signal_param_newpath => $this->getHookPath($path2),
769
+                            Filesystem::signal_param_run => &$run
770
+                        )
771
+                    );
772
+                }
773
+                if ($run) {
774
+                    $this->verifyPath(dirname($path2), basename($path2));
775
+
776
+                    $manager = Filesystem::getMountManager();
777
+                    $mount1 = $this->getMount($path1);
778
+                    $mount2 = $this->getMount($path2);
779
+                    $storage1 = $mount1->getStorage();
780
+                    $storage2 = $mount2->getStorage();
781
+                    $internalPath1 = $mount1->getInternalPath($absolutePath1);
782
+                    $internalPath2 = $mount2->getInternalPath($absolutePath2);
783
+
784
+                    $this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true);
785
+                    try {
786
+                        $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true);
787
+
788
+                        if ($internalPath1 === '') {
789
+                            if ($mount1 instanceof MoveableMount) {
790
+                                if ($this->isTargetAllowed($absolutePath2)) {
791
+                                    /**
792
+                                     * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1
793
+                                     */
794
+                                    $sourceMountPoint = $mount1->getMountPoint();
795
+                                    $result = $mount1->moveMount($absolutePath2);
796
+                                    $manager->moveMount($sourceMountPoint, $mount1->getMountPoint());
797
+                                } else {
798
+                                    $result = false;
799
+                                }
800
+                            } else {
801
+                                $result = false;
802
+                            }
803
+                            // moving a file/folder within the same mount point
804
+                        } elseif ($storage1 === $storage2) {
805
+                            if ($storage1) {
806
+                                $result = $storage1->rename($internalPath1, $internalPath2);
807
+                            } else {
808
+                                $result = false;
809
+                            }
810
+                            // moving a file/folder between storages (from $storage1 to $storage2)
811
+                        } else {
812
+                            $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
813
+                        }
814
+
815
+                        if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
816
+                            // if it was a rename from a part file to a regular file it was a write and not a rename operation
817
+                            $this->writeUpdate($storage2, $internalPath2);
818
+                        } else if ($result) {
819
+                            if ($internalPath1 !== '') { // don't do a cache update for moved mounts
820
+                                $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2);
821
+                            }
822
+                        }
823
+                    } catch(\Exception $e) {
824
+                        throw $e;
825
+                    } finally {
826
+                        $this->changeLock($path1, ILockingProvider::LOCK_SHARED, true);
827
+                        $this->changeLock($path2, ILockingProvider::LOCK_SHARED, true);
828
+                    }
829
+
830
+                    if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) {
831
+                        if ($this->shouldEmitHooks()) {
832
+                            $this->emit_file_hooks_post($exists, $path2);
833
+                        }
834
+                    } elseif ($result) {
835
+                        if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) {
836
+                            \OC_Hook::emit(
837
+                                Filesystem::CLASSNAME,
838
+                                Filesystem::signal_post_rename,
839
+                                array(
840
+                                    Filesystem::signal_param_oldpath => $this->getHookPath($path1),
841
+                                    Filesystem::signal_param_newpath => $this->getHookPath($path2)
842
+                                )
843
+                            );
844
+                        }
845
+                    }
846
+                }
847
+            } catch(\Exception $e) {
848
+                throw $e;
849
+            } finally {
850
+                $this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true);
851
+                $this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true);
852
+            }
853
+        }
854
+        return $result;
855
+    }
856
+
857
+    /**
858
+     * Copy a file/folder from the source path to target path
859
+     *
860
+     * @param string $path1 source path
861
+     * @param string $path2 target path
862
+     * @param bool $preserveMtime whether to preserve mtime on the copy
863
+     *
864
+     * @return bool|mixed
865
+     */
866
+    public function copy($path1, $path2, $preserveMtime = false) {
867
+        $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1));
868
+        $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2));
869
+        $result = false;
870
+        if (
871
+            Filesystem::isValidPath($path2)
872
+            and Filesystem::isValidPath($path1)
873
+            and !Filesystem::isFileBlacklisted($path2)
874
+        ) {
875
+            $path1 = $this->getRelativePath($absolutePath1);
876
+            $path2 = $this->getRelativePath($absolutePath2);
877
+
878
+            if ($path1 == null or $path2 == null) {
879
+                return false;
880
+            }
881
+            $run = true;
882
+
883
+            $this->lockFile($path2, ILockingProvider::LOCK_SHARED);
884
+            $this->lockFile($path1, ILockingProvider::LOCK_SHARED);
885
+            $lockTypePath1 = ILockingProvider::LOCK_SHARED;
886
+            $lockTypePath2 = ILockingProvider::LOCK_SHARED;
887
+
888
+            try {
889
+
890
+                $exists = $this->file_exists($path2);
891
+                if ($this->shouldEmitHooks()) {
892
+                    \OC_Hook::emit(
893
+                        Filesystem::CLASSNAME,
894
+                        Filesystem::signal_copy,
895
+                        array(
896
+                            Filesystem::signal_param_oldpath => $this->getHookPath($path1),
897
+                            Filesystem::signal_param_newpath => $this->getHookPath($path2),
898
+                            Filesystem::signal_param_run => &$run
899
+                        )
900
+                    );
901
+                    $this->emit_file_hooks_pre($exists, $path2, $run);
902
+                }
903
+                if ($run) {
904
+                    $mount1 = $this->getMount($path1);
905
+                    $mount2 = $this->getMount($path2);
906
+                    $storage1 = $mount1->getStorage();
907
+                    $internalPath1 = $mount1->getInternalPath($absolutePath1);
908
+                    $storage2 = $mount2->getStorage();
909
+                    $internalPath2 = $mount2->getInternalPath($absolutePath2);
910
+
911
+                    $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE);
912
+                    $lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE;
913
+
914
+                    if ($mount1->getMountPoint() == $mount2->getMountPoint()) {
915
+                        if ($storage1) {
916
+                            $result = $storage1->copy($internalPath1, $internalPath2);
917
+                        } else {
918
+                            $result = false;
919
+                        }
920
+                    } else {
921
+                        $result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2);
922
+                    }
923
+
924
+                    $this->writeUpdate($storage2, $internalPath2);
925
+
926
+                    $this->changeLock($path2, ILockingProvider::LOCK_SHARED);
927
+                    $lockTypePath2 = ILockingProvider::LOCK_SHARED;
928
+
929
+                    if ($this->shouldEmitHooks() && $result !== false) {
930
+                        \OC_Hook::emit(
931
+                            Filesystem::CLASSNAME,
932
+                            Filesystem::signal_post_copy,
933
+                            array(
934
+                                Filesystem::signal_param_oldpath => $this->getHookPath($path1),
935
+                                Filesystem::signal_param_newpath => $this->getHookPath($path2)
936
+                            )
937
+                        );
938
+                        $this->emit_file_hooks_post($exists, $path2);
939
+                    }
940
+
941
+                }
942
+            } catch (\Exception $e) {
943
+                $this->unlockFile($path2, $lockTypePath2);
944
+                $this->unlockFile($path1, $lockTypePath1);
945
+                throw $e;
946
+            }
947
+
948
+            $this->unlockFile($path2, $lockTypePath2);
949
+            $this->unlockFile($path1, $lockTypePath1);
950
+
951
+        }
952
+        return $result;
953
+    }
954
+
955
+    /**
956
+     * @param string $path
957
+     * @param string $mode 'r' or 'w'
958
+     * @return resource
959
+     */
960
+    public function fopen($path, $mode) {
961
+        $mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support
962
+        $hooks = array();
963
+        switch ($mode) {
964
+            case 'r':
965
+                $hooks[] = 'read';
966
+                break;
967
+            case 'r+':
968
+            case 'w+':
969
+            case 'x+':
970
+            case 'a+':
971
+                $hooks[] = 'read';
972
+                $hooks[] = 'write';
973
+                break;
974
+            case 'w':
975
+            case 'x':
976
+            case 'a':
977
+                $hooks[] = 'write';
978
+                break;
979
+            default:
980
+                \OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR);
981
+        }
982
+
983
+        if ($mode !== 'r' && $mode !== 'w') {
984
+            \OC::$server->getLogger()->info('Trying to open a file with a mode other than "r" or "w" can cause severe performance issues with some backends');
985
+        }
986
+
987
+        return $this->basicOperation('fopen', $path, $hooks, $mode);
988
+    }
989
+
990
+    /**
991
+     * @param string $path
992
+     * @return bool|string
993
+     * @throws \OCP\Files\InvalidPathException
994
+     */
995
+    public function toTmpFile($path) {
996
+        $this->assertPathLength($path);
997
+        if (Filesystem::isValidPath($path)) {
998
+            $source = $this->fopen($path, 'r');
999
+            if ($source) {
1000
+                $extension = pathinfo($path, PATHINFO_EXTENSION);
1001
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension);
1002
+                file_put_contents($tmpFile, $source);
1003
+                return $tmpFile;
1004
+            } else {
1005
+                return false;
1006
+            }
1007
+        } else {
1008
+            return false;
1009
+        }
1010
+    }
1011
+
1012
+    /**
1013
+     * @param string $tmpFile
1014
+     * @param string $path
1015
+     * @return bool|mixed
1016
+     * @throws \OCP\Files\InvalidPathException
1017
+     */
1018
+    public function fromTmpFile($tmpFile, $path) {
1019
+        $this->assertPathLength($path);
1020
+        if (Filesystem::isValidPath($path)) {
1021
+
1022
+            // Get directory that the file is going into
1023
+            $filePath = dirname($path);
1024
+
1025
+            // Create the directories if any
1026
+            if (!$this->file_exists($filePath)) {
1027
+                $result = $this->createParentDirectories($filePath);
1028
+                if ($result === false) {
1029
+                    return false;
1030
+                }
1031
+            }
1032
+
1033
+            $source = fopen($tmpFile, 'r');
1034
+            if ($source) {
1035
+                $result = $this->file_put_contents($path, $source);
1036
+                // $this->file_put_contents() might have already closed
1037
+                // the resource, so we check it, before trying to close it
1038
+                // to avoid messages in the error log.
1039
+                if (is_resource($source)) {
1040
+                    fclose($source);
1041
+                }
1042
+                unlink($tmpFile);
1043
+                return $result;
1044
+            } else {
1045
+                return false;
1046
+            }
1047
+        } else {
1048
+            return false;
1049
+        }
1050
+    }
1051
+
1052
+
1053
+    /**
1054
+     * @param string $path
1055
+     * @return mixed
1056
+     * @throws \OCP\Files\InvalidPathException
1057
+     */
1058
+    public function getMimeType($path) {
1059
+        $this->assertPathLength($path);
1060
+        return $this->basicOperation('getMimeType', $path);
1061
+    }
1062
+
1063
+    /**
1064
+     * @param string $type
1065
+     * @param string $path
1066
+     * @param bool $raw
1067
+     * @return bool|null|string
1068
+     */
1069
+    public function hash($type, $path, $raw = false) {
1070
+        $postFix = ($path[strlen($path) - 1] === '/') ? '/' : '';
1071
+        $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1072
+        if (Filesystem::isValidPath($path)) {
1073
+            $path = $this->getRelativePath($absolutePath);
1074
+            if ($path == null) {
1075
+                return false;
1076
+            }
1077
+            if ($this->shouldEmitHooks($path)) {
1078
+                \OC_Hook::emit(
1079
+                    Filesystem::CLASSNAME,
1080
+                    Filesystem::signal_read,
1081
+                    array(Filesystem::signal_param_path => $this->getHookPath($path))
1082
+                );
1083
+            }
1084
+            list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1085
+            if ($storage) {
1086
+                $result = $storage->hash($type, $internalPath, $raw);
1087
+                return $result;
1088
+            }
1089
+        }
1090
+        return null;
1091
+    }
1092
+
1093
+    /**
1094
+     * @param string $path
1095
+     * @return mixed
1096
+     * @throws \OCP\Files\InvalidPathException
1097
+     */
1098
+    public function free_space($path = '/') {
1099
+        $this->assertPathLength($path);
1100
+        $result = $this->basicOperation('free_space', $path);
1101
+        if ($result === null) {
1102
+            throw new InvalidPathException();
1103
+        }
1104
+        return $result;
1105
+    }
1106
+
1107
+    /**
1108
+     * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage
1109
+     *
1110
+     * @param string $operation
1111
+     * @param string $path
1112
+     * @param array $hooks (optional)
1113
+     * @param mixed $extraParam (optional)
1114
+     * @return mixed
1115
+     * @throws \Exception
1116
+     *
1117
+     * This method takes requests for basic filesystem functions (e.g. reading & writing
1118
+     * files), processes hooks and proxies, sanitises paths, and finally passes them on to
1119
+     * \OC\Files\Storage\Storage for delegation to a storage backend for execution
1120
+     */
1121
+    private function basicOperation($operation, $path, $hooks = [], $extraParam = null) {
1122
+        $postFix = ($path[strlen($path) - 1] === '/') ? '/' : '';
1123
+        $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
1124
+        if (Filesystem::isValidPath($path)
1125
+            and !Filesystem::isFileBlacklisted($path)
1126
+        ) {
1127
+            $path = $this->getRelativePath($absolutePath);
1128
+            if ($path == null) {
1129
+                return false;
1130
+            }
1131
+
1132
+            if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) {
1133
+                // always a shared lock during pre-hooks so the hook can read the file
1134
+                $this->lockFile($path, ILockingProvider::LOCK_SHARED);
1135
+            }
1136
+
1137
+            $run = $this->runHooks($hooks, $path);
1138
+            /** @var \OC\Files\Storage\Storage $storage */
1139
+            list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1140
+            if ($run and $storage) {
1141
+                if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1142
+                    $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
1143
+                }
1144
+                try {
1145
+                    if (!is_null($extraParam)) {
1146
+                        $result = $storage->$operation($internalPath, $extraParam);
1147
+                    } else {
1148
+                        $result = $storage->$operation($internalPath);
1149
+                    }
1150
+                } catch (\Exception $e) {
1151
+                    if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1152
+                        $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1153
+                    } else if (in_array('read', $hooks)) {
1154
+                        $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1155
+                    }
1156
+                    throw $e;
1157
+                }
1158
+
1159
+                if ($result && in_array('delete', $hooks) and $result) {
1160
+                    $this->removeUpdate($storage, $internalPath);
1161
+                }
1162
+                if ($result && in_array('write', $hooks) and $operation !== 'fopen') {
1163
+                    $this->writeUpdate($storage, $internalPath);
1164
+                }
1165
+                if ($result && in_array('touch', $hooks)) {
1166
+                    $this->writeUpdate($storage, $internalPath, $extraParam);
1167
+                }
1168
+
1169
+                if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) {
1170
+                    $this->changeLock($path, ILockingProvider::LOCK_SHARED);
1171
+                }
1172
+
1173
+                $unlockLater = false;
1174
+                if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) {
1175
+                    $unlockLater = true;
1176
+                    // make sure our unlocking callback will still be called if connection is aborted
1177
+                    ignore_user_abort(true);
1178
+                    $result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) {
1179
+                        if (in_array('write', $hooks)) {
1180
+                            $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1181
+                        } else if (in_array('read', $hooks)) {
1182
+                            $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1183
+                        }
1184
+                    });
1185
+                }
1186
+
1187
+                if ($this->shouldEmitHooks($path) && $result !== false) {
1188
+                    if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open
1189
+                        $this->runHooks($hooks, $path, true);
1190
+                    }
1191
+                }
1192
+
1193
+                if (!$unlockLater
1194
+                    && (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks))
1195
+                ) {
1196
+                    $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1197
+                }
1198
+                return $result;
1199
+            } else {
1200
+                $this->unlockFile($path, ILockingProvider::LOCK_SHARED);
1201
+            }
1202
+        }
1203
+        return null;
1204
+    }
1205
+
1206
+    /**
1207
+     * get the path relative to the default root for hook usage
1208
+     *
1209
+     * @param string $path
1210
+     * @return string
1211
+     */
1212
+    private function getHookPath($path) {
1213
+        if (!Filesystem::getView()) {
1214
+            return $path;
1215
+        }
1216
+        return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path));
1217
+    }
1218
+
1219
+    private function shouldEmitHooks($path = '') {
1220
+        if ($path && Cache\Scanner::isPartialFile($path)) {
1221
+            return false;
1222
+        }
1223
+        if (!Filesystem::$loaded) {
1224
+            return false;
1225
+        }
1226
+        $defaultRoot = Filesystem::getRoot();
1227
+        if ($defaultRoot === null) {
1228
+            return false;
1229
+        }
1230
+        if ($this->fakeRoot === $defaultRoot) {
1231
+            return true;
1232
+        }
1233
+        $fullPath = $this->getAbsolutePath($path);
1234
+
1235
+        if ($fullPath === $defaultRoot) {
1236
+            return true;
1237
+        }
1238
+
1239
+        return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
1240
+    }
1241
+
1242
+    /**
1243
+     * @param string[] $hooks
1244
+     * @param string $path
1245
+     * @param bool $post
1246
+     * @return bool
1247
+     */
1248
+    private function runHooks($hooks, $path, $post = false) {
1249
+        $relativePath = $path;
1250
+        $path = $this->getHookPath($path);
1251
+        $prefix = ($post) ? 'post_' : '';
1252
+        $run = true;
1253
+        if ($this->shouldEmitHooks($relativePath)) {
1254
+            foreach ($hooks as $hook) {
1255
+                if ($hook != 'read') {
1256
+                    \OC_Hook::emit(
1257
+                        Filesystem::CLASSNAME,
1258
+                        $prefix . $hook,
1259
+                        array(
1260
+                            Filesystem::signal_param_run => &$run,
1261
+                            Filesystem::signal_param_path => $path
1262
+                        )
1263
+                    );
1264
+                } elseif (!$post) {
1265
+                    \OC_Hook::emit(
1266
+                        Filesystem::CLASSNAME,
1267
+                        $prefix . $hook,
1268
+                        array(
1269
+                            Filesystem::signal_param_path => $path
1270
+                        )
1271
+                    );
1272
+                }
1273
+            }
1274
+        }
1275
+        return $run;
1276
+    }
1277
+
1278
+    /**
1279
+     * check if a file or folder has been updated since $time
1280
+     *
1281
+     * @param string $path
1282
+     * @param int $time
1283
+     * @return bool
1284
+     */
1285
+    public function hasUpdated($path, $time) {
1286
+        return $this->basicOperation('hasUpdated', $path, array(), $time);
1287
+    }
1288
+
1289
+    /**
1290
+     * @param string $ownerId
1291
+     * @return \OC\User\User
1292
+     */
1293
+    private function getUserObjectForOwner($ownerId) {
1294
+        $owner = $this->userManager->get($ownerId);
1295
+        if ($owner instanceof IUser) {
1296
+            return $owner;
1297
+        } else {
1298
+            return new User($ownerId, null);
1299
+        }
1300
+    }
1301
+
1302
+    /**
1303
+     * Get file info from cache
1304
+     *
1305
+     * If the file is not in cached it will be scanned
1306
+     * If the file has changed on storage the cache will be updated
1307
+     *
1308
+     * @param \OC\Files\Storage\Storage $storage
1309
+     * @param string $internalPath
1310
+     * @param string $relativePath
1311
+     * @return ICacheEntry|bool
1312
+     */
1313
+    private function getCacheEntry($storage, $internalPath, $relativePath) {
1314
+        $cache = $storage->getCache($internalPath);
1315
+        $data = $cache->get($internalPath);
1316
+        $watcher = $storage->getWatcher($internalPath);
1317
+
1318
+        try {
1319
+            // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data
1320
+            if (!$data || $data['size'] === -1) {
1321
+                $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1322
+                if (!$storage->file_exists($internalPath)) {
1323
+                    $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1324
+                    return false;
1325
+                }
1326
+                $scanner = $storage->getScanner($internalPath);
1327
+                $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1328
+                $data = $cache->get($internalPath);
1329
+                $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1330
+            } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) {
1331
+                $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED);
1332
+                $watcher->update($internalPath, $data);
1333
+                $storage->getPropagator()->propagateChange($internalPath, time());
1334
+                $data = $cache->get($internalPath);
1335
+                $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED);
1336
+            }
1337
+        } catch (LockedException $e) {
1338
+            // if the file is locked we just use the old cache info
1339
+        }
1340
+
1341
+        return $data;
1342
+    }
1343
+
1344
+    /**
1345
+     * get the filesystem info
1346
+     *
1347
+     * @param string $path
1348
+     * @param boolean|string $includeMountPoints true to add mountpoint sizes,
1349
+     * 'ext' to add only ext storage mount point sizes. Defaults to true.
1350
+     * defaults to true
1351
+     * @return \OC\Files\FileInfo|false False if file does not exist
1352
+     */
1353
+    public function getFileInfo($path, $includeMountPoints = true) {
1354
+        $this->assertPathLength($path);
1355
+        if (!Filesystem::isValidPath($path)) {
1356
+            return false;
1357
+        }
1358
+        if (Cache\Scanner::isPartialFile($path)) {
1359
+            return $this->getPartFileInfo($path);
1360
+        }
1361
+        $relativePath = $path;
1362
+        $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1363
+
1364
+        $mount = Filesystem::getMountManager()->find($path);
1365
+        if (!$mount) {
1366
+            return false;
1367
+        }
1368
+        $storage = $mount->getStorage();
1369
+        $internalPath = $mount->getInternalPath($path);
1370
+        if ($storage) {
1371
+            $data = $this->getCacheEntry($storage, $internalPath, $relativePath);
1372
+
1373
+            if (!$data instanceof ICacheEntry) {
1374
+                return false;
1375
+            }
1376
+
1377
+            if ($mount instanceof MoveableMount && $internalPath === '') {
1378
+                $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE;
1379
+            }
1380
+
1381
+            $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath));
1382
+            $info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner);
1383
+
1384
+            if ($data and isset($data['fileid'])) {
1385
+                if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') {
1386
+                    //add the sizes of other mount points to the folder
1387
+                    $extOnly = ($includeMountPoints === 'ext');
1388
+                    $mounts = Filesystem::getMountManager()->findIn($path);
1389
+                    $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
1390
+                        $subStorage = $mount->getStorage();
1391
+                        return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
1392
+                    }));
1393
+                }
1394
+            }
1395
+
1396
+            return $info;
1397
+        }
1398
+
1399
+        return false;
1400
+    }
1401
+
1402
+    /**
1403
+     * get the content of a directory
1404
+     *
1405
+     * @param string $directory path under datadirectory
1406
+     * @param string $mimetype_filter limit returned content to this mimetype or mimepart
1407
+     * @return FileInfo[]
1408
+     */
1409
+    public function getDirectoryContent($directory, $mimetype_filter = '') {
1410
+        $this->assertPathLength($directory);
1411
+        if (!Filesystem::isValidPath($directory)) {
1412
+            return [];
1413
+        }
1414
+        $path = $this->getAbsolutePath($directory);
1415
+        $path = Filesystem::normalizePath($path);
1416
+        $mount = $this->getMount($directory);
1417
+        if (!$mount) {
1418
+            return [];
1419
+        }
1420
+        $storage = $mount->getStorage();
1421
+        $internalPath = $mount->getInternalPath($path);
1422
+        if ($storage) {
1423
+            $cache = $storage->getCache($internalPath);
1424
+            $user = \OC_User::getUser();
1425
+
1426
+            $data = $this->getCacheEntry($storage, $internalPath, $directory);
1427
+
1428
+            if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) {
1429
+                return [];
1430
+            }
1431
+
1432
+            $folderId = $data['fileid'];
1433
+            $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter
1434
+
1435
+            $sharingDisabled = \OCP\Util::isSharingDisabledForUser();
1436
+            /**
1437
+             * @var \OC\Files\FileInfo[] $files
1438
+             */
1439
+            $files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1440
+                if ($sharingDisabled) {
1441
+                    $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1442
+                }
1443
+                $owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
1444
+                return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
1445
+            }, $contents);
1446
+
1447
+            //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
1448
+            $mounts = Filesystem::getMountManager()->findIn($path);
1449
+            $dirLength = strlen($path);
1450
+            foreach ($mounts as $mount) {
1451
+                $mountPoint = $mount->getMountPoint();
1452
+                $subStorage = $mount->getStorage();
1453
+                if ($subStorage) {
1454
+                    $subCache = $subStorage->getCache('');
1455
+
1456
+                    $rootEntry = $subCache->get('');
1457
+                    if (!$rootEntry) {
1458
+                        $subScanner = $subStorage->getScanner('');
1459
+                        try {
1460
+                            $subScanner->scanFile('');
1461
+                        } catch (\OCP\Files\StorageNotAvailableException $e) {
1462
+                            continue;
1463
+                        } catch (\OCP\Files\StorageInvalidException $e) {
1464
+                            continue;
1465
+                        } catch (\Exception $e) {
1466
+                            // sometimes when the storage is not available it can be any exception
1467
+                            \OC::$server->getLogger()->logException($e, [
1468
+                                'message' => 'Exception while scanning storage "' . $subStorage->getId() . '"',
1469
+                                'level' => \OCP\Util::ERROR,
1470
+                                'app' => 'lib',
1471
+                            ]);
1472
+                            continue;
1473
+                        }
1474
+                        $rootEntry = $subCache->get('');
1475
+                    }
1476
+
1477
+                    if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) {
1478
+                        $relativePath = trim(substr($mountPoint, $dirLength), '/');
1479
+                        if ($pos = strpos($relativePath, '/')) {
1480
+                            //mountpoint inside subfolder add size to the correct folder
1481
+                            $entryName = substr($relativePath, 0, $pos);
1482
+                            foreach ($files as &$entry) {
1483
+                                if ($entry->getName() === $entryName) {
1484
+                                    $entry->addSubEntry($rootEntry, $mountPoint);
1485
+                                }
1486
+                            }
1487
+                        } else { //mountpoint in this folder, add an entry for it
1488
+                            $rootEntry['name'] = $relativePath;
1489
+                            $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
1490
+                            $permissions = $rootEntry['permissions'];
1491
+                            // do not allow renaming/deleting the mount point if they are not shared files/folders
1492
+                            // for shared files/folders we use the permissions given by the owner
1493
+                            if ($mount instanceof MoveableMount) {
1494
+                                $rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
1495
+                            } else {
1496
+                                $rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE));
1497
+                            }
1498
+
1499
+                            //remove any existing entry with the same name
1500
+                            foreach ($files as $i => $file) {
1501
+                                if ($file['name'] === $rootEntry['name']) {
1502
+                                    unset($files[$i]);
1503
+                                    break;
1504
+                                }
1505
+                            }
1506
+                            $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
1507
+
1508
+                            // if sharing was disabled for the user we remove the share permissions
1509
+                            if (\OCP\Util::isSharingDisabledForUser()) {
1510
+                                $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1511
+                            }
1512
+
1513
+                            $owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
1514
+                            $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1515
+                        }
1516
+                    }
1517
+                }
1518
+            }
1519
+
1520
+            if ($mimetype_filter) {
1521
+                $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
1522
+                    if (strpos($mimetype_filter, '/')) {
1523
+                        return $file->getMimetype() === $mimetype_filter;
1524
+                    } else {
1525
+                        return $file->getMimePart() === $mimetype_filter;
1526
+                    }
1527
+                });
1528
+            }
1529
+
1530
+            return $files;
1531
+        } else {
1532
+            return [];
1533
+        }
1534
+    }
1535
+
1536
+    /**
1537
+     * change file metadata
1538
+     *
1539
+     * @param string $path
1540
+     * @param array|\OCP\Files\FileInfo $data
1541
+     * @return int
1542
+     *
1543
+     * returns the fileid of the updated file
1544
+     */
1545
+    public function putFileInfo($path, $data) {
1546
+        $this->assertPathLength($path);
1547
+        if ($data instanceof FileInfo) {
1548
+            $data = $data->getData();
1549
+        }
1550
+        $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1551
+        /**
1552
+         * @var \OC\Files\Storage\Storage $storage
1553
+         * @var string $internalPath
1554
+         */
1555
+        list($storage, $internalPath) = Filesystem::resolvePath($path);
1556
+        if ($storage) {
1557
+            $cache = $storage->getCache($path);
1558
+
1559
+            if (!$cache->inCache($internalPath)) {
1560
+                $scanner = $storage->getScanner($internalPath);
1561
+                $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW);
1562
+            }
1563
+
1564
+            return $cache->put($internalPath, $data);
1565
+        } else {
1566
+            return -1;
1567
+        }
1568
+    }
1569
+
1570
+    /**
1571
+     * search for files with the name matching $query
1572
+     *
1573
+     * @param string $query
1574
+     * @return FileInfo[]
1575
+     */
1576
+    public function search($query) {
1577
+        return $this->searchCommon('search', array('%' . $query . '%'));
1578
+    }
1579
+
1580
+    /**
1581
+     * search for files with the name matching $query
1582
+     *
1583
+     * @param string $query
1584
+     * @return FileInfo[]
1585
+     */
1586
+    public function searchRaw($query) {
1587
+        return $this->searchCommon('search', array($query));
1588
+    }
1589
+
1590
+    /**
1591
+     * search for files by mimetype
1592
+     *
1593
+     * @param string $mimetype
1594
+     * @return FileInfo[]
1595
+     */
1596
+    public function searchByMime($mimetype) {
1597
+        return $this->searchCommon('searchByMime', array($mimetype));
1598
+    }
1599
+
1600
+    /**
1601
+     * search for files by tag
1602
+     *
1603
+     * @param string|int $tag name or tag id
1604
+     * @param string $userId owner of the tags
1605
+     * @return FileInfo[]
1606
+     */
1607
+    public function searchByTag($tag, $userId) {
1608
+        return $this->searchCommon('searchByTag', array($tag, $userId));
1609
+    }
1610
+
1611
+    /**
1612
+     * @param string $method cache method
1613
+     * @param array $args
1614
+     * @return FileInfo[]
1615
+     */
1616
+    private function searchCommon($method, $args) {
1617
+        $files = array();
1618
+        $rootLength = strlen($this->fakeRoot);
1619
+
1620
+        $mount = $this->getMount('');
1621
+        $mountPoint = $mount->getMountPoint();
1622
+        $storage = $mount->getStorage();
1623
+        if ($storage) {
1624
+            $cache = $storage->getCache('');
1625
+
1626
+            $results = call_user_func_array(array($cache, $method), $args);
1627
+            foreach ($results as $result) {
1628
+                if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') {
1629
+                    $internalPath = $result['path'];
1630
+                    $path = $mountPoint . $result['path'];
1631
+                    $result['path'] = substr($mountPoint . $result['path'], $rootLength);
1632
+                    $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1633
+                    $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1634
+                }
1635
+            }
1636
+
1637
+            $mounts = Filesystem::getMountManager()->findIn($this->fakeRoot);
1638
+            foreach ($mounts as $mount) {
1639
+                $mountPoint = $mount->getMountPoint();
1640
+                $storage = $mount->getStorage();
1641
+                if ($storage) {
1642
+                    $cache = $storage->getCache('');
1643
+
1644
+                    $relativeMountPoint = substr($mountPoint, $rootLength);
1645
+                    $results = call_user_func_array(array($cache, $method), $args);
1646
+                    if ($results) {
1647
+                        foreach ($results as $result) {
1648
+                            $internalPath = $result['path'];
1649
+                            $result['path'] = rtrim($relativeMountPoint . $result['path'], '/');
1650
+                            $path = rtrim($mountPoint . $internalPath, '/');
1651
+                            $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1652
+                            $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1653
+                        }
1654
+                    }
1655
+                }
1656
+            }
1657
+        }
1658
+        return $files;
1659
+    }
1660
+
1661
+    /**
1662
+     * Get the owner for a file or folder
1663
+     *
1664
+     * @param string $path
1665
+     * @return string the user id of the owner
1666
+     * @throws NotFoundException
1667
+     */
1668
+    public function getOwner($path) {
1669
+        $info = $this->getFileInfo($path);
1670
+        if (!$info) {
1671
+            throw new NotFoundException($path . ' not found while trying to get owner');
1672
+        }
1673
+        return $info->getOwner()->getUID();
1674
+    }
1675
+
1676
+    /**
1677
+     * get the ETag for a file or folder
1678
+     *
1679
+     * @param string $path
1680
+     * @return string
1681
+     */
1682
+    public function getETag($path) {
1683
+        /**
1684
+         * @var Storage\Storage $storage
1685
+         * @var string $internalPath
1686
+         */
1687
+        list($storage, $internalPath) = $this->resolvePath($path);
1688
+        if ($storage) {
1689
+            return $storage->getETag($internalPath);
1690
+        } else {
1691
+            return null;
1692
+        }
1693
+    }
1694
+
1695
+    /**
1696
+     * Get the path of a file by id, relative to the view
1697
+     *
1698
+     * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file
1699
+     *
1700
+     * @param int $id
1701
+     * @throws NotFoundException
1702
+     * @return string
1703
+     */
1704
+    public function getPath($id) {
1705
+        $id = (int)$id;
1706
+        $manager = Filesystem::getMountManager();
1707
+        $mounts = $manager->findIn($this->fakeRoot);
1708
+        $mounts[] = $manager->find($this->fakeRoot);
1709
+        // reverse the array so we start with the storage this view is in
1710
+        // which is the most likely to contain the file we're looking for
1711
+        $mounts = array_reverse($mounts);
1712
+        foreach ($mounts as $mount) {
1713
+            /**
1714
+             * @var \OC\Files\Mount\MountPoint $mount
1715
+             */
1716
+            if ($mount->getStorage()) {
1717
+                $cache = $mount->getStorage()->getCache();
1718
+                $internalPath = $cache->getPathById($id);
1719
+                if (is_string($internalPath)) {
1720
+                    $fullPath = $mount->getMountPoint() . $internalPath;
1721
+                    if (!is_null($path = $this->getRelativePath($fullPath))) {
1722
+                        return $path;
1723
+                    }
1724
+                }
1725
+            }
1726
+        }
1727
+        throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id));
1728
+    }
1729
+
1730
+    /**
1731
+     * @param string $path
1732
+     * @throws InvalidPathException
1733
+     */
1734
+    private function assertPathLength($path) {
1735
+        $maxLen = min(PHP_MAXPATHLEN, 4000);
1736
+        // Check for the string length - performed using isset() instead of strlen()
1737
+        // because isset() is about 5x-40x faster.
1738
+        if (isset($path[$maxLen])) {
1739
+            $pathLen = strlen($path);
1740
+            throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path");
1741
+        }
1742
+    }
1743
+
1744
+    /**
1745
+     * check if it is allowed to move a mount point to a given target.
1746
+     * It is not allowed to move a mount point into a different mount point or
1747
+     * into an already shared folder
1748
+     *
1749
+     * @param string $target path
1750
+     * @return boolean
1751
+     */
1752
+    private function isTargetAllowed($target) {
1753
+
1754
+        list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target);
1755
+        if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) {
1756
+            \OCP\Util::writeLog('files',
1757
+                'It is not allowed to move one mount point into another one',
1758
+                \OCP\Util::DEBUG);
1759
+            return false;
1760
+        }
1761
+
1762
+        // note: cannot use the view because the target is already locked
1763
+        $fileId = (int)$targetStorage->getCache()->getId($targetInternalPath);
1764
+        if ($fileId === -1) {
1765
+            // target might not exist, need to check parent instead
1766
+            $fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath));
1767
+        }
1768
+
1769
+        // check if any of the parents were shared by the current owner (include collections)
1770
+        $shares = \OCP\Share::getItemShared(
1771
+            'folder',
1772
+            $fileId,
1773
+            \OCP\Share::FORMAT_NONE,
1774
+            null,
1775
+            true
1776
+        );
1777
+
1778
+        if (count($shares) > 0) {
1779
+            \OCP\Util::writeLog('files',
1780
+                'It is not allowed to move one mount point into a shared folder',
1781
+                \OCP\Util::DEBUG);
1782
+            return false;
1783
+        }
1784
+
1785
+        return true;
1786
+    }
1787
+
1788
+    /**
1789
+     * Get a fileinfo object for files that are ignored in the cache (part files)
1790
+     *
1791
+     * @param string $path
1792
+     * @return \OCP\Files\FileInfo
1793
+     */
1794
+    private function getPartFileInfo($path) {
1795
+        $mount = $this->getMount($path);
1796
+        $storage = $mount->getStorage();
1797
+        $internalPath = $mount->getInternalPath($this->getAbsolutePath($path));
1798
+        $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1799
+        return new FileInfo(
1800
+            $this->getAbsolutePath($path),
1801
+            $storage,
1802
+            $internalPath,
1803
+            [
1804
+                'fileid' => null,
1805
+                'mimetype' => $storage->getMimeType($internalPath),
1806
+                'name' => basename($path),
1807
+                'etag' => null,
1808
+                'size' => $storage->filesize($internalPath),
1809
+                'mtime' => $storage->filemtime($internalPath),
1810
+                'encrypted' => false,
1811
+                'permissions' => \OCP\Constants::PERMISSION_ALL
1812
+            ],
1813
+            $mount,
1814
+            $owner
1815
+        );
1816
+    }
1817
+
1818
+    /**
1819
+     * @param string $path
1820
+     * @param string $fileName
1821
+     * @throws InvalidPathException
1822
+     */
1823
+    public function verifyPath($path, $fileName) {
1824
+        try {
1825
+            /** @type \OCP\Files\Storage $storage */
1826
+            list($storage, $internalPath) = $this->resolvePath($path);
1827
+            $storage->verifyPath($internalPath, $fileName);
1828
+        } catch (ReservedWordException $ex) {
1829
+            $l = \OC::$server->getL10N('lib');
1830
+            throw new InvalidPathException($l->t('File name is a reserved word'));
1831
+        } catch (InvalidCharacterInPathException $ex) {
1832
+            $l = \OC::$server->getL10N('lib');
1833
+            throw new InvalidPathException($l->t('File name contains at least one invalid character'));
1834
+        } catch (FileNameTooLongException $ex) {
1835
+            $l = \OC::$server->getL10N('lib');
1836
+            throw new InvalidPathException($l->t('File name is too long'));
1837
+        } catch (InvalidDirectoryException $ex) {
1838
+            $l = \OC::$server->getL10N('lib');
1839
+            throw new InvalidPathException($l->t('Dot files are not allowed'));
1840
+        } catch (EmptyFileNameException $ex) {
1841
+            $l = \OC::$server->getL10N('lib');
1842
+            throw new InvalidPathException($l->t('Empty filename is not allowed'));
1843
+        }
1844
+    }
1845
+
1846
+    /**
1847
+     * get all parent folders of $path
1848
+     *
1849
+     * @param string $path
1850
+     * @return string[]
1851
+     */
1852
+    private function getParents($path) {
1853
+        $path = trim($path, '/');
1854
+        if (!$path) {
1855
+            return [];
1856
+        }
1857
+
1858
+        $parts = explode('/', $path);
1859
+
1860
+        // remove the single file
1861
+        array_pop($parts);
1862
+        $result = array('/');
1863
+        $resultPath = '';
1864
+        foreach ($parts as $part) {
1865
+            if ($part) {
1866
+                $resultPath .= '/' . $part;
1867
+                $result[] = $resultPath;
1868
+            }
1869
+        }
1870
+        return $result;
1871
+    }
1872
+
1873
+    /**
1874
+     * Returns the mount point for which to lock
1875
+     *
1876
+     * @param string $absolutePath absolute path
1877
+     * @param bool $useParentMount true to return parent mount instead of whatever
1878
+     * is mounted directly on the given path, false otherwise
1879
+     * @return \OC\Files\Mount\MountPoint mount point for which to apply locks
1880
+     */
1881
+    private function getMountForLock($absolutePath, $useParentMount = false) {
1882
+        $results = [];
1883
+        $mount = Filesystem::getMountManager()->find($absolutePath);
1884
+        if (!$mount) {
1885
+            return $results;
1886
+        }
1887
+
1888
+        if ($useParentMount) {
1889
+            // find out if something is mounted directly on the path
1890
+            $internalPath = $mount->getInternalPath($absolutePath);
1891
+            if ($internalPath === '') {
1892
+                // resolve the parent mount instead
1893
+                $mount = Filesystem::getMountManager()->find(dirname($absolutePath));
1894
+            }
1895
+        }
1896
+
1897
+        return $mount;
1898
+    }
1899
+
1900
+    /**
1901
+     * Lock the given path
1902
+     *
1903
+     * @param string $path the path of the file to lock, relative to the view
1904
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1905
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1906
+     *
1907
+     * @return bool False if the path is excluded from locking, true otherwise
1908
+     * @throws \OCP\Lock\LockedException if the path is already locked
1909
+     */
1910
+    private function lockPath($path, $type, $lockMountPoint = false) {
1911
+        $absolutePath = $this->getAbsolutePath($path);
1912
+        $absolutePath = Filesystem::normalizePath($absolutePath);
1913
+        if (!$this->shouldLockFile($absolutePath)) {
1914
+            return false;
1915
+        }
1916
+
1917
+        $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1918
+        if ($mount) {
1919
+            try {
1920
+                $storage = $mount->getStorage();
1921
+                if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1922
+                    $storage->acquireLock(
1923
+                        $mount->getInternalPath($absolutePath),
1924
+                        $type,
1925
+                        $this->lockingProvider
1926
+                    );
1927
+                }
1928
+            } catch (\OCP\Lock\LockedException $e) {
1929
+                // rethrow with the a human-readable path
1930
+                throw new \OCP\Lock\LockedException(
1931
+                    $this->getPathRelativeToFiles($absolutePath),
1932
+                    $e
1933
+                );
1934
+            }
1935
+        }
1936
+
1937
+        return true;
1938
+    }
1939
+
1940
+    /**
1941
+     * Change the lock type
1942
+     *
1943
+     * @param string $path the path of the file to lock, relative to the view
1944
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1945
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1946
+     *
1947
+     * @return bool False if the path is excluded from locking, true otherwise
1948
+     * @throws \OCP\Lock\LockedException if the path is already locked
1949
+     */
1950
+    public function changeLock($path, $type, $lockMountPoint = false) {
1951
+        $path = Filesystem::normalizePath($path);
1952
+        $absolutePath = $this->getAbsolutePath($path);
1953
+        $absolutePath = Filesystem::normalizePath($absolutePath);
1954
+        if (!$this->shouldLockFile($absolutePath)) {
1955
+            return false;
1956
+        }
1957
+
1958
+        $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
1959
+        if ($mount) {
1960
+            try {
1961
+                $storage = $mount->getStorage();
1962
+                if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
1963
+                    $storage->changeLock(
1964
+                        $mount->getInternalPath($absolutePath),
1965
+                        $type,
1966
+                        $this->lockingProvider
1967
+                    );
1968
+                }
1969
+            } catch (\OCP\Lock\LockedException $e) {
1970
+                try {
1971
+                    // rethrow with the a human-readable path
1972
+                    throw new \OCP\Lock\LockedException(
1973
+                        $this->getPathRelativeToFiles($absolutePath),
1974
+                        $e
1975
+                    );
1976
+                } catch (\InvalidArgumentException $e) {
1977
+                    throw new \OCP\Lock\LockedException(
1978
+                        $absolutePath,
1979
+                        $e
1980
+                    );
1981
+                }
1982
+            }
1983
+        }
1984
+
1985
+        return true;
1986
+    }
1987
+
1988
+    /**
1989
+     * Unlock the given path
1990
+     *
1991
+     * @param string $path the path of the file to unlock, relative to the view
1992
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
1993
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
1994
+     *
1995
+     * @return bool False if the path is excluded from locking, true otherwise
1996
+     */
1997
+    private function unlockPath($path, $type, $lockMountPoint = false) {
1998
+        $absolutePath = $this->getAbsolutePath($path);
1999
+        $absolutePath = Filesystem::normalizePath($absolutePath);
2000
+        if (!$this->shouldLockFile($absolutePath)) {
2001
+            return false;
2002
+        }
2003
+
2004
+        $mount = $this->getMountForLock($absolutePath, $lockMountPoint);
2005
+        if ($mount) {
2006
+            $storage = $mount->getStorage();
2007
+            if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) {
2008
+                $storage->releaseLock(
2009
+                    $mount->getInternalPath($absolutePath),
2010
+                    $type,
2011
+                    $this->lockingProvider
2012
+                );
2013
+            }
2014
+        }
2015
+
2016
+        return true;
2017
+    }
2018
+
2019
+    /**
2020
+     * Lock a path and all its parents up to the root of the view
2021
+     *
2022
+     * @param string $path the path of the file to lock relative to the view
2023
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2024
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2025
+     *
2026
+     * @return bool False if the path is excluded from locking, true otherwise
2027
+     */
2028
+    public function lockFile($path, $type, $lockMountPoint = false) {
2029
+        $absolutePath = $this->getAbsolutePath($path);
2030
+        $absolutePath = Filesystem::normalizePath($absolutePath);
2031
+        if (!$this->shouldLockFile($absolutePath)) {
2032
+            return false;
2033
+        }
2034
+
2035
+        $this->lockPath($path, $type, $lockMountPoint);
2036
+
2037
+        $parents = $this->getParents($path);
2038
+        foreach ($parents as $parent) {
2039
+            $this->lockPath($parent, ILockingProvider::LOCK_SHARED);
2040
+        }
2041
+
2042
+        return true;
2043
+    }
2044
+
2045
+    /**
2046
+     * Unlock a path and all its parents up to the root of the view
2047
+     *
2048
+     * @param string $path the path of the file to lock relative to the view
2049
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
2050
+     * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage
2051
+     *
2052
+     * @return bool False if the path is excluded from locking, true otherwise
2053
+     */
2054
+    public function unlockFile($path, $type, $lockMountPoint = false) {
2055
+        $absolutePath = $this->getAbsolutePath($path);
2056
+        $absolutePath = Filesystem::normalizePath($absolutePath);
2057
+        if (!$this->shouldLockFile($absolutePath)) {
2058
+            return false;
2059
+        }
2060
+
2061
+        $this->unlockPath($path, $type, $lockMountPoint);
2062
+
2063
+        $parents = $this->getParents($path);
2064
+        foreach ($parents as $parent) {
2065
+            $this->unlockPath($parent, ILockingProvider::LOCK_SHARED);
2066
+        }
2067
+
2068
+        return true;
2069
+    }
2070
+
2071
+    /**
2072
+     * Only lock files in data/user/files/
2073
+     *
2074
+     * @param string $path Absolute path to the file/folder we try to (un)lock
2075
+     * @return bool
2076
+     */
2077
+    protected function shouldLockFile($path) {
2078
+        $path = Filesystem::normalizePath($path);
2079
+
2080
+        $pathSegments = explode('/', $path);
2081
+        if (isset($pathSegments[2])) {
2082
+            // E.g.: /username/files/path-to-file
2083
+            return ($pathSegments[2] === 'files') && (count($pathSegments) > 3);
2084
+        }
2085
+
2086
+        return strpos($path, '/appdata_') !== 0;
2087
+    }
2088
+
2089
+    /**
2090
+     * Shortens the given absolute path to be relative to
2091
+     * "$user/files".
2092
+     *
2093
+     * @param string $absolutePath absolute path which is under "files"
2094
+     *
2095
+     * @return string path relative to "files" with trimmed slashes or null
2096
+     * if the path was NOT relative to files
2097
+     *
2098
+     * @throws \InvalidArgumentException if the given path was not under "files"
2099
+     * @since 8.1.0
2100
+     */
2101
+    public function getPathRelativeToFiles($absolutePath) {
2102
+        $path = Filesystem::normalizePath($absolutePath);
2103
+        $parts = explode('/', trim($path, '/'), 3);
2104
+        // "$user", "files", "path/to/dir"
2105
+        if (!isset($parts[1]) || $parts[1] !== 'files') {
2106
+            $this->logger->error(
2107
+                '$absolutePath must be relative to "files", value is "%s"',
2108
+                [
2109
+                    $absolutePath
2110
+                ]
2111
+            );
2112
+            throw new \InvalidArgumentException('$absolutePath must be relative to "files"');
2113
+        }
2114
+        if (isset($parts[2])) {
2115
+            return $parts[2];
2116
+        }
2117
+        return '';
2118
+    }
2119
+
2120
+    /**
2121
+     * @param string $filename
2122
+     * @return array
2123
+     * @throws \OC\User\NoUserException
2124
+     * @throws NotFoundException
2125
+     */
2126
+    public function getUidAndFilename($filename) {
2127
+        $info = $this->getFileInfo($filename);
2128
+        if (!$info instanceof \OCP\Files\FileInfo) {
2129
+            throw new NotFoundException($this->getAbsolutePath($filename) . ' not found');
2130
+        }
2131
+        $uid = $info->getOwner()->getUID();
2132
+        if ($uid != \OCP\User::getUser()) {
2133
+            Filesystem::initMountPoints($uid);
2134
+            $ownerView = new View('/' . $uid . '/files');
2135
+            try {
2136
+                $filename = $ownerView->getPath($info['fileid']);
2137
+            } catch (NotFoundException $e) {
2138
+                throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid);
2139
+            }
2140
+        }
2141
+        return [$uid, $filename];
2142
+    }
2143
+
2144
+    /**
2145
+     * Creates parent non-existing folders
2146
+     *
2147
+     * @param string $filePath
2148
+     * @return bool
2149
+     */
2150
+    private function createParentDirectories($filePath) {
2151
+        $directoryParts = explode('/', $filePath);
2152
+        $directoryParts = array_filter($directoryParts);
2153
+        foreach ($directoryParts as $key => $part) {
2154
+            $currentPathElements = array_slice($directoryParts, 0, $key);
2155
+            $currentPath = '/' . implode('/', $currentPathElements);
2156
+            if ($this->is_file($currentPath)) {
2157
+                return false;
2158
+            }
2159
+            if (!$this->file_exists($currentPath)) {
2160
+                $this->mkdir($currentPath);
2161
+            }
2162
+        }
2163
+
2164
+        return true;
2165
+    }
2166 2166
 }
Please login to merge, or discard this patch.
Spacing   +40 added lines, -40 removed lines patch added patch discarded remove patch
@@ -126,9 +126,9 @@  discard block
 block discarded – undo
126 126
 			$path = '/';
127 127
 		}
128 128
 		if ($path[0] !== '/') {
129
-			$path = '/' . $path;
129
+			$path = '/'.$path;
130 130
 		}
131
-		return $this->fakeRoot . $path;
131
+		return $this->fakeRoot.$path;
132 132
 	}
133 133
 
134 134
 	/**
@@ -140,7 +140,7 @@  discard block
 block discarded – undo
140 140
 	public function chroot($fakeRoot) {
141 141
 		if (!$fakeRoot == '') {
142 142
 			if ($fakeRoot[0] !== '/') {
143
-				$fakeRoot = '/' . $fakeRoot;
143
+				$fakeRoot = '/'.$fakeRoot;
144 144
 			}
145 145
 		}
146 146
 		$this->fakeRoot = $fakeRoot;
@@ -172,7 +172,7 @@  discard block
 block discarded – undo
172 172
 		}
173 173
 
174 174
 		// missing slashes can cause wrong matches!
175
-		$root = rtrim($this->fakeRoot, '/') . '/';
175
+		$root = rtrim($this->fakeRoot, '/').'/';
176 176
 
177 177
 		if (strpos($path, $root) !== 0) {
178 178
 			return null;
@@ -278,7 +278,7 @@  discard block
 block discarded – undo
278 278
 		if ($mount instanceof MoveableMount) {
279 279
 			// cut of /user/files to get the relative path to data/user/files
280 280
 			$pathParts = explode('/', $path, 4);
281
-			$relPath = '/' . $pathParts[3];
281
+			$relPath = '/'.$pathParts[3];
282 282
 			$this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true);
283 283
 			\OC_Hook::emit(
284 284
 				Filesystem::CLASSNAME, "umount",
@@ -700,7 +700,7 @@  discard block
 block discarded – undo
700 700
 		}
701 701
 		$postFix = ($path[strlen($path) - 1] === '/') ? '/' : '';
702 702
 		$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
703
-		$mount = Filesystem::getMountManager()->find($absolutePath . $postFix);
703
+		$mount = Filesystem::getMountManager()->find($absolutePath.$postFix);
704 704
 		if ($mount and $mount->getInternalPath($absolutePath) === '') {
705 705
 			return $this->removeMount($mount, $absolutePath);
706 706
 		}
@@ -820,7 +820,7 @@  discard block
 block discarded – undo
820 820
 								$this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2);
821 821
 							}
822 822
 						}
823
-					} catch(\Exception $e) {
823
+					} catch (\Exception $e) {
824 824
 						throw $e;
825 825
 					} finally {
826 826
 						$this->changeLock($path1, ILockingProvider::LOCK_SHARED, true);
@@ -844,7 +844,7 @@  discard block
 block discarded – undo
844 844
 						}
845 845
 					}
846 846
 				}
847
-			} catch(\Exception $e) {
847
+			} catch (\Exception $e) {
848 848
 				throw $e;
849 849
 			} finally {
850 850
 				$this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true);
@@ -977,7 +977,7 @@  discard block
 block discarded – undo
977 977
 				$hooks[] = 'write';
978 978
 				break;
979 979
 			default:
980
-				\OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR);
980
+				\OCP\Util::writeLog('core', 'invalid mode ('.$mode.') for '.$path, \OCP\Util::ERROR);
981 981
 		}
982 982
 
983 983
 		if ($mode !== 'r' && $mode !== 'w') {
@@ -1081,7 +1081,7 @@  discard block
 block discarded – undo
1081 1081
 					array(Filesystem::signal_param_path => $this->getHookPath($path))
1082 1082
 				);
1083 1083
 			}
1084
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1084
+			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath.$postFix);
1085 1085
 			if ($storage) {
1086 1086
 				$result = $storage->hash($type, $internalPath, $raw);
1087 1087
 				return $result;
@@ -1136,7 +1136,7 @@  discard block
 block discarded – undo
1136 1136
 
1137 1137
 			$run = $this->runHooks($hooks, $path);
1138 1138
 			/** @var \OC\Files\Storage\Storage $storage */
1139
-			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix);
1139
+			list($storage, $internalPath) = Filesystem::resolvePath($absolutePath.$postFix);
1140 1140
 			if ($run and $storage) {
1141 1141
 				if (in_array('write', $hooks) || in_array('delete', $hooks)) {
1142 1142
 					$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
@@ -1175,7 +1175,7 @@  discard block
 block discarded – undo
1175 1175
 					$unlockLater = true;
1176 1176
 					// make sure our unlocking callback will still be called if connection is aborted
1177 1177
 					ignore_user_abort(true);
1178
-					$result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) {
1178
+					$result = CallbackWrapper::wrap($result, null, null, function() use ($hooks, $path) {
1179 1179
 						if (in_array('write', $hooks)) {
1180 1180
 							$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
1181 1181
 						} else if (in_array('read', $hooks)) {
@@ -1236,7 +1236,7 @@  discard block
 block discarded – undo
1236 1236
 			return true;
1237 1237
 		}
1238 1238
 
1239
-		return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/');
1239
+		return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot.'/');
1240 1240
 	}
1241 1241
 
1242 1242
 	/**
@@ -1255,7 +1255,7 @@  discard block
 block discarded – undo
1255 1255
 				if ($hook != 'read') {
1256 1256
 					\OC_Hook::emit(
1257 1257
 						Filesystem::CLASSNAME,
1258
-						$prefix . $hook,
1258
+						$prefix.$hook,
1259 1259
 						array(
1260 1260
 							Filesystem::signal_param_run => &$run,
1261 1261
 							Filesystem::signal_param_path => $path
@@ -1264,7 +1264,7 @@  discard block
 block discarded – undo
1264 1264
 				} elseif (!$post) {
1265 1265
 					\OC_Hook::emit(
1266 1266
 						Filesystem::CLASSNAME,
1267
-						$prefix . $hook,
1267
+						$prefix.$hook,
1268 1268
 						array(
1269 1269
 							Filesystem::signal_param_path => $path
1270 1270
 						)
@@ -1359,7 +1359,7 @@  discard block
 block discarded – undo
1359 1359
 			return $this->getPartFileInfo($path);
1360 1360
 		}
1361 1361
 		$relativePath = $path;
1362
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1362
+		$path = Filesystem::normalizePath($this->fakeRoot.'/'.$path);
1363 1363
 
1364 1364
 		$mount = Filesystem::getMountManager()->find($path);
1365 1365
 		if (!$mount) {
@@ -1386,7 +1386,7 @@  discard block
 block discarded – undo
1386 1386
 					//add the sizes of other mount points to the folder
1387 1387
 					$extOnly = ($includeMountPoints === 'ext');
1388 1388
 					$mounts = Filesystem::getMountManager()->findIn($path);
1389
-					$info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
1389
+					$info->setSubMounts(array_filter($mounts, function(IMountPoint $mount) use ($extOnly) {
1390 1390
 						$subStorage = $mount->getStorage();
1391 1391
 						return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
1392 1392
 					}));
@@ -1436,12 +1436,12 @@  discard block
 block discarded – undo
1436 1436
 			/**
1437 1437
 			 * @var \OC\Files\FileInfo[] $files
1438 1438
 			 */
1439
-			$files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1439
+			$files = array_map(function(ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
1440 1440
 				if ($sharingDisabled) {
1441 1441
 					$content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
1442 1442
 				}
1443 1443
 				$owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
1444
-				return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
1444
+				return new FileInfo($path.'/'.$content['name'], $storage, $content['path'], $content, $mount, $owner);
1445 1445
 			}, $contents);
1446 1446
 
1447 1447
 			//add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
@@ -1465,7 +1465,7 @@  discard block
 block discarded – undo
1465 1465
 						} catch (\Exception $e) {
1466 1466
 							// sometimes when the storage is not available it can be any exception
1467 1467
 							\OC::$server->getLogger()->logException($e, [
1468
-								'message' => 'Exception while scanning storage "' . $subStorage->getId() . '"',
1468
+								'message' => 'Exception while scanning storage "'.$subStorage->getId().'"',
1469 1469
 								'level' => \OCP\Util::ERROR,
1470 1470
 								'app' => 'lib',
1471 1471
 							]);
@@ -1503,7 +1503,7 @@  discard block
 block discarded – undo
1503 1503
 									break;
1504 1504
 								}
1505 1505
 							}
1506
-							$rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
1506
+							$rootEntry['path'] = substr(Filesystem::normalizePath($path.'/'.$rootEntry['name']), strlen($user) + 2); // full path without /$user/
1507 1507
 
1508 1508
 							// if sharing was disabled for the user we remove the share permissions
1509 1509
 							if (\OCP\Util::isSharingDisabledForUser()) {
@@ -1511,14 +1511,14 @@  discard block
 block discarded – undo
1511 1511
 							}
1512 1512
 
1513 1513
 							$owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
1514
-							$files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1514
+							$files[] = new FileInfo($path.'/'.$rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
1515 1515
 						}
1516 1516
 					}
1517 1517
 				}
1518 1518
 			}
1519 1519
 
1520 1520
 			if ($mimetype_filter) {
1521
-				$files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
1521
+				$files = array_filter($files, function(FileInfo $file) use ($mimetype_filter) {
1522 1522
 					if (strpos($mimetype_filter, '/')) {
1523 1523
 						return $file->getMimetype() === $mimetype_filter;
1524 1524
 					} else {
@@ -1547,7 +1547,7 @@  discard block
 block discarded – undo
1547 1547
 		if ($data instanceof FileInfo) {
1548 1548
 			$data = $data->getData();
1549 1549
 		}
1550
-		$path = Filesystem::normalizePath($this->fakeRoot . '/' . $path);
1550
+		$path = Filesystem::normalizePath($this->fakeRoot.'/'.$path);
1551 1551
 		/**
1552 1552
 		 * @var \OC\Files\Storage\Storage $storage
1553 1553
 		 * @var string $internalPath
@@ -1574,7 +1574,7 @@  discard block
 block discarded – undo
1574 1574
 	 * @return FileInfo[]
1575 1575
 	 */
1576 1576
 	public function search($query) {
1577
-		return $this->searchCommon('search', array('%' . $query . '%'));
1577
+		return $this->searchCommon('search', array('%'.$query.'%'));
1578 1578
 	}
1579 1579
 
1580 1580
 	/**
@@ -1625,10 +1625,10 @@  discard block
 block discarded – undo
1625 1625
 
1626 1626
 			$results = call_user_func_array(array($cache, $method), $args);
1627 1627
 			foreach ($results as $result) {
1628
-				if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') {
1628
+				if (substr($mountPoint.$result['path'], 0, $rootLength + 1) === $this->fakeRoot.'/') {
1629 1629
 					$internalPath = $result['path'];
1630
-					$path = $mountPoint . $result['path'];
1631
-					$result['path'] = substr($mountPoint . $result['path'], $rootLength);
1630
+					$path = $mountPoint.$result['path'];
1631
+					$result['path'] = substr($mountPoint.$result['path'], $rootLength);
1632 1632
 					$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1633 1633
 					$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1634 1634
 				}
@@ -1646,8 +1646,8 @@  discard block
 block discarded – undo
1646 1646
 					if ($results) {
1647 1647
 						foreach ($results as $result) {
1648 1648
 							$internalPath = $result['path'];
1649
-							$result['path'] = rtrim($relativeMountPoint . $result['path'], '/');
1650
-							$path = rtrim($mountPoint . $internalPath, '/');
1649
+							$result['path'] = rtrim($relativeMountPoint.$result['path'], '/');
1650
+							$path = rtrim($mountPoint.$internalPath, '/');
1651 1651
 							$owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath));
1652 1652
 							$files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner);
1653 1653
 						}
@@ -1668,7 +1668,7 @@  discard block
 block discarded – undo
1668 1668
 	public function getOwner($path) {
1669 1669
 		$info = $this->getFileInfo($path);
1670 1670
 		if (!$info) {
1671
-			throw new NotFoundException($path . ' not found while trying to get owner');
1671
+			throw new NotFoundException($path.' not found while trying to get owner');
1672 1672
 		}
1673 1673
 		return $info->getOwner()->getUID();
1674 1674
 	}
@@ -1702,7 +1702,7 @@  discard block
 block discarded – undo
1702 1702
 	 * @return string
1703 1703
 	 */
1704 1704
 	public function getPath($id) {
1705
-		$id = (int)$id;
1705
+		$id = (int) $id;
1706 1706
 		$manager = Filesystem::getMountManager();
1707 1707
 		$mounts = $manager->findIn($this->fakeRoot);
1708 1708
 		$mounts[] = $manager->find($this->fakeRoot);
@@ -1717,7 +1717,7 @@  discard block
 block discarded – undo
1717 1717
 				$cache = $mount->getStorage()->getCache();
1718 1718
 				$internalPath = $cache->getPathById($id);
1719 1719
 				if (is_string($internalPath)) {
1720
-					$fullPath = $mount->getMountPoint() . $internalPath;
1720
+					$fullPath = $mount->getMountPoint().$internalPath;
1721 1721
 					if (!is_null($path = $this->getRelativePath($fullPath))) {
1722 1722
 						return $path;
1723 1723
 					}
@@ -1760,10 +1760,10 @@  discard block
 block discarded – undo
1760 1760
 		}
1761 1761
 
1762 1762
 		// note: cannot use the view because the target is already locked
1763
-		$fileId = (int)$targetStorage->getCache()->getId($targetInternalPath);
1763
+		$fileId = (int) $targetStorage->getCache()->getId($targetInternalPath);
1764 1764
 		if ($fileId === -1) {
1765 1765
 			// target might not exist, need to check parent instead
1766
-			$fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath));
1766
+			$fileId = (int) $targetStorage->getCache()->getId(dirname($targetInternalPath));
1767 1767
 		}
1768 1768
 
1769 1769
 		// check if any of the parents were shared by the current owner (include collections)
@@ -1863,7 +1863,7 @@  discard block
 block discarded – undo
1863 1863
 		$resultPath = '';
1864 1864
 		foreach ($parts as $part) {
1865 1865
 			if ($part) {
1866
-				$resultPath .= '/' . $part;
1866
+				$resultPath .= '/'.$part;
1867 1867
 				$result[] = $resultPath;
1868 1868
 			}
1869 1869
 		}
@@ -2126,16 +2126,16 @@  discard block
 block discarded – undo
2126 2126
 	public function getUidAndFilename($filename) {
2127 2127
 		$info = $this->getFileInfo($filename);
2128 2128
 		if (!$info instanceof \OCP\Files\FileInfo) {
2129
-			throw new NotFoundException($this->getAbsolutePath($filename) . ' not found');
2129
+			throw new NotFoundException($this->getAbsolutePath($filename).' not found');
2130 2130
 		}
2131 2131
 		$uid = $info->getOwner()->getUID();
2132 2132
 		if ($uid != \OCP\User::getUser()) {
2133 2133
 			Filesystem::initMountPoints($uid);
2134
-			$ownerView = new View('/' . $uid . '/files');
2134
+			$ownerView = new View('/'.$uid.'/files');
2135 2135
 			try {
2136 2136
 				$filename = $ownerView->getPath($info['fileid']);
2137 2137
 			} catch (NotFoundException $e) {
2138
-				throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid);
2138
+				throw new NotFoundException('File with id '.$info['fileid'].' not found for user '.$uid);
2139 2139
 			}
2140 2140
 		}
2141 2141
 		return [$uid, $filename];
@@ -2152,7 +2152,7 @@  discard block
 block discarded – undo
2152 2152
 		$directoryParts = array_filter($directoryParts);
2153 2153
 		foreach ($directoryParts as $key => $part) {
2154 2154
 			$currentPathElements = array_slice($directoryParts, 0, $key);
2155
-			$currentPath = '/' . implode('/', $currentPathElements);
2155
+			$currentPath = '/'.implode('/', $currentPathElements);
2156 2156
 			if ($this->is_file($currentPath)) {
2157 2157
 				return false;
2158 2158
 			}
Please login to merge, or discard this patch.
lib/private/Installer.php 2 patches
Indentation   +565 added lines, -565 removed lines patch added patch discarded remove patch
@@ -57,569 +57,569 @@
 block discarded – undo
57 57
  * This class provides the functionality needed to install, update and remove apps
58 58
  */
59 59
 class Installer {
60
-	/** @var AppFetcher */
61
-	private $appFetcher;
62
-	/** @var IClientService */
63
-	private $clientService;
64
-	/** @var ITempManager */
65
-	private $tempManager;
66
-	/** @var ILogger */
67
-	private $logger;
68
-	/** @var IConfig */
69
-	private $config;
70
-	/** @var array - for caching the result of app fetcher */
71
-	private $apps = null;
72
-	/** @var bool|null - for caching the result of the ready status */
73
-	private $isInstanceReadyForUpdates = null;
74
-
75
-	/**
76
-	 * @param AppFetcher $appFetcher
77
-	 * @param IClientService $clientService
78
-	 * @param ITempManager $tempManager
79
-	 * @param ILogger $logger
80
-	 * @param IConfig $config
81
-	 */
82
-	public function __construct(AppFetcher $appFetcher,
83
-								IClientService $clientService,
84
-								ITempManager $tempManager,
85
-								ILogger $logger,
86
-								IConfig $config) {
87
-		$this->appFetcher = $appFetcher;
88
-		$this->clientService = $clientService;
89
-		$this->tempManager = $tempManager;
90
-		$this->logger = $logger;
91
-		$this->config = $config;
92
-	}
93
-
94
-	/**
95
-	 * Installs an app that is located in one of the app folders already
96
-	 *
97
-	 * @param string $appId App to install
98
-	 * @throws \Exception
99
-	 * @return string app ID
100
-	 */
101
-	public function installApp($appId) {
102
-		$app = \OC_App::findAppInDirectories($appId);
103
-		if($app === false) {
104
-			throw new \Exception('App not found in any app directory');
105
-		}
106
-
107
-		$basedir = $app['path'].'/'.$appId;
108
-		$info = OC_App::getAppInfo($basedir.'/appinfo/info.xml', true);
109
-
110
-		$l = \OC::$server->getL10N('core');
111
-
112
-		if(!is_array($info)) {
113
-			throw new \Exception(
114
-				$l->t('App "%s" cannot be installed because appinfo file cannot be read.',
115
-					[$info['name']]
116
-				)
117
-			);
118
-		}
119
-
120
-		$version = \OCP\Util::getVersion();
121
-		if (!\OC_App::isAppCompatible($version, $info)) {
122
-			throw new \Exception(
123
-				// TODO $l
124
-				$l->t('App "%s" cannot be installed because it is not compatible with this version of the server.',
125
-					[$info['name']]
126
-				)
127
-			);
128
-		}
129
-
130
-		// check for required dependencies
131
-		\OC_App::checkAppDependencies($this->config, $l, $info);
132
-		\OC_App::registerAutoloading($appId, $basedir);
133
-
134
-		//install the database
135
-		if(is_file($basedir.'/appinfo/database.xml')) {
136
-			if (\OC::$server->getConfig()->getAppValue($info['id'], 'installed_version') === null) {
137
-				OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml');
138
-			} else {
139
-				OC_DB::updateDbFromStructure($basedir.'/appinfo/database.xml');
140
-			}
141
-		} else {
142
-			$ms = new \OC\DB\MigrationService($info['id'], \OC::$server->getDatabaseConnection());
143
-			$ms->migrate();
144
-		}
145
-
146
-		\OC_App::setupBackgroundJobs($info['background-jobs']);
147
-		if(isset($info['settings']) && is_array($info['settings'])) {
148
-			\OC::$server->getSettingsManager()->setupSettings($info['settings']);
149
-		}
150
-
151
-		//run appinfo/install.php
152
-		if((!isset($data['noinstall']) or $data['noinstall']==false)) {
153
-			self::includeAppScript($basedir . '/appinfo/install.php');
154
-		}
155
-
156
-		$appData = OC_App::getAppInfo($appId);
157
-		OC_App::executeRepairSteps($appId, $appData['repair-steps']['install']);
158
-
159
-		//set the installed version
160
-		\OC::$server->getConfig()->setAppValue($info['id'], 'installed_version', OC_App::getAppVersion($info['id'], false));
161
-		\OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no');
162
-
163
-		//set remote/public handlers
164
-		foreach($info['remote'] as $name=>$path) {
165
-			\OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path);
166
-		}
167
-		foreach($info['public'] as $name=>$path) {
168
-			\OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path);
169
-		}
170
-
171
-		OC_App::setAppTypes($info['id']);
172
-
173
-		return $info['id'];
174
-	}
175
-
176
-	/**
177
-	 * @brief checks whether or not an app is installed
178
-	 * @param string $app app
179
-	 * @returns bool
180
-	 *
181
-	 * Checks whether or not an app is installed, i.e. registered in apps table.
182
-	 */
183
-	public static function isInstalled( $app ) {
184
-		return (\OC::$server->getConfig()->getAppValue($app, "installed_version", null) !== null);
185
-	}
186
-
187
-	/**
188
-	 * Updates the specified app from the appstore
189
-	 *
190
-	 * @param string $appId
191
-	 * @return bool
192
-	 */
193
-	public function updateAppstoreApp($appId) {
194
-		if($this->isUpdateAvailable($appId)) {
195
-			try {
196
-				$this->downloadApp($appId);
197
-			} catch (\Exception $e) {
198
-				$this->logger->logException($e, [
199
-					'level' => \OCP\Util::ERROR,
200
-					'app' => 'core',
201
-				]);
202
-				return false;
203
-			}
204
-			return OC_App::updateApp($appId);
205
-		}
206
-
207
-		return false;
208
-	}
209
-
210
-	/**
211
-	 * Downloads an app and puts it into the app directory
212
-	 *
213
-	 * @param string $appId
214
-	 *
215
-	 * @throws \Exception If the installation was not successful
216
-	 */
217
-	public function downloadApp($appId) {
218
-		$appId = strtolower($appId);
219
-
220
-		$apps = $this->appFetcher->get();
221
-		foreach($apps as $app) {
222
-			if($app['id'] === $appId) {
223
-				// Load the certificate
224
-				$certificate = new X509();
225
-				$certificate->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
226
-				$loadedCertificate = $certificate->loadX509($app['certificate']);
227
-
228
-				// Verify if the certificate has been revoked
229
-				$crl = new X509();
230
-				$crl->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
231
-				$crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl'));
232
-				if($crl->validateSignature() !== true) {
233
-					throw new \Exception('Could not validate CRL signature');
234
-				}
235
-				$csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString();
236
-				$revoked = $crl->getRevoked($csn);
237
-				if ($revoked !== false) {
238
-					throw new \Exception(
239
-						sprintf(
240
-							'Certificate "%s" has been revoked',
241
-							$csn
242
-						)
243
-					);
244
-				}
245
-
246
-				// Verify if the certificate has been issued by the Nextcloud Code Authority CA
247
-				if($certificate->validateSignature() !== true) {
248
-					throw new \Exception(
249
-						sprintf(
250
-							'App with id %s has a certificate not issued by a trusted Code Signing Authority',
251
-							$appId
252
-						)
253
-					);
254
-				}
255
-
256
-				// Verify if the certificate is issued for the requested app id
257
-				$certInfo = openssl_x509_parse($app['certificate']);
258
-				if(!isset($certInfo['subject']['CN'])) {
259
-					throw new \Exception(
260
-						sprintf(
261
-							'App with id %s has a cert with no CN',
262
-							$appId
263
-						)
264
-					);
265
-				}
266
-				if($certInfo['subject']['CN'] !== $appId) {
267
-					throw new \Exception(
268
-						sprintf(
269
-							'App with id %s has a cert issued to %s',
270
-							$appId,
271
-							$certInfo['subject']['CN']
272
-						)
273
-					);
274
-				}
275
-
276
-				// Download the release
277
-				$tempFile = $this->tempManager->getTemporaryFile('.tar.gz');
278
-				$client = $this->clientService->newClient();
279
-				$client->get($app['releases'][0]['download'], ['save_to' => $tempFile]);
280
-
281
-				// Check if the signature actually matches the downloaded content
282
-				$certificate = openssl_get_publickey($app['certificate']);
283
-				$verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512);
284
-				openssl_free_key($certificate);
285
-
286
-				if($verified === true) {
287
-					// Seems to match, let's proceed
288
-					$extractDir = $this->tempManager->getTemporaryFolder();
289
-					$archive = new TAR($tempFile);
290
-
291
-					if($archive) {
292
-						if (!$archive->extract($extractDir)) {
293
-							throw new \Exception(
294
-								sprintf(
295
-									'Could not extract app %s',
296
-									$appId
297
-								)
298
-							);
299
-						}
300
-						$allFiles = scandir($extractDir);
301
-						$folders = array_diff($allFiles, ['.', '..']);
302
-						$folders = array_values($folders);
303
-
304
-						if(count($folders) > 1) {
305
-							throw new \Exception(
306
-								sprintf(
307
-									'Extracted app %s has more than 1 folder',
308
-									$appId
309
-								)
310
-							);
311
-						}
312
-
313
-						// Check if appinfo/info.xml has the same app ID as well
314
-						$loadEntities = libxml_disable_entity_loader(false);
315
-						$xml = simplexml_load_file($extractDir . '/' . $folders[0] . '/appinfo/info.xml');
316
-						libxml_disable_entity_loader($loadEntities);
317
-						if((string)$xml->id !== $appId) {
318
-							throw new \Exception(
319
-								sprintf(
320
-									'App for id %s has a wrong app ID in info.xml: %s',
321
-									$appId,
322
-									(string)$xml->id
323
-								)
324
-							);
325
-						}
326
-
327
-						// Check if the version is lower than before
328
-						$currentVersion = OC_App::getAppVersion($appId);
329
-						$newVersion = (string)$xml->version;
330
-						if(version_compare($currentVersion, $newVersion) === 1) {
331
-							throw new \Exception(
332
-								sprintf(
333
-									'App for id %s has version %s and tried to update to lower version %s',
334
-									$appId,
335
-									$currentVersion,
336
-									$newVersion
337
-								)
338
-							);
339
-						}
340
-
341
-						$baseDir = OC_App::getInstallPath() . '/' . $appId;
342
-						// Remove old app with the ID if existent
343
-						OC_Helper::rmdirr($baseDir);
344
-						// Move to app folder
345
-						if(@mkdir($baseDir)) {
346
-							$extractDir .= '/' . $folders[0];
347
-							OC_Helper::copyr($extractDir, $baseDir);
348
-						}
349
-						OC_Helper::copyr($extractDir, $baseDir);
350
-						OC_Helper::rmdirr($extractDir);
351
-						return;
352
-					} else {
353
-						throw new \Exception(
354
-							sprintf(
355
-								'Could not extract app with ID %s to %s',
356
-								$appId,
357
-								$extractDir
358
-							)
359
-						);
360
-					}
361
-				} else {
362
-					// Signature does not match
363
-					throw new \Exception(
364
-						sprintf(
365
-							'App with id %s has invalid signature',
366
-							$appId
367
-						)
368
-					);
369
-				}
370
-			}
371
-		}
372
-
373
-		throw new \Exception(
374
-			sprintf(
375
-				'Could not download app %s',
376
-				$appId
377
-			)
378
-		);
379
-	}
380
-
381
-	/**
382
-	 * Check if an update for the app is available
383
-	 *
384
-	 * @param string $appId
385
-	 * @return string|false false or the version number of the update
386
-	 */
387
-	public function isUpdateAvailable($appId) {
388
-		if ($this->isInstanceReadyForUpdates === null) {
389
-			$installPath = OC_App::getInstallPath();
390
-			if ($installPath === false || $installPath === null) {
391
-				$this->isInstanceReadyForUpdates = false;
392
-			} else {
393
-				$this->isInstanceReadyForUpdates = true;
394
-			}
395
-		}
396
-
397
-		if ($this->isInstanceReadyForUpdates === false) {
398
-			return false;
399
-		}
400
-
401
-		if ($this->isInstalledFromGit($appId) === true) {
402
-			return false;
403
-		}
404
-
405
-		if ($this->apps === null) {
406
-			$this->apps = $this->appFetcher->get();
407
-		}
408
-
409
-		foreach($this->apps as $app) {
410
-			if($app['id'] === $appId) {
411
-				$currentVersion = OC_App::getAppVersion($appId);
412
-				$newestVersion = $app['releases'][0]['version'];
413
-				if (version_compare($newestVersion, $currentVersion, '>')) {
414
-					return $newestVersion;
415
-				} else {
416
-					return false;
417
-				}
418
-			}
419
-		}
420
-
421
-		return false;
422
-	}
423
-
424
-	/**
425
-	 * Check if app has been installed from git
426
-	 * @param string $name name of the application to remove
427
-	 * @return boolean
428
-	 *
429
-	 * The function will check if the path contains a .git folder
430
-	 */
431
-	private function isInstalledFromGit($appId) {
432
-		$app = \OC_App::findAppInDirectories($appId);
433
-		if($app === false) {
434
-			return false;
435
-		}
436
-		$basedir = $app['path'].'/'.$appId;
437
-		return file_exists($basedir.'/.git/');
438
-	}
439
-
440
-	/**
441
-	 * Check if app is already downloaded
442
-	 * @param string $name name of the application to remove
443
-	 * @return boolean
444
-	 *
445
-	 * The function will check if the app is already downloaded in the apps repository
446
-	 */
447
-	public function isDownloaded($name) {
448
-		foreach(\OC::$APPSROOTS as $dir) {
449
-			$dirToTest  = $dir['path'];
450
-			$dirToTest .= '/';
451
-			$dirToTest .= $name;
452
-			$dirToTest .= '/';
453
-
454
-			if (is_dir($dirToTest)) {
455
-				return true;
456
-			}
457
-		}
458
-
459
-		return false;
460
-	}
461
-
462
-	/**
463
-	 * Removes an app
464
-	 * @param string $appId ID of the application to remove
465
-	 * @return boolean
466
-	 *
467
-	 *
468
-	 * This function works as follows
469
-	 *   -# call uninstall repair steps
470
-	 *   -# removing the files
471
-	 *
472
-	 * The function will not delete preferences, tables and the configuration,
473
-	 * this has to be done by the function oc_app_uninstall().
474
-	 */
475
-	public function removeApp($appId) {
476
-		if($this->isDownloaded( $appId )) {
477
-			$appDir = OC_App::getInstallPath() . '/' . $appId;
478
-			OC_Helper::rmdirr($appDir);
479
-			return true;
480
-		}else{
481
-			\OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', \OCP\Util::ERROR);
482
-
483
-			return false;
484
-		}
485
-
486
-	}
487
-
488
-	/**
489
-	 * Installs the app within the bundle and marks the bundle as installed
490
-	 *
491
-	 * @param Bundle $bundle
492
-	 * @throws \Exception If app could not get installed
493
-	 */
494
-	public function installAppBundle(Bundle $bundle) {
495
-		$appIds = $bundle->getAppIdentifiers();
496
-		foreach($appIds as $appId) {
497
-			if(!$this->isDownloaded($appId)) {
498
-				$this->downloadApp($appId);
499
-			}
500
-			$this->installApp($appId);
501
-			$app = new OC_App();
502
-			$app->enable($appId);
503
-		}
504
-		$bundles = json_decode($this->config->getAppValue('core', 'installed.bundles', json_encode([])), true);
505
-		$bundles[] = $bundle->getIdentifier();
506
-		$this->config->setAppValue('core', 'installed.bundles', json_encode($bundles));
507
-	}
508
-
509
-	/**
510
-	 * Installs shipped apps
511
-	 *
512
-	 * This function installs all apps found in the 'apps' directory that should be enabled by default;
513
-	 * @param bool $softErrors When updating we ignore errors and simply log them, better to have a
514
-	 *                         working ownCloud at the end instead of an aborted update.
515
-	 * @return array Array of error messages (appid => Exception)
516
-	 */
517
-	public static function installShippedApps($softErrors = false) {
518
-		$errors = [];
519
-		foreach(\OC::$APPSROOTS as $app_dir) {
520
-			if($dir = opendir( $app_dir['path'] )) {
521
-				while( false !== ( $filename = readdir( $dir ))) {
522
-					if( $filename[0] !== '.' and is_dir($app_dir['path']."/$filename") ) {
523
-						if( file_exists( $app_dir['path']."/$filename/appinfo/info.xml" )) {
524
-							if(!Installer::isInstalled($filename)) {
525
-								$info=OC_App::getAppInfo($filename);
526
-								$enabled = isset($info['default_enable']);
527
-								if (($enabled || in_array($filename, \OC::$server->getAppManager()->getAlwaysEnabledApps()))
528
-									  && \OC::$server->getConfig()->getAppValue($filename, 'enabled') !== 'no') {
529
-									if ($softErrors) {
530
-										try {
531
-											Installer::installShippedApp($filename);
532
-										} catch (HintException $e) {
533
-											if ($e->getPrevious() instanceof TableExistsException) {
534
-												$errors[$filename] = $e;
535
-												continue;
536
-											}
537
-											throw $e;
538
-										}
539
-									} else {
540
-										Installer::installShippedApp($filename);
541
-									}
542
-									\OC::$server->getConfig()->setAppValue($filename, 'enabled', 'yes');
543
-								}
544
-							}
545
-						}
546
-					}
547
-				}
548
-				closedir( $dir );
549
-			}
550
-		}
551
-
552
-		return $errors;
553
-	}
554
-
555
-	/**
556
-	 * install an app already placed in the app folder
557
-	 * @param string $app id of the app to install
558
-	 * @return integer
559
-	 */
560
-	public static function installShippedApp($app) {
561
-		//install the database
562
-		$appPath = OC_App::getAppPath($app);
563
-		\OC_App::registerAutoloading($app, $appPath);
564
-
565
-		if(is_file("$appPath/appinfo/database.xml")) {
566
-			try {
567
-				OC_DB::createDbFromStructure("$appPath/appinfo/database.xml");
568
-			} catch (TableExistsException $e) {
569
-				throw new HintException(
570
-					'Failed to enable app ' . $app,
571
-					'Please ask for help via one of our <a href="https://nextcloud.com/support/" target="_blank" rel="noreferrer noopener">support channels</a>.',
572
-					0, $e
573
-				);
574
-			}
575
-		} else {
576
-			$ms = new \OC\DB\MigrationService($app, \OC::$server->getDatabaseConnection());
577
-			$ms->migrate();
578
-		}
579
-
580
-		//run appinfo/install.php
581
-		self::includeAppScript("$appPath/appinfo/install.php");
582
-
583
-		$info = OC_App::getAppInfo($app);
584
-		if (is_null($info)) {
585
-			return false;
586
-		}
587
-		\OC_App::setupBackgroundJobs($info['background-jobs']);
588
-
589
-		OC_App::executeRepairSteps($app, $info['repair-steps']['install']);
590
-
591
-		$config = \OC::$server->getConfig();
592
-
593
-		$config->setAppValue($app, 'installed_version', OC_App::getAppVersion($app));
594
-		if (array_key_exists('ocsid', $info)) {
595
-			$config->setAppValue($app, 'ocsid', $info['ocsid']);
596
-		}
597
-
598
-		//set remote/public handlers
599
-		foreach($info['remote'] as $name=>$path) {
600
-			$config->setAppValue('core', 'remote_'.$name, $app.'/'.$path);
601
-		}
602
-		foreach($info['public'] as $name=>$path) {
603
-			$config->setAppValue('core', 'public_'.$name, $app.'/'.$path);
604
-		}
605
-
606
-		OC_App::setAppTypes($info['id']);
607
-
608
-		if(isset($info['settings']) && is_array($info['settings'])) {
609
-			// requires that autoloading was registered for the app,
610
-			// as happens before running the install.php some lines above
611
-			\OC::$server->getSettingsManager()->setupSettings($info['settings']);
612
-		}
613
-
614
-		return $info['id'];
615
-	}
616
-
617
-	/**
618
-	 * @param string $script
619
-	 */
620
-	private static function includeAppScript($script) {
621
-		if ( file_exists($script) ){
622
-			include $script;
623
-		}
624
-	}
60
+    /** @var AppFetcher */
61
+    private $appFetcher;
62
+    /** @var IClientService */
63
+    private $clientService;
64
+    /** @var ITempManager */
65
+    private $tempManager;
66
+    /** @var ILogger */
67
+    private $logger;
68
+    /** @var IConfig */
69
+    private $config;
70
+    /** @var array - for caching the result of app fetcher */
71
+    private $apps = null;
72
+    /** @var bool|null - for caching the result of the ready status */
73
+    private $isInstanceReadyForUpdates = null;
74
+
75
+    /**
76
+     * @param AppFetcher $appFetcher
77
+     * @param IClientService $clientService
78
+     * @param ITempManager $tempManager
79
+     * @param ILogger $logger
80
+     * @param IConfig $config
81
+     */
82
+    public function __construct(AppFetcher $appFetcher,
83
+                                IClientService $clientService,
84
+                                ITempManager $tempManager,
85
+                                ILogger $logger,
86
+                                IConfig $config) {
87
+        $this->appFetcher = $appFetcher;
88
+        $this->clientService = $clientService;
89
+        $this->tempManager = $tempManager;
90
+        $this->logger = $logger;
91
+        $this->config = $config;
92
+    }
93
+
94
+    /**
95
+     * Installs an app that is located in one of the app folders already
96
+     *
97
+     * @param string $appId App to install
98
+     * @throws \Exception
99
+     * @return string app ID
100
+     */
101
+    public function installApp($appId) {
102
+        $app = \OC_App::findAppInDirectories($appId);
103
+        if($app === false) {
104
+            throw new \Exception('App not found in any app directory');
105
+        }
106
+
107
+        $basedir = $app['path'].'/'.$appId;
108
+        $info = OC_App::getAppInfo($basedir.'/appinfo/info.xml', true);
109
+
110
+        $l = \OC::$server->getL10N('core');
111
+
112
+        if(!is_array($info)) {
113
+            throw new \Exception(
114
+                $l->t('App "%s" cannot be installed because appinfo file cannot be read.',
115
+                    [$info['name']]
116
+                )
117
+            );
118
+        }
119
+
120
+        $version = \OCP\Util::getVersion();
121
+        if (!\OC_App::isAppCompatible($version, $info)) {
122
+            throw new \Exception(
123
+                // TODO $l
124
+                $l->t('App "%s" cannot be installed because it is not compatible with this version of the server.',
125
+                    [$info['name']]
126
+                )
127
+            );
128
+        }
129
+
130
+        // check for required dependencies
131
+        \OC_App::checkAppDependencies($this->config, $l, $info);
132
+        \OC_App::registerAutoloading($appId, $basedir);
133
+
134
+        //install the database
135
+        if(is_file($basedir.'/appinfo/database.xml')) {
136
+            if (\OC::$server->getConfig()->getAppValue($info['id'], 'installed_version') === null) {
137
+                OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml');
138
+            } else {
139
+                OC_DB::updateDbFromStructure($basedir.'/appinfo/database.xml');
140
+            }
141
+        } else {
142
+            $ms = new \OC\DB\MigrationService($info['id'], \OC::$server->getDatabaseConnection());
143
+            $ms->migrate();
144
+        }
145
+
146
+        \OC_App::setupBackgroundJobs($info['background-jobs']);
147
+        if(isset($info['settings']) && is_array($info['settings'])) {
148
+            \OC::$server->getSettingsManager()->setupSettings($info['settings']);
149
+        }
150
+
151
+        //run appinfo/install.php
152
+        if((!isset($data['noinstall']) or $data['noinstall']==false)) {
153
+            self::includeAppScript($basedir . '/appinfo/install.php');
154
+        }
155
+
156
+        $appData = OC_App::getAppInfo($appId);
157
+        OC_App::executeRepairSteps($appId, $appData['repair-steps']['install']);
158
+
159
+        //set the installed version
160
+        \OC::$server->getConfig()->setAppValue($info['id'], 'installed_version', OC_App::getAppVersion($info['id'], false));
161
+        \OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no');
162
+
163
+        //set remote/public handlers
164
+        foreach($info['remote'] as $name=>$path) {
165
+            \OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path);
166
+        }
167
+        foreach($info['public'] as $name=>$path) {
168
+            \OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path);
169
+        }
170
+
171
+        OC_App::setAppTypes($info['id']);
172
+
173
+        return $info['id'];
174
+    }
175
+
176
+    /**
177
+     * @brief checks whether or not an app is installed
178
+     * @param string $app app
179
+     * @returns bool
180
+     *
181
+     * Checks whether or not an app is installed, i.e. registered in apps table.
182
+     */
183
+    public static function isInstalled( $app ) {
184
+        return (\OC::$server->getConfig()->getAppValue($app, "installed_version", null) !== null);
185
+    }
186
+
187
+    /**
188
+     * Updates the specified app from the appstore
189
+     *
190
+     * @param string $appId
191
+     * @return bool
192
+     */
193
+    public function updateAppstoreApp($appId) {
194
+        if($this->isUpdateAvailable($appId)) {
195
+            try {
196
+                $this->downloadApp($appId);
197
+            } catch (\Exception $e) {
198
+                $this->logger->logException($e, [
199
+                    'level' => \OCP\Util::ERROR,
200
+                    'app' => 'core',
201
+                ]);
202
+                return false;
203
+            }
204
+            return OC_App::updateApp($appId);
205
+        }
206
+
207
+        return false;
208
+    }
209
+
210
+    /**
211
+     * Downloads an app and puts it into the app directory
212
+     *
213
+     * @param string $appId
214
+     *
215
+     * @throws \Exception If the installation was not successful
216
+     */
217
+    public function downloadApp($appId) {
218
+        $appId = strtolower($appId);
219
+
220
+        $apps = $this->appFetcher->get();
221
+        foreach($apps as $app) {
222
+            if($app['id'] === $appId) {
223
+                // Load the certificate
224
+                $certificate = new X509();
225
+                $certificate->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
226
+                $loadedCertificate = $certificate->loadX509($app['certificate']);
227
+
228
+                // Verify if the certificate has been revoked
229
+                $crl = new X509();
230
+                $crl->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
231
+                $crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl'));
232
+                if($crl->validateSignature() !== true) {
233
+                    throw new \Exception('Could not validate CRL signature');
234
+                }
235
+                $csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString();
236
+                $revoked = $crl->getRevoked($csn);
237
+                if ($revoked !== false) {
238
+                    throw new \Exception(
239
+                        sprintf(
240
+                            'Certificate "%s" has been revoked',
241
+                            $csn
242
+                        )
243
+                    );
244
+                }
245
+
246
+                // Verify if the certificate has been issued by the Nextcloud Code Authority CA
247
+                if($certificate->validateSignature() !== true) {
248
+                    throw new \Exception(
249
+                        sprintf(
250
+                            'App with id %s has a certificate not issued by a trusted Code Signing Authority',
251
+                            $appId
252
+                        )
253
+                    );
254
+                }
255
+
256
+                // Verify if the certificate is issued for the requested app id
257
+                $certInfo = openssl_x509_parse($app['certificate']);
258
+                if(!isset($certInfo['subject']['CN'])) {
259
+                    throw new \Exception(
260
+                        sprintf(
261
+                            'App with id %s has a cert with no CN',
262
+                            $appId
263
+                        )
264
+                    );
265
+                }
266
+                if($certInfo['subject']['CN'] !== $appId) {
267
+                    throw new \Exception(
268
+                        sprintf(
269
+                            'App with id %s has a cert issued to %s',
270
+                            $appId,
271
+                            $certInfo['subject']['CN']
272
+                        )
273
+                    );
274
+                }
275
+
276
+                // Download the release
277
+                $tempFile = $this->tempManager->getTemporaryFile('.tar.gz');
278
+                $client = $this->clientService->newClient();
279
+                $client->get($app['releases'][0]['download'], ['save_to' => $tempFile]);
280
+
281
+                // Check if the signature actually matches the downloaded content
282
+                $certificate = openssl_get_publickey($app['certificate']);
283
+                $verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512);
284
+                openssl_free_key($certificate);
285
+
286
+                if($verified === true) {
287
+                    // Seems to match, let's proceed
288
+                    $extractDir = $this->tempManager->getTemporaryFolder();
289
+                    $archive = new TAR($tempFile);
290
+
291
+                    if($archive) {
292
+                        if (!$archive->extract($extractDir)) {
293
+                            throw new \Exception(
294
+                                sprintf(
295
+                                    'Could not extract app %s',
296
+                                    $appId
297
+                                )
298
+                            );
299
+                        }
300
+                        $allFiles = scandir($extractDir);
301
+                        $folders = array_diff($allFiles, ['.', '..']);
302
+                        $folders = array_values($folders);
303
+
304
+                        if(count($folders) > 1) {
305
+                            throw new \Exception(
306
+                                sprintf(
307
+                                    'Extracted app %s has more than 1 folder',
308
+                                    $appId
309
+                                )
310
+                            );
311
+                        }
312
+
313
+                        // Check if appinfo/info.xml has the same app ID as well
314
+                        $loadEntities = libxml_disable_entity_loader(false);
315
+                        $xml = simplexml_load_file($extractDir . '/' . $folders[0] . '/appinfo/info.xml');
316
+                        libxml_disable_entity_loader($loadEntities);
317
+                        if((string)$xml->id !== $appId) {
318
+                            throw new \Exception(
319
+                                sprintf(
320
+                                    'App for id %s has a wrong app ID in info.xml: %s',
321
+                                    $appId,
322
+                                    (string)$xml->id
323
+                                )
324
+                            );
325
+                        }
326
+
327
+                        // Check if the version is lower than before
328
+                        $currentVersion = OC_App::getAppVersion($appId);
329
+                        $newVersion = (string)$xml->version;
330
+                        if(version_compare($currentVersion, $newVersion) === 1) {
331
+                            throw new \Exception(
332
+                                sprintf(
333
+                                    'App for id %s has version %s and tried to update to lower version %s',
334
+                                    $appId,
335
+                                    $currentVersion,
336
+                                    $newVersion
337
+                                )
338
+                            );
339
+                        }
340
+
341
+                        $baseDir = OC_App::getInstallPath() . '/' . $appId;
342
+                        // Remove old app with the ID if existent
343
+                        OC_Helper::rmdirr($baseDir);
344
+                        // Move to app folder
345
+                        if(@mkdir($baseDir)) {
346
+                            $extractDir .= '/' . $folders[0];
347
+                            OC_Helper::copyr($extractDir, $baseDir);
348
+                        }
349
+                        OC_Helper::copyr($extractDir, $baseDir);
350
+                        OC_Helper::rmdirr($extractDir);
351
+                        return;
352
+                    } else {
353
+                        throw new \Exception(
354
+                            sprintf(
355
+                                'Could not extract app with ID %s to %s',
356
+                                $appId,
357
+                                $extractDir
358
+                            )
359
+                        );
360
+                    }
361
+                } else {
362
+                    // Signature does not match
363
+                    throw new \Exception(
364
+                        sprintf(
365
+                            'App with id %s has invalid signature',
366
+                            $appId
367
+                        )
368
+                    );
369
+                }
370
+            }
371
+        }
372
+
373
+        throw new \Exception(
374
+            sprintf(
375
+                'Could not download app %s',
376
+                $appId
377
+            )
378
+        );
379
+    }
380
+
381
+    /**
382
+     * Check if an update for the app is available
383
+     *
384
+     * @param string $appId
385
+     * @return string|false false or the version number of the update
386
+     */
387
+    public function isUpdateAvailable($appId) {
388
+        if ($this->isInstanceReadyForUpdates === null) {
389
+            $installPath = OC_App::getInstallPath();
390
+            if ($installPath === false || $installPath === null) {
391
+                $this->isInstanceReadyForUpdates = false;
392
+            } else {
393
+                $this->isInstanceReadyForUpdates = true;
394
+            }
395
+        }
396
+
397
+        if ($this->isInstanceReadyForUpdates === false) {
398
+            return false;
399
+        }
400
+
401
+        if ($this->isInstalledFromGit($appId) === true) {
402
+            return false;
403
+        }
404
+
405
+        if ($this->apps === null) {
406
+            $this->apps = $this->appFetcher->get();
407
+        }
408
+
409
+        foreach($this->apps as $app) {
410
+            if($app['id'] === $appId) {
411
+                $currentVersion = OC_App::getAppVersion($appId);
412
+                $newestVersion = $app['releases'][0]['version'];
413
+                if (version_compare($newestVersion, $currentVersion, '>')) {
414
+                    return $newestVersion;
415
+                } else {
416
+                    return false;
417
+                }
418
+            }
419
+        }
420
+
421
+        return false;
422
+    }
423
+
424
+    /**
425
+     * Check if app has been installed from git
426
+     * @param string $name name of the application to remove
427
+     * @return boolean
428
+     *
429
+     * The function will check if the path contains a .git folder
430
+     */
431
+    private function isInstalledFromGit($appId) {
432
+        $app = \OC_App::findAppInDirectories($appId);
433
+        if($app === false) {
434
+            return false;
435
+        }
436
+        $basedir = $app['path'].'/'.$appId;
437
+        return file_exists($basedir.'/.git/');
438
+    }
439
+
440
+    /**
441
+     * Check if app is already downloaded
442
+     * @param string $name name of the application to remove
443
+     * @return boolean
444
+     *
445
+     * The function will check if the app is already downloaded in the apps repository
446
+     */
447
+    public function isDownloaded($name) {
448
+        foreach(\OC::$APPSROOTS as $dir) {
449
+            $dirToTest  = $dir['path'];
450
+            $dirToTest .= '/';
451
+            $dirToTest .= $name;
452
+            $dirToTest .= '/';
453
+
454
+            if (is_dir($dirToTest)) {
455
+                return true;
456
+            }
457
+        }
458
+
459
+        return false;
460
+    }
461
+
462
+    /**
463
+     * Removes an app
464
+     * @param string $appId ID of the application to remove
465
+     * @return boolean
466
+     *
467
+     *
468
+     * This function works as follows
469
+     *   -# call uninstall repair steps
470
+     *   -# removing the files
471
+     *
472
+     * The function will not delete preferences, tables and the configuration,
473
+     * this has to be done by the function oc_app_uninstall().
474
+     */
475
+    public function removeApp($appId) {
476
+        if($this->isDownloaded( $appId )) {
477
+            $appDir = OC_App::getInstallPath() . '/' . $appId;
478
+            OC_Helper::rmdirr($appDir);
479
+            return true;
480
+        }else{
481
+            \OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', \OCP\Util::ERROR);
482
+
483
+            return false;
484
+        }
485
+
486
+    }
487
+
488
+    /**
489
+     * Installs the app within the bundle and marks the bundle as installed
490
+     *
491
+     * @param Bundle $bundle
492
+     * @throws \Exception If app could not get installed
493
+     */
494
+    public function installAppBundle(Bundle $bundle) {
495
+        $appIds = $bundle->getAppIdentifiers();
496
+        foreach($appIds as $appId) {
497
+            if(!$this->isDownloaded($appId)) {
498
+                $this->downloadApp($appId);
499
+            }
500
+            $this->installApp($appId);
501
+            $app = new OC_App();
502
+            $app->enable($appId);
503
+        }
504
+        $bundles = json_decode($this->config->getAppValue('core', 'installed.bundles', json_encode([])), true);
505
+        $bundles[] = $bundle->getIdentifier();
506
+        $this->config->setAppValue('core', 'installed.bundles', json_encode($bundles));
507
+    }
508
+
509
+    /**
510
+     * Installs shipped apps
511
+     *
512
+     * This function installs all apps found in the 'apps' directory that should be enabled by default;
513
+     * @param bool $softErrors When updating we ignore errors and simply log them, better to have a
514
+     *                         working ownCloud at the end instead of an aborted update.
515
+     * @return array Array of error messages (appid => Exception)
516
+     */
517
+    public static function installShippedApps($softErrors = false) {
518
+        $errors = [];
519
+        foreach(\OC::$APPSROOTS as $app_dir) {
520
+            if($dir = opendir( $app_dir['path'] )) {
521
+                while( false !== ( $filename = readdir( $dir ))) {
522
+                    if( $filename[0] !== '.' and is_dir($app_dir['path']."/$filename") ) {
523
+                        if( file_exists( $app_dir['path']."/$filename/appinfo/info.xml" )) {
524
+                            if(!Installer::isInstalled($filename)) {
525
+                                $info=OC_App::getAppInfo($filename);
526
+                                $enabled = isset($info['default_enable']);
527
+                                if (($enabled || in_array($filename, \OC::$server->getAppManager()->getAlwaysEnabledApps()))
528
+                                      && \OC::$server->getConfig()->getAppValue($filename, 'enabled') !== 'no') {
529
+                                    if ($softErrors) {
530
+                                        try {
531
+                                            Installer::installShippedApp($filename);
532
+                                        } catch (HintException $e) {
533
+                                            if ($e->getPrevious() instanceof TableExistsException) {
534
+                                                $errors[$filename] = $e;
535
+                                                continue;
536
+                                            }
537
+                                            throw $e;
538
+                                        }
539
+                                    } else {
540
+                                        Installer::installShippedApp($filename);
541
+                                    }
542
+                                    \OC::$server->getConfig()->setAppValue($filename, 'enabled', 'yes');
543
+                                }
544
+                            }
545
+                        }
546
+                    }
547
+                }
548
+                closedir( $dir );
549
+            }
550
+        }
551
+
552
+        return $errors;
553
+    }
554
+
555
+    /**
556
+     * install an app already placed in the app folder
557
+     * @param string $app id of the app to install
558
+     * @return integer
559
+     */
560
+    public static function installShippedApp($app) {
561
+        //install the database
562
+        $appPath = OC_App::getAppPath($app);
563
+        \OC_App::registerAutoloading($app, $appPath);
564
+
565
+        if(is_file("$appPath/appinfo/database.xml")) {
566
+            try {
567
+                OC_DB::createDbFromStructure("$appPath/appinfo/database.xml");
568
+            } catch (TableExistsException $e) {
569
+                throw new HintException(
570
+                    'Failed to enable app ' . $app,
571
+                    'Please ask for help via one of our <a href="https://nextcloud.com/support/" target="_blank" rel="noreferrer noopener">support channels</a>.',
572
+                    0, $e
573
+                );
574
+            }
575
+        } else {
576
+            $ms = new \OC\DB\MigrationService($app, \OC::$server->getDatabaseConnection());
577
+            $ms->migrate();
578
+        }
579
+
580
+        //run appinfo/install.php
581
+        self::includeAppScript("$appPath/appinfo/install.php");
582
+
583
+        $info = OC_App::getAppInfo($app);
584
+        if (is_null($info)) {
585
+            return false;
586
+        }
587
+        \OC_App::setupBackgroundJobs($info['background-jobs']);
588
+
589
+        OC_App::executeRepairSteps($app, $info['repair-steps']['install']);
590
+
591
+        $config = \OC::$server->getConfig();
592
+
593
+        $config->setAppValue($app, 'installed_version', OC_App::getAppVersion($app));
594
+        if (array_key_exists('ocsid', $info)) {
595
+            $config->setAppValue($app, 'ocsid', $info['ocsid']);
596
+        }
597
+
598
+        //set remote/public handlers
599
+        foreach($info['remote'] as $name=>$path) {
600
+            $config->setAppValue('core', 'remote_'.$name, $app.'/'.$path);
601
+        }
602
+        foreach($info['public'] as $name=>$path) {
603
+            $config->setAppValue('core', 'public_'.$name, $app.'/'.$path);
604
+        }
605
+
606
+        OC_App::setAppTypes($info['id']);
607
+
608
+        if(isset($info['settings']) && is_array($info['settings'])) {
609
+            // requires that autoloading was registered for the app,
610
+            // as happens before running the install.php some lines above
611
+            \OC::$server->getSettingsManager()->setupSettings($info['settings']);
612
+        }
613
+
614
+        return $info['id'];
615
+    }
616
+
617
+    /**
618
+     * @param string $script
619
+     */
620
+    private static function includeAppScript($script) {
621
+        if ( file_exists($script) ){
622
+            include $script;
623
+        }
624
+    }
625 625
 }
Please login to merge, or discard this patch.
Spacing   +54 added lines, -54 removed lines patch added patch discarded remove patch
@@ -100,7 +100,7 @@  discard block
 block discarded – undo
100 100
 	 */
101 101
 	public function installApp($appId) {
102 102
 		$app = \OC_App::findAppInDirectories($appId);
103
-		if($app === false) {
103
+		if ($app === false) {
104 104
 			throw new \Exception('App not found in any app directory');
105 105
 		}
106 106
 
@@ -109,7 +109,7 @@  discard block
 block discarded – undo
109 109
 
110 110
 		$l = \OC::$server->getL10N('core');
111 111
 
112
-		if(!is_array($info)) {
112
+		if (!is_array($info)) {
113 113
 			throw new \Exception(
114 114
 				$l->t('App "%s" cannot be installed because appinfo file cannot be read.',
115 115
 					[$info['name']]
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
 		\OC_App::registerAutoloading($appId, $basedir);
133 133
 
134 134
 		//install the database
135
-		if(is_file($basedir.'/appinfo/database.xml')) {
135
+		if (is_file($basedir.'/appinfo/database.xml')) {
136 136
 			if (\OC::$server->getConfig()->getAppValue($info['id'], 'installed_version') === null) {
137 137
 				OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml');
138 138
 			} else {
@@ -144,13 +144,13 @@  discard block
 block discarded – undo
144 144
 		}
145 145
 
146 146
 		\OC_App::setupBackgroundJobs($info['background-jobs']);
147
-		if(isset($info['settings']) && is_array($info['settings'])) {
147
+		if (isset($info['settings']) && is_array($info['settings'])) {
148 148
 			\OC::$server->getSettingsManager()->setupSettings($info['settings']);
149 149
 		}
150 150
 
151 151
 		//run appinfo/install.php
152
-		if((!isset($data['noinstall']) or $data['noinstall']==false)) {
153
-			self::includeAppScript($basedir . '/appinfo/install.php');
152
+		if ((!isset($data['noinstall']) or $data['noinstall'] == false)) {
153
+			self::includeAppScript($basedir.'/appinfo/install.php');
154 154
 		}
155 155
 
156 156
 		$appData = OC_App::getAppInfo($appId);
@@ -161,10 +161,10 @@  discard block
 block discarded – undo
161 161
 		\OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no');
162 162
 
163 163
 		//set remote/public handlers
164
-		foreach($info['remote'] as $name=>$path) {
164
+		foreach ($info['remote'] as $name=>$path) {
165 165
 			\OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path);
166 166
 		}
167
-		foreach($info['public'] as $name=>$path) {
167
+		foreach ($info['public'] as $name=>$path) {
168 168
 			\OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path);
169 169
 		}
170 170
 
@@ -180,7 +180,7 @@  discard block
 block discarded – undo
180 180
 	 *
181 181
 	 * Checks whether or not an app is installed, i.e. registered in apps table.
182 182
 	 */
183
-	public static function isInstalled( $app ) {
183
+	public static function isInstalled($app) {
184 184
 		return (\OC::$server->getConfig()->getAppValue($app, "installed_version", null) !== null);
185 185
 	}
186 186
 
@@ -191,7 +191,7 @@  discard block
 block discarded – undo
191 191
 	 * @return bool
192 192
 	 */
193 193
 	public function updateAppstoreApp($appId) {
194
-		if($this->isUpdateAvailable($appId)) {
194
+		if ($this->isUpdateAvailable($appId)) {
195 195
 			try {
196 196
 				$this->downloadApp($appId);
197 197
 			} catch (\Exception $e) {
@@ -218,18 +218,18 @@  discard block
 block discarded – undo
218 218
 		$appId = strtolower($appId);
219 219
 
220 220
 		$apps = $this->appFetcher->get();
221
-		foreach($apps as $app) {
222
-			if($app['id'] === $appId) {
221
+		foreach ($apps as $app) {
222
+			if ($app['id'] === $appId) {
223 223
 				// Load the certificate
224 224
 				$certificate = new X509();
225
-				$certificate->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
225
+				$certificate->loadCA(file_get_contents(__DIR__.'/../../resources/codesigning/root.crt'));
226 226
 				$loadedCertificate = $certificate->loadX509($app['certificate']);
227 227
 
228 228
 				// Verify if the certificate has been revoked
229 229
 				$crl = new X509();
230
-				$crl->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
231
-				$crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl'));
232
-				if($crl->validateSignature() !== true) {
230
+				$crl->loadCA(file_get_contents(__DIR__.'/../../resources/codesigning/root.crt'));
231
+				$crl->loadCRL(file_get_contents(__DIR__.'/../../resources/codesigning/root.crl'));
232
+				if ($crl->validateSignature() !== true) {
233 233
 					throw new \Exception('Could not validate CRL signature');
234 234
 				}
235 235
 				$csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString();
@@ -244,7 +244,7 @@  discard block
 block discarded – undo
244 244
 				}
245 245
 
246 246
 				// Verify if the certificate has been issued by the Nextcloud Code Authority CA
247
-				if($certificate->validateSignature() !== true) {
247
+				if ($certificate->validateSignature() !== true) {
248 248
 					throw new \Exception(
249 249
 						sprintf(
250 250
 							'App with id %s has a certificate not issued by a trusted Code Signing Authority',
@@ -255,7 +255,7 @@  discard block
 block discarded – undo
255 255
 
256 256
 				// Verify if the certificate is issued for the requested app id
257 257
 				$certInfo = openssl_x509_parse($app['certificate']);
258
-				if(!isset($certInfo['subject']['CN'])) {
258
+				if (!isset($certInfo['subject']['CN'])) {
259 259
 					throw new \Exception(
260 260
 						sprintf(
261 261
 							'App with id %s has a cert with no CN',
@@ -263,7 +263,7 @@  discard block
 block discarded – undo
263 263
 						)
264 264
 					);
265 265
 				}
266
-				if($certInfo['subject']['CN'] !== $appId) {
266
+				if ($certInfo['subject']['CN'] !== $appId) {
267 267
 					throw new \Exception(
268 268
 						sprintf(
269 269
 							'App with id %s has a cert issued to %s',
@@ -280,15 +280,15 @@  discard block
 block discarded – undo
280 280
 
281 281
 				// Check if the signature actually matches the downloaded content
282 282
 				$certificate = openssl_get_publickey($app['certificate']);
283
-				$verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512);
283
+				$verified = (bool) openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512);
284 284
 				openssl_free_key($certificate);
285 285
 
286
-				if($verified === true) {
286
+				if ($verified === true) {
287 287
 					// Seems to match, let's proceed
288 288
 					$extractDir = $this->tempManager->getTemporaryFolder();
289 289
 					$archive = new TAR($tempFile);
290 290
 
291
-					if($archive) {
291
+					if ($archive) {
292 292
 						if (!$archive->extract($extractDir)) {
293 293
 							throw new \Exception(
294 294
 								sprintf(
@@ -301,7 +301,7 @@  discard block
 block discarded – undo
301 301
 						$folders = array_diff($allFiles, ['.', '..']);
302 302
 						$folders = array_values($folders);
303 303
 
304
-						if(count($folders) > 1) {
304
+						if (count($folders) > 1) {
305 305
 							throw new \Exception(
306 306
 								sprintf(
307 307
 									'Extracted app %s has more than 1 folder',
@@ -312,22 +312,22 @@  discard block
 block discarded – undo
312 312
 
313 313
 						// Check if appinfo/info.xml has the same app ID as well
314 314
 						$loadEntities = libxml_disable_entity_loader(false);
315
-						$xml = simplexml_load_file($extractDir . '/' . $folders[0] . '/appinfo/info.xml');
315
+						$xml = simplexml_load_file($extractDir.'/'.$folders[0].'/appinfo/info.xml');
316 316
 						libxml_disable_entity_loader($loadEntities);
317
-						if((string)$xml->id !== $appId) {
317
+						if ((string) $xml->id !== $appId) {
318 318
 							throw new \Exception(
319 319
 								sprintf(
320 320
 									'App for id %s has a wrong app ID in info.xml: %s',
321 321
 									$appId,
322
-									(string)$xml->id
322
+									(string) $xml->id
323 323
 								)
324 324
 							);
325 325
 						}
326 326
 
327 327
 						// Check if the version is lower than before
328 328
 						$currentVersion = OC_App::getAppVersion($appId);
329
-						$newVersion = (string)$xml->version;
330
-						if(version_compare($currentVersion, $newVersion) === 1) {
329
+						$newVersion = (string) $xml->version;
330
+						if (version_compare($currentVersion, $newVersion) === 1) {
331 331
 							throw new \Exception(
332 332
 								sprintf(
333 333
 									'App for id %s has version %s and tried to update to lower version %s',
@@ -338,12 +338,12 @@  discard block
 block discarded – undo
338 338
 							);
339 339
 						}
340 340
 
341
-						$baseDir = OC_App::getInstallPath() . '/' . $appId;
341
+						$baseDir = OC_App::getInstallPath().'/'.$appId;
342 342
 						// Remove old app with the ID if existent
343 343
 						OC_Helper::rmdirr($baseDir);
344 344
 						// Move to app folder
345
-						if(@mkdir($baseDir)) {
346
-							$extractDir .= '/' . $folders[0];
345
+						if (@mkdir($baseDir)) {
346
+							$extractDir .= '/'.$folders[0];
347 347
 							OC_Helper::copyr($extractDir, $baseDir);
348 348
 						}
349 349
 						OC_Helper::copyr($extractDir, $baseDir);
@@ -406,8 +406,8 @@  discard block
 block discarded – undo
406 406
 			$this->apps = $this->appFetcher->get();
407 407
 		}
408 408
 
409
-		foreach($this->apps as $app) {
410
-			if($app['id'] === $appId) {
409
+		foreach ($this->apps as $app) {
410
+			if ($app['id'] === $appId) {
411 411
 				$currentVersion = OC_App::getAppVersion($appId);
412 412
 				$newestVersion = $app['releases'][0]['version'];
413 413
 				if (version_compare($newestVersion, $currentVersion, '>')) {
@@ -430,7 +430,7 @@  discard block
 block discarded – undo
430 430
 	 */
431 431
 	private function isInstalledFromGit($appId) {
432 432
 		$app = \OC_App::findAppInDirectories($appId);
433
-		if($app === false) {
433
+		if ($app === false) {
434 434
 			return false;
435 435
 		}
436 436
 		$basedir = $app['path'].'/'.$appId;
@@ -445,7 +445,7 @@  discard block
 block discarded – undo
445 445
 	 * The function will check if the app is already downloaded in the apps repository
446 446
 	 */
447 447
 	public function isDownloaded($name) {
448
-		foreach(\OC::$APPSROOTS as $dir) {
448
+		foreach (\OC::$APPSROOTS as $dir) {
449 449
 			$dirToTest  = $dir['path'];
450 450
 			$dirToTest .= '/';
451 451
 			$dirToTest .= $name;
@@ -473,11 +473,11 @@  discard block
 block discarded – undo
473 473
 	 * this has to be done by the function oc_app_uninstall().
474 474
 	 */
475 475
 	public function removeApp($appId) {
476
-		if($this->isDownloaded( $appId )) {
477
-			$appDir = OC_App::getInstallPath() . '/' . $appId;
476
+		if ($this->isDownloaded($appId)) {
477
+			$appDir = OC_App::getInstallPath().'/'.$appId;
478 478
 			OC_Helper::rmdirr($appDir);
479 479
 			return true;
480
-		}else{
480
+		} else {
481 481
 			\OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', \OCP\Util::ERROR);
482 482
 
483 483
 			return false;
@@ -493,8 +493,8 @@  discard block
 block discarded – undo
493 493
 	 */
494 494
 	public function installAppBundle(Bundle $bundle) {
495 495
 		$appIds = $bundle->getAppIdentifiers();
496
-		foreach($appIds as $appId) {
497
-			if(!$this->isDownloaded($appId)) {
496
+		foreach ($appIds as $appId) {
497
+			if (!$this->isDownloaded($appId)) {
498 498
 				$this->downloadApp($appId);
499 499
 			}
500 500
 			$this->installApp($appId);
@@ -516,13 +516,13 @@  discard block
 block discarded – undo
516 516
 	 */
517 517
 	public static function installShippedApps($softErrors = false) {
518 518
 		$errors = [];
519
-		foreach(\OC::$APPSROOTS as $app_dir) {
520
-			if($dir = opendir( $app_dir['path'] )) {
521
-				while( false !== ( $filename = readdir( $dir ))) {
522
-					if( $filename[0] !== '.' and is_dir($app_dir['path']."/$filename") ) {
523
-						if( file_exists( $app_dir['path']."/$filename/appinfo/info.xml" )) {
524
-							if(!Installer::isInstalled($filename)) {
525
-								$info=OC_App::getAppInfo($filename);
519
+		foreach (\OC::$APPSROOTS as $app_dir) {
520
+			if ($dir = opendir($app_dir['path'])) {
521
+				while (false !== ($filename = readdir($dir))) {
522
+					if ($filename[0] !== '.' and is_dir($app_dir['path']."/$filename")) {
523
+						if (file_exists($app_dir['path']."/$filename/appinfo/info.xml")) {
524
+							if (!Installer::isInstalled($filename)) {
525
+								$info = OC_App::getAppInfo($filename);
526 526
 								$enabled = isset($info['default_enable']);
527 527
 								if (($enabled || in_array($filename, \OC::$server->getAppManager()->getAlwaysEnabledApps()))
528 528
 									  && \OC::$server->getConfig()->getAppValue($filename, 'enabled') !== 'no') {
@@ -545,7 +545,7 @@  discard block
 block discarded – undo
545 545
 						}
546 546
 					}
547 547
 				}
548
-				closedir( $dir );
548
+				closedir($dir);
549 549
 			}
550 550
 		}
551 551
 
@@ -562,12 +562,12 @@  discard block
 block discarded – undo
562 562
 		$appPath = OC_App::getAppPath($app);
563 563
 		\OC_App::registerAutoloading($app, $appPath);
564 564
 
565
-		if(is_file("$appPath/appinfo/database.xml")) {
565
+		if (is_file("$appPath/appinfo/database.xml")) {
566 566
 			try {
567 567
 				OC_DB::createDbFromStructure("$appPath/appinfo/database.xml");
568 568
 			} catch (TableExistsException $e) {
569 569
 				throw new HintException(
570
-					'Failed to enable app ' . $app,
570
+					'Failed to enable app '.$app,
571 571
 					'Please ask for help via one of our <a href="https://nextcloud.com/support/" target="_blank" rel="noreferrer noopener">support channels</a>.',
572 572
 					0, $e
573 573
 				);
@@ -596,16 +596,16 @@  discard block
 block discarded – undo
596 596
 		}
597 597
 
598 598
 		//set remote/public handlers
599
-		foreach($info['remote'] as $name=>$path) {
599
+		foreach ($info['remote'] as $name=>$path) {
600 600
 			$config->setAppValue('core', 'remote_'.$name, $app.'/'.$path);
601 601
 		}
602
-		foreach($info['public'] as $name=>$path) {
602
+		foreach ($info['public'] as $name=>$path) {
603 603
 			$config->setAppValue('core', 'public_'.$name, $app.'/'.$path);
604 604
 		}
605 605
 
606 606
 		OC_App::setAppTypes($info['id']);
607 607
 
608
-		if(isset($info['settings']) && is_array($info['settings'])) {
608
+		if (isset($info['settings']) && is_array($info['settings'])) {
609 609
 			// requires that autoloading was registered for the app,
610 610
 			// as happens before running the install.php some lines above
611 611
 			\OC::$server->getSettingsManager()->setupSettings($info['settings']);
@@ -618,7 +618,7 @@  discard block
 block discarded – undo
618 618
 	 * @param string $script
619 619
 	 */
620 620
 	private static function includeAppScript($script) {
621
-		if ( file_exists($script) ){
621
+		if (file_exists($script)) {
622 622
 			include $script;
623 623
 		}
624 624
 	}
Please login to merge, or discard this patch.