Completed
Push — master ( 9b9ca0...f3dbfd )
by Lukas
13:11
created
lib/private/Files/ObjectStore/ObjectStoreStorage.php 3 patches
Doc Comments   +6 added lines patch added patch discarded remove patch
@@ -162,6 +162,9 @@  discard block
 block discarded – undo
162 162
 		return true;
163 163
 	}
164 164
 
165
+	/**
166
+	 * @param string $path
167
+	 */
165 168
 	private function rmObjects($path) {
166 169
 		$children = $this->getCache()->getFolderContents($path);
167 170
 		foreach ($children as $child) {
@@ -364,6 +367,9 @@  discard block
 block discarded – undo
364 367
 		return true;
365 368
 	}
366 369
 
370
+	/**
371
+	 * @param string $path
372
+	 */
367 373
 	public function writeBack($tmpFile, $path) {
368 374
 		$stat = $this->stat($path);
369 375
 		if (empty($stat)) {
Please login to merge, or discard this patch.
Indentation   +370 added lines, -370 removed lines patch added patch discarded remove patch
@@ -31,374 +31,374 @@
 block discarded – undo
31 31
 use OCP\Files\ObjectStore\IObjectStore;
32 32
 
33 33
 class ObjectStoreStorage extends \OC\Files\Storage\Common {
34
-	/**
35
-	 * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
36
-	 */
37
-	protected $objectStore;
38
-	/**
39
-	 * @var string $id
40
-	 */
41
-	protected $id;
42
-	/**
43
-	 * @var \OC\User\User $user
44
-	 */
45
-	protected $user;
46
-
47
-	private $objectPrefix = 'urn:oid:';
48
-
49
-	public function __construct($params) {
50
-		if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
51
-			$this->objectStore = $params['objectstore'];
52
-		} else {
53
-			throw new \Exception('missing IObjectStore instance');
54
-		}
55
-		if (isset($params['storageid'])) {
56
-			$this->id = 'object::store:' . $params['storageid'];
57
-		} else {
58
-			$this->id = 'object::store:' . $this->objectStore->getStorageId();
59
-		}
60
-		if (isset($params['objectPrefix'])) {
61
-			$this->objectPrefix = $params['objectPrefix'];
62
-		}
63
-		//initialize cache with root directory in cache
64
-		if (!$this->is_dir('/')) {
65
-			$this->mkdir('/');
66
-		}
67
-	}
68
-
69
-	public function mkdir($path) {
70
-		$path = $this->normalizePath($path);
71
-
72
-		if ($this->file_exists($path)) {
73
-			return false;
74
-		}
75
-
76
-		$mTime = time();
77
-		$data = [
78
-			'mimetype' => 'httpd/unix-directory',
79
-			'size' => 0,
80
-			'mtime' => $mTime,
81
-			'storage_mtime' => $mTime,
82
-			'permissions' => \OCP\Constants::PERMISSION_ALL,
83
-		];
84
-		if ($path === '') {
85
-			//create root on the fly
86
-			$data['etag'] = $this->getETag('');
87
-			$this->getCache()->put('', $data);
88
-			return true;
89
-		} else {
90
-			// if parent does not exist, create it
91
-			$parent = $this->normalizePath(dirname($path));
92
-			$parentType = $this->filetype($parent);
93
-			if ($parentType === false) {
94
-				if (!$this->mkdir($parent)) {
95
-					// something went wrong
96
-					return false;
97
-				}
98
-			} else if ($parentType === 'file') {
99
-				// parent is a file
100
-				return false;
101
-			}
102
-			// finally create the new dir
103
-			$mTime = time(); // update mtime
104
-			$data['mtime'] = $mTime;
105
-			$data['storage_mtime'] = $mTime;
106
-			$data['etag'] = $this->getETag($path);
107
-			$this->getCache()->put($path, $data);
108
-			return true;
109
-		}
110
-	}
111
-
112
-	/**
113
-	 * @param string $path
114
-	 * @return string
115
-	 */
116
-	private function normalizePath($path) {
117
-		$path = trim($path, '/');
118
-		//FIXME why do we sometimes get a path like 'files//username'?
119
-		$path = str_replace('//', '/', $path);
120
-
121
-		// dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
122
-		if (!$path || $path === '.') {
123
-			$path = '';
124
-		}
125
-
126
-		return $path;
127
-	}
128
-
129
-	/**
130
-	 * Object Stores use a NoopScanner because metadata is directly stored in
131
-	 * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
132
-	 *
133
-	 * @param string $path
134
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
135
-	 * @return \OC\Files\ObjectStore\NoopScanner
136
-	 */
137
-	public function getScanner($path = '', $storage = null) {
138
-		if (!$storage) {
139
-			$storage = $this;
140
-		}
141
-		if (!isset($this->scanner)) {
142
-			$this->scanner = new NoopScanner($storage);
143
-		}
144
-		return $this->scanner;
145
-	}
146
-
147
-	public function getId() {
148
-		return $this->id;
149
-	}
150
-
151
-	public function rmdir($path) {
152
-		$path = $this->normalizePath($path);
153
-
154
-		if (!$this->is_dir($path)) {
155
-			return false;
156
-		}
157
-
158
-		$this->rmObjects($path);
159
-
160
-		$this->getCache()->remove($path);
161
-
162
-		return true;
163
-	}
164
-
165
-	private function rmObjects($path) {
166
-		$children = $this->getCache()->getFolderContents($path);
167
-		foreach ($children as $child) {
168
-			if ($child['mimetype'] === 'httpd/unix-directory') {
169
-				$this->rmObjects($child['path']);
170
-			} else {
171
-				$this->unlink($child['path']);
172
-			}
173
-		}
174
-	}
175
-
176
-	public function unlink($path) {
177
-		$path = $this->normalizePath($path);
178
-		$stat = $this->stat($path);
179
-
180
-		if ($stat && isset($stat['fileid'])) {
181
-			if ($stat['mimetype'] === 'httpd/unix-directory') {
182
-				return $this->rmdir($path);
183
-			}
184
-			try {
185
-				$this->objectStore->deleteObject($this->getURN($stat['fileid']));
186
-			} catch (\Exception $ex) {
187
-				if ($ex->getCode() !== 404) {
188
-					\OCP\Util::writeLog('objectstore', 'Could not delete object: ' . $ex->getMessage(), \OCP\Util::ERROR);
189
-					return false;
190
-				} else {
191
-					//removing from cache is ok as it does not exist in the objectstore anyway
192
-				}
193
-			}
194
-			$this->getCache()->remove($path);
195
-			return true;
196
-		}
197
-		return false;
198
-	}
199
-
200
-	public function stat($path) {
201
-		$path = $this->normalizePath($path);
202
-		$cacheEntry = $this->getCache()->get($path);
203
-		if ($cacheEntry instanceof CacheEntry) {
204
-			return $cacheEntry->getData();
205
-		} else {
206
-			return false;
207
-		}
208
-	}
209
-
210
-	/**
211
-	 * Override this method if you need a different unique resource identifier for your object storage implementation.
212
-	 * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
213
-	 * You may need a mapping table to store your URN if it cannot be generated from the fileid.
214
-	 *
215
-	 * @param int $fileId the fileid
216
-	 * @return null|string the unified resource name used to identify the object
217
-	 */
218
-	protected function getURN($fileId) {
219
-		if (is_numeric($fileId)) {
220
-			return $this->objectPrefix . $fileId;
221
-		}
222
-		return null;
223
-	}
224
-
225
-	public function opendir($path) {
226
-		$path = $this->normalizePath($path);
227
-
228
-		try {
229
-			$files = array();
230
-			$folderContents = $this->getCache()->getFolderContents($path);
231
-			foreach ($folderContents as $file) {
232
-				$files[] = $file['name'];
233
-			}
234
-
235
-			return IteratorDirectory::wrap($files);
236
-		} catch (\Exception $e) {
237
-			\OCP\Util::writeLog('objectstore', $e->getMessage(), \OCP\Util::ERROR);
238
-			return false;
239
-		}
240
-	}
241
-
242
-	public function filetype($path) {
243
-		$path = $this->normalizePath($path);
244
-		$stat = $this->stat($path);
245
-		if ($stat) {
246
-			if ($stat['mimetype'] === 'httpd/unix-directory') {
247
-				return 'dir';
248
-			}
249
-			return 'file';
250
-		} else {
251
-			return false;
252
-		}
253
-	}
254
-
255
-	public function fopen($path, $mode) {
256
-		$path = $this->normalizePath($path);
257
-
258
-		switch ($mode) {
259
-			case 'r':
260
-			case 'rb':
261
-				$stat = $this->stat($path);
262
-				if (is_array($stat)) {
263
-					try {
264
-						return $this->objectStore->readObject($this->getURN($stat['fileid']));
265
-					} catch (\Exception $ex) {
266
-						\OCP\Util::writeLog('objectstore', 'Could not get object: ' . $ex->getMessage(), \OCP\Util::ERROR);
267
-						return false;
268
-					}
269
-				} else {
270
-					return false;
271
-				}
272
-			case 'w':
273
-			case 'wb':
274
-			case 'a':
275
-			case 'ab':
276
-			case 'r+':
277
-			case 'w+':
278
-			case 'wb+':
279
-			case 'a+':
280
-			case 'x':
281
-			case 'x+':
282
-			case 'c':
283
-			case 'c+':
284
-				if (strrpos($path, '.') !== false) {
285
-					$ext = substr($path, strrpos($path, '.'));
286
-				} else {
287
-					$ext = '';
288
-				}
289
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
290
-				if ($this->file_exists($path)) {
291
-					$source = $this->fopen($path, 'r');
292
-					file_put_contents($tmpFile, $source);
293
-				}
294
-				$handle = fopen($tmpFile, $mode);
295
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
296
-					$this->writeBack($tmpFile, $path);
297
-				});
298
-		}
299
-		return false;
300
-	}
301
-
302
-	public function file_exists($path) {
303
-		$path = $this->normalizePath($path);
304
-		return (bool)$this->stat($path);
305
-	}
306
-
307
-	public function rename($source, $target) {
308
-		$source = $this->normalizePath($source);
309
-		$target = $this->normalizePath($target);
310
-		$this->remove($target);
311
-		$this->getCache()->move($source, $target);
312
-		$this->touch(dirname($target));
313
-		return true;
314
-	}
315
-
316
-	public function getMimeType($path) {
317
-		$path = $this->normalizePath($path);
318
-		$stat = $this->stat($path);
319
-		if (is_array($stat)) {
320
-			return $stat['mimetype'];
321
-		} else {
322
-			return false;
323
-		}
324
-	}
325
-
326
-	public function touch($path, $mtime = null) {
327
-		if (is_null($mtime)) {
328
-			$mtime = time();
329
-		}
330
-
331
-		$path = $this->normalizePath($path);
332
-		$dirName = dirname($path);
333
-		$parentExists = $this->is_dir($dirName);
334
-		if (!$parentExists) {
335
-			return false;
336
-		}
337
-
338
-		$stat = $this->stat($path);
339
-		if (is_array($stat)) {
340
-			// update existing mtime in db
341
-			$stat['mtime'] = $mtime;
342
-			$this->getCache()->update($stat['fileid'], $stat);
343
-		} else {
344
-			$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
345
-			// create new file
346
-			$stat = array(
347
-				'etag' => $this->getETag($path),
348
-				'mimetype' => $mimeType,
349
-				'size' => 0,
350
-				'mtime' => $mtime,
351
-				'storage_mtime' => $mtime,
352
-				'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
353
-			);
354
-			$fileId = $this->getCache()->put($path, $stat);
355
-			try {
356
-				//read an empty file from memory
357
-				$this->objectStore->writeObject($this->getURN($fileId), fopen('php://memory', 'r'));
358
-			} catch (\Exception $ex) {
359
-				$this->getCache()->remove($path);
360
-				\OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR);
361
-				return false;
362
-			}
363
-		}
364
-		return true;
365
-	}
366
-
367
-	public function writeBack($tmpFile, $path) {
368
-		$stat = $this->stat($path);
369
-		if (empty($stat)) {
370
-			// create new file
371
-			$stat = array(
372
-				'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
373
-			);
374
-		}
375
-		// update stat with new data
376
-		$mTime = time();
377
-		$stat['size'] = filesize($tmpFile);
378
-		$stat['mtime'] = $mTime;
379
-		$stat['storage_mtime'] = $mTime;
380
-		$stat['mimetype'] = \OC::$server->getMimeTypeDetector()->detect($tmpFile);
381
-		$stat['etag'] = $this->getETag($path);
382
-
383
-		$fileId = $this->getCache()->put($path, $stat);
384
-		try {
385
-			//upload to object storage
386
-			$this->objectStore->writeObject($this->getURN($fileId), fopen($tmpFile, 'r'));
387
-		} catch (\Exception $ex) {
388
-			$this->getCache()->remove($path);
389
-			\OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR);
390
-			throw $ex; // make this bubble up
391
-		}
392
-	}
393
-
394
-	/**
395
-	 * external changes are not supported, exclusive access to the object storage is assumed
396
-	 *
397
-	 * @param string $path
398
-	 * @param int $time
399
-	 * @return false
400
-	 */
401
-	public function hasUpdated($path, $time) {
402
-		return false;
403
-	}
34
+    /**
35
+     * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
36
+     */
37
+    protected $objectStore;
38
+    /**
39
+     * @var string $id
40
+     */
41
+    protected $id;
42
+    /**
43
+     * @var \OC\User\User $user
44
+     */
45
+    protected $user;
46
+
47
+    private $objectPrefix = 'urn:oid:';
48
+
49
+    public function __construct($params) {
50
+        if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
51
+            $this->objectStore = $params['objectstore'];
52
+        } else {
53
+            throw new \Exception('missing IObjectStore instance');
54
+        }
55
+        if (isset($params['storageid'])) {
56
+            $this->id = 'object::store:' . $params['storageid'];
57
+        } else {
58
+            $this->id = 'object::store:' . $this->objectStore->getStorageId();
59
+        }
60
+        if (isset($params['objectPrefix'])) {
61
+            $this->objectPrefix = $params['objectPrefix'];
62
+        }
63
+        //initialize cache with root directory in cache
64
+        if (!$this->is_dir('/')) {
65
+            $this->mkdir('/');
66
+        }
67
+    }
68
+
69
+    public function mkdir($path) {
70
+        $path = $this->normalizePath($path);
71
+
72
+        if ($this->file_exists($path)) {
73
+            return false;
74
+        }
75
+
76
+        $mTime = time();
77
+        $data = [
78
+            'mimetype' => 'httpd/unix-directory',
79
+            'size' => 0,
80
+            'mtime' => $mTime,
81
+            'storage_mtime' => $mTime,
82
+            'permissions' => \OCP\Constants::PERMISSION_ALL,
83
+        ];
84
+        if ($path === '') {
85
+            //create root on the fly
86
+            $data['etag'] = $this->getETag('');
87
+            $this->getCache()->put('', $data);
88
+            return true;
89
+        } else {
90
+            // if parent does not exist, create it
91
+            $parent = $this->normalizePath(dirname($path));
92
+            $parentType = $this->filetype($parent);
93
+            if ($parentType === false) {
94
+                if (!$this->mkdir($parent)) {
95
+                    // something went wrong
96
+                    return false;
97
+                }
98
+            } else if ($parentType === 'file') {
99
+                // parent is a file
100
+                return false;
101
+            }
102
+            // finally create the new dir
103
+            $mTime = time(); // update mtime
104
+            $data['mtime'] = $mTime;
105
+            $data['storage_mtime'] = $mTime;
106
+            $data['etag'] = $this->getETag($path);
107
+            $this->getCache()->put($path, $data);
108
+            return true;
109
+        }
110
+    }
111
+
112
+    /**
113
+     * @param string $path
114
+     * @return string
115
+     */
116
+    private function normalizePath($path) {
117
+        $path = trim($path, '/');
118
+        //FIXME why do we sometimes get a path like 'files//username'?
119
+        $path = str_replace('//', '/', $path);
120
+
121
+        // dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
122
+        if (!$path || $path === '.') {
123
+            $path = '';
124
+        }
125
+
126
+        return $path;
127
+    }
128
+
129
+    /**
130
+     * Object Stores use a NoopScanner because metadata is directly stored in
131
+     * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
132
+     *
133
+     * @param string $path
134
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
135
+     * @return \OC\Files\ObjectStore\NoopScanner
136
+     */
137
+    public function getScanner($path = '', $storage = null) {
138
+        if (!$storage) {
139
+            $storage = $this;
140
+        }
141
+        if (!isset($this->scanner)) {
142
+            $this->scanner = new NoopScanner($storage);
143
+        }
144
+        return $this->scanner;
145
+    }
146
+
147
+    public function getId() {
148
+        return $this->id;
149
+    }
150
+
151
+    public function rmdir($path) {
152
+        $path = $this->normalizePath($path);
153
+
154
+        if (!$this->is_dir($path)) {
155
+            return false;
156
+        }
157
+
158
+        $this->rmObjects($path);
159
+
160
+        $this->getCache()->remove($path);
161
+
162
+        return true;
163
+    }
164
+
165
+    private function rmObjects($path) {
166
+        $children = $this->getCache()->getFolderContents($path);
167
+        foreach ($children as $child) {
168
+            if ($child['mimetype'] === 'httpd/unix-directory') {
169
+                $this->rmObjects($child['path']);
170
+            } else {
171
+                $this->unlink($child['path']);
172
+            }
173
+        }
174
+    }
175
+
176
+    public function unlink($path) {
177
+        $path = $this->normalizePath($path);
178
+        $stat = $this->stat($path);
179
+
180
+        if ($stat && isset($stat['fileid'])) {
181
+            if ($stat['mimetype'] === 'httpd/unix-directory') {
182
+                return $this->rmdir($path);
183
+            }
184
+            try {
185
+                $this->objectStore->deleteObject($this->getURN($stat['fileid']));
186
+            } catch (\Exception $ex) {
187
+                if ($ex->getCode() !== 404) {
188
+                    \OCP\Util::writeLog('objectstore', 'Could not delete object: ' . $ex->getMessage(), \OCP\Util::ERROR);
189
+                    return false;
190
+                } else {
191
+                    //removing from cache is ok as it does not exist in the objectstore anyway
192
+                }
193
+            }
194
+            $this->getCache()->remove($path);
195
+            return true;
196
+        }
197
+        return false;
198
+    }
199
+
200
+    public function stat($path) {
201
+        $path = $this->normalizePath($path);
202
+        $cacheEntry = $this->getCache()->get($path);
203
+        if ($cacheEntry instanceof CacheEntry) {
204
+            return $cacheEntry->getData();
205
+        } else {
206
+            return false;
207
+        }
208
+    }
209
+
210
+    /**
211
+     * Override this method if you need a different unique resource identifier for your object storage implementation.
212
+     * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
213
+     * You may need a mapping table to store your URN if it cannot be generated from the fileid.
214
+     *
215
+     * @param int $fileId the fileid
216
+     * @return null|string the unified resource name used to identify the object
217
+     */
218
+    protected function getURN($fileId) {
219
+        if (is_numeric($fileId)) {
220
+            return $this->objectPrefix . $fileId;
221
+        }
222
+        return null;
223
+    }
224
+
225
+    public function opendir($path) {
226
+        $path = $this->normalizePath($path);
227
+
228
+        try {
229
+            $files = array();
230
+            $folderContents = $this->getCache()->getFolderContents($path);
231
+            foreach ($folderContents as $file) {
232
+                $files[] = $file['name'];
233
+            }
234
+
235
+            return IteratorDirectory::wrap($files);
236
+        } catch (\Exception $e) {
237
+            \OCP\Util::writeLog('objectstore', $e->getMessage(), \OCP\Util::ERROR);
238
+            return false;
239
+        }
240
+    }
241
+
242
+    public function filetype($path) {
243
+        $path = $this->normalizePath($path);
244
+        $stat = $this->stat($path);
245
+        if ($stat) {
246
+            if ($stat['mimetype'] === 'httpd/unix-directory') {
247
+                return 'dir';
248
+            }
249
+            return 'file';
250
+        } else {
251
+            return false;
252
+        }
253
+    }
254
+
255
+    public function fopen($path, $mode) {
256
+        $path = $this->normalizePath($path);
257
+
258
+        switch ($mode) {
259
+            case 'r':
260
+            case 'rb':
261
+                $stat = $this->stat($path);
262
+                if (is_array($stat)) {
263
+                    try {
264
+                        return $this->objectStore->readObject($this->getURN($stat['fileid']));
265
+                    } catch (\Exception $ex) {
266
+                        \OCP\Util::writeLog('objectstore', 'Could not get object: ' . $ex->getMessage(), \OCP\Util::ERROR);
267
+                        return false;
268
+                    }
269
+                } else {
270
+                    return false;
271
+                }
272
+            case 'w':
273
+            case 'wb':
274
+            case 'a':
275
+            case 'ab':
276
+            case 'r+':
277
+            case 'w+':
278
+            case 'wb+':
279
+            case 'a+':
280
+            case 'x':
281
+            case 'x+':
282
+            case 'c':
283
+            case 'c+':
284
+                if (strrpos($path, '.') !== false) {
285
+                    $ext = substr($path, strrpos($path, '.'));
286
+                } else {
287
+                    $ext = '';
288
+                }
289
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
290
+                if ($this->file_exists($path)) {
291
+                    $source = $this->fopen($path, 'r');
292
+                    file_put_contents($tmpFile, $source);
293
+                }
294
+                $handle = fopen($tmpFile, $mode);
295
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
296
+                    $this->writeBack($tmpFile, $path);
297
+                });
298
+        }
299
+        return false;
300
+    }
301
+
302
+    public function file_exists($path) {
303
+        $path = $this->normalizePath($path);
304
+        return (bool)$this->stat($path);
305
+    }
306
+
307
+    public function rename($source, $target) {
308
+        $source = $this->normalizePath($source);
309
+        $target = $this->normalizePath($target);
310
+        $this->remove($target);
311
+        $this->getCache()->move($source, $target);
312
+        $this->touch(dirname($target));
313
+        return true;
314
+    }
315
+
316
+    public function getMimeType($path) {
317
+        $path = $this->normalizePath($path);
318
+        $stat = $this->stat($path);
319
+        if (is_array($stat)) {
320
+            return $stat['mimetype'];
321
+        } else {
322
+            return false;
323
+        }
324
+    }
325
+
326
+    public function touch($path, $mtime = null) {
327
+        if (is_null($mtime)) {
328
+            $mtime = time();
329
+        }
330
+
331
+        $path = $this->normalizePath($path);
332
+        $dirName = dirname($path);
333
+        $parentExists = $this->is_dir($dirName);
334
+        if (!$parentExists) {
335
+            return false;
336
+        }
337
+
338
+        $stat = $this->stat($path);
339
+        if (is_array($stat)) {
340
+            // update existing mtime in db
341
+            $stat['mtime'] = $mtime;
342
+            $this->getCache()->update($stat['fileid'], $stat);
343
+        } else {
344
+            $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
345
+            // create new file
346
+            $stat = array(
347
+                'etag' => $this->getETag($path),
348
+                'mimetype' => $mimeType,
349
+                'size' => 0,
350
+                'mtime' => $mtime,
351
+                'storage_mtime' => $mtime,
352
+                'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
353
+            );
354
+            $fileId = $this->getCache()->put($path, $stat);
355
+            try {
356
+                //read an empty file from memory
357
+                $this->objectStore->writeObject($this->getURN($fileId), fopen('php://memory', 'r'));
358
+            } catch (\Exception $ex) {
359
+                $this->getCache()->remove($path);
360
+                \OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR);
361
+                return false;
362
+            }
363
+        }
364
+        return true;
365
+    }
366
+
367
+    public function writeBack($tmpFile, $path) {
368
+        $stat = $this->stat($path);
369
+        if (empty($stat)) {
370
+            // create new file
371
+            $stat = array(
372
+                'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
373
+            );
374
+        }
375
+        // update stat with new data
376
+        $mTime = time();
377
+        $stat['size'] = filesize($tmpFile);
378
+        $stat['mtime'] = $mTime;
379
+        $stat['storage_mtime'] = $mTime;
380
+        $stat['mimetype'] = \OC::$server->getMimeTypeDetector()->detect($tmpFile);
381
+        $stat['etag'] = $this->getETag($path);
382
+
383
+        $fileId = $this->getCache()->put($path, $stat);
384
+        try {
385
+            //upload to object storage
386
+            $this->objectStore->writeObject($this->getURN($fileId), fopen($tmpFile, 'r'));
387
+        } catch (\Exception $ex) {
388
+            $this->getCache()->remove($path);
389
+            \OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR);
390
+            throw $ex; // make this bubble up
391
+        }
392
+    }
393
+
394
+    /**
395
+     * external changes are not supported, exclusive access to the object storage is assumed
396
+     *
397
+     * @param string $path
398
+     * @param int $time
399
+     * @return false
400
+     */
401
+    public function hasUpdated($path, $time) {
402
+        return false;
403
+    }
404 404
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -53,9 +53,9 @@  discard block
 block discarded – undo
53 53
 			throw new \Exception('missing IObjectStore instance');
54 54
 		}
55 55
 		if (isset($params['storageid'])) {
56
-			$this->id = 'object::store:' . $params['storageid'];
56
+			$this->id = 'object::store:'.$params['storageid'];
57 57
 		} else {
58
-			$this->id = 'object::store:' . $this->objectStore->getStorageId();
58
+			$this->id = 'object::store:'.$this->objectStore->getStorageId();
59 59
 		}
60 60
 		if (isset($params['objectPrefix'])) {
61 61
 			$this->objectPrefix = $params['objectPrefix'];
@@ -185,7 +185,7 @@  discard block
 block discarded – undo
185 185
 				$this->objectStore->deleteObject($this->getURN($stat['fileid']));
186 186
 			} catch (\Exception $ex) {
187 187
 				if ($ex->getCode() !== 404) {
188
-					\OCP\Util::writeLog('objectstore', 'Could not delete object: ' . $ex->getMessage(), \OCP\Util::ERROR);
188
+					\OCP\Util::writeLog('objectstore', 'Could not delete object: '.$ex->getMessage(), \OCP\Util::ERROR);
189 189
 					return false;
190 190
 				} else {
191 191
 					//removing from cache is ok as it does not exist in the objectstore anyway
@@ -217,7 +217,7 @@  discard block
 block discarded – undo
217 217
 	 */
218 218
 	protected function getURN($fileId) {
219 219
 		if (is_numeric($fileId)) {
220
-			return $this->objectPrefix . $fileId;
220
+			return $this->objectPrefix.$fileId;
221 221
 		}
222 222
 		return null;
223 223
 	}
@@ -263,7 +263,7 @@  discard block
 block discarded – undo
263 263
 					try {
264 264
 						return $this->objectStore->readObject($this->getURN($stat['fileid']));
265 265
 					} catch (\Exception $ex) {
266
-						\OCP\Util::writeLog('objectstore', 'Could not get object: ' . $ex->getMessage(), \OCP\Util::ERROR);
266
+						\OCP\Util::writeLog('objectstore', 'Could not get object: '.$ex->getMessage(), \OCP\Util::ERROR);
267 267
 						return false;
268 268
 					}
269 269
 				} else {
@@ -292,7 +292,7 @@  discard block
 block discarded – undo
292 292
 					file_put_contents($tmpFile, $source);
293 293
 				}
294 294
 				$handle = fopen($tmpFile, $mode);
295
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
295
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
296 296
 					$this->writeBack($tmpFile, $path);
297 297
 				});
298 298
 		}
@@ -301,7 +301,7 @@  discard block
 block discarded – undo
301 301
 
302 302
 	public function file_exists($path) {
303 303
 		$path = $this->normalizePath($path);
304
-		return (bool)$this->stat($path);
304
+		return (bool) $this->stat($path);
305 305
 	}
306 306
 
307 307
 	public function rename($source, $target) {
@@ -357,7 +357,7 @@  discard block
 block discarded – undo
357 357
 				$this->objectStore->writeObject($this->getURN($fileId), fopen('php://memory', 'r'));
358 358
 			} catch (\Exception $ex) {
359 359
 				$this->getCache()->remove($path);
360
-				\OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR);
360
+				\OCP\Util::writeLog('objectstore', 'Could not create object: '.$ex->getMessage(), \OCP\Util::ERROR);
361 361
 				return false;
362 362
 			}
363 363
 		}
@@ -386,7 +386,7 @@  discard block
 block discarded – undo
386 386
 			$this->objectStore->writeObject($this->getURN($fileId), fopen($tmpFile, 'r'));
387 387
 		} catch (\Exception $ex) {
388 388
 			$this->getCache()->remove($path);
389
-			\OCP\Util::writeLog('objectstore', 'Could not create object: ' . $ex->getMessage(), \OCP\Util::ERROR);
389
+			\OCP\Util::writeLog('objectstore', 'Could not create object: '.$ex->getMessage(), \OCP\Util::ERROR);
390 390
 			throw $ex; // make this bubble up
391 391
 		}
392 392
 	}
Please login to merge, or discard this patch.
lib/private/Files/Storage/DAV.php 4 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -38,7 +38,6 @@
 block discarded – undo
38 38
 use GuzzleHttp\Message\ResponseInterface;
39 39
 use Icewind\Streams\CallbackWrapper;
40 40
 use OC\Files\Filesystem;
41
-use OC\Files\Stream\Close;
42 41
 use Icewind\Streams\IteratorDirectory;
43 42
 use OC\MemCache\ArrayCache;
44 43
 use OCP\AppFramework\Http;
Please login to merge, or discard this patch.
Braces   +5 added lines, -2 removed lines patch added patch discarded remove patch
@@ -90,8 +90,11 @@
 block discarded – undo
90 90
 		if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
91 91
 			$host = $params['host'];
92 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);
93
+			if (substr($host, 0, 8) == "https://") {
94
+			    $host = substr($host, 8);
95
+			} else if (substr($host, 0, 7) == "http://") {
96
+			    $host = substr($host, 7);
97
+			}
95 98
 			$this->host = $host;
96 99
 			$this->user = $params['user'];
97 100
 			$this->password = $params['password'];
Please login to merge, or discard this patch.
Indentation   +793 added lines, -793 removed lines patch added patch discarded remove patch
@@ -59,798 +59,798 @@
 block discarded – undo
59 59
  * @package OC\Files\Storage
60 60
  */
61 61
 class DAV extends Common {
62
-	/** @var string */
63
-	protected $password;
64
-	/** @var string */
65
-	protected $user;
66
-	/** @var string */
67
-	protected $authType;
68
-	/** @var string */
69
-	protected $host;
70
-	/** @var bool */
71
-	protected $secure;
72
-	/** @var string */
73
-	protected $root;
74
-	/** @var string */
75
-	protected $certPath;
76
-	/** @var bool */
77
-	protected $ready;
78
-	/** @var Client */
79
-	protected $client;
80
-	/** @var ArrayCache */
81
-	protected $statCache;
82
-	/** @var \OCP\Http\Client\IClientService */
83
-	protected $httpClientService;
84
-
85
-	/**
86
-	 * @param array $params
87
-	 * @throws \Exception
88
-	 */
89
-	public function __construct($params) {
90
-		$this->statCache = new ArrayCache();
91
-		$this->httpClientService = \OC::$server->getHTTPClientService();
92
-		if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
93
-			$host = $params['host'];
94
-			//remove leading http[s], will be generated in createBaseUri()
95
-			if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
96
-			else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
97
-			$this->host = $host;
98
-			$this->user = $params['user'];
99
-			$this->password = $params['password'];
100
-			if (isset($params['authType'])) {
101
-				$this->authType = $params['authType'];
102
-			}
103
-			if (isset($params['secure'])) {
104
-				if (is_string($params['secure'])) {
105
-					$this->secure = ($params['secure'] === 'true');
106
-				} else {
107
-					$this->secure = (bool)$params['secure'];
108
-				}
109
-			} else {
110
-				$this->secure = false;
111
-			}
112
-			if ($this->secure === true) {
113
-				// inject mock for testing
114
-				$certManager = \OC::$server->getCertificateManager();
115
-				if (is_null($certManager)) { //no user
116
-					$certManager = \OC::$server->getCertificateManager(null);
117
-				}
118
-				$certPath = $certManager->getAbsoluteBundlePath();
119
-				if (file_exists($certPath)) {
120
-					$this->certPath = $certPath;
121
-				}
122
-			}
123
-			$this->root = isset($params['root']) ? $params['root'] : '/';
124
-			if (!$this->root || $this->root[0] != '/') {
125
-				$this->root = '/' . $this->root;
126
-			}
127
-			if (substr($this->root, -1, 1) != '/') {
128
-				$this->root .= '/';
129
-			}
130
-		} else {
131
-			throw new \Exception('Invalid webdav storage configuration');
132
-		}
133
-	}
134
-
135
-	protected function init() {
136
-		if ($this->ready) {
137
-			return;
138
-		}
139
-		$this->ready = true;
140
-
141
-		$settings = [
142
-			'baseUri' => $this->createBaseUri(),
143
-			'userName' => $this->user,
144
-			'password' => $this->password,
145
-		];
146
-		if (isset($this->authType)) {
147
-			$settings['authType'] = $this->authType;
148
-		}
149
-
150
-		$proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
151
-		if($proxy !== '') {
152
-			$settings['proxy'] = $proxy;
153
-		}
154
-
155
-		$this->client = new Client($settings);
156
-		$this->client->setThrowExceptions(true);
157
-		if ($this->secure === true && $this->certPath) {
158
-			$this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
159
-		}
160
-	}
161
-
162
-	/**
163
-	 * Clear the stat cache
164
-	 */
165
-	public function clearStatCache() {
166
-		$this->statCache->clear();
167
-	}
168
-
169
-	/** {@inheritdoc} */
170
-	public function getId() {
171
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
172
-	}
173
-
174
-	/** {@inheritdoc} */
175
-	public function createBaseUri() {
176
-		$baseUri = 'http';
177
-		if ($this->secure) {
178
-			$baseUri .= 's';
179
-		}
180
-		$baseUri .= '://' . $this->host . $this->root;
181
-		return $baseUri;
182
-	}
183
-
184
-	/** {@inheritdoc} */
185
-	public function mkdir($path) {
186
-		$this->init();
187
-		$path = $this->cleanPath($path);
188
-		$result = $this->simpleResponse('MKCOL', $path, null, 201);
189
-		if ($result) {
190
-			$this->statCache->set($path, true);
191
-		}
192
-		return $result;
193
-	}
194
-
195
-	/** {@inheritdoc} */
196
-	public function rmdir($path) {
197
-		$this->init();
198
-		$path = $this->cleanPath($path);
199
-		// FIXME: some WebDAV impl return 403 when trying to DELETE
200
-		// a non-empty folder
201
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
202
-		$this->statCache->clear($path . '/');
203
-		$this->statCache->remove($path);
204
-		return $result;
205
-	}
206
-
207
-	/** {@inheritdoc} */
208
-	public function opendir($path) {
209
-		$this->init();
210
-		$path = $this->cleanPath($path);
211
-		try {
212
-			$response = $this->client->propFind(
213
-				$this->encodePath($path),
214
-				['{DAV:}href'],
215
-				1
216
-			);
217
-			if ($response === false) {
218
-				return false;
219
-			}
220
-			$content = [];
221
-			$files = array_keys($response);
222
-			array_shift($files); //the first entry is the current directory
223
-
224
-			if (!$this->statCache->hasKey($path)) {
225
-				$this->statCache->set($path, true);
226
-			}
227
-			foreach ($files as $file) {
228
-				$file = urldecode($file);
229
-				// do not store the real entry, we might not have all properties
230
-				if (!$this->statCache->hasKey($path)) {
231
-					$this->statCache->set($file, true);
232
-				}
233
-				$file = basename($file);
234
-				$content[] = $file;
235
-			}
236
-			return IteratorDirectory::wrap($content);
237
-		} catch (\Exception $e) {
238
-			$this->convertException($e, $path);
239
-		}
240
-		return false;
241
-	}
242
-
243
-	/**
244
-	 * Propfind call with cache handling.
245
-	 *
246
-	 * First checks if information is cached.
247
-	 * If not, request it from the server then store to cache.
248
-	 *
249
-	 * @param string $path path to propfind
250
-	 *
251
-	 * @return array|boolean propfind response or false if the entry was not found
252
-	 *
253
-	 * @throws ClientHttpException
254
-	 */
255
-	protected function propfind($path) {
256
-		$path = $this->cleanPath($path);
257
-		$cachedResponse = $this->statCache->get($path);
258
-		// we either don't know it, or we know it exists but need more details
259
-		if (is_null($cachedResponse) || $cachedResponse === true) {
260
-			$this->init();
261
-			try {
262
-				$response = $this->client->propFind(
263
-					$this->encodePath($path),
264
-					array(
265
-						'{DAV:}getlastmodified',
266
-						'{DAV:}getcontentlength',
267
-						'{DAV:}getcontenttype',
268
-						'{http://owncloud.org/ns}permissions',
269
-						'{http://open-collaboration-services.org/ns}share-permissions',
270
-						'{DAV:}resourcetype',
271
-						'{DAV:}getetag',
272
-					)
273
-				);
274
-				$this->statCache->set($path, $response);
275
-			} catch (ClientHttpException $e) {
276
-				if ($e->getHttpStatus() === 404) {
277
-					$this->statCache->clear($path . '/');
278
-					$this->statCache->set($path, false);
279
-					return false;
280
-				}
281
-				$this->convertException($e, $path);
282
-			} catch (\Exception $e) {
283
-				$this->convertException($e, $path);
284
-			}
285
-		} else {
286
-			$response = $cachedResponse;
287
-		}
288
-		return $response;
289
-	}
290
-
291
-	/** {@inheritdoc} */
292
-	public function filetype($path) {
293
-		try {
294
-			$response = $this->propfind($path);
295
-			if ($response === false) {
296
-				return false;
297
-			}
298
-			$responseType = [];
299
-			if (isset($response["{DAV:}resourcetype"])) {
300
-				/** @var ResourceType[] $response */
301
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
302
-			}
303
-			return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
304
-		} catch (\Exception $e) {
305
-			$this->convertException($e, $path);
306
-		}
307
-		return false;
308
-	}
309
-
310
-	/** {@inheritdoc} */
311
-	public function file_exists($path) {
312
-		try {
313
-			$path = $this->cleanPath($path);
314
-			$cachedState = $this->statCache->get($path);
315
-			if ($cachedState === false) {
316
-				// we know the file doesn't exist
317
-				return false;
318
-			} else if (!is_null($cachedState)) {
319
-				return true;
320
-			}
321
-			// need to get from server
322
-			return ($this->propfind($path) !== false);
323
-		} catch (\Exception $e) {
324
-			$this->convertException($e, $path);
325
-		}
326
-		return false;
327
-	}
328
-
329
-	/** {@inheritdoc} */
330
-	public function unlink($path) {
331
-		$this->init();
332
-		$path = $this->cleanPath($path);
333
-		$result = $this->simpleResponse('DELETE', $path, null, 204);
334
-		$this->statCache->clear($path . '/');
335
-		$this->statCache->remove($path);
336
-		return $result;
337
-	}
338
-
339
-	/** {@inheritdoc} */
340
-	public function fopen($path, $mode) {
341
-		$this->init();
342
-		$path = $this->cleanPath($path);
343
-		switch ($mode) {
344
-			case 'r':
345
-			case 'rb':
346
-				try {
347
-					$response = $this->httpClientService
348
-							->newClient()
349
-							->get($this->createBaseUri() . $this->encodePath($path), [
350
-									'auth' => [$this->user, $this->password],
351
-									'stream' => true
352
-							]);
353
-				} catch (RequestException $e) {
354
-					if ($e->getResponse() instanceof ResponseInterface
355
-						&& $e->getResponse()->getStatusCode() === 404) {
356
-						return false;
357
-					} else {
358
-						throw $e;
359
-					}
360
-				}
361
-
362
-				if ($response->getStatusCode() !== Http::STATUS_OK) {
363
-					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
364
-						throw new \OCP\Lock\LockedException($path);
365
-					} else {
366
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
367
-					}
368
-				}
369
-
370
-				return $response->getBody();
371
-			case 'w':
372
-			case 'wb':
373
-			case 'a':
374
-			case 'ab':
375
-			case 'r+':
376
-			case 'w+':
377
-			case 'wb+':
378
-			case 'a+':
379
-			case 'x':
380
-			case 'x+':
381
-			case 'c':
382
-			case 'c+':
383
-				//emulate these
384
-				$tempManager = \OC::$server->getTempManager();
385
-				if (strrpos($path, '.') !== false) {
386
-					$ext = substr($path, strrpos($path, '.'));
387
-				} else {
388
-					$ext = '';
389
-				}
390
-				if ($this->file_exists($path)) {
391
-					if (!$this->isUpdatable($path)) {
392
-						return false;
393
-					}
394
-					if ($mode === 'w' or $mode === 'w+') {
395
-						$tmpFile = $tempManager->getTemporaryFile($ext);
396
-					} else {
397
-						$tmpFile = $this->getCachedFile($path);
398
-					}
399
-				} else {
400
-					if (!$this->isCreatable(dirname($path))) {
401
-						return false;
402
-					}
403
-					$tmpFile = $tempManager->getTemporaryFile($ext);
404
-				}
405
-				$handle = fopen($tmpFile, $mode);
406
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
407
-					$this->writeBack($tmpFile, $path);
408
-				});
409
-		}
410
-	}
411
-
412
-	/**
413
-	 * @param string $tmpFile
414
-	 */
415
-	public function writeBack($tmpFile, $path) {
416
-		$this->uploadFile($tmpFile, $path);
417
-		unlink($tmpFile);
418
-	}
419
-
420
-	/** {@inheritdoc} */
421
-	public function free_space($path) {
422
-		$this->init();
423
-		$path = $this->cleanPath($path);
424
-		try {
425
-			// TODO: cacheable ?
426
-			$response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
427
-			if ($response === false) {
428
-				return FileInfo::SPACE_UNKNOWN;
429
-			}
430
-			if (isset($response['{DAV:}quota-available-bytes'])) {
431
-				return (int)$response['{DAV:}quota-available-bytes'];
432
-			} else {
433
-				return FileInfo::SPACE_UNKNOWN;
434
-			}
435
-		} catch (\Exception $e) {
436
-			return FileInfo::SPACE_UNKNOWN;
437
-		}
438
-	}
439
-
440
-	/** {@inheritdoc} */
441
-	public function touch($path, $mtime = null) {
442
-		$this->init();
443
-		if (is_null($mtime)) {
444
-			$mtime = time();
445
-		}
446
-		$path = $this->cleanPath($path);
447
-
448
-		// if file exists, update the mtime, else create a new empty file
449
-		if ($this->file_exists($path)) {
450
-			try {
451
-				$this->statCache->remove($path);
452
-				$this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
453
-				// non-owncloud clients might not have accepted the property, need to recheck it
454
-				$response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
455
-				if ($response === false) {
456
-					return false;
457
-				}
458
-				if (isset($response['{DAV:}getlastmodified'])) {
459
-					$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
460
-					if ($remoteMtime !== $mtime) {
461
-						// server has not accepted the mtime
462
-						return false;
463
-					}
464
-				}
465
-			} catch (ClientHttpException $e) {
466
-				if ($e->getHttpStatus() === 501) {
467
-					return false;
468
-				}
469
-				$this->convertException($e, $path);
470
-				return false;
471
-			} catch (\Exception $e) {
472
-				$this->convertException($e, $path);
473
-				return false;
474
-			}
475
-		} else {
476
-			$this->file_put_contents($path, '');
477
-		}
478
-		return true;
479
-	}
480
-
481
-	/**
482
-	 * @param string $path
483
-	 * @param string $data
484
-	 * @return int
485
-	 */
486
-	public function file_put_contents($path, $data) {
487
-		$path = $this->cleanPath($path);
488
-		$result = parent::file_put_contents($path, $data);
489
-		$this->statCache->remove($path);
490
-		return $result;
491
-	}
492
-
493
-	/**
494
-	 * @param string $path
495
-	 * @param string $target
496
-	 */
497
-	protected function uploadFile($path, $target) {
498
-		$this->init();
499
-
500
-		// invalidate
501
-		$target = $this->cleanPath($target);
502
-		$this->statCache->remove($target);
503
-		$source = fopen($path, 'r');
504
-
505
-		$this->httpClientService
506
-			->newClient()
507
-			->put($this->createBaseUri() . $this->encodePath($target), [
508
-				'body' => $source,
509
-				'auth' => [$this->user, $this->password]
510
-			]);
511
-
512
-		$this->removeCachedFile($target);
513
-	}
514
-
515
-	/** {@inheritdoc} */
516
-	public function rename($path1, $path2) {
517
-		$this->init();
518
-		$path1 = $this->cleanPath($path1);
519
-		$path2 = $this->cleanPath($path2);
520
-		try {
521
-			// overwrite directory ?
522
-			if ($this->is_dir($path2)) {
523
-				// needs trailing slash in destination
524
-				$path2 = rtrim($path2, '/') . '/';
525
-			}
526
-			$this->client->request(
527
-				'MOVE',
528
-				$this->encodePath($path1),
529
-				null,
530
-				[
531
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
532
-				]
533
-			);
534
-			$this->statCache->clear($path1 . '/');
535
-			$this->statCache->clear($path2 . '/');
536
-			$this->statCache->set($path1, false);
537
-			$this->statCache->set($path2, true);
538
-			$this->removeCachedFile($path1);
539
-			$this->removeCachedFile($path2);
540
-			return true;
541
-		} catch (\Exception $e) {
542
-			$this->convertException($e);
543
-		}
544
-		return false;
545
-	}
546
-
547
-	/** {@inheritdoc} */
548
-	public function copy($path1, $path2) {
549
-		$this->init();
550
-		$path1 = $this->cleanPath($path1);
551
-		$path2 = $this->cleanPath($path2);
552
-		try {
553
-			// overwrite directory ?
554
-			if ($this->is_dir($path2)) {
555
-				// needs trailing slash in destination
556
-				$path2 = rtrim($path2, '/') . '/';
557
-			}
558
-			$this->client->request(
559
-				'COPY',
560
-				$this->encodePath($path1),
561
-				null,
562
-				[
563
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
564
-				]
565
-			);
566
-			$this->statCache->clear($path2 . '/');
567
-			$this->statCache->set($path2, true);
568
-			$this->removeCachedFile($path2);
569
-			return true;
570
-		} catch (\Exception $e) {
571
-			$this->convertException($e);
572
-		}
573
-		return false;
574
-	}
575
-
576
-	/** {@inheritdoc} */
577
-	public function stat($path) {
578
-		try {
579
-			$response = $this->propfind($path);
580
-			if (!$response) {
581
-				return false;
582
-			}
583
-			return [
584
-				'mtime' => strtotime($response['{DAV:}getlastmodified']),
585
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
586
-			];
587
-		} catch (\Exception $e) {
588
-			$this->convertException($e, $path);
589
-		}
590
-		return array();
591
-	}
592
-
593
-	/** {@inheritdoc} */
594
-	public function getMimeType($path) {
595
-		try {
596
-			$response = $this->propfind($path);
597
-			if ($response === false) {
598
-				return false;
599
-			}
600
-			$responseType = [];
601
-			if (isset($response["{DAV:}resourcetype"])) {
602
-				/** @var ResourceType[] $response */
603
-				$responseType = $response["{DAV:}resourcetype"]->getValue();
604
-			}
605
-			$type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
606
-			if ($type == 'dir') {
607
-				return 'httpd/unix-directory';
608
-			} elseif (isset($response['{DAV:}getcontenttype'])) {
609
-				return $response['{DAV:}getcontenttype'];
610
-			} else {
611
-				return false;
612
-			}
613
-		} catch (\Exception $e) {
614
-			$this->convertException($e, $path);
615
-		}
616
-		return false;
617
-	}
618
-
619
-	/**
620
-	 * @param string $path
621
-	 * @return string
622
-	 */
623
-	public function cleanPath($path) {
624
-		if ($path === '') {
625
-			return $path;
626
-		}
627
-		$path = Filesystem::normalizePath($path);
628
-		// remove leading slash
629
-		return substr($path, 1);
630
-	}
631
-
632
-	/**
633
-	 * URL encodes the given path but keeps the slashes
634
-	 *
635
-	 * @param string $path to encode
636
-	 * @return string encoded path
637
-	 */
638
-	protected function encodePath($path) {
639
-		// slashes need to stay
640
-		return str_replace('%2F', '/', rawurlencode($path));
641
-	}
642
-
643
-	/**
644
-	 * @param string $method
645
-	 * @param string $path
646
-	 * @param string|resource|null $body
647
-	 * @param int $expected
648
-	 * @return bool
649
-	 * @throws StorageInvalidException
650
-	 * @throws StorageNotAvailableException
651
-	 */
652
-	protected function simpleResponse($method, $path, $body, $expected) {
653
-		$path = $this->cleanPath($path);
654
-		try {
655
-			$response = $this->client->request($method, $this->encodePath($path), $body);
656
-			return $response['statusCode'] == $expected;
657
-		} catch (ClientHttpException $e) {
658
-			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
659
-				$this->statCache->clear($path . '/');
660
-				$this->statCache->set($path, false);
661
-				return false;
662
-			}
663
-
664
-			$this->convertException($e, $path);
665
-		} catch (\Exception $e) {
666
-			$this->convertException($e, $path);
667
-		}
668
-		return false;
669
-	}
670
-
671
-	/**
672
-	 * check if curl is installed
673
-	 */
674
-	public static function checkDependencies() {
675
-		return true;
676
-	}
677
-
678
-	/** {@inheritdoc} */
679
-	public function isUpdatable($path) {
680
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
681
-	}
682
-
683
-	/** {@inheritdoc} */
684
-	public function isCreatable($path) {
685
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
686
-	}
687
-
688
-	/** {@inheritdoc} */
689
-	public function isSharable($path) {
690
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
691
-	}
692
-
693
-	/** {@inheritdoc} */
694
-	public function isDeletable($path) {
695
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
696
-	}
697
-
698
-	/** {@inheritdoc} */
699
-	public function getPermissions($path) {
700
-		$this->init();
701
-		$path = $this->cleanPath($path);
702
-		$response = $this->propfind($path);
703
-		if ($response === false) {
704
-			return 0;
705
-		}
706
-		if (isset($response['{http://owncloud.org/ns}permissions'])) {
707
-			return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
708
-		} else if ($this->is_dir($path)) {
709
-			return Constants::PERMISSION_ALL;
710
-		} else if ($this->file_exists($path)) {
711
-			return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
712
-		} else {
713
-			return 0;
714
-		}
715
-	}
716
-
717
-	/** {@inheritdoc} */
718
-	public function getETag($path) {
719
-		$this->init();
720
-		$path = $this->cleanPath($path);
721
-		$response = $this->propfind($path);
722
-		if ($response === false) {
723
-			return null;
724
-		}
725
-		if (isset($response['{DAV:}getetag'])) {
726
-			return trim($response['{DAV:}getetag'], '"');
727
-		}
728
-		return parent::getEtag($path);
729
-	}
730
-
731
-	/**
732
-	 * @param string $permissionsString
733
-	 * @return int
734
-	 */
735
-	protected function parsePermissions($permissionsString) {
736
-		$permissions = Constants::PERMISSION_READ;
737
-		if (strpos($permissionsString, 'R') !== false) {
738
-			$permissions |= Constants::PERMISSION_SHARE;
739
-		}
740
-		if (strpos($permissionsString, 'D') !== false) {
741
-			$permissions |= Constants::PERMISSION_DELETE;
742
-		}
743
-		if (strpos($permissionsString, 'W') !== false) {
744
-			$permissions |= Constants::PERMISSION_UPDATE;
745
-		}
746
-		if (strpos($permissionsString, 'CK') !== false) {
747
-			$permissions |= Constants::PERMISSION_CREATE;
748
-			$permissions |= Constants::PERMISSION_UPDATE;
749
-		}
750
-		return $permissions;
751
-	}
752
-
753
-	/**
754
-	 * check if a file or folder has been updated since $time
755
-	 *
756
-	 * @param string $path
757
-	 * @param int $time
758
-	 * @throws \OCP\Files\StorageNotAvailableException
759
-	 * @return bool
760
-	 */
761
-	public function hasUpdated($path, $time) {
762
-		$this->init();
763
-		$path = $this->cleanPath($path);
764
-		try {
765
-			// force refresh for $path
766
-			$this->statCache->remove($path);
767
-			$response = $this->propfind($path);
768
-			if ($response === false) {
769
-				if ($path === '') {
770
-					// if root is gone it means the storage is not available
771
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
772
-				}
773
-				return false;
774
-			}
775
-			if (isset($response['{DAV:}getetag'])) {
776
-				$cachedData = $this->getCache()->get($path);
777
-				$etag = null;
778
-				if (isset($response['{DAV:}getetag'])) {
779
-					$etag = trim($response['{DAV:}getetag'], '"');
780
-				}
781
-				if (!empty($etag) && $cachedData['etag'] !== $etag) {
782
-					return true;
783
-				} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
784
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
785
-					return $sharePermissions !== $cachedData['permissions'];
786
-				} else if (isset($response['{http://owncloud.org/ns}permissions'])) {
787
-					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
788
-					return $permissions !== $cachedData['permissions'];
789
-				} else {
790
-					return false;
791
-				}
792
-			} else {
793
-				$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
794
-				return $remoteMtime > $time;
795
-			}
796
-		} catch (ClientHttpException $e) {
797
-			if ($e->getHttpStatus() === 405) {
798
-				if ($path === '') {
799
-					// if root is gone it means the storage is not available
800
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
801
-				}
802
-				return false;
803
-			}
804
-			$this->convertException($e, $path);
805
-			return false;
806
-		} catch (\Exception $e) {
807
-			$this->convertException($e, $path);
808
-			return false;
809
-		}
810
-	}
811
-
812
-	/**
813
-	 * Interpret the given exception and decide whether it is due to an
814
-	 * unavailable storage, invalid storage or other.
815
-	 * This will either throw StorageInvalidException, StorageNotAvailableException
816
-	 * or do nothing.
817
-	 *
818
-	 * @param Exception $e sabre exception
819
-	 * @param string $path optional path from the operation
820
-	 *
821
-	 * @throws StorageInvalidException if the storage is invalid, for example
822
-	 * when the authentication expired or is invalid
823
-	 * @throws StorageNotAvailableException if the storage is not available,
824
-	 * which might be temporary
825
-	 */
826
-	protected function convertException(Exception $e, $path = '') {
827
-		\OC::$server->getLogger()->logException($e);
828
-		Util::writeLog('files_external', $e->getMessage(), Util::ERROR);
829
-		if ($e instanceof ClientHttpException) {
830
-			if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
831
-				throw new \OCP\Lock\LockedException($path);
832
-			}
833
-			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
834
-				// either password was changed or was invalid all along
835
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
836
-			} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
837
-				// ignore exception for MethodNotAllowed, false will be returned
838
-				return;
839
-			}
840
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
841
-		} else if ($e instanceof ClientException) {
842
-			// connection timeout or refused, server could be temporarily down
843
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
844
-		} else if ($e instanceof \InvalidArgumentException) {
845
-			// parse error because the server returned HTML instead of XML,
846
-			// possibly temporarily down
847
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
848
-		} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
849
-			// rethrow
850
-			throw $e;
851
-		}
852
-
853
-		// TODO: only log for now, but in the future need to wrap/rethrow exception
854
-	}
62
+    /** @var string */
63
+    protected $password;
64
+    /** @var string */
65
+    protected $user;
66
+    /** @var string */
67
+    protected $authType;
68
+    /** @var string */
69
+    protected $host;
70
+    /** @var bool */
71
+    protected $secure;
72
+    /** @var string */
73
+    protected $root;
74
+    /** @var string */
75
+    protected $certPath;
76
+    /** @var bool */
77
+    protected $ready;
78
+    /** @var Client */
79
+    protected $client;
80
+    /** @var ArrayCache */
81
+    protected $statCache;
82
+    /** @var \OCP\Http\Client\IClientService */
83
+    protected $httpClientService;
84
+
85
+    /**
86
+     * @param array $params
87
+     * @throws \Exception
88
+     */
89
+    public function __construct($params) {
90
+        $this->statCache = new ArrayCache();
91
+        $this->httpClientService = \OC::$server->getHTTPClientService();
92
+        if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
93
+            $host = $params['host'];
94
+            //remove leading http[s], will be generated in createBaseUri()
95
+            if (substr($host, 0, 8) == "https://") $host = substr($host, 8);
96
+            else if (substr($host, 0, 7) == "http://") $host = substr($host, 7);
97
+            $this->host = $host;
98
+            $this->user = $params['user'];
99
+            $this->password = $params['password'];
100
+            if (isset($params['authType'])) {
101
+                $this->authType = $params['authType'];
102
+            }
103
+            if (isset($params['secure'])) {
104
+                if (is_string($params['secure'])) {
105
+                    $this->secure = ($params['secure'] === 'true');
106
+                } else {
107
+                    $this->secure = (bool)$params['secure'];
108
+                }
109
+            } else {
110
+                $this->secure = false;
111
+            }
112
+            if ($this->secure === true) {
113
+                // inject mock for testing
114
+                $certManager = \OC::$server->getCertificateManager();
115
+                if (is_null($certManager)) { //no user
116
+                    $certManager = \OC::$server->getCertificateManager(null);
117
+                }
118
+                $certPath = $certManager->getAbsoluteBundlePath();
119
+                if (file_exists($certPath)) {
120
+                    $this->certPath = $certPath;
121
+                }
122
+            }
123
+            $this->root = isset($params['root']) ? $params['root'] : '/';
124
+            if (!$this->root || $this->root[0] != '/') {
125
+                $this->root = '/' . $this->root;
126
+            }
127
+            if (substr($this->root, -1, 1) != '/') {
128
+                $this->root .= '/';
129
+            }
130
+        } else {
131
+            throw new \Exception('Invalid webdav storage configuration');
132
+        }
133
+    }
134
+
135
+    protected function init() {
136
+        if ($this->ready) {
137
+            return;
138
+        }
139
+        $this->ready = true;
140
+
141
+        $settings = [
142
+            'baseUri' => $this->createBaseUri(),
143
+            'userName' => $this->user,
144
+            'password' => $this->password,
145
+        ];
146
+        if (isset($this->authType)) {
147
+            $settings['authType'] = $this->authType;
148
+        }
149
+
150
+        $proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
151
+        if($proxy !== '') {
152
+            $settings['proxy'] = $proxy;
153
+        }
154
+
155
+        $this->client = new Client($settings);
156
+        $this->client->setThrowExceptions(true);
157
+        if ($this->secure === true && $this->certPath) {
158
+            $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
159
+        }
160
+    }
161
+
162
+    /**
163
+     * Clear the stat cache
164
+     */
165
+    public function clearStatCache() {
166
+        $this->statCache->clear();
167
+    }
168
+
169
+    /** {@inheritdoc} */
170
+    public function getId() {
171
+        return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
172
+    }
173
+
174
+    /** {@inheritdoc} */
175
+    public function createBaseUri() {
176
+        $baseUri = 'http';
177
+        if ($this->secure) {
178
+            $baseUri .= 's';
179
+        }
180
+        $baseUri .= '://' . $this->host . $this->root;
181
+        return $baseUri;
182
+    }
183
+
184
+    /** {@inheritdoc} */
185
+    public function mkdir($path) {
186
+        $this->init();
187
+        $path = $this->cleanPath($path);
188
+        $result = $this->simpleResponse('MKCOL', $path, null, 201);
189
+        if ($result) {
190
+            $this->statCache->set($path, true);
191
+        }
192
+        return $result;
193
+    }
194
+
195
+    /** {@inheritdoc} */
196
+    public function rmdir($path) {
197
+        $this->init();
198
+        $path = $this->cleanPath($path);
199
+        // FIXME: some WebDAV impl return 403 when trying to DELETE
200
+        // a non-empty folder
201
+        $result = $this->simpleResponse('DELETE', $path . '/', null, 204);
202
+        $this->statCache->clear($path . '/');
203
+        $this->statCache->remove($path);
204
+        return $result;
205
+    }
206
+
207
+    /** {@inheritdoc} */
208
+    public function opendir($path) {
209
+        $this->init();
210
+        $path = $this->cleanPath($path);
211
+        try {
212
+            $response = $this->client->propFind(
213
+                $this->encodePath($path),
214
+                ['{DAV:}href'],
215
+                1
216
+            );
217
+            if ($response === false) {
218
+                return false;
219
+            }
220
+            $content = [];
221
+            $files = array_keys($response);
222
+            array_shift($files); //the first entry is the current directory
223
+
224
+            if (!$this->statCache->hasKey($path)) {
225
+                $this->statCache->set($path, true);
226
+            }
227
+            foreach ($files as $file) {
228
+                $file = urldecode($file);
229
+                // do not store the real entry, we might not have all properties
230
+                if (!$this->statCache->hasKey($path)) {
231
+                    $this->statCache->set($file, true);
232
+                }
233
+                $file = basename($file);
234
+                $content[] = $file;
235
+            }
236
+            return IteratorDirectory::wrap($content);
237
+        } catch (\Exception $e) {
238
+            $this->convertException($e, $path);
239
+        }
240
+        return false;
241
+    }
242
+
243
+    /**
244
+     * Propfind call with cache handling.
245
+     *
246
+     * First checks if information is cached.
247
+     * If not, request it from the server then store to cache.
248
+     *
249
+     * @param string $path path to propfind
250
+     *
251
+     * @return array|boolean propfind response or false if the entry was not found
252
+     *
253
+     * @throws ClientHttpException
254
+     */
255
+    protected function propfind($path) {
256
+        $path = $this->cleanPath($path);
257
+        $cachedResponse = $this->statCache->get($path);
258
+        // we either don't know it, or we know it exists but need more details
259
+        if (is_null($cachedResponse) || $cachedResponse === true) {
260
+            $this->init();
261
+            try {
262
+                $response = $this->client->propFind(
263
+                    $this->encodePath($path),
264
+                    array(
265
+                        '{DAV:}getlastmodified',
266
+                        '{DAV:}getcontentlength',
267
+                        '{DAV:}getcontenttype',
268
+                        '{http://owncloud.org/ns}permissions',
269
+                        '{http://open-collaboration-services.org/ns}share-permissions',
270
+                        '{DAV:}resourcetype',
271
+                        '{DAV:}getetag',
272
+                    )
273
+                );
274
+                $this->statCache->set($path, $response);
275
+            } catch (ClientHttpException $e) {
276
+                if ($e->getHttpStatus() === 404) {
277
+                    $this->statCache->clear($path . '/');
278
+                    $this->statCache->set($path, false);
279
+                    return false;
280
+                }
281
+                $this->convertException($e, $path);
282
+            } catch (\Exception $e) {
283
+                $this->convertException($e, $path);
284
+            }
285
+        } else {
286
+            $response = $cachedResponse;
287
+        }
288
+        return $response;
289
+    }
290
+
291
+    /** {@inheritdoc} */
292
+    public function filetype($path) {
293
+        try {
294
+            $response = $this->propfind($path);
295
+            if ($response === false) {
296
+                return false;
297
+            }
298
+            $responseType = [];
299
+            if (isset($response["{DAV:}resourcetype"])) {
300
+                /** @var ResourceType[] $response */
301
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
302
+            }
303
+            return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
304
+        } catch (\Exception $e) {
305
+            $this->convertException($e, $path);
306
+        }
307
+        return false;
308
+    }
309
+
310
+    /** {@inheritdoc} */
311
+    public function file_exists($path) {
312
+        try {
313
+            $path = $this->cleanPath($path);
314
+            $cachedState = $this->statCache->get($path);
315
+            if ($cachedState === false) {
316
+                // we know the file doesn't exist
317
+                return false;
318
+            } else if (!is_null($cachedState)) {
319
+                return true;
320
+            }
321
+            // need to get from server
322
+            return ($this->propfind($path) !== false);
323
+        } catch (\Exception $e) {
324
+            $this->convertException($e, $path);
325
+        }
326
+        return false;
327
+    }
328
+
329
+    /** {@inheritdoc} */
330
+    public function unlink($path) {
331
+        $this->init();
332
+        $path = $this->cleanPath($path);
333
+        $result = $this->simpleResponse('DELETE', $path, null, 204);
334
+        $this->statCache->clear($path . '/');
335
+        $this->statCache->remove($path);
336
+        return $result;
337
+    }
338
+
339
+    /** {@inheritdoc} */
340
+    public function fopen($path, $mode) {
341
+        $this->init();
342
+        $path = $this->cleanPath($path);
343
+        switch ($mode) {
344
+            case 'r':
345
+            case 'rb':
346
+                try {
347
+                    $response = $this->httpClientService
348
+                            ->newClient()
349
+                            ->get($this->createBaseUri() . $this->encodePath($path), [
350
+                                    'auth' => [$this->user, $this->password],
351
+                                    'stream' => true
352
+                            ]);
353
+                } catch (RequestException $e) {
354
+                    if ($e->getResponse() instanceof ResponseInterface
355
+                        && $e->getResponse()->getStatusCode() === 404) {
356
+                        return false;
357
+                    } else {
358
+                        throw $e;
359
+                    }
360
+                }
361
+
362
+                if ($response->getStatusCode() !== Http::STATUS_OK) {
363
+                    if ($response->getStatusCode() === Http::STATUS_LOCKED) {
364
+                        throw new \OCP\Lock\LockedException($path);
365
+                    } else {
366
+                        Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
367
+                    }
368
+                }
369
+
370
+                return $response->getBody();
371
+            case 'w':
372
+            case 'wb':
373
+            case 'a':
374
+            case 'ab':
375
+            case 'r+':
376
+            case 'w+':
377
+            case 'wb+':
378
+            case 'a+':
379
+            case 'x':
380
+            case 'x+':
381
+            case 'c':
382
+            case 'c+':
383
+                //emulate these
384
+                $tempManager = \OC::$server->getTempManager();
385
+                if (strrpos($path, '.') !== false) {
386
+                    $ext = substr($path, strrpos($path, '.'));
387
+                } else {
388
+                    $ext = '';
389
+                }
390
+                if ($this->file_exists($path)) {
391
+                    if (!$this->isUpdatable($path)) {
392
+                        return false;
393
+                    }
394
+                    if ($mode === 'w' or $mode === 'w+') {
395
+                        $tmpFile = $tempManager->getTemporaryFile($ext);
396
+                    } else {
397
+                        $tmpFile = $this->getCachedFile($path);
398
+                    }
399
+                } else {
400
+                    if (!$this->isCreatable(dirname($path))) {
401
+                        return false;
402
+                    }
403
+                    $tmpFile = $tempManager->getTemporaryFile($ext);
404
+                }
405
+                $handle = fopen($tmpFile, $mode);
406
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
407
+                    $this->writeBack($tmpFile, $path);
408
+                });
409
+        }
410
+    }
411
+
412
+    /**
413
+     * @param string $tmpFile
414
+     */
415
+    public function writeBack($tmpFile, $path) {
416
+        $this->uploadFile($tmpFile, $path);
417
+        unlink($tmpFile);
418
+    }
419
+
420
+    /** {@inheritdoc} */
421
+    public function free_space($path) {
422
+        $this->init();
423
+        $path = $this->cleanPath($path);
424
+        try {
425
+            // TODO: cacheable ?
426
+            $response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
427
+            if ($response === false) {
428
+                return FileInfo::SPACE_UNKNOWN;
429
+            }
430
+            if (isset($response['{DAV:}quota-available-bytes'])) {
431
+                return (int)$response['{DAV:}quota-available-bytes'];
432
+            } else {
433
+                return FileInfo::SPACE_UNKNOWN;
434
+            }
435
+        } catch (\Exception $e) {
436
+            return FileInfo::SPACE_UNKNOWN;
437
+        }
438
+    }
439
+
440
+    /** {@inheritdoc} */
441
+    public function touch($path, $mtime = null) {
442
+        $this->init();
443
+        if (is_null($mtime)) {
444
+            $mtime = time();
445
+        }
446
+        $path = $this->cleanPath($path);
447
+
448
+        // if file exists, update the mtime, else create a new empty file
449
+        if ($this->file_exists($path)) {
450
+            try {
451
+                $this->statCache->remove($path);
452
+                $this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
453
+                // non-owncloud clients might not have accepted the property, need to recheck it
454
+                $response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
455
+                if ($response === false) {
456
+                    return false;
457
+                }
458
+                if (isset($response['{DAV:}getlastmodified'])) {
459
+                    $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
460
+                    if ($remoteMtime !== $mtime) {
461
+                        // server has not accepted the mtime
462
+                        return false;
463
+                    }
464
+                }
465
+            } catch (ClientHttpException $e) {
466
+                if ($e->getHttpStatus() === 501) {
467
+                    return false;
468
+                }
469
+                $this->convertException($e, $path);
470
+                return false;
471
+            } catch (\Exception $e) {
472
+                $this->convertException($e, $path);
473
+                return false;
474
+            }
475
+        } else {
476
+            $this->file_put_contents($path, '');
477
+        }
478
+        return true;
479
+    }
480
+
481
+    /**
482
+     * @param string $path
483
+     * @param string $data
484
+     * @return int
485
+     */
486
+    public function file_put_contents($path, $data) {
487
+        $path = $this->cleanPath($path);
488
+        $result = parent::file_put_contents($path, $data);
489
+        $this->statCache->remove($path);
490
+        return $result;
491
+    }
492
+
493
+    /**
494
+     * @param string $path
495
+     * @param string $target
496
+     */
497
+    protected function uploadFile($path, $target) {
498
+        $this->init();
499
+
500
+        // invalidate
501
+        $target = $this->cleanPath($target);
502
+        $this->statCache->remove($target);
503
+        $source = fopen($path, 'r');
504
+
505
+        $this->httpClientService
506
+            ->newClient()
507
+            ->put($this->createBaseUri() . $this->encodePath($target), [
508
+                'body' => $source,
509
+                'auth' => [$this->user, $this->password]
510
+            ]);
511
+
512
+        $this->removeCachedFile($target);
513
+    }
514
+
515
+    /** {@inheritdoc} */
516
+    public function rename($path1, $path2) {
517
+        $this->init();
518
+        $path1 = $this->cleanPath($path1);
519
+        $path2 = $this->cleanPath($path2);
520
+        try {
521
+            // overwrite directory ?
522
+            if ($this->is_dir($path2)) {
523
+                // needs trailing slash in destination
524
+                $path2 = rtrim($path2, '/') . '/';
525
+            }
526
+            $this->client->request(
527
+                'MOVE',
528
+                $this->encodePath($path1),
529
+                null,
530
+                [
531
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
532
+                ]
533
+            );
534
+            $this->statCache->clear($path1 . '/');
535
+            $this->statCache->clear($path2 . '/');
536
+            $this->statCache->set($path1, false);
537
+            $this->statCache->set($path2, true);
538
+            $this->removeCachedFile($path1);
539
+            $this->removeCachedFile($path2);
540
+            return true;
541
+        } catch (\Exception $e) {
542
+            $this->convertException($e);
543
+        }
544
+        return false;
545
+    }
546
+
547
+    /** {@inheritdoc} */
548
+    public function copy($path1, $path2) {
549
+        $this->init();
550
+        $path1 = $this->cleanPath($path1);
551
+        $path2 = $this->cleanPath($path2);
552
+        try {
553
+            // overwrite directory ?
554
+            if ($this->is_dir($path2)) {
555
+                // needs trailing slash in destination
556
+                $path2 = rtrim($path2, '/') . '/';
557
+            }
558
+            $this->client->request(
559
+                'COPY',
560
+                $this->encodePath($path1),
561
+                null,
562
+                [
563
+                    'Destination' => $this->createBaseUri() . $this->encodePath($path2),
564
+                ]
565
+            );
566
+            $this->statCache->clear($path2 . '/');
567
+            $this->statCache->set($path2, true);
568
+            $this->removeCachedFile($path2);
569
+            return true;
570
+        } catch (\Exception $e) {
571
+            $this->convertException($e);
572
+        }
573
+        return false;
574
+    }
575
+
576
+    /** {@inheritdoc} */
577
+    public function stat($path) {
578
+        try {
579
+            $response = $this->propfind($path);
580
+            if (!$response) {
581
+                return false;
582
+            }
583
+            return [
584
+                'mtime' => strtotime($response['{DAV:}getlastmodified']),
585
+                'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
586
+            ];
587
+        } catch (\Exception $e) {
588
+            $this->convertException($e, $path);
589
+        }
590
+        return array();
591
+    }
592
+
593
+    /** {@inheritdoc} */
594
+    public function getMimeType($path) {
595
+        try {
596
+            $response = $this->propfind($path);
597
+            if ($response === false) {
598
+                return false;
599
+            }
600
+            $responseType = [];
601
+            if (isset($response["{DAV:}resourcetype"])) {
602
+                /** @var ResourceType[] $response */
603
+                $responseType = $response["{DAV:}resourcetype"]->getValue();
604
+            }
605
+            $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
606
+            if ($type == 'dir') {
607
+                return 'httpd/unix-directory';
608
+            } elseif (isset($response['{DAV:}getcontenttype'])) {
609
+                return $response['{DAV:}getcontenttype'];
610
+            } else {
611
+                return false;
612
+            }
613
+        } catch (\Exception $e) {
614
+            $this->convertException($e, $path);
615
+        }
616
+        return false;
617
+    }
618
+
619
+    /**
620
+     * @param string $path
621
+     * @return string
622
+     */
623
+    public function cleanPath($path) {
624
+        if ($path === '') {
625
+            return $path;
626
+        }
627
+        $path = Filesystem::normalizePath($path);
628
+        // remove leading slash
629
+        return substr($path, 1);
630
+    }
631
+
632
+    /**
633
+     * URL encodes the given path but keeps the slashes
634
+     *
635
+     * @param string $path to encode
636
+     * @return string encoded path
637
+     */
638
+    protected function encodePath($path) {
639
+        // slashes need to stay
640
+        return str_replace('%2F', '/', rawurlencode($path));
641
+    }
642
+
643
+    /**
644
+     * @param string $method
645
+     * @param string $path
646
+     * @param string|resource|null $body
647
+     * @param int $expected
648
+     * @return bool
649
+     * @throws StorageInvalidException
650
+     * @throws StorageNotAvailableException
651
+     */
652
+    protected function simpleResponse($method, $path, $body, $expected) {
653
+        $path = $this->cleanPath($path);
654
+        try {
655
+            $response = $this->client->request($method, $this->encodePath($path), $body);
656
+            return $response['statusCode'] == $expected;
657
+        } catch (ClientHttpException $e) {
658
+            if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
659
+                $this->statCache->clear($path . '/');
660
+                $this->statCache->set($path, false);
661
+                return false;
662
+            }
663
+
664
+            $this->convertException($e, $path);
665
+        } catch (\Exception $e) {
666
+            $this->convertException($e, $path);
667
+        }
668
+        return false;
669
+    }
670
+
671
+    /**
672
+     * check if curl is installed
673
+     */
674
+    public static function checkDependencies() {
675
+        return true;
676
+    }
677
+
678
+    /** {@inheritdoc} */
679
+    public function isUpdatable($path) {
680
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
681
+    }
682
+
683
+    /** {@inheritdoc} */
684
+    public function isCreatable($path) {
685
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
686
+    }
687
+
688
+    /** {@inheritdoc} */
689
+    public function isSharable($path) {
690
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
691
+    }
692
+
693
+    /** {@inheritdoc} */
694
+    public function isDeletable($path) {
695
+        return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
696
+    }
697
+
698
+    /** {@inheritdoc} */
699
+    public function getPermissions($path) {
700
+        $this->init();
701
+        $path = $this->cleanPath($path);
702
+        $response = $this->propfind($path);
703
+        if ($response === false) {
704
+            return 0;
705
+        }
706
+        if (isset($response['{http://owncloud.org/ns}permissions'])) {
707
+            return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
708
+        } else if ($this->is_dir($path)) {
709
+            return Constants::PERMISSION_ALL;
710
+        } else if ($this->file_exists($path)) {
711
+            return Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
712
+        } else {
713
+            return 0;
714
+        }
715
+    }
716
+
717
+    /** {@inheritdoc} */
718
+    public function getETag($path) {
719
+        $this->init();
720
+        $path = $this->cleanPath($path);
721
+        $response = $this->propfind($path);
722
+        if ($response === false) {
723
+            return null;
724
+        }
725
+        if (isset($response['{DAV:}getetag'])) {
726
+            return trim($response['{DAV:}getetag'], '"');
727
+        }
728
+        return parent::getEtag($path);
729
+    }
730
+
731
+    /**
732
+     * @param string $permissionsString
733
+     * @return int
734
+     */
735
+    protected function parsePermissions($permissionsString) {
736
+        $permissions = Constants::PERMISSION_READ;
737
+        if (strpos($permissionsString, 'R') !== false) {
738
+            $permissions |= Constants::PERMISSION_SHARE;
739
+        }
740
+        if (strpos($permissionsString, 'D') !== false) {
741
+            $permissions |= Constants::PERMISSION_DELETE;
742
+        }
743
+        if (strpos($permissionsString, 'W') !== false) {
744
+            $permissions |= Constants::PERMISSION_UPDATE;
745
+        }
746
+        if (strpos($permissionsString, 'CK') !== false) {
747
+            $permissions |= Constants::PERMISSION_CREATE;
748
+            $permissions |= Constants::PERMISSION_UPDATE;
749
+        }
750
+        return $permissions;
751
+    }
752
+
753
+    /**
754
+     * check if a file or folder has been updated since $time
755
+     *
756
+     * @param string $path
757
+     * @param int $time
758
+     * @throws \OCP\Files\StorageNotAvailableException
759
+     * @return bool
760
+     */
761
+    public function hasUpdated($path, $time) {
762
+        $this->init();
763
+        $path = $this->cleanPath($path);
764
+        try {
765
+            // force refresh for $path
766
+            $this->statCache->remove($path);
767
+            $response = $this->propfind($path);
768
+            if ($response === false) {
769
+                if ($path === '') {
770
+                    // if root is gone it means the storage is not available
771
+                    throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
772
+                }
773
+                return false;
774
+            }
775
+            if (isset($response['{DAV:}getetag'])) {
776
+                $cachedData = $this->getCache()->get($path);
777
+                $etag = null;
778
+                if (isset($response['{DAV:}getetag'])) {
779
+                    $etag = trim($response['{DAV:}getetag'], '"');
780
+                }
781
+                if (!empty($etag) && $cachedData['etag'] !== $etag) {
782
+                    return true;
783
+                } else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
784
+                    $sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
785
+                    return $sharePermissions !== $cachedData['permissions'];
786
+                } else if (isset($response['{http://owncloud.org/ns}permissions'])) {
787
+                    $permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
788
+                    return $permissions !== $cachedData['permissions'];
789
+                } else {
790
+                    return false;
791
+                }
792
+            } else {
793
+                $remoteMtime = strtotime($response['{DAV:}getlastmodified']);
794
+                return $remoteMtime > $time;
795
+            }
796
+        } catch (ClientHttpException $e) {
797
+            if ($e->getHttpStatus() === 405) {
798
+                if ($path === '') {
799
+                    // if root is gone it means the storage is not available
800
+                    throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
801
+                }
802
+                return false;
803
+            }
804
+            $this->convertException($e, $path);
805
+            return false;
806
+        } catch (\Exception $e) {
807
+            $this->convertException($e, $path);
808
+            return false;
809
+        }
810
+    }
811
+
812
+    /**
813
+     * Interpret the given exception and decide whether it is due to an
814
+     * unavailable storage, invalid storage or other.
815
+     * This will either throw StorageInvalidException, StorageNotAvailableException
816
+     * or do nothing.
817
+     *
818
+     * @param Exception $e sabre exception
819
+     * @param string $path optional path from the operation
820
+     *
821
+     * @throws StorageInvalidException if the storage is invalid, for example
822
+     * when the authentication expired or is invalid
823
+     * @throws StorageNotAvailableException if the storage is not available,
824
+     * which might be temporary
825
+     */
826
+    protected function convertException(Exception $e, $path = '') {
827
+        \OC::$server->getLogger()->logException($e);
828
+        Util::writeLog('files_external', $e->getMessage(), Util::ERROR);
829
+        if ($e instanceof ClientHttpException) {
830
+            if ($e->getHttpStatus() === Http::STATUS_LOCKED) {
831
+                throw new \OCP\Lock\LockedException($path);
832
+            }
833
+            if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
834
+                // either password was changed or was invalid all along
835
+                throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
836
+            } else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
837
+                // ignore exception for MethodNotAllowed, false will be returned
838
+                return;
839
+            }
840
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
841
+        } else if ($e instanceof ClientException) {
842
+            // connection timeout or refused, server could be temporarily down
843
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
844
+        } else if ($e instanceof \InvalidArgumentException) {
845
+            // parse error because the server returned HTML instead of XML,
846
+            // possibly temporarily down
847
+            throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
848
+        } else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
849
+            // rethrow
850
+            throw $e;
851
+        }
852
+
853
+        // TODO: only log for now, but in the future need to wrap/rethrow exception
854
+    }
855 855
 }
856 856
 
Please login to merge, or discard this patch.
Spacing   +34 added lines, -34 removed lines patch added patch discarded remove patch
@@ -104,7 +104,7 @@  discard block
 block discarded – undo
104 104
 				if (is_string($params['secure'])) {
105 105
 					$this->secure = ($params['secure'] === 'true');
106 106
 				} else {
107
-					$this->secure = (bool)$params['secure'];
107
+					$this->secure = (bool) $params['secure'];
108 108
 				}
109 109
 			} else {
110 110
 				$this->secure = false;
@@ -122,7 +122,7 @@  discard block
 block discarded – undo
122 122
 			}
123 123
 			$this->root = isset($params['root']) ? $params['root'] : '/';
124 124
 			if (!$this->root || $this->root[0] != '/') {
125
-				$this->root = '/' . $this->root;
125
+				$this->root = '/'.$this->root;
126 126
 			}
127 127
 			if (substr($this->root, -1, 1) != '/') {
128 128
 				$this->root .= '/';
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
 		}
149 149
 
150 150
 		$proxy = \OC::$server->getConfig()->getSystemValue('proxy', '');
151
-		if($proxy !== '') {
151
+		if ($proxy !== '') {
152 152
 			$settings['proxy'] = $proxy;
153 153
 		}
154 154
 
@@ -168,7 +168,7 @@  discard block
 block discarded – undo
168 168
 
169 169
 	/** {@inheritdoc} */
170 170
 	public function getId() {
171
-		return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
171
+		return 'webdav::'.$this->user.'@'.$this->host.'/'.$this->root;
172 172
 	}
173 173
 
174 174
 	/** {@inheritdoc} */
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
 		if ($this->secure) {
178 178
 			$baseUri .= 's';
179 179
 		}
180
-		$baseUri .= '://' . $this->host . $this->root;
180
+		$baseUri .= '://'.$this->host.$this->root;
181 181
 		return $baseUri;
182 182
 	}
183 183
 
@@ -198,8 +198,8 @@  discard block
 block discarded – undo
198 198
 		$path = $this->cleanPath($path);
199 199
 		// FIXME: some WebDAV impl return 403 when trying to DELETE
200 200
 		// a non-empty folder
201
-		$result = $this->simpleResponse('DELETE', $path . '/', null, 204);
202
-		$this->statCache->clear($path . '/');
201
+		$result = $this->simpleResponse('DELETE', $path.'/', null, 204);
202
+		$this->statCache->clear($path.'/');
203 203
 		$this->statCache->remove($path);
204 204
 		return $result;
205 205
 	}
@@ -274,7 +274,7 @@  discard block
 block discarded – undo
274 274
 				$this->statCache->set($path, $response);
275 275
 			} catch (ClientHttpException $e) {
276 276
 				if ($e->getHttpStatus() === 404) {
277
-					$this->statCache->clear($path . '/');
277
+					$this->statCache->clear($path.'/');
278 278
 					$this->statCache->set($path, false);
279 279
 					return false;
280 280
 				}
@@ -331,7 +331,7 @@  discard block
 block discarded – undo
331 331
 		$this->init();
332 332
 		$path = $this->cleanPath($path);
333 333
 		$result = $this->simpleResponse('DELETE', $path, null, 204);
334
-		$this->statCache->clear($path . '/');
334
+		$this->statCache->clear($path.'/');
335 335
 		$this->statCache->remove($path);
336 336
 		return $result;
337 337
 	}
@@ -346,7 +346,7 @@  discard block
 block discarded – undo
346 346
 				try {
347 347
 					$response = $this->httpClientService
348 348
 							->newClient()
349
-							->get($this->createBaseUri() . $this->encodePath($path), [
349
+							->get($this->createBaseUri().$this->encodePath($path), [
350 350
 									'auth' => [$this->user, $this->password],
351 351
 									'stream' => true
352 352
 							]);
@@ -363,7 +363,7 @@  discard block
 block discarded – undo
363 363
 					if ($response->getStatusCode() === Http::STATUS_LOCKED) {
364 364
 						throw new \OCP\Lock\LockedException($path);
365 365
 					} else {
366
-						Util::writeLog("webdav client", 'Guzzle get returned status code ' . $response->getStatusCode(), Util::ERROR);
366
+						Util::writeLog("webdav client", 'Guzzle get returned status code '.$response->getStatusCode(), Util::ERROR);
367 367
 					}
368 368
 				}
369 369
 
@@ -403,7 +403,7 @@  discard block
 block discarded – undo
403 403
 					$tmpFile = $tempManager->getTemporaryFile($ext);
404 404
 				}
405 405
 				$handle = fopen($tmpFile, $mode);
406
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
406
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
407 407
 					$this->writeBack($tmpFile, $path);
408 408
 				});
409 409
 		}
@@ -428,7 +428,7 @@  discard block
 block discarded – undo
428 428
 				return FileInfo::SPACE_UNKNOWN;
429 429
 			}
430 430
 			if (isset($response['{DAV:}quota-available-bytes'])) {
431
-				return (int)$response['{DAV:}quota-available-bytes'];
431
+				return (int) $response['{DAV:}quota-available-bytes'];
432 432
 			} else {
433 433
 				return FileInfo::SPACE_UNKNOWN;
434 434
 			}
@@ -504,7 +504,7 @@  discard block
 block discarded – undo
504 504
 
505 505
 		$this->httpClientService
506 506
 			->newClient()
507
-			->put($this->createBaseUri() . $this->encodePath($target), [
507
+			->put($this->createBaseUri().$this->encodePath($target), [
508 508
 				'body' => $source,
509 509
 				'auth' => [$this->user, $this->password]
510 510
 			]);
@@ -521,18 +521,18 @@  discard block
 block discarded – undo
521 521
 			// overwrite directory ?
522 522
 			if ($this->is_dir($path2)) {
523 523
 				// needs trailing slash in destination
524
-				$path2 = rtrim($path2, '/') . '/';
524
+				$path2 = rtrim($path2, '/').'/';
525 525
 			}
526 526
 			$this->client->request(
527 527
 				'MOVE',
528 528
 				$this->encodePath($path1),
529 529
 				null,
530 530
 				[
531
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
531
+					'Destination' => $this->createBaseUri().$this->encodePath($path2),
532 532
 				]
533 533
 			);
534
-			$this->statCache->clear($path1 . '/');
535
-			$this->statCache->clear($path2 . '/');
534
+			$this->statCache->clear($path1.'/');
535
+			$this->statCache->clear($path2.'/');
536 536
 			$this->statCache->set($path1, false);
537 537
 			$this->statCache->set($path2, true);
538 538
 			$this->removeCachedFile($path1);
@@ -553,17 +553,17 @@  discard block
 block discarded – undo
553 553
 			// overwrite directory ?
554 554
 			if ($this->is_dir($path2)) {
555 555
 				// needs trailing slash in destination
556
-				$path2 = rtrim($path2, '/') . '/';
556
+				$path2 = rtrim($path2, '/').'/';
557 557
 			}
558 558
 			$this->client->request(
559 559
 				'COPY',
560 560
 				$this->encodePath($path1),
561 561
 				null,
562 562
 				[
563
-					'Destination' => $this->createBaseUri() . $this->encodePath($path2),
563
+					'Destination' => $this->createBaseUri().$this->encodePath($path2),
564 564
 				]
565 565
 			);
566
-			$this->statCache->clear($path2 . '/');
566
+			$this->statCache->clear($path2.'/');
567 567
 			$this->statCache->set($path2, true);
568 568
 			$this->removeCachedFile($path2);
569 569
 			return true;
@@ -582,7 +582,7 @@  discard block
 block discarded – undo
582 582
 			}
583 583
 			return [
584 584
 				'mtime' => strtotime($response['{DAV:}getlastmodified']),
585
-				'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
585
+				'size' => (int) isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
586 586
 			];
587 587
 		} catch (\Exception $e) {
588 588
 			$this->convertException($e, $path);
@@ -656,7 +656,7 @@  discard block
 block discarded – undo
656 656
 			return $response['statusCode'] == $expected;
657 657
 		} catch (ClientHttpException $e) {
658 658
 			if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
659
-				$this->statCache->clear($path . '/');
659
+				$this->statCache->clear($path.'/');
660 660
 				$this->statCache->set($path, false);
661 661
 				return false;
662 662
 			}
@@ -677,22 +677,22 @@  discard block
 block discarded – undo
677 677
 
678 678
 	/** {@inheritdoc} */
679 679
 	public function isUpdatable($path) {
680
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
680
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_UPDATE);
681 681
 	}
682 682
 
683 683
 	/** {@inheritdoc} */
684 684
 	public function isCreatable($path) {
685
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_CREATE);
685
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_CREATE);
686 686
 	}
687 687
 
688 688
 	/** {@inheritdoc} */
689 689
 	public function isSharable($path) {
690
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);
690
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_SHARE);
691 691
 	}
692 692
 
693 693
 	/** {@inheritdoc} */
694 694
 	public function isDeletable($path) {
695
-		return (bool)($this->getPermissions($path) & Constants::PERMISSION_DELETE);
695
+		return (bool) ($this->getPermissions($path) & Constants::PERMISSION_DELETE);
696 696
 	}
697 697
 
698 698
 	/** {@inheritdoc} */
@@ -768,7 +768,7 @@  discard block
 block discarded – undo
768 768
 			if ($response === false) {
769 769
 				if ($path === '') {
770 770
 					// if root is gone it means the storage is not available
771
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
771
+					throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
772 772
 				}
773 773
 				return false;
774 774
 			}
@@ -781,7 +781,7 @@  discard block
 block discarded – undo
781 781
 				if (!empty($etag) && $cachedData['etag'] !== $etag) {
782 782
 					return true;
783 783
 				} else if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
784
-					$sharePermissions = (int)$response['{http://open-collaboration-services.org/ns}share-permissions'];
784
+					$sharePermissions = (int) $response['{http://open-collaboration-services.org/ns}share-permissions'];
785 785
 					return $sharePermissions !== $cachedData['permissions'];
786 786
 				} else if (isset($response['{http://owncloud.org/ns}permissions'])) {
787 787
 					$permissions = $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
@@ -797,7 +797,7 @@  discard block
 block discarded – undo
797 797
 			if ($e->getHttpStatus() === 405) {
798 798
 				if ($path === '') {
799 799
 					// if root is gone it means the storage is not available
800
-					throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
800
+					throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
801 801
 				}
802 802
 				return false;
803 803
 			}
@@ -832,19 +832,19 @@  discard block
 block discarded – undo
832 832
 			}
833 833
 			if ($e->getHttpStatus() === Http::STATUS_UNAUTHORIZED) {
834 834
 				// either password was changed or was invalid all along
835
-				throw new StorageInvalidException(get_class($e) . ': ' . $e->getMessage());
835
+				throw new StorageInvalidException(get_class($e).': '.$e->getMessage());
836 836
 			} else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) {
837 837
 				// ignore exception for MethodNotAllowed, false will be returned
838 838
 				return;
839 839
 			}
840
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
840
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
841 841
 		} else if ($e instanceof ClientException) {
842 842
 			// connection timeout or refused, server could be temporarily down
843
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
843
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
844 844
 		} else if ($e instanceof \InvalidArgumentException) {
845 845
 			// parse error because the server returned HTML instead of XML,
846 846
 			// possibly temporarily down
847
-			throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
847
+			throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
848 848
 		} else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
849 849
 			// rethrow
850 850
 			throw $e;
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Notify/SMBNotifyHandler.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -102,7 +102,7 @@
 block discarded – undo
102 102
 
103 103
 	/**
104 104
 	 * @param \Icewind\SMB\Change $change
105
-	 * @return IChange|null
105
+	 * @return null|Change
106 106
 	 */
107 107
 	private function mapChange(\Icewind\SMB\Change $change) {
108 108
 		$path = $this->relativePath($change->getPath());
Please login to merge, or discard this patch.
Indentation   +109 added lines, -109 removed lines patch added patch discarded remove patch
@@ -29,122 +29,122 @@
 block discarded – undo
29 29
 use OCP\Files\Notify\INotifyHandler;
30 30
 
31 31
 class SMBNotifyHandler implements INotifyHandler {
32
-	/**
33
-	 * @var \Icewind\SMB\INotifyHandler
34
-	 */
35
-	private $shareNotifyHandler;
32
+    /**
33
+     * @var \Icewind\SMB\INotifyHandler
34
+     */
35
+    private $shareNotifyHandler;
36 36
 
37
-	/**
38
-	 * @var string
39
-	 */
40
-	private $root;
37
+    /**
38
+     * @var string
39
+     */
40
+    private $root;
41 41
 
42
-	private $oldRenamePath = null;
42
+    private $oldRenamePath = null;
43 43
 
44
-	/**
45
-	 * SMBNotifyHandler constructor.
46
-	 *
47
-	 * @param \Icewind\SMB\INotifyHandler $shareNotifyHandler
48
-	 * @param string $root
49
-	 */
50
-	public function __construct(\Icewind\SMB\INotifyHandler $shareNotifyHandler, $root) {
51
-		$this->shareNotifyHandler = $shareNotifyHandler;
52
-		$this->root = $root;
53
-	}
44
+    /**
45
+     * SMBNotifyHandler constructor.
46
+     *
47
+     * @param \Icewind\SMB\INotifyHandler $shareNotifyHandler
48
+     * @param string $root
49
+     */
50
+    public function __construct(\Icewind\SMB\INotifyHandler $shareNotifyHandler, $root) {
51
+        $this->shareNotifyHandler = $shareNotifyHandler;
52
+        $this->root = $root;
53
+    }
54 54
 
55
-	private function relativePath($fullPath) {
56
-		if ($fullPath === $this->root) {
57
-			return '';
58
-		} else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
59
-			return substr($fullPath, strlen($this->root));
60
-		} else {
61
-			return null;
62
-		}
63
-	}
55
+    private function relativePath($fullPath) {
56
+        if ($fullPath === $this->root) {
57
+            return '';
58
+        } else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
59
+            return substr($fullPath, strlen($this->root));
60
+        } else {
61
+            return null;
62
+        }
63
+    }
64 64
 
65
-	public function listen(callable $callback) {
66
-		$oldRenamePath = null;
67
-		$this->shareNotifyHandler->listen(function (\Icewind\SMB\Change $shareChange) use ($callback) {
68
-			$change = $this->mapChange($shareChange);
69
-			if (!is_null($change)) {
70
-				return $callback($change);
71
-			} else {
72
-				return true;
73
-			}
74
-		});
75
-	}
65
+    public function listen(callable $callback) {
66
+        $oldRenamePath = null;
67
+        $this->shareNotifyHandler->listen(function (\Icewind\SMB\Change $shareChange) use ($callback) {
68
+            $change = $this->mapChange($shareChange);
69
+            if (!is_null($change)) {
70
+                return $callback($change);
71
+            } else {
72
+                return true;
73
+            }
74
+        });
75
+    }
76 76
 
77
-	/**
78
-	 * Get all changes detected since the start of the notify process or the last call to getChanges
79
-	 *
80
-	 * @return IChange[]
81
-	 */
82
-	public function getChanges() {
83
-		$shareChanges = $this->shareNotifyHandler->getChanges();
84
-		$changes = [];
85
-		foreach ($shareChanges as $shareChange) {
86
-			$change = $this->mapChange($shareChange);
87
-			if ($change) {
88
-				$changes[] = $change;
89
-			}
90
-		}
91
-		return $changes;
92
-	}
77
+    /**
78
+     * Get all changes detected since the start of the notify process or the last call to getChanges
79
+     *
80
+     * @return IChange[]
81
+     */
82
+    public function getChanges() {
83
+        $shareChanges = $this->shareNotifyHandler->getChanges();
84
+        $changes = [];
85
+        foreach ($shareChanges as $shareChange) {
86
+            $change = $this->mapChange($shareChange);
87
+            if ($change) {
88
+                $changes[] = $change;
89
+            }
90
+        }
91
+        return $changes;
92
+    }
93 93
 
94
-	/**
95
-	 * Stop listening for changes
96
-	 *
97
-	 * Note that any pending changes will be discarded
98
-	 */
99
-	public function stop() {
100
-		$this->shareNotifyHandler->stop();
101
-	}
94
+    /**
95
+     * Stop listening for changes
96
+     *
97
+     * Note that any pending changes will be discarded
98
+     */
99
+    public function stop() {
100
+        $this->shareNotifyHandler->stop();
101
+    }
102 102
 
103
-	/**
104
-	 * @param \Icewind\SMB\Change $change
105
-	 * @return IChange|null
106
-	 */
107
-	private function mapChange(\Icewind\SMB\Change $change) {
108
-		$path = $this->relativePath($change->getPath());
109
-		if (is_null($path)) {
110
-			return null;
111
-		}
112
-		if ($change->getCode() === \Icewind\SMB\INotifyHandler::NOTIFY_RENAMED_OLD) {
113
-			$this->oldRenamePath = $path;
114
-			return null;
115
-		}
116
-		$type = $this->mapNotifyType($change->getCode());
117
-		if (is_null($type)) {
118
-			return null;
119
-		}
120
-		if ($type === IChange::RENAMED) {
121
-			if (!is_null($this->oldRenamePath)) {
122
-				$result = new RenameChange($type, $this->oldRenamePath, $path);
123
-				$this->oldRenamePath = null;
124
-			} else {
125
-				$result = null;
126
-			}
127
-		} else {
128
-			$result = new Change($type, $path);
129
-		}
130
-		return $result;
131
-	}
103
+    /**
104
+     * @param \Icewind\SMB\Change $change
105
+     * @return IChange|null
106
+     */
107
+    private function mapChange(\Icewind\SMB\Change $change) {
108
+        $path = $this->relativePath($change->getPath());
109
+        if (is_null($path)) {
110
+            return null;
111
+        }
112
+        if ($change->getCode() === \Icewind\SMB\INotifyHandler::NOTIFY_RENAMED_OLD) {
113
+            $this->oldRenamePath = $path;
114
+            return null;
115
+        }
116
+        $type = $this->mapNotifyType($change->getCode());
117
+        if (is_null($type)) {
118
+            return null;
119
+        }
120
+        if ($type === IChange::RENAMED) {
121
+            if (!is_null($this->oldRenamePath)) {
122
+                $result = new RenameChange($type, $this->oldRenamePath, $path);
123
+                $this->oldRenamePath = null;
124
+            } else {
125
+                $result = null;
126
+            }
127
+        } else {
128
+            $result = new Change($type, $path);
129
+        }
130
+        return $result;
131
+    }
132 132
 
133
-	private function mapNotifyType($smbType) {
134
-		switch ($smbType) {
135
-			case \Icewind\SMB\INotifyHandler::NOTIFY_ADDED:
136
-				return IChange::ADDED;
137
-			case \Icewind\SMB\INotifyHandler::NOTIFY_REMOVED:
138
-				return IChange::REMOVED;
139
-			case \Icewind\SMB\INotifyHandler::NOTIFY_MODIFIED:
140
-			case \Icewind\SMB\INotifyHandler::NOTIFY_ADDED_STREAM:
141
-			case \Icewind\SMB\INotifyHandler::NOTIFY_MODIFIED_STREAM:
142
-			case \Icewind\SMB\INotifyHandler::NOTIFY_REMOVED_STREAM:
143
-				return IChange::MODIFIED;
144
-			case \Icewind\SMB\INotifyHandler::NOTIFY_RENAMED_NEW:
145
-				return IChange::RENAMED;
146
-			default:
147
-				return null;
148
-		}
149
-	}
133
+    private function mapNotifyType($smbType) {
134
+        switch ($smbType) {
135
+            case \Icewind\SMB\INotifyHandler::NOTIFY_ADDED:
136
+                return IChange::ADDED;
137
+            case \Icewind\SMB\INotifyHandler::NOTIFY_REMOVED:
138
+                return IChange::REMOVED;
139
+            case \Icewind\SMB\INotifyHandler::NOTIFY_MODIFIED:
140
+            case \Icewind\SMB\INotifyHandler::NOTIFY_ADDED_STREAM:
141
+            case \Icewind\SMB\INotifyHandler::NOTIFY_MODIFIED_STREAM:
142
+            case \Icewind\SMB\INotifyHandler::NOTIFY_REMOVED_STREAM:
143
+                return IChange::MODIFIED;
144
+            case \Icewind\SMB\INotifyHandler::NOTIFY_RENAMED_NEW:
145
+                return IChange::RENAMED;
146
+            default:
147
+                return null;
148
+        }
149
+    }
150 150
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -64,7 +64,7 @@
 block discarded – undo
64 64
 
65 65
 	public function listen(callable $callback) {
66 66
 		$oldRenamePath = null;
67
-		$this->shareNotifyHandler->listen(function (\Icewind\SMB\Change $shareChange) use ($callback) {
67
+		$this->shareNotifyHandler->listen(function(\Icewind\SMB\Change $shareChange) use ($callback) {
68 68
 			$change = $this->mapChange($shareChange);
69 69
 			if (!is_null($change)) {
70 70
 				return $callback($change);
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Storage/SMB.php 4 patches
Unused Use Statements   -3 removed lines patch added patch discarded remove patch
@@ -31,14 +31,11 @@
 block discarded – undo
31 31
 
32 32
 namespace OCA\Files_External\Lib\Storage;
33 33
 
34
-use Icewind\SMB\Change;
35 34
 use Icewind\SMB\Exception\ConnectException;
36 35
 use Icewind\SMB\Exception\Exception;
37 36
 use Icewind\SMB\Exception\ForbiddenException;
38 37
 use Icewind\SMB\Exception\NotFoundException;
39
-use Icewind\SMB\INotifyHandler;
40 38
 use Icewind\SMB\IFileInfo;
41
-use Icewind\SMB\IShare;
42 39
 use Icewind\SMB\NativeServer;
43 40
 use Icewind\SMB\Server;
44 41
 use Icewind\Streams\CallbackWrapper;
Please login to merge, or discard this patch.
Doc Comments   +3 added lines patch added patch discarded remove patch
@@ -498,6 +498,9 @@
 block discarded – undo
498 498
 		});
499 499
 	}
500 500
 
501
+	/**
502
+	 * @param string $path
503
+	 */
501 504
 	public function notify($path) {
502 505
 		$path = '/' . ltrim($path, '/');
503 506
 		$shareNotifyHandler = $this->share->notify($this->buildPath($path));
Please login to merge, or discard this patch.
Indentation   +450 added lines, -450 removed lines patch added patch discarded remove patch
@@ -53,454 +53,454 @@
 block discarded – undo
53 53
 use OCP\Files\StorageNotAvailableException;
54 54
 
55 55
 class SMB extends Common implements INotifyStorage {
56
-	/**
57
-	 * @var \Icewind\SMB\Server
58
-	 */
59
-	protected $server;
60
-
61
-	/**
62
-	 * @var \Icewind\SMB\Share
63
-	 */
64
-	protected $share;
65
-
66
-	/**
67
-	 * @var string
68
-	 */
69
-	protected $root;
70
-
71
-	/**
72
-	 * @var \Icewind\SMB\FileInfo[]
73
-	 */
74
-	protected $statCache;
75
-
76
-	public function __construct($params) {
77
-		if (isset($params['host']) && isset($params['user']) && isset($params['password']) && isset($params['share'])) {
78
-			if (Server::NativeAvailable()) {
79
-				$this->server = new NativeServer($params['host'], $params['user'], $params['password']);
80
-			} else {
81
-				$this->server = new Server($params['host'], $params['user'], $params['password']);
82
-			}
83
-			$this->share = $this->server->getShare(trim($params['share'], '/'));
84
-
85
-			$this->root = isset($params['root']) ? $params['root'] : '/';
86
-			if (!$this->root || $this->root[0] != '/') {
87
-				$this->root = '/' . $this->root;
88
-			}
89
-			if (substr($this->root, -1, 1) != '/') {
90
-				$this->root .= '/';
91
-			}
92
-		} else {
93
-			throw new \Exception('Invalid configuration');
94
-		}
95
-		$this->statCache = new CappedMemoryCache();
96
-	}
97
-
98
-	/**
99
-	 * @return string
100
-	 */
101
-	public function getId() {
102
-		// FIXME: double slash to keep compatible with the old storage ids,
103
-		// failure to do so will lead to creation of a new storage id and
104
-		// loss of shares from the storage
105
-		return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
106
-	}
107
-
108
-	/**
109
-	 * @param string $path
110
-	 * @return string
111
-	 */
112
-	protected function buildPath($path) {
113
-		return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
114
-	}
115
-
116
-	protected function relativePath($fullPath) {
117
-		if ($fullPath === $this->root) {
118
-			return '';
119
-		} else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
120
-			return substr($fullPath, strlen($this->root));
121
-		} else {
122
-			return null;
123
-		}
124
-	}
125
-
126
-	/**
127
-	 * @param string $path
128
-	 * @return \Icewind\SMB\IFileInfo
129
-	 * @throws StorageNotAvailableException
130
-	 */
131
-	protected function getFileInfo($path) {
132
-		try {
133
-			$path = $this->buildPath($path);
134
-			if (!isset($this->statCache[$path])) {
135
-				$this->statCache[$path] = $this->share->stat($path);
136
-			}
137
-			return $this->statCache[$path];
138
-		} catch (ConnectException $e) {
139
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
140
-		}
141
-	}
142
-
143
-	/**
144
-	 * @param string $path
145
-	 * @return \Icewind\SMB\IFileInfo[]
146
-	 * @throws StorageNotAvailableException
147
-	 */
148
-	protected function getFolderContents($path) {
149
-		try {
150
-			$path = $this->buildPath($path);
151
-			$files = $this->share->dir($path);
152
-			foreach ($files as $file) {
153
-				$this->statCache[$path . '/' . $file->getName()] = $file;
154
-			}
155
-			return array_filter($files, function (IFileInfo $file) {
156
-				return !$file->isHidden();
157
-			});
158
-		} catch (ConnectException $e) {
159
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
160
-		}
161
-	}
162
-
163
-	/**
164
-	 * @param \Icewind\SMB\IFileInfo $info
165
-	 * @return array
166
-	 */
167
-	protected function formatInfo($info) {
168
-		return array(
169
-			'size' => $info->getSize(),
170
-			'mtime' => $info->getMTime()
171
-		);
172
-	}
173
-
174
-	/**
175
-	 * @param string $path
176
-	 * @return array
177
-	 */
178
-	public function stat($path) {
179
-		$result = $this->formatInfo($this->getFileInfo($path));
180
-		if ($this->remoteIsShare() && $this->isRootDir($path)) {
181
-			$result['mtime'] = $this->shareMTime();
182
-		}
183
-		return $result;
184
-	}
185
-
186
-	/**
187
-	 * get the best guess for the modification time of the share
188
-	 *
189
-	 * @return int
190
-	 */
191
-	private function shareMTime() {
192
-		$highestMTime = 0;
193
-		$files = $this->share->dir($this->root);
194
-		foreach ($files as $fileInfo) {
195
-			if ($fileInfo->getMTime() > $highestMTime) {
196
-				$highestMTime = $fileInfo->getMTime();
197
-			}
198
-		}
199
-		return $highestMTime;
200
-	}
201
-
202
-	/**
203
-	 * Check if the path is our root dir (not the smb one)
204
-	 *
205
-	 * @param string $path the path
206
-	 * @return bool
207
-	 */
208
-	private function isRootDir($path) {
209
-		return $path === '' || $path === '/' || $path === '.';
210
-	}
211
-
212
-	/**
213
-	 * Check if our root points to a smb share
214
-	 *
215
-	 * @return bool true if our root points to a share false otherwise
216
-	 */
217
-	private function remoteIsShare() {
218
-		return $this->share->getName() && (!$this->root || $this->root === '/');
219
-	}
220
-
221
-	/**
222
-	 * @param string $path
223
-	 * @return bool
224
-	 */
225
-	public function unlink($path) {
226
-		try {
227
-			if ($this->is_dir($path)) {
228
-				return $this->rmdir($path);
229
-			} else {
230
-				$path = $this->buildPath($path);
231
-				unset($this->statCache[$path]);
232
-				$this->share->del($path);
233
-				return true;
234
-			}
235
-		} catch (NotFoundException $e) {
236
-			return false;
237
-		} catch (ForbiddenException $e) {
238
-			return false;
239
-		} catch (ConnectException $e) {
240
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
241
-		}
242
-	}
243
-
244
-	/**
245
-	 * @param string $path1 the old name
246
-	 * @param string $path2 the new name
247
-	 * @return bool
248
-	 */
249
-	public function rename($path1, $path2) {
250
-		try {
251
-			$this->remove($path2);
252
-			$path1 = $this->buildPath($path1);
253
-			$path2 = $this->buildPath($path2);
254
-			return $this->share->rename($path1, $path2);
255
-		} catch (NotFoundException $e) {
256
-			return false;
257
-		} catch (ForbiddenException $e) {
258
-			return false;
259
-		} catch (ConnectException $e) {
260
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
261
-		}
262
-	}
263
-
264
-	/**
265
-	 * check if a file or folder has been updated since $time
266
-	 *
267
-	 * @param string $path
268
-	 * @param int $time
269
-	 * @return bool
270
-	 */
271
-	public function hasUpdated($path, $time) {
272
-		if (!$path and $this->root == '/') {
273
-			// mtime doesn't work for shares, but giving the nature of the backend,
274
-			// doing a full update is still just fast enough
275
-			return true;
276
-		} else {
277
-			$actualTime = $this->filemtime($path);
278
-			return $actualTime > $time;
279
-		}
280
-	}
281
-
282
-	/**
283
-	 * @param string $path
284
-	 * @param string $mode
285
-	 * @return resource|false
286
-	 */
287
-	public function fopen($path, $mode) {
288
-		$fullPath = $this->buildPath($path);
289
-		try {
290
-			switch ($mode) {
291
-				case 'r':
292
-				case 'rb':
293
-					if (!$this->file_exists($path)) {
294
-						return false;
295
-					}
296
-					return $this->share->read($fullPath);
297
-				case 'w':
298
-				case 'wb':
299
-					$source = $this->share->write($fullPath);
300
-					return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
301
-						unset($this->statCache[$fullPath]);
302
-					});
303
-				case 'a':
304
-				case 'ab':
305
-				case 'r+':
306
-				case 'w+':
307
-				case 'wb+':
308
-				case 'a+':
309
-				case 'x':
310
-				case 'x+':
311
-				case 'c':
312
-				case 'c+':
313
-					//emulate these
314
-					if (strrpos($path, '.') !== false) {
315
-						$ext = substr($path, strrpos($path, '.'));
316
-					} else {
317
-						$ext = '';
318
-					}
319
-					if ($this->file_exists($path)) {
320
-						if (!$this->isUpdatable($path)) {
321
-							return false;
322
-						}
323
-						$tmpFile = $this->getCachedFile($path);
324
-					} else {
325
-						if (!$this->isCreatable(dirname($path))) {
326
-							return false;
327
-						}
328
-						$tmpFile = \OCP\Files::tmpFile($ext);
329
-					}
330
-					$source = fopen($tmpFile, $mode);
331
-					$share = $this->share;
332
-					return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
333
-						unset($this->statCache[$fullPath]);
334
-						$share->put($tmpFile, $fullPath);
335
-						unlink($tmpFile);
336
-					});
337
-			}
338
-			return false;
339
-		} catch (NotFoundException $e) {
340
-			return false;
341
-		} catch (ForbiddenException $e) {
342
-			return false;
343
-		} catch (ConnectException $e) {
344
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
345
-		}
346
-	}
347
-
348
-	public function rmdir($path) {
349
-		try {
350
-			$this->statCache = array();
351
-			$content = $this->share->dir($this->buildPath($path));
352
-			foreach ($content as $file) {
353
-				if ($file->isDirectory()) {
354
-					$this->rmdir($path . '/' . $file->getName());
355
-				} else {
356
-					$this->share->del($file->getPath());
357
-				}
358
-			}
359
-			$this->share->rmdir($this->buildPath($path));
360
-			return true;
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 touch($path, $time = null) {
371
-		try {
372
-			if (!$this->file_exists($path)) {
373
-				$fh = $this->share->write($this->buildPath($path));
374
-				fclose($fh);
375
-				return true;
376
-			}
377
-			return false;
378
-		} catch (ConnectException $e) {
379
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
380
-		}
381
-	}
382
-
383
-	public function opendir($path) {
384
-		try {
385
-			$files = $this->getFolderContents($path);
386
-		} catch (NotFoundException $e) {
387
-			return false;
388
-		} catch (ForbiddenException $e) {
389
-			return false;
390
-		}
391
-		$names = array_map(function ($info) {
392
-			/** @var \Icewind\SMB\IFileInfo $info */
393
-			return $info->getName();
394
-		}, $files);
395
-		return IteratorDirectory::wrap($names);
396
-	}
397
-
398
-	public function filetype($path) {
399
-		try {
400
-			return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
401
-		} catch (NotFoundException $e) {
402
-			return false;
403
-		} catch (ForbiddenException $e) {
404
-			return false;
405
-		}
406
-	}
407
-
408
-	public function mkdir($path) {
409
-		$path = $this->buildPath($path);
410
-		try {
411
-			$this->share->mkdir($path);
412
-			return true;
413
-		} catch (ConnectException $e) {
414
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
415
-		} catch (Exception $e) {
416
-			return false;
417
-		}
418
-	}
419
-
420
-	public function file_exists($path) {
421
-		try {
422
-			$this->getFileInfo($path);
423
-			return true;
424
-		} catch (NotFoundException $e) {
425
-			return false;
426
-		} catch (ForbiddenException $e) {
427
-			return false;
428
-		} catch (ConnectException $e) {
429
-			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
430
-		}
431
-	}
432
-
433
-	public function isReadable($path) {
434
-		try {
435
-			$info = $this->getFileInfo($path);
436
-			return !$info->isHidden();
437
-		} catch (NotFoundException $e) {
438
-			return false;
439
-		} catch (ForbiddenException $e) {
440
-			return false;
441
-		}
442
-	}
443
-
444
-	public function isUpdatable($path) {
445
-		try {
446
-			$info = $this->getFileInfo($path);
447
-			// following windows behaviour for read-only folders: they can be written into
448
-			// (https://support.microsoft.com/en-us/kb/326549 - "cause" section)
449
-			return !$info->isHidden() && (!$info->isReadOnly() || $this->is_dir($path));
450
-		} catch (NotFoundException $e) {
451
-			return false;
452
-		} catch (ForbiddenException $e) {
453
-			return false;
454
-		}
455
-	}
456
-
457
-	public function isDeletable($path) {
458
-		try {
459
-			$info = $this->getFileInfo($path);
460
-			return !$info->isHidden() && !$info->isReadOnly();
461
-		} catch (NotFoundException $e) {
462
-			return false;
463
-		} catch (ForbiddenException $e) {
464
-			return false;
465
-		}
466
-	}
467
-
468
-	/**
469
-	 * check if smbclient is installed
470
-	 */
471
-	public static function checkDependencies() {
472
-		return (
473
-			(bool)\OC_Helper::findBinaryPath('smbclient')
474
-			|| Server::NativeAvailable()
475
-		) ? true : ['smbclient'];
476
-	}
477
-
478
-	/**
479
-	 * Test a storage for availability
480
-	 *
481
-	 * @return bool
482
-	 */
483
-	public function test() {
484
-		try {
485
-			return parent::test();
486
-		} catch (Exception $e) {
487
-			return false;
488
-		}
489
-	}
490
-
491
-	public function listen($path, callable $callback) {
492
-		$this->notify($path)->listen(function (IChange $change) use ($callback) {
493
-			if ($change instanceof IRenameChange) {
494
-				return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
495
-			} else {
496
-				return $callback($change->getType(), $change->getPath());
497
-			}
498
-		});
499
-	}
500
-
501
-	public function notify($path) {
502
-		$path = '/' . ltrim($path, '/');
503
-		$shareNotifyHandler = $this->share->notify($this->buildPath($path));
504
-		return new SMBNotifyHandler($shareNotifyHandler, $this->root);
505
-	}
56
+    /**
57
+     * @var \Icewind\SMB\Server
58
+     */
59
+    protected $server;
60
+
61
+    /**
62
+     * @var \Icewind\SMB\Share
63
+     */
64
+    protected $share;
65
+
66
+    /**
67
+     * @var string
68
+     */
69
+    protected $root;
70
+
71
+    /**
72
+     * @var \Icewind\SMB\FileInfo[]
73
+     */
74
+    protected $statCache;
75
+
76
+    public function __construct($params) {
77
+        if (isset($params['host']) && isset($params['user']) && isset($params['password']) && isset($params['share'])) {
78
+            if (Server::NativeAvailable()) {
79
+                $this->server = new NativeServer($params['host'], $params['user'], $params['password']);
80
+            } else {
81
+                $this->server = new Server($params['host'], $params['user'], $params['password']);
82
+            }
83
+            $this->share = $this->server->getShare(trim($params['share'], '/'));
84
+
85
+            $this->root = isset($params['root']) ? $params['root'] : '/';
86
+            if (!$this->root || $this->root[0] != '/') {
87
+                $this->root = '/' . $this->root;
88
+            }
89
+            if (substr($this->root, -1, 1) != '/') {
90
+                $this->root .= '/';
91
+            }
92
+        } else {
93
+            throw new \Exception('Invalid configuration');
94
+        }
95
+        $this->statCache = new CappedMemoryCache();
96
+    }
97
+
98
+    /**
99
+     * @return string
100
+     */
101
+    public function getId() {
102
+        // FIXME: double slash to keep compatible with the old storage ids,
103
+        // failure to do so will lead to creation of a new storage id and
104
+        // loss of shares from the storage
105
+        return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
106
+    }
107
+
108
+    /**
109
+     * @param string $path
110
+     * @return string
111
+     */
112
+    protected function buildPath($path) {
113
+        return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
114
+    }
115
+
116
+    protected function relativePath($fullPath) {
117
+        if ($fullPath === $this->root) {
118
+            return '';
119
+        } else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
120
+            return substr($fullPath, strlen($this->root));
121
+        } else {
122
+            return null;
123
+        }
124
+    }
125
+
126
+    /**
127
+     * @param string $path
128
+     * @return \Icewind\SMB\IFileInfo
129
+     * @throws StorageNotAvailableException
130
+     */
131
+    protected function getFileInfo($path) {
132
+        try {
133
+            $path = $this->buildPath($path);
134
+            if (!isset($this->statCache[$path])) {
135
+                $this->statCache[$path] = $this->share->stat($path);
136
+            }
137
+            return $this->statCache[$path];
138
+        } catch (ConnectException $e) {
139
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
140
+        }
141
+    }
142
+
143
+    /**
144
+     * @param string $path
145
+     * @return \Icewind\SMB\IFileInfo[]
146
+     * @throws StorageNotAvailableException
147
+     */
148
+    protected function getFolderContents($path) {
149
+        try {
150
+            $path = $this->buildPath($path);
151
+            $files = $this->share->dir($path);
152
+            foreach ($files as $file) {
153
+                $this->statCache[$path . '/' . $file->getName()] = $file;
154
+            }
155
+            return array_filter($files, function (IFileInfo $file) {
156
+                return !$file->isHidden();
157
+            });
158
+        } catch (ConnectException $e) {
159
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
160
+        }
161
+    }
162
+
163
+    /**
164
+     * @param \Icewind\SMB\IFileInfo $info
165
+     * @return array
166
+     */
167
+    protected function formatInfo($info) {
168
+        return array(
169
+            'size' => $info->getSize(),
170
+            'mtime' => $info->getMTime()
171
+        );
172
+    }
173
+
174
+    /**
175
+     * @param string $path
176
+     * @return array
177
+     */
178
+    public function stat($path) {
179
+        $result = $this->formatInfo($this->getFileInfo($path));
180
+        if ($this->remoteIsShare() && $this->isRootDir($path)) {
181
+            $result['mtime'] = $this->shareMTime();
182
+        }
183
+        return $result;
184
+    }
185
+
186
+    /**
187
+     * get the best guess for the modification time of the share
188
+     *
189
+     * @return int
190
+     */
191
+    private function shareMTime() {
192
+        $highestMTime = 0;
193
+        $files = $this->share->dir($this->root);
194
+        foreach ($files as $fileInfo) {
195
+            if ($fileInfo->getMTime() > $highestMTime) {
196
+                $highestMTime = $fileInfo->getMTime();
197
+            }
198
+        }
199
+        return $highestMTime;
200
+    }
201
+
202
+    /**
203
+     * Check if the path is our root dir (not the smb one)
204
+     *
205
+     * @param string $path the path
206
+     * @return bool
207
+     */
208
+    private function isRootDir($path) {
209
+        return $path === '' || $path === '/' || $path === '.';
210
+    }
211
+
212
+    /**
213
+     * Check if our root points to a smb share
214
+     *
215
+     * @return bool true if our root points to a share false otherwise
216
+     */
217
+    private function remoteIsShare() {
218
+        return $this->share->getName() && (!$this->root || $this->root === '/');
219
+    }
220
+
221
+    /**
222
+     * @param string $path
223
+     * @return bool
224
+     */
225
+    public function unlink($path) {
226
+        try {
227
+            if ($this->is_dir($path)) {
228
+                return $this->rmdir($path);
229
+            } else {
230
+                $path = $this->buildPath($path);
231
+                unset($this->statCache[$path]);
232
+                $this->share->del($path);
233
+                return true;
234
+            }
235
+        } catch (NotFoundException $e) {
236
+            return false;
237
+        } catch (ForbiddenException $e) {
238
+            return false;
239
+        } catch (ConnectException $e) {
240
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
241
+        }
242
+    }
243
+
244
+    /**
245
+     * @param string $path1 the old name
246
+     * @param string $path2 the new name
247
+     * @return bool
248
+     */
249
+    public function rename($path1, $path2) {
250
+        try {
251
+            $this->remove($path2);
252
+            $path1 = $this->buildPath($path1);
253
+            $path2 = $this->buildPath($path2);
254
+            return $this->share->rename($path1, $path2);
255
+        } catch (NotFoundException $e) {
256
+            return false;
257
+        } catch (ForbiddenException $e) {
258
+            return false;
259
+        } catch (ConnectException $e) {
260
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
261
+        }
262
+    }
263
+
264
+    /**
265
+     * check if a file or folder has been updated since $time
266
+     *
267
+     * @param string $path
268
+     * @param int $time
269
+     * @return bool
270
+     */
271
+    public function hasUpdated($path, $time) {
272
+        if (!$path and $this->root == '/') {
273
+            // mtime doesn't work for shares, but giving the nature of the backend,
274
+            // doing a full update is still just fast enough
275
+            return true;
276
+        } else {
277
+            $actualTime = $this->filemtime($path);
278
+            return $actualTime > $time;
279
+        }
280
+    }
281
+
282
+    /**
283
+     * @param string $path
284
+     * @param string $mode
285
+     * @return resource|false
286
+     */
287
+    public function fopen($path, $mode) {
288
+        $fullPath = $this->buildPath($path);
289
+        try {
290
+            switch ($mode) {
291
+                case 'r':
292
+                case 'rb':
293
+                    if (!$this->file_exists($path)) {
294
+                        return false;
295
+                    }
296
+                    return $this->share->read($fullPath);
297
+                case 'w':
298
+                case 'wb':
299
+                    $source = $this->share->write($fullPath);
300
+                    return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
301
+                        unset($this->statCache[$fullPath]);
302
+                    });
303
+                case 'a':
304
+                case 'ab':
305
+                case 'r+':
306
+                case 'w+':
307
+                case 'wb+':
308
+                case 'a+':
309
+                case 'x':
310
+                case 'x+':
311
+                case 'c':
312
+                case 'c+':
313
+                    //emulate these
314
+                    if (strrpos($path, '.') !== false) {
315
+                        $ext = substr($path, strrpos($path, '.'));
316
+                    } else {
317
+                        $ext = '';
318
+                    }
319
+                    if ($this->file_exists($path)) {
320
+                        if (!$this->isUpdatable($path)) {
321
+                            return false;
322
+                        }
323
+                        $tmpFile = $this->getCachedFile($path);
324
+                    } else {
325
+                        if (!$this->isCreatable(dirname($path))) {
326
+                            return false;
327
+                        }
328
+                        $tmpFile = \OCP\Files::tmpFile($ext);
329
+                    }
330
+                    $source = fopen($tmpFile, $mode);
331
+                    $share = $this->share;
332
+                    return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
333
+                        unset($this->statCache[$fullPath]);
334
+                        $share->put($tmpFile, $fullPath);
335
+                        unlink($tmpFile);
336
+                    });
337
+            }
338
+            return false;
339
+        } catch (NotFoundException $e) {
340
+            return false;
341
+        } catch (ForbiddenException $e) {
342
+            return false;
343
+        } catch (ConnectException $e) {
344
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
345
+        }
346
+    }
347
+
348
+    public function rmdir($path) {
349
+        try {
350
+            $this->statCache = array();
351
+            $content = $this->share->dir($this->buildPath($path));
352
+            foreach ($content as $file) {
353
+                if ($file->isDirectory()) {
354
+                    $this->rmdir($path . '/' . $file->getName());
355
+                } else {
356
+                    $this->share->del($file->getPath());
357
+                }
358
+            }
359
+            $this->share->rmdir($this->buildPath($path));
360
+            return true;
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 touch($path, $time = null) {
371
+        try {
372
+            if (!$this->file_exists($path)) {
373
+                $fh = $this->share->write($this->buildPath($path));
374
+                fclose($fh);
375
+                return true;
376
+            }
377
+            return false;
378
+        } catch (ConnectException $e) {
379
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
380
+        }
381
+    }
382
+
383
+    public function opendir($path) {
384
+        try {
385
+            $files = $this->getFolderContents($path);
386
+        } catch (NotFoundException $e) {
387
+            return false;
388
+        } catch (ForbiddenException $e) {
389
+            return false;
390
+        }
391
+        $names = array_map(function ($info) {
392
+            /** @var \Icewind\SMB\IFileInfo $info */
393
+            return $info->getName();
394
+        }, $files);
395
+        return IteratorDirectory::wrap($names);
396
+    }
397
+
398
+    public function filetype($path) {
399
+        try {
400
+            return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
401
+        } catch (NotFoundException $e) {
402
+            return false;
403
+        } catch (ForbiddenException $e) {
404
+            return false;
405
+        }
406
+    }
407
+
408
+    public function mkdir($path) {
409
+        $path = $this->buildPath($path);
410
+        try {
411
+            $this->share->mkdir($path);
412
+            return true;
413
+        } catch (ConnectException $e) {
414
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
415
+        } catch (Exception $e) {
416
+            return false;
417
+        }
418
+    }
419
+
420
+    public function file_exists($path) {
421
+        try {
422
+            $this->getFileInfo($path);
423
+            return true;
424
+        } catch (NotFoundException $e) {
425
+            return false;
426
+        } catch (ForbiddenException $e) {
427
+            return false;
428
+        } catch (ConnectException $e) {
429
+            throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
430
+        }
431
+    }
432
+
433
+    public function isReadable($path) {
434
+        try {
435
+            $info = $this->getFileInfo($path);
436
+            return !$info->isHidden();
437
+        } catch (NotFoundException $e) {
438
+            return false;
439
+        } catch (ForbiddenException $e) {
440
+            return false;
441
+        }
442
+    }
443
+
444
+    public function isUpdatable($path) {
445
+        try {
446
+            $info = $this->getFileInfo($path);
447
+            // following windows behaviour for read-only folders: they can be written into
448
+            // (https://support.microsoft.com/en-us/kb/326549 - "cause" section)
449
+            return !$info->isHidden() && (!$info->isReadOnly() || $this->is_dir($path));
450
+        } catch (NotFoundException $e) {
451
+            return false;
452
+        } catch (ForbiddenException $e) {
453
+            return false;
454
+        }
455
+    }
456
+
457
+    public function isDeletable($path) {
458
+        try {
459
+            $info = $this->getFileInfo($path);
460
+            return !$info->isHidden() && !$info->isReadOnly();
461
+        } catch (NotFoundException $e) {
462
+            return false;
463
+        } catch (ForbiddenException $e) {
464
+            return false;
465
+        }
466
+    }
467
+
468
+    /**
469
+     * check if smbclient is installed
470
+     */
471
+    public static function checkDependencies() {
472
+        return (
473
+            (bool)\OC_Helper::findBinaryPath('smbclient')
474
+            || Server::NativeAvailable()
475
+        ) ? true : ['smbclient'];
476
+    }
477
+
478
+    /**
479
+     * Test a storage for availability
480
+     *
481
+     * @return bool
482
+     */
483
+    public function test() {
484
+        try {
485
+            return parent::test();
486
+        } catch (Exception $e) {
487
+            return false;
488
+        }
489
+    }
490
+
491
+    public function listen($path, callable $callback) {
492
+        $this->notify($path)->listen(function (IChange $change) use ($callback) {
493
+            if ($change instanceof IRenameChange) {
494
+                return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
495
+            } else {
496
+                return $callback($change->getType(), $change->getPath());
497
+            }
498
+        });
499
+    }
500
+
501
+    public function notify($path) {
502
+        $path = '/' . ltrim($path, '/');
503
+        $shareNotifyHandler = $this->share->notify($this->buildPath($path));
504
+        return new SMBNotifyHandler($shareNotifyHandler, $this->root);
505
+    }
506 506
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -84,7 +84,7 @@  discard block
 block discarded – undo
84 84
 
85 85
 			$this->root = isset($params['root']) ? $params['root'] : '/';
86 86
 			if (!$this->root || $this->root[0] != '/') {
87
-				$this->root = '/' . $this->root;
87
+				$this->root = '/'.$this->root;
88 88
 			}
89 89
 			if (substr($this->root, -1, 1) != '/') {
90 90
 				$this->root .= '/';
@@ -102,7 +102,7 @@  discard block
 block discarded – undo
102 102
 		// FIXME: double slash to keep compatible with the old storage ids,
103 103
 		// failure to do so will lead to creation of a new storage id and
104 104
 		// loss of shares from the storage
105
-		return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
105
+		return 'smb::'.$this->server->getUser().'@'.$this->server->getHost().'//'.$this->share->getName().'/'.$this->root;
106 106
 	}
107 107
 
108 108
 	/**
@@ -110,7 +110,7 @@  discard block
 block discarded – undo
110 110
 	 * @return string
111 111
 	 */
112 112
 	protected function buildPath($path) {
113
-		return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
113
+		return Filesystem::normalizePath($this->root.'/'.$path, true, false, true);
114 114
 	}
115 115
 
116 116
 	protected function relativePath($fullPath) {
@@ -150,9 +150,9 @@  discard block
 block discarded – undo
150 150
 			$path = $this->buildPath($path);
151 151
 			$files = $this->share->dir($path);
152 152
 			foreach ($files as $file) {
153
-				$this->statCache[$path . '/' . $file->getName()] = $file;
153
+				$this->statCache[$path.'/'.$file->getName()] = $file;
154 154
 			}
155
-			return array_filter($files, function (IFileInfo $file) {
155
+			return array_filter($files, function(IFileInfo $file) {
156 156
 				return !$file->isHidden();
157 157
 			});
158 158
 		} catch (ConnectException $e) {
@@ -297,7 +297,7 @@  discard block
 block discarded – undo
297 297
 				case 'w':
298 298
 				case 'wb':
299 299
 					$source = $this->share->write($fullPath);
300
-					return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
300
+					return CallBackWrapper::wrap($source, null, null, function() use ($fullPath) {
301 301
 						unset($this->statCache[$fullPath]);
302 302
 					});
303 303
 				case 'a':
@@ -329,7 +329,7 @@  discard block
 block discarded – undo
329 329
 					}
330 330
 					$source = fopen($tmpFile, $mode);
331 331
 					$share = $this->share;
332
-					return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
332
+					return CallbackWrapper::wrap($source, null, null, function() use ($tmpFile, $fullPath, $share) {
333 333
 						unset($this->statCache[$fullPath]);
334 334
 						$share->put($tmpFile, $fullPath);
335 335
 						unlink($tmpFile);
@@ -351,7 +351,7 @@  discard block
 block discarded – undo
351 351
 			$content = $this->share->dir($this->buildPath($path));
352 352
 			foreach ($content as $file) {
353 353
 				if ($file->isDirectory()) {
354
-					$this->rmdir($path . '/' . $file->getName());
354
+					$this->rmdir($path.'/'.$file->getName());
355 355
 				} else {
356 356
 					$this->share->del($file->getPath());
357 357
 				}
@@ -388,7 +388,7 @@  discard block
 block discarded – undo
388 388
 		} catch (ForbiddenException $e) {
389 389
 			return false;
390 390
 		}
391
-		$names = array_map(function ($info) {
391
+		$names = array_map(function($info) {
392 392
 			/** @var \Icewind\SMB\IFileInfo $info */
393 393
 			return $info->getName();
394 394
 		}, $files);
@@ -470,7 +470,7 @@  discard block
 block discarded – undo
470 470
 	 */
471 471
 	public static function checkDependencies() {
472 472
 		return (
473
-			(bool)\OC_Helper::findBinaryPath('smbclient')
473
+			(bool) \OC_Helper::findBinaryPath('smbclient')
474 474
 			|| Server::NativeAvailable()
475 475
 		) ? true : ['smbclient'];
476 476
 	}
@@ -489,7 +489,7 @@  discard block
 block discarded – undo
489 489
 	}
490 490
 
491 491
 	public function listen($path, callable $callback) {
492
-		$this->notify($path)->listen(function (IChange $change) use ($callback) {
492
+		$this->notify($path)->listen(function(IChange $change) use ($callback) {
493 493
 			if ($change instanceof IRenameChange) {
494 494
 				return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
495 495
 			} else {
@@ -499,7 +499,7 @@  discard block
 block discarded – undo
499 499
 	}
500 500
 
501 501
 	public function notify($path) {
502
-		$path = '/' . ltrim($path, '/');
502
+		$path = '/'.ltrim($path, '/');
503 503
 		$shareNotifyHandler = $this->share->notify($this->buildPath($path));
504 504
 		return new SMBNotifyHandler($shareNotifyHandler, $this->root);
505 505
 	}
Please login to merge, or discard this patch.
lib/private/Files/Node/File.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -31,7 +31,7 @@
 block discarded – undo
31 31
 	 * Creates a Folder that represents a non-existing path
32 32
 	 *
33 33
 	 * @param string $path path
34
-	 * @return string non-existing node class
34
+	 * @return NonExistingFile non-existing node class
35 35
 	 */
36 36
 	protected function createNonExistingNode($path) {
37 37
 		return new NonExistingFile($this->root, $this->view, $path);
Please login to merge, or discard this patch.
Indentation   +102 added lines, -102 removed lines patch added patch discarded remove patch
@@ -29,113 +29,113 @@
 block discarded – undo
29 29
 use OCP\Files\NotPermittedException;
30 30
 
31 31
 class File extends Node implements \OCP\Files\File {
32
-	/**
33
-	 * Creates a Folder that represents a non-existing path
34
-	 *
35
-	 * @param string $path path
36
-	 * @return string non-existing node class
37
-	 */
38
-	protected function createNonExistingNode($path) {
39
-		return new NonExistingFile($this->root, $this->view, $path);
40
-	}
32
+    /**
33
+     * Creates a Folder that represents a non-existing path
34
+     *
35
+     * @param string $path path
36
+     * @return string non-existing node class
37
+     */
38
+    protected function createNonExistingNode($path) {
39
+        return new NonExistingFile($this->root, $this->view, $path);
40
+    }
41 41
 
42
-	/**
43
-	 * @return string
44
-	 * @throws \OCP\Files\NotPermittedException
45
-	 */
46
-	public function getContent() {
47
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_READ)) {
48
-			/**
49
-			 * @var \OC\Files\Storage\Storage $storage;
50
-			 */
51
-			return $this->view->file_get_contents($this->path);
52
-		} else {
53
-			throw new NotPermittedException();
54
-		}
55
-	}
42
+    /**
43
+     * @return string
44
+     * @throws \OCP\Files\NotPermittedException
45
+     */
46
+    public function getContent() {
47
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_READ)) {
48
+            /**
49
+             * @var \OC\Files\Storage\Storage $storage;
50
+             */
51
+            return $this->view->file_get_contents($this->path);
52
+        } else {
53
+            throw new NotPermittedException();
54
+        }
55
+    }
56 56
 
57
-	/**
58
-	 * @param string $data
59
-	 * @throws \OCP\Files\NotPermittedException
60
-	 */
61
-	public function putContent($data) {
62
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) {
63
-			$this->sendHooks(array('preWrite'));
64
-			$this->view->file_put_contents($this->path, $data);
65
-			$this->fileInfo = null;
66
-			$this->sendHooks(array('postWrite'));
67
-		} else {
68
-			throw new NotPermittedException();
69
-		}
70
-	}
57
+    /**
58
+     * @param string $data
59
+     * @throws \OCP\Files\NotPermittedException
60
+     */
61
+    public function putContent($data) {
62
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) {
63
+            $this->sendHooks(array('preWrite'));
64
+            $this->view->file_put_contents($this->path, $data);
65
+            $this->fileInfo = null;
66
+            $this->sendHooks(array('postWrite'));
67
+        } else {
68
+            throw new NotPermittedException();
69
+        }
70
+    }
71 71
 
72
-	/**
73
-	 * @param string $mode
74
-	 * @return resource
75
-	 * @throws \OCP\Files\NotPermittedException
76
-	 */
77
-	public function fopen($mode) {
78
-		$preHooks = array();
79
-		$postHooks = array();
80
-		$requiredPermissions = \OCP\Constants::PERMISSION_READ;
81
-		switch ($mode) {
82
-			case 'r+':
83
-			case 'rb+':
84
-			case 'w+':
85
-			case 'wb+':
86
-			case 'x+':
87
-			case 'xb+':
88
-			case 'a+':
89
-			case 'ab+':
90
-			case 'w':
91
-			case 'wb':
92
-			case 'x':
93
-			case 'xb':
94
-			case 'a':
95
-			case 'ab':
96
-				$preHooks[] = 'preWrite';
97
-				$postHooks[] = 'postWrite';
98
-				$requiredPermissions |= \OCP\Constants::PERMISSION_UPDATE;
99
-				break;
100
-		}
72
+    /**
73
+     * @param string $mode
74
+     * @return resource
75
+     * @throws \OCP\Files\NotPermittedException
76
+     */
77
+    public function fopen($mode) {
78
+        $preHooks = array();
79
+        $postHooks = array();
80
+        $requiredPermissions = \OCP\Constants::PERMISSION_READ;
81
+        switch ($mode) {
82
+            case 'r+':
83
+            case 'rb+':
84
+            case 'w+':
85
+            case 'wb+':
86
+            case 'x+':
87
+            case 'xb+':
88
+            case 'a+':
89
+            case 'ab+':
90
+            case 'w':
91
+            case 'wb':
92
+            case 'x':
93
+            case 'xb':
94
+            case 'a':
95
+            case 'ab':
96
+                $preHooks[] = 'preWrite';
97
+                $postHooks[] = 'postWrite';
98
+                $requiredPermissions |= \OCP\Constants::PERMISSION_UPDATE;
99
+                break;
100
+        }
101 101
 
102
-		if ($this->checkPermissions($requiredPermissions)) {
103
-			$this->sendHooks($preHooks);
104
-			$result = $this->view->fopen($this->path, $mode);
105
-			$this->sendHooks($postHooks);
106
-			return $result;
107
-		} else {
108
-			throw new NotPermittedException();
109
-		}
110
-	}
102
+        if ($this->checkPermissions($requiredPermissions)) {
103
+            $this->sendHooks($preHooks);
104
+            $result = $this->view->fopen($this->path, $mode);
105
+            $this->sendHooks($postHooks);
106
+            return $result;
107
+        } else {
108
+            throw new NotPermittedException();
109
+        }
110
+    }
111 111
 
112
-	public function delete() {
113
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
114
-			$this->sendHooks(array('preDelete'));
115
-			$fileInfo = $this->getFileInfo();
116
-			$this->view->unlink($this->path);
117
-			$nonExisting = new NonExistingFile($this->root, $this->view, $this->path, $fileInfo);
118
-			$this->root->emit('\OC\Files', 'postDelete', array($nonExisting));
119
-			$this->exists = false;
120
-			$this->fileInfo = null;
121
-		} else {
122
-			throw new NotPermittedException();
123
-		}
124
-	}
112
+    public function delete() {
113
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
114
+            $this->sendHooks(array('preDelete'));
115
+            $fileInfo = $this->getFileInfo();
116
+            $this->view->unlink($this->path);
117
+            $nonExisting = new NonExistingFile($this->root, $this->view, $this->path, $fileInfo);
118
+            $this->root->emit('\OC\Files', 'postDelete', array($nonExisting));
119
+            $this->exists = false;
120
+            $this->fileInfo = null;
121
+        } else {
122
+            throw new NotPermittedException();
123
+        }
124
+    }
125 125
 
126
-	/**
127
-	 * @param string $type
128
-	 * @param bool $raw
129
-	 * @return string
130
-	 */
131
-	public function hash($type, $raw = false) {
132
-		return $this->view->hash($type, $this->path, $raw);
133
-	}
126
+    /**
127
+     * @param string $type
128
+     * @param bool $raw
129
+     * @return string
130
+     */
131
+    public function hash($type, $raw = false) {
132
+        return $this->view->hash($type, $this->path, $raw);
133
+    }
134 134
 
135
-	/**
136
-	 * @inheritdoc
137
-	 */
138
-	public function getChecksum() {
139
-		return $this->getFileInfo()->getChecksum();
140
-	}
135
+    /**
136
+     * @inheritdoc
137
+     */
138
+    public function getChecksum() {
139
+        return $this->getFileInfo()->getChecksum();
140
+    }
141 141
 }
Please login to merge, or discard this patch.
lib/private/Files/Node/Folder.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -37,7 +37,7 @@
 block discarded – undo
37 37
 	 * Creates a Folder that represents a non-existing path
38 38
 	 *
39 39
 	 * @param string $path path
40
-	 * @return string non-existing node class
40
+	 * @return NonExistingFolder non-existing node class
41 41
 	 */
42 42
 	protected function createNonExistingNode($path) {
43 43
 		return new NonExistingFolder($this->root, $this->view, $path);
Please login to merge, or discard this patch.
Spacing   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -55,7 +55,7 @@  discard block
 block discarded – undo
55 55
 		if (!$this->isValidPath($path)) {
56 56
 			throw new NotPermittedException('Invalid path');
57 57
 		}
58
-		return $this->path . $this->normalizePath($path);
58
+		return $this->path.$this->normalizePath($path);
59 59
 	}
60 60
 
61 61
 	/**
@@ -68,7 +68,7 @@  discard block
 block discarded – undo
68 68
 		}
69 69
 		if ($path === $this->path) {
70 70
 			return '/';
71
-		} else if (strpos($path, $this->path . '/') !== 0) {
71
+		} else if (strpos($path, $this->path.'/') !== 0) {
72 72
 			return null;
73 73
 		} else {
74 74
 			$path = substr($path, strlen($this->path));
@@ -83,7 +83,7 @@  discard block
 block discarded – undo
83 83
 	 * @return bool
84 84
 	 */
85 85
 	public function isSubNode($node) {
86
-		return strpos($node->getPath(), $this->path . '/') === 0;
86
+		return strpos($node->getPath(), $this->path.'/') === 0;
87 87
 	}
88 88
 
89 89
 	/**
@@ -95,7 +95,7 @@  discard block
 block discarded – undo
95 95
 	public function getDirectoryListing() {
96 96
 		$folderContent = $this->view->getDirectoryContent($this->path);
97 97
 
98
-		return array_map(function (FileInfo $info) {
98
+		return array_map(function(FileInfo $info) {
99 99
 			if ($info->getMimetype() === 'httpd/unix-directory') {
100 100
 				return new Folder($this->root, $this->view, $info->getPath(), $info);
101 101
 			} else {
@@ -196,7 +196,7 @@  discard block
 block discarded – undo
196 196
 	 */
197 197
 	public function search($query) {
198 198
 		if (is_string($query)) {
199
-			return $this->searchCommon('search', array('%' . $query . '%'));
199
+			return $this->searchCommon('search', array('%'.$query.'%'));
200 200
 		} else {
201 201
 			return $this->searchCommon('searchQuery', array($query));
202 202
 		}
@@ -236,7 +236,7 @@  discard block
 block discarded – undo
236 236
 		$internalPath = $mount->getInternalPath($this->path);
237 237
 		$internalPath = rtrim($internalPath, '/');
238 238
 		if ($internalPath !== '') {
239
-			$internalPath = $internalPath . '/';
239
+			$internalPath = $internalPath.'/';
240 240
 		}
241 241
 		$internalRootLength = strlen($internalPath);
242 242
 
@@ -248,7 +248,7 @@  discard block
 block discarded – undo
248 248
 				$result['internalPath'] = $result['path'];
249 249
 				$result['path'] = substr($result['path'], $internalRootLength);
250 250
 				$result['storage'] = $storage;
251
-				$files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
251
+				$files[] = new \OC\Files\FileInfo($this->path.'/'.$result['path'], $storage, $result['internalPath'], $result, $mount);
252 252
 			}
253 253
 		}
254 254
 
@@ -262,14 +262,14 @@  discard block
 block discarded – undo
262 262
 				$results = call_user_func_array(array($cache, $method), $args);
263 263
 				foreach ($results as $result) {
264 264
 					$result['internalPath'] = $result['path'];
265
-					$result['path'] = $relativeMountPoint . $result['path'];
265
+					$result['path'] = $relativeMountPoint.$result['path'];
266 266
 					$result['storage'] = $storage;
267
-					$files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
267
+					$files[] = new \OC\Files\FileInfo($this->path.'/'.$result['path'], $storage, $result['internalPath'], $result, $mount);
268 268
 				}
269 269
 			}
270 270
 		}
271 271
 
272
-		return array_map(function (FileInfo $file) {
272
+		return array_map(function(FileInfo $file) {
273 273
 			return $this->createNode($file->getPath(), $file);
274 274
 		}, $files);
275 275
 	}
@@ -280,16 +280,16 @@  discard block
 block discarded – undo
280 280
 	 */
281 281
 	public function getById($id) {
282 282
 		$mountCache = $this->root->getUserMountCache();
283
-		$mountsContainingFile = $mountCache->getMountsForFileId((int)$id);
283
+		$mountsContainingFile = $mountCache->getMountsForFileId((int) $id);
284 284
 		$mounts = $this->root->getMountsIn($this->path);
285 285
 		$mounts[] = $this->root->getMount($this->path);
286 286
 		/** @var IMountPoint[] $folderMounts */
287
-		$folderMounts = array_combine(array_map(function (IMountPoint $mountPoint) {
287
+		$folderMounts = array_combine(array_map(function(IMountPoint $mountPoint) {
288 288
 			return $mountPoint->getMountPoint();
289 289
 		}, $mounts), $mounts);
290 290
 
291 291
 		/** @var ICachedMountInfo[] $mountsContainingFile */
292
-		$mountsContainingFile = array_values(array_filter($mountsContainingFile, function (ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
292
+		$mountsContainingFile = array_values(array_filter($mountsContainingFile, function(ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
293 293
 			return isset($folderMounts[$cachedMountInfo->getMountPoint()]);
294 294
 		}));
295 295
 
@@ -300,25 +300,25 @@  discard block
 block discarded – undo
300 300
 		// we only need to get the cache info once, since all mounts we found point to the same storage
301 301
 
302 302
 		$mount = $folderMounts[$mountsContainingFile[0]->getMountPoint()];
303
-		$cacheEntry = $mount->getStorage()->getCache()->get((int)$id);
303
+		$cacheEntry = $mount->getStorage()->getCache()->get((int) $id);
304 304
 		if (!$cacheEntry) {
305 305
 			return [];
306 306
 		}
307 307
 		// cache jails will hide the "true" internal path
308
-		$internalPath = ltrim($mountsContainingFile[0]->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/');
308
+		$internalPath = ltrim($mountsContainingFile[0]->getRootInternalPath().'/'.$cacheEntry->getPath(), '/');
309 309
 
310
-		$nodes = array_map(function (ICachedMountInfo $cachedMountInfo) use ($cacheEntry, $folderMounts, $internalPath) {
310
+		$nodes = array_map(function(ICachedMountInfo $cachedMountInfo) use ($cacheEntry, $folderMounts, $internalPath) {
311 311
 			$mount = $folderMounts[$cachedMountInfo->getMountPoint()];
312 312
 			$pathRelativeToMount = substr($internalPath, strlen($cachedMountInfo->getRootInternalPath()));
313 313
 			$pathRelativeToMount = ltrim($pathRelativeToMount, '/');
314
-			$absolutePath = $cachedMountInfo->getMountPoint() . $pathRelativeToMount;
314
+			$absolutePath = $cachedMountInfo->getMountPoint().$pathRelativeToMount;
315 315
 			return $this->root->createNode($absolutePath, new \OC\Files\FileInfo(
316 316
 				$absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
317 317
 				\OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
318 318
 			));
319 319
 		}, $mountsContainingFile);
320 320
 
321
-		return array_filter($nodes, function (Node $node) {
321
+		return array_filter($nodes, function(Node $node) {
322 322
 			return $this->getRelativePath($node->getPath());
323 323
 		});
324 324
 	}
@@ -362,10 +362,10 @@  discard block
 block discarded – undo
362 362
 		$mounts = $this->root->getMountsIn($this->path);
363 363
 		$mounts[] = $this->getMountPoint();
364 364
 
365
-		$mounts = array_filter($mounts, function (IMountPoint $mount) {
365
+		$mounts = array_filter($mounts, function(IMountPoint $mount) {
366 366
 			return $mount->getStorage();
367 367
 		});
368
-		$storageIds = array_map(function (IMountPoint $mount) {
368
+		$storageIds = array_map(function(IMountPoint $mount) {
369 369
 			return $mount->getStorage()->getCache()->getNumericStorageId();
370 370
 		}, $mounts);
371 371
 		/** @var IMountPoint[] $mountMap */
@@ -390,7 +390,7 @@  discard block
 block discarded – undo
390 390
 
391 391
 		$result = $query->execute()->fetchAll();
392 392
 
393
-		$files = array_filter(array_map(function (array $entry) use ($mountMap, $mimetypeLoader) {
393
+		$files = array_filter(array_map(function(array $entry) use ($mountMap, $mimetypeLoader) {
394 394
 			$mount = $mountMap[$entry['storage']];
395 395
 			$entry['internalPath'] = $entry['path'];
396 396
 			$entry['mimetype'] = $mimetypeLoader->getMimetypeById($entry['mimetype']);
@@ -403,7 +403,7 @@  discard block
 block discarded – undo
403 403
 			return $this->root->createNode($fileInfo->getPath(), $fileInfo);
404 404
 		}, $result));
405 405
 
406
-		return array_values(array_filter($files, function (Node $node) {
406
+		return array_values(array_filter($files, function(Node $node) {
407 407
 			$relative = $this->getRelativePath($node->getPath());
408 408
 			return $relative !== null && $relative !== '/';
409 409
 		}));
@@ -417,13 +417,13 @@  discard block
 block discarded – undo
417 417
 			$rootLength = strlen($jailRoot) + 1;
418 418
 			if ($path === $jailRoot) {
419 419
 				return $mount->getMountPoint();
420
-			} else if (substr($path, 0, $rootLength) === $jailRoot . '/') {
421
-				return $mount->getMountPoint() . substr($path, $rootLength);
420
+			} else if (substr($path, 0, $rootLength) === $jailRoot.'/') {
421
+				return $mount->getMountPoint().substr($path, $rootLength);
422 422
 			} else {
423 423
 				return null;
424 424
 			}
425 425
 		} else {
426
-			return $mount->getMountPoint() . $path;
426
+			return $mount->getMountPoint().$path;
427 427
 		}
428 428
 	}
429 429
 }
Please login to merge, or discard this patch.
Indentation   +390 added lines, -390 removed lines patch added patch discarded remove patch
@@ -36,394 +36,394 @@
 block discarded – undo
36 36
 use OCP\Files\Search\ISearchOperator;
37 37
 
38 38
 class Folder extends Node implements \OCP\Files\Folder {
39
-	/**
40
-	 * Creates a Folder that represents a non-existing path
41
-	 *
42
-	 * @param string $path path
43
-	 * @return string non-existing node class
44
-	 */
45
-	protected function createNonExistingNode($path) {
46
-		return new NonExistingFolder($this->root, $this->view, $path);
47
-	}
48
-
49
-	/**
50
-	 * @param string $path path relative to the folder
51
-	 * @return string
52
-	 * @throws \OCP\Files\NotPermittedException
53
-	 */
54
-	public function getFullPath($path) {
55
-		if (!$this->isValidPath($path)) {
56
-			throw new NotPermittedException('Invalid path');
57
-		}
58
-		return $this->path . $this->normalizePath($path);
59
-	}
60
-
61
-	/**
62
-	 * @param string $path
63
-	 * @return string
64
-	 */
65
-	public function getRelativePath($path) {
66
-		if ($this->path === '' or $this->path === '/') {
67
-			return $this->normalizePath($path);
68
-		}
69
-		if ($path === $this->path) {
70
-			return '/';
71
-		} else if (strpos($path, $this->path . '/') !== 0) {
72
-			return null;
73
-		} else {
74
-			$path = substr($path, strlen($this->path));
75
-			return $this->normalizePath($path);
76
-		}
77
-	}
78
-
79
-	/**
80
-	 * check if a node is a (grand-)child of the folder
81
-	 *
82
-	 * @param \OC\Files\Node\Node $node
83
-	 * @return bool
84
-	 */
85
-	public function isSubNode($node) {
86
-		return strpos($node->getPath(), $this->path . '/') === 0;
87
-	}
88
-
89
-	/**
90
-	 * get the content of this directory
91
-	 *
92
-	 * @throws \OCP\Files\NotFoundException
93
-	 * @return Node[]
94
-	 */
95
-	public function getDirectoryListing() {
96
-		$folderContent = $this->view->getDirectoryContent($this->path);
97
-
98
-		return array_map(function (FileInfo $info) {
99
-			if ($info->getMimetype() === 'httpd/unix-directory') {
100
-				return new Folder($this->root, $this->view, $info->getPath(), $info);
101
-			} else {
102
-				return new File($this->root, $this->view, $info->getPath(), $info);
103
-			}
104
-		}, $folderContent);
105
-	}
106
-
107
-	/**
108
-	 * @param string $path
109
-	 * @param FileInfo $info
110
-	 * @return File|Folder
111
-	 */
112
-	protected function createNode($path, FileInfo $info = null) {
113
-		if (is_null($info)) {
114
-			$isDir = $this->view->is_dir($path);
115
-		} else {
116
-			$isDir = $info->getType() === FileInfo::TYPE_FOLDER;
117
-		}
118
-		if ($isDir) {
119
-			return new Folder($this->root, $this->view, $path, $info);
120
-		} else {
121
-			return new File($this->root, $this->view, $path, $info);
122
-		}
123
-	}
124
-
125
-	/**
126
-	 * Get the node at $path
127
-	 *
128
-	 * @param string $path
129
-	 * @return \OC\Files\Node\Node
130
-	 * @throws \OCP\Files\NotFoundException
131
-	 */
132
-	public function get($path) {
133
-		return $this->root->get($this->getFullPath($path));
134
-	}
135
-
136
-	/**
137
-	 * @param string $path
138
-	 * @return bool
139
-	 */
140
-	public function nodeExists($path) {
141
-		try {
142
-			$this->get($path);
143
-			return true;
144
-		} catch (NotFoundException $e) {
145
-			return false;
146
-		}
147
-	}
148
-
149
-	/**
150
-	 * @param string $path
151
-	 * @return \OC\Files\Node\Folder
152
-	 * @throws \OCP\Files\NotPermittedException
153
-	 */
154
-	public function newFolder($path) {
155
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
156
-			$fullPath = $this->getFullPath($path);
157
-			$nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath);
158
-			$this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
159
-			$this->root->emit('\OC\Files', 'preCreate', array($nonExisting));
160
-			$this->view->mkdir($fullPath);
161
-			$node = new Folder($this->root, $this->view, $fullPath);
162
-			$this->root->emit('\OC\Files', 'postWrite', array($node));
163
-			$this->root->emit('\OC\Files', 'postCreate', array($node));
164
-			return $node;
165
-		} else {
166
-			throw new NotPermittedException('No create permission for folder');
167
-		}
168
-	}
169
-
170
-	/**
171
-	 * @param string $path
172
-	 * @return \OC\Files\Node\File
173
-	 * @throws \OCP\Files\NotPermittedException
174
-	 */
175
-	public function newFile($path) {
176
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
177
-			$fullPath = $this->getFullPath($path);
178
-			$nonExisting = new NonExistingFile($this->root, $this->view, $fullPath);
179
-			$this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
180
-			$this->root->emit('\OC\Files', 'preCreate', array($nonExisting));
181
-			$this->view->touch($fullPath);
182
-			$node = new File($this->root, $this->view, $fullPath);
183
-			$this->root->emit('\OC\Files', 'postWrite', array($node));
184
-			$this->root->emit('\OC\Files', 'postCreate', array($node));
185
-			return $node;
186
-		} else {
187
-			throw new NotPermittedException('No create permission for path');
188
-		}
189
-	}
190
-
191
-	/**
192
-	 * search for files with the name matching $query
193
-	 *
194
-	 * @param string|ISearchOperator $query
195
-	 * @return \OC\Files\Node\Node[]
196
-	 */
197
-	public function search($query) {
198
-		if (is_string($query)) {
199
-			return $this->searchCommon('search', array('%' . $query . '%'));
200
-		} else {
201
-			return $this->searchCommon('searchQuery', array($query));
202
-		}
203
-	}
204
-
205
-	/**
206
-	 * search for files by mimetype
207
-	 *
208
-	 * @param string $mimetype
209
-	 * @return Node[]
210
-	 */
211
-	public function searchByMime($mimetype) {
212
-		return $this->searchCommon('searchByMime', array($mimetype));
213
-	}
214
-
215
-	/**
216
-	 * search for files by tag
217
-	 *
218
-	 * @param string|int $tag name or tag id
219
-	 * @param string $userId owner of the tags
220
-	 * @return Node[]
221
-	 */
222
-	public function searchByTag($tag, $userId) {
223
-		return $this->searchCommon('searchByTag', array($tag, $userId));
224
-	}
225
-
226
-	/**
227
-	 * @param string $method cache method
228
-	 * @param array $args call args
229
-	 * @return \OC\Files\Node\Node[]
230
-	 */
231
-	private function searchCommon($method, $args) {
232
-		$files = array();
233
-		$rootLength = strlen($this->path);
234
-		$mount = $this->root->getMount($this->path);
235
-		$storage = $mount->getStorage();
236
-		$internalPath = $mount->getInternalPath($this->path);
237
-		$internalPath = rtrim($internalPath, '/');
238
-		if ($internalPath !== '') {
239
-			$internalPath = $internalPath . '/';
240
-		}
241
-		$internalRootLength = strlen($internalPath);
242
-
243
-		$cache = $storage->getCache('');
244
-
245
-		$results = call_user_func_array(array($cache, $method), $args);
246
-		foreach ($results as $result) {
247
-			if ($internalRootLength === 0 or substr($result['path'], 0, $internalRootLength) === $internalPath) {
248
-				$result['internalPath'] = $result['path'];
249
-				$result['path'] = substr($result['path'], $internalRootLength);
250
-				$result['storage'] = $storage;
251
-				$files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
252
-			}
253
-		}
254
-
255
-		$mounts = $this->root->getMountsIn($this->path);
256
-		foreach ($mounts as $mount) {
257
-			$storage = $mount->getStorage();
258
-			if ($storage) {
259
-				$cache = $storage->getCache('');
260
-
261
-				$relativeMountPoint = substr($mount->getMountPoint(), $rootLength);
262
-				$results = call_user_func_array(array($cache, $method), $args);
263
-				foreach ($results as $result) {
264
-					$result['internalPath'] = $result['path'];
265
-					$result['path'] = $relativeMountPoint . $result['path'];
266
-					$result['storage'] = $storage;
267
-					$files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
268
-				}
269
-			}
270
-		}
271
-
272
-		return array_map(function (FileInfo $file) {
273
-			return $this->createNode($file->getPath(), $file);
274
-		}, $files);
275
-	}
276
-
277
-	/**
278
-	 * @param int $id
279
-	 * @return \OC\Files\Node\Node[]
280
-	 */
281
-	public function getById($id) {
282
-		$mountCache = $this->root->getUserMountCache();
283
-		$mountsContainingFile = $mountCache->getMountsForFileId((int)$id);
284
-		$mounts = $this->root->getMountsIn($this->path);
285
-		$mounts[] = $this->root->getMount($this->path);
286
-		/** @var IMountPoint[] $folderMounts */
287
-		$folderMounts = array_combine(array_map(function (IMountPoint $mountPoint) {
288
-			return $mountPoint->getMountPoint();
289
-		}, $mounts), $mounts);
290
-
291
-		/** @var ICachedMountInfo[] $mountsContainingFile */
292
-		$mountsContainingFile = array_values(array_filter($mountsContainingFile, function (ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
293
-			return isset($folderMounts[$cachedMountInfo->getMountPoint()]);
294
-		}));
295
-
296
-		if (count($mountsContainingFile) === 0) {
297
-			return [];
298
-		}
299
-
300
-		// we only need to get the cache info once, since all mounts we found point to the same storage
301
-
302
-		$mount = $folderMounts[$mountsContainingFile[0]->getMountPoint()];
303
-		$cacheEntry = $mount->getStorage()->getCache()->get((int)$id);
304
-		if (!$cacheEntry) {
305
-			return [];
306
-		}
307
-		// cache jails will hide the "true" internal path
308
-		$internalPath = ltrim($mountsContainingFile[0]->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/');
309
-
310
-		$nodes = array_map(function (ICachedMountInfo $cachedMountInfo) use ($cacheEntry, $folderMounts, $internalPath) {
311
-			$mount = $folderMounts[$cachedMountInfo->getMountPoint()];
312
-			$pathRelativeToMount = substr($internalPath, strlen($cachedMountInfo->getRootInternalPath()));
313
-			$pathRelativeToMount = ltrim($pathRelativeToMount, '/');
314
-			$absolutePath = $cachedMountInfo->getMountPoint() . $pathRelativeToMount;
315
-			return $this->root->createNode($absolutePath, new \OC\Files\FileInfo(
316
-				$absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
317
-				\OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
318
-			));
319
-		}, $mountsContainingFile);
320
-
321
-		return array_filter($nodes, function (Node $node) {
322
-			return $this->getRelativePath($node->getPath());
323
-		});
324
-	}
325
-
326
-	public function getFreeSpace() {
327
-		return $this->view->free_space($this->path);
328
-	}
329
-
330
-	public function delete() {
331
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
332
-			$this->sendHooks(array('preDelete'));
333
-			$fileInfo = $this->getFileInfo();
334
-			$this->view->rmdir($this->path);
335
-			$nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo);
336
-			$this->root->emit('\OC\Files', 'postDelete', array($nonExisting));
337
-			$this->exists = false;
338
-		} else {
339
-			throw new NotPermittedException('No delete permission for path');
340
-		}
341
-	}
342
-
343
-	/**
344
-	 * Add a suffix to the name in case the file exists
345
-	 *
346
-	 * @param string $name
347
-	 * @return string
348
-	 * @throws NotPermittedException
349
-	 */
350
-	public function getNonExistingName($name) {
351
-		$uniqueName = \OC_Helper::buildNotExistingFileNameForView($this->getPath(), $name, $this->view);
352
-		return trim($this->getRelativePath($uniqueName), '/');
353
-	}
354
-
355
-	/**
356
-	 * @param int $limit
357
-	 * @param int $offset
358
-	 * @return \OCP\Files\Node[]
359
-	 */
360
-	public function getRecent($limit, $offset = 0) {
361
-		$mimetypeLoader = \OC::$server->getMimeTypeLoader();
362
-		$mounts = $this->root->getMountsIn($this->path);
363
-		$mounts[] = $this->getMountPoint();
364
-
365
-		$mounts = array_filter($mounts, function (IMountPoint $mount) {
366
-			return $mount->getStorage();
367
-		});
368
-		$storageIds = array_map(function (IMountPoint $mount) {
369
-			return $mount->getStorage()->getCache()->getNumericStorageId();
370
-		}, $mounts);
371
-		/** @var IMountPoint[] $mountMap */
372
-		$mountMap = array_combine($storageIds, $mounts);
373
-		$folderMimetype = $mimetypeLoader->getId(FileInfo::MIMETYPE_FOLDER);
374
-
375
-		//todo look into options of filtering path based on storage id (only search in files/ for home storage, filter by share root for shared, etc)
376
-
377
-		$builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
378
-		$query = $builder
379
-			->select('f.*')
380
-			->from('filecache', 'f')
381
-			->andWhere($builder->expr()->in('f.storage', $builder->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)))
382
-			->andWhere($builder->expr()->orX(
383
-			// handle non empty folders separate
384
-				$builder->expr()->neq('f.mimetype', $builder->createNamedParameter($folderMimetype, IQueryBuilder::PARAM_INT)),
385
-				$builder->expr()->eq('f.size', new Literal(0))
386
-			))
387
-			->orderBy('f.mtime', 'DESC')
388
-			->setMaxResults($limit)
389
-			->setFirstResult($offset);
390
-
391
-		$result = $query->execute()->fetchAll();
392
-
393
-		$files = array_filter(array_map(function (array $entry) use ($mountMap, $mimetypeLoader) {
394
-			$mount = $mountMap[$entry['storage']];
395
-			$entry['internalPath'] = $entry['path'];
396
-			$entry['mimetype'] = $mimetypeLoader->getMimetypeById($entry['mimetype']);
397
-			$entry['mimepart'] = $mimetypeLoader->getMimetypeById($entry['mimepart']);
398
-			$path = $this->getAbsolutePath($mount, $entry['path']);
399
-			if (is_null($path)) {
400
-				return null;
401
-			}
402
-			$fileInfo = new \OC\Files\FileInfo($path, $mount->getStorage(), $entry['internalPath'], $entry, $mount);
403
-			return $this->root->createNode($fileInfo->getPath(), $fileInfo);
404
-		}, $result));
405
-
406
-		return array_values(array_filter($files, function (Node $node) {
407
-			$relative = $this->getRelativePath($node->getPath());
408
-			return $relative !== null && $relative !== '/';
409
-		}));
410
-	}
411
-
412
-	private function getAbsolutePath(IMountPoint $mount, $path) {
413
-		$storage = $mount->getStorage();
414
-		if ($storage->instanceOfStorage('\OC\Files\Storage\Wrapper\Jail')) {
415
-			/** @var \OC\Files\Storage\Wrapper\Jail $storage */
416
-			$jailRoot = $storage->getUnjailedPath('');
417
-			$rootLength = strlen($jailRoot) + 1;
418
-			if ($path === $jailRoot) {
419
-				return $mount->getMountPoint();
420
-			} else if (substr($path, 0, $rootLength) === $jailRoot . '/') {
421
-				return $mount->getMountPoint() . substr($path, $rootLength);
422
-			} else {
423
-				return null;
424
-			}
425
-		} else {
426
-			return $mount->getMountPoint() . $path;
427
-		}
428
-	}
39
+    /**
40
+     * Creates a Folder that represents a non-existing path
41
+     *
42
+     * @param string $path path
43
+     * @return string non-existing node class
44
+     */
45
+    protected function createNonExistingNode($path) {
46
+        return new NonExistingFolder($this->root, $this->view, $path);
47
+    }
48
+
49
+    /**
50
+     * @param string $path path relative to the folder
51
+     * @return string
52
+     * @throws \OCP\Files\NotPermittedException
53
+     */
54
+    public function getFullPath($path) {
55
+        if (!$this->isValidPath($path)) {
56
+            throw new NotPermittedException('Invalid path');
57
+        }
58
+        return $this->path . $this->normalizePath($path);
59
+    }
60
+
61
+    /**
62
+     * @param string $path
63
+     * @return string
64
+     */
65
+    public function getRelativePath($path) {
66
+        if ($this->path === '' or $this->path === '/') {
67
+            return $this->normalizePath($path);
68
+        }
69
+        if ($path === $this->path) {
70
+            return '/';
71
+        } else if (strpos($path, $this->path . '/') !== 0) {
72
+            return null;
73
+        } else {
74
+            $path = substr($path, strlen($this->path));
75
+            return $this->normalizePath($path);
76
+        }
77
+    }
78
+
79
+    /**
80
+     * check if a node is a (grand-)child of the folder
81
+     *
82
+     * @param \OC\Files\Node\Node $node
83
+     * @return bool
84
+     */
85
+    public function isSubNode($node) {
86
+        return strpos($node->getPath(), $this->path . '/') === 0;
87
+    }
88
+
89
+    /**
90
+     * get the content of this directory
91
+     *
92
+     * @throws \OCP\Files\NotFoundException
93
+     * @return Node[]
94
+     */
95
+    public function getDirectoryListing() {
96
+        $folderContent = $this->view->getDirectoryContent($this->path);
97
+
98
+        return array_map(function (FileInfo $info) {
99
+            if ($info->getMimetype() === 'httpd/unix-directory') {
100
+                return new Folder($this->root, $this->view, $info->getPath(), $info);
101
+            } else {
102
+                return new File($this->root, $this->view, $info->getPath(), $info);
103
+            }
104
+        }, $folderContent);
105
+    }
106
+
107
+    /**
108
+     * @param string $path
109
+     * @param FileInfo $info
110
+     * @return File|Folder
111
+     */
112
+    protected function createNode($path, FileInfo $info = null) {
113
+        if (is_null($info)) {
114
+            $isDir = $this->view->is_dir($path);
115
+        } else {
116
+            $isDir = $info->getType() === FileInfo::TYPE_FOLDER;
117
+        }
118
+        if ($isDir) {
119
+            return new Folder($this->root, $this->view, $path, $info);
120
+        } else {
121
+            return new File($this->root, $this->view, $path, $info);
122
+        }
123
+    }
124
+
125
+    /**
126
+     * Get the node at $path
127
+     *
128
+     * @param string $path
129
+     * @return \OC\Files\Node\Node
130
+     * @throws \OCP\Files\NotFoundException
131
+     */
132
+    public function get($path) {
133
+        return $this->root->get($this->getFullPath($path));
134
+    }
135
+
136
+    /**
137
+     * @param string $path
138
+     * @return bool
139
+     */
140
+    public function nodeExists($path) {
141
+        try {
142
+            $this->get($path);
143
+            return true;
144
+        } catch (NotFoundException $e) {
145
+            return false;
146
+        }
147
+    }
148
+
149
+    /**
150
+     * @param string $path
151
+     * @return \OC\Files\Node\Folder
152
+     * @throws \OCP\Files\NotPermittedException
153
+     */
154
+    public function newFolder($path) {
155
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
156
+            $fullPath = $this->getFullPath($path);
157
+            $nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath);
158
+            $this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
159
+            $this->root->emit('\OC\Files', 'preCreate', array($nonExisting));
160
+            $this->view->mkdir($fullPath);
161
+            $node = new Folder($this->root, $this->view, $fullPath);
162
+            $this->root->emit('\OC\Files', 'postWrite', array($node));
163
+            $this->root->emit('\OC\Files', 'postCreate', array($node));
164
+            return $node;
165
+        } else {
166
+            throw new NotPermittedException('No create permission for folder');
167
+        }
168
+    }
169
+
170
+    /**
171
+     * @param string $path
172
+     * @return \OC\Files\Node\File
173
+     * @throws \OCP\Files\NotPermittedException
174
+     */
175
+    public function newFile($path) {
176
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
177
+            $fullPath = $this->getFullPath($path);
178
+            $nonExisting = new NonExistingFile($this->root, $this->view, $fullPath);
179
+            $this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
180
+            $this->root->emit('\OC\Files', 'preCreate', array($nonExisting));
181
+            $this->view->touch($fullPath);
182
+            $node = new File($this->root, $this->view, $fullPath);
183
+            $this->root->emit('\OC\Files', 'postWrite', array($node));
184
+            $this->root->emit('\OC\Files', 'postCreate', array($node));
185
+            return $node;
186
+        } else {
187
+            throw new NotPermittedException('No create permission for path');
188
+        }
189
+    }
190
+
191
+    /**
192
+     * search for files with the name matching $query
193
+     *
194
+     * @param string|ISearchOperator $query
195
+     * @return \OC\Files\Node\Node[]
196
+     */
197
+    public function search($query) {
198
+        if (is_string($query)) {
199
+            return $this->searchCommon('search', array('%' . $query . '%'));
200
+        } else {
201
+            return $this->searchCommon('searchQuery', array($query));
202
+        }
203
+    }
204
+
205
+    /**
206
+     * search for files by mimetype
207
+     *
208
+     * @param string $mimetype
209
+     * @return Node[]
210
+     */
211
+    public function searchByMime($mimetype) {
212
+        return $this->searchCommon('searchByMime', array($mimetype));
213
+    }
214
+
215
+    /**
216
+     * search for files by tag
217
+     *
218
+     * @param string|int $tag name or tag id
219
+     * @param string $userId owner of the tags
220
+     * @return Node[]
221
+     */
222
+    public function searchByTag($tag, $userId) {
223
+        return $this->searchCommon('searchByTag', array($tag, $userId));
224
+    }
225
+
226
+    /**
227
+     * @param string $method cache method
228
+     * @param array $args call args
229
+     * @return \OC\Files\Node\Node[]
230
+     */
231
+    private function searchCommon($method, $args) {
232
+        $files = array();
233
+        $rootLength = strlen($this->path);
234
+        $mount = $this->root->getMount($this->path);
235
+        $storage = $mount->getStorage();
236
+        $internalPath = $mount->getInternalPath($this->path);
237
+        $internalPath = rtrim($internalPath, '/');
238
+        if ($internalPath !== '') {
239
+            $internalPath = $internalPath . '/';
240
+        }
241
+        $internalRootLength = strlen($internalPath);
242
+
243
+        $cache = $storage->getCache('');
244
+
245
+        $results = call_user_func_array(array($cache, $method), $args);
246
+        foreach ($results as $result) {
247
+            if ($internalRootLength === 0 or substr($result['path'], 0, $internalRootLength) === $internalPath) {
248
+                $result['internalPath'] = $result['path'];
249
+                $result['path'] = substr($result['path'], $internalRootLength);
250
+                $result['storage'] = $storage;
251
+                $files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
252
+            }
253
+        }
254
+
255
+        $mounts = $this->root->getMountsIn($this->path);
256
+        foreach ($mounts as $mount) {
257
+            $storage = $mount->getStorage();
258
+            if ($storage) {
259
+                $cache = $storage->getCache('');
260
+
261
+                $relativeMountPoint = substr($mount->getMountPoint(), $rootLength);
262
+                $results = call_user_func_array(array($cache, $method), $args);
263
+                foreach ($results as $result) {
264
+                    $result['internalPath'] = $result['path'];
265
+                    $result['path'] = $relativeMountPoint . $result['path'];
266
+                    $result['storage'] = $storage;
267
+                    $files[] = new \OC\Files\FileInfo($this->path . '/' . $result['path'], $storage, $result['internalPath'], $result, $mount);
268
+                }
269
+            }
270
+        }
271
+
272
+        return array_map(function (FileInfo $file) {
273
+            return $this->createNode($file->getPath(), $file);
274
+        }, $files);
275
+    }
276
+
277
+    /**
278
+     * @param int $id
279
+     * @return \OC\Files\Node\Node[]
280
+     */
281
+    public function getById($id) {
282
+        $mountCache = $this->root->getUserMountCache();
283
+        $mountsContainingFile = $mountCache->getMountsForFileId((int)$id);
284
+        $mounts = $this->root->getMountsIn($this->path);
285
+        $mounts[] = $this->root->getMount($this->path);
286
+        /** @var IMountPoint[] $folderMounts */
287
+        $folderMounts = array_combine(array_map(function (IMountPoint $mountPoint) {
288
+            return $mountPoint->getMountPoint();
289
+        }, $mounts), $mounts);
290
+
291
+        /** @var ICachedMountInfo[] $mountsContainingFile */
292
+        $mountsContainingFile = array_values(array_filter($mountsContainingFile, function (ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
293
+            return isset($folderMounts[$cachedMountInfo->getMountPoint()]);
294
+        }));
295
+
296
+        if (count($mountsContainingFile) === 0) {
297
+            return [];
298
+        }
299
+
300
+        // we only need to get the cache info once, since all mounts we found point to the same storage
301
+
302
+        $mount = $folderMounts[$mountsContainingFile[0]->getMountPoint()];
303
+        $cacheEntry = $mount->getStorage()->getCache()->get((int)$id);
304
+        if (!$cacheEntry) {
305
+            return [];
306
+        }
307
+        // cache jails will hide the "true" internal path
308
+        $internalPath = ltrim($mountsContainingFile[0]->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/');
309
+
310
+        $nodes = array_map(function (ICachedMountInfo $cachedMountInfo) use ($cacheEntry, $folderMounts, $internalPath) {
311
+            $mount = $folderMounts[$cachedMountInfo->getMountPoint()];
312
+            $pathRelativeToMount = substr($internalPath, strlen($cachedMountInfo->getRootInternalPath()));
313
+            $pathRelativeToMount = ltrim($pathRelativeToMount, '/');
314
+            $absolutePath = $cachedMountInfo->getMountPoint() . $pathRelativeToMount;
315
+            return $this->root->createNode($absolutePath, new \OC\Files\FileInfo(
316
+                $absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
317
+                \OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
318
+            ));
319
+        }, $mountsContainingFile);
320
+
321
+        return array_filter($nodes, function (Node $node) {
322
+            return $this->getRelativePath($node->getPath());
323
+        });
324
+    }
325
+
326
+    public function getFreeSpace() {
327
+        return $this->view->free_space($this->path);
328
+    }
329
+
330
+    public function delete() {
331
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
332
+            $this->sendHooks(array('preDelete'));
333
+            $fileInfo = $this->getFileInfo();
334
+            $this->view->rmdir($this->path);
335
+            $nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo);
336
+            $this->root->emit('\OC\Files', 'postDelete', array($nonExisting));
337
+            $this->exists = false;
338
+        } else {
339
+            throw new NotPermittedException('No delete permission for path');
340
+        }
341
+    }
342
+
343
+    /**
344
+     * Add a suffix to the name in case the file exists
345
+     *
346
+     * @param string $name
347
+     * @return string
348
+     * @throws NotPermittedException
349
+     */
350
+    public function getNonExistingName($name) {
351
+        $uniqueName = \OC_Helper::buildNotExistingFileNameForView($this->getPath(), $name, $this->view);
352
+        return trim($this->getRelativePath($uniqueName), '/');
353
+    }
354
+
355
+    /**
356
+     * @param int $limit
357
+     * @param int $offset
358
+     * @return \OCP\Files\Node[]
359
+     */
360
+    public function getRecent($limit, $offset = 0) {
361
+        $mimetypeLoader = \OC::$server->getMimeTypeLoader();
362
+        $mounts = $this->root->getMountsIn($this->path);
363
+        $mounts[] = $this->getMountPoint();
364
+
365
+        $mounts = array_filter($mounts, function (IMountPoint $mount) {
366
+            return $mount->getStorage();
367
+        });
368
+        $storageIds = array_map(function (IMountPoint $mount) {
369
+            return $mount->getStorage()->getCache()->getNumericStorageId();
370
+        }, $mounts);
371
+        /** @var IMountPoint[] $mountMap */
372
+        $mountMap = array_combine($storageIds, $mounts);
373
+        $folderMimetype = $mimetypeLoader->getId(FileInfo::MIMETYPE_FOLDER);
374
+
375
+        //todo look into options of filtering path based on storage id (only search in files/ for home storage, filter by share root for shared, etc)
376
+
377
+        $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
378
+        $query = $builder
379
+            ->select('f.*')
380
+            ->from('filecache', 'f')
381
+            ->andWhere($builder->expr()->in('f.storage', $builder->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)))
382
+            ->andWhere($builder->expr()->orX(
383
+            // handle non empty folders separate
384
+                $builder->expr()->neq('f.mimetype', $builder->createNamedParameter($folderMimetype, IQueryBuilder::PARAM_INT)),
385
+                $builder->expr()->eq('f.size', new Literal(0))
386
+            ))
387
+            ->orderBy('f.mtime', 'DESC')
388
+            ->setMaxResults($limit)
389
+            ->setFirstResult($offset);
390
+
391
+        $result = $query->execute()->fetchAll();
392
+
393
+        $files = array_filter(array_map(function (array $entry) use ($mountMap, $mimetypeLoader) {
394
+            $mount = $mountMap[$entry['storage']];
395
+            $entry['internalPath'] = $entry['path'];
396
+            $entry['mimetype'] = $mimetypeLoader->getMimetypeById($entry['mimetype']);
397
+            $entry['mimepart'] = $mimetypeLoader->getMimetypeById($entry['mimepart']);
398
+            $path = $this->getAbsolutePath($mount, $entry['path']);
399
+            if (is_null($path)) {
400
+                return null;
401
+            }
402
+            $fileInfo = new \OC\Files\FileInfo($path, $mount->getStorage(), $entry['internalPath'], $entry, $mount);
403
+            return $this->root->createNode($fileInfo->getPath(), $fileInfo);
404
+        }, $result));
405
+
406
+        return array_values(array_filter($files, function (Node $node) {
407
+            $relative = $this->getRelativePath($node->getPath());
408
+            return $relative !== null && $relative !== '/';
409
+        }));
410
+    }
411
+
412
+    private function getAbsolutePath(IMountPoint $mount, $path) {
413
+        $storage = $mount->getStorage();
414
+        if ($storage->instanceOfStorage('\OC\Files\Storage\Wrapper\Jail')) {
415
+            /** @var \OC\Files\Storage\Wrapper\Jail $storage */
416
+            $jailRoot = $storage->getUnjailedPath('');
417
+            $rootLength = strlen($jailRoot) + 1;
418
+            if ($path === $jailRoot) {
419
+                return $mount->getMountPoint();
420
+            } else if (substr($path, 0, $rootLength) === $jailRoot . '/') {
421
+                return $mount->getMountPoint() . substr($path, $rootLength);
422
+            } else {
423
+                return null;
424
+            }
425
+        } else {
426
+            return $mount->getMountPoint() . $path;
427
+        }
428
+    }
429 429
 }
Please login to merge, or discard this patch.
lib/public/Settings/IIconSection.php 2 patches
Doc Comments   +1 added lines patch added patch discarded remove patch
@@ -33,6 +33,7 @@
 block discarded – undo
33 33
 	 *
34 34
 	 * @returns string
35 35
 	 * @since 12
36
+	 * @return string
36 37
 	 */
37 38
 	public function getIcon();
38 39
 }
Please login to merge, or discard this patch.
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -27,12 +27,12 @@
 block discarded – undo
27 27
  * @since 12
28 28
  */
29 29
 interface IIconSection extends ISection {
30
-	/**
31
-	 * returns the relative path to an 16*16 icon describing the section.
32
-	 * e.g. '/core/img/places/files.svg'
33
-	 *
34
-	 * @returns string
35
-	 * @since 12
36
-	 */
37
-	public function getIcon();
30
+    /**
31
+     * returns the relative path to an 16*16 icon describing the section.
32
+     * e.g. '/core/img/places/files.svg'
33
+     *
34
+     * @returns string
35
+     * @since 12
36
+     */
37
+    public function getIcon();
38 38
 }
Please login to merge, or discard this patch.
apps/dav/lib/CalDAV/Publishing/Xml/Publisher.php 2 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -20,7 +20,6 @@
 block discarded – undo
20 20
  */
21 21
 namespace OCA\DAV\CalDAV\Publishing\Xml;
22 22
 
23
-use OCA\DAV\CalDAV\Publishing\PublishPlugin as Plugin;
24 23
 use Sabre\Xml\Writer;
25 24
 use Sabre\Xml\XmlSerializable;
26 25
 
Please login to merge, or discard this patch.
Indentation   +50 added lines, -50 removed lines patch added patch discarded remove patch
@@ -26,58 +26,58 @@
 block discarded – undo
26 26
 
27 27
 class Publisher implements XmlSerializable {
28 28
 
29
-	/**
30
-	 * @var string $publishUrl
31
-	 */
32
-	protected $publishUrl;
29
+    /**
30
+     * @var string $publishUrl
31
+     */
32
+    protected $publishUrl;
33 33
 
34
-	/**
35
-	 * @var boolean $isPublished
36
-	 */
37
-	protected $isPublished;
34
+    /**
35
+     * @var boolean $isPublished
36
+     */
37
+    protected $isPublished;
38 38
 
39
-	/**
40
-	 * @param string $publishUrl
41
-	 * @param boolean $isPublished
42
-	 */
43
-	function __construct($publishUrl, $isPublished) {
44
-		$this->publishUrl = $publishUrl;
45
-		$this->isPublished = $isPublished;
46
-	}
39
+    /**
40
+     * @param string $publishUrl
41
+     * @param boolean $isPublished
42
+     */
43
+    function __construct($publishUrl, $isPublished) {
44
+        $this->publishUrl = $publishUrl;
45
+        $this->isPublished = $isPublished;
46
+    }
47 47
 
48
-	/**
49
-	 * @return string
50
-	 */
51
-	function getValue() {
52
-		return $this->publishUrl;
53
-	}
48
+    /**
49
+     * @return string
50
+     */
51
+    function getValue() {
52
+        return $this->publishUrl;
53
+    }
54 54
 
55
-	/**
56
-	 * The xmlSerialize metod is called during xml writing.
57
-	 *
58
-	 * Use the $writer argument to write its own xml serialization.
59
-	 *
60
-	 * An important note: do _not_ create a parent element. Any element
61
-	 * implementing XmlSerializble should only ever write what's considered
62
-	 * its 'inner xml'.
63
-	 *
64
-	 * The parent of the current element is responsible for writing a
65
-	 * containing element.
66
-	 *
67
-	 * This allows serializers to be re-used for different element names.
68
-	 *
69
-	 * If you are opening new elements, you must also close them again.
70
-	 *
71
-	 * @param Writer $writer
72
-	 * @return void
73
-	 */
74
-	function xmlSerialize(Writer $writer) {
75
-		if (!$this->isPublished) {
76
-			// for pre-publish-url
77
-			$writer->write($this->publishUrl);
78
-		} else {
79
-			// for publish-url
80
-			$writer->writeElement('{DAV:}href', $this->publishUrl);
81
-		}
82
-	}
55
+    /**
56
+     * The xmlSerialize metod is called during xml writing.
57
+     *
58
+     * Use the $writer argument to write its own xml serialization.
59
+     *
60
+     * An important note: do _not_ create a parent element. Any element
61
+     * implementing XmlSerializble should only ever write what's considered
62
+     * its 'inner xml'.
63
+     *
64
+     * The parent of the current element is responsible for writing a
65
+     * containing element.
66
+     *
67
+     * This allows serializers to be re-used for different element names.
68
+     *
69
+     * If you are opening new elements, you must also close them again.
70
+     *
71
+     * @param Writer $writer
72
+     * @return void
73
+     */
74
+    function xmlSerialize(Writer $writer) {
75
+        if (!$this->isPublished) {
76
+            // for pre-publish-url
77
+            $writer->write($this->publishUrl);
78
+        } else {
79
+            // for publish-url
80
+            $writer->writeElement('{DAV:}href', $this->publishUrl);
81
+        }
82
+    }
83 83
 }
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/FilesPlugin.php 4 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -31,7 +31,6 @@
 block discarded – undo
31 31
 namespace OCA\DAV\Connector\Sabre;
32 32
 
33 33
 use OC\Files\View;
34
-use OCA\DAV\Upload\FutureFile;
35 34
 use OCP\Files\ForbiddenException;
36 35
 use OCP\IPreview;
37 36
 use Sabre\DAV\Exception\Forbidden;
Please login to merge, or discard this patch.
Braces   +3 added lines, -1 removed lines patch added patch discarded remove patch
@@ -245,7 +245,9 @@
 block discarded – undo
245 245
 	function httpGet(RequestInterface $request, ResponseInterface $response) {
246 246
 		// Only handle valid files
247 247
 		$node = $this->tree->getNodeForPath($request->getPath());
248
-		if (!($node instanceof IFile)) return;
248
+		if (!($node instanceof IFile)) {
249
+		    return;
250
+		}
249 251
 
250 252
 		// adds a 'Content-Disposition: attachment' header in case no disposition
251 253
 		// header has been set before
Please login to merge, or discard this patch.
Indentation   +391 added lines, -391 removed lines patch added patch discarded remove patch
@@ -51,395 +51,395 @@
 block discarded – undo
51 51
 
52 52
 class FilesPlugin extends ServerPlugin {
53 53
 
54
-	// namespace
55
-	const NS_OWNCLOUD = 'http://owncloud.org/ns';
56
-	const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
57
-	const FILEID_PROPERTYNAME = '{http://owncloud.org/ns}id';
58
-	const INTERNAL_FILEID_PROPERTYNAME = '{http://owncloud.org/ns}fileid';
59
-	const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions';
60
-	const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions';
61
-	const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
62
-	const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
63
-	const GETETAG_PROPERTYNAME = '{DAV:}getetag';
64
-	const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
65
-	const OWNER_ID_PROPERTYNAME = '{http://owncloud.org/ns}owner-id';
66
-	const OWNER_DISPLAY_NAME_PROPERTYNAME = '{http://owncloud.org/ns}owner-display-name';
67
-	const CHECKSUMS_PROPERTYNAME = '{http://owncloud.org/ns}checksums';
68
-	const DATA_FINGERPRINT_PROPERTYNAME = '{http://owncloud.org/ns}data-fingerprint';
69
-	const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview';
70
-
71
-	/**
72
-	 * Reference to main server object
73
-	 *
74
-	 * @var \Sabre\DAV\Server
75
-	 */
76
-	private $server;
77
-
78
-	/**
79
-	 * @var Tree
80
-	 */
81
-	private $tree;
82
-
83
-	/**
84
-	 * Whether this is public webdav.
85
-	 * If true, some returned information will be stripped off.
86
-	 *
87
-	 * @var bool
88
-	 */
89
-	private $isPublic;
90
-
91
-	/**
92
-	 * @var View
93
-	 */
94
-	private $fileView;
95
-
96
-	/**
97
-	 * @var bool
98
-	 */
99
-	private $downloadAttachment;
100
-
101
-	/**
102
-	 * @var IConfig
103
-	 */
104
-	private $config;
105
-
106
-	/**
107
-	 * @var IRequest
108
-	 */
109
-	private $request;
110
-
111
-	/**
112
-	 * @var IPreview
113
-	 */
114
-	private $previewManager;
115
-
116
-	/**
117
-	 * @param Tree $tree
118
-	 * @param IConfig $config
119
-	 * @param IRequest $request
120
-	 * @param IPreview $previewManager
121
-	 * @param bool $isPublic
122
-	 * @param bool $downloadAttachment
123
-	 */
124
-	public function __construct(Tree $tree,
125
-								IConfig $config,
126
-								IRequest $request,
127
-								IPreview $previewManager,
128
-								$isPublic = false,
129
-								$downloadAttachment = true) {
130
-		$this->tree = $tree;
131
-		$this->config = $config;
132
-		$this->request = $request;
133
-		$this->isPublic = $isPublic;
134
-		$this->downloadAttachment = $downloadAttachment;
135
-		$this->previewManager = $previewManager;
136
-	}
137
-
138
-	/**
139
-	 * This initializes the plugin.
140
-	 *
141
-	 * This function is called by \Sabre\DAV\Server, after
142
-	 * addPlugin is called.
143
-	 *
144
-	 * This method should set up the required event subscriptions.
145
-	 *
146
-	 * @param \Sabre\DAV\Server $server
147
-	 * @return void
148
-	 */
149
-	public function initialize(\Sabre\DAV\Server $server) {
150
-
151
-		$server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
152
-		$server->xml->namespaceMap[self::NS_NEXTCLOUD] = 'nc';
153
-		$server->protectedProperties[] = self::FILEID_PROPERTYNAME;
154
-		$server->protectedProperties[] = self::INTERNAL_FILEID_PROPERTYNAME;
155
-		$server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME;
156
-		$server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME;
157
-		$server->protectedProperties[] = self::SIZE_PROPERTYNAME;
158
-		$server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
159
-		$server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME;
160
-		$server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME;
161
-		$server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME;
162
-		$server->protectedProperties[] = self::DATA_FINGERPRINT_PROPERTYNAME;
163
-		$server->protectedProperties[] = self::HAS_PREVIEW_PROPERTYNAME;
164
-
165
-		// normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH
166
-		$allowedProperties = ['{DAV:}getetag'];
167
-		$server->protectedProperties = array_diff($server->protectedProperties, $allowedProperties);
168
-
169
-		$this->server = $server;
170
-		$this->server->on('propFind', array($this, 'handleGetProperties'));
171
-		$this->server->on('propPatch', array($this, 'handleUpdateProperties'));
172
-		$this->server->on('afterBind', array($this, 'sendFileIdHeader'));
173
-		$this->server->on('afterWriteContent', array($this, 'sendFileIdHeader'));
174
-		$this->server->on('afterMethod:GET', [$this,'httpGet']);
175
-		$this->server->on('afterMethod:GET', array($this, 'handleDownloadToken'));
176
-		$this->server->on('afterResponse', function($request, ResponseInterface $response) {
177
-			$body = $response->getBody();
178
-			if (is_resource($body)) {
179
-				fclose($body);
180
-			}
181
-		});
182
-		$this->server->on('beforeMove', [$this, 'checkMove']);
183
-	}
184
-
185
-	/**
186
-	 * Plugin that checks if a move can actually be performed.
187
-	 *
188
-	 * @param string $source source path
189
-	 * @param string $destination destination path
190
-	 * @throws Forbidden
191
-	 * @throws NotFound
192
-	 */
193
-	function checkMove($source, $destination) {
194
-		$sourceNode = $this->tree->getNodeForPath($source);
195
-		if (!$sourceNode instanceof Node) {
196
-			return;
197
-		}
198
-		list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($source);
199
-		list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destination);
200
-
201
-		if ($sourceDir !== $destinationDir) {
202
-			$sourceNodeFileInfo = $sourceNode->getFileInfo();
203
-			if (is_null($sourceNodeFileInfo)) {
204
-				throw new NotFound($source . ' does not exist');
205
-			}
206
-
207
-			if (!$sourceNodeFileInfo->isDeletable()) {
208
-				throw new Forbidden($source . " cannot be deleted");
209
-			}
210
-		}
211
-	}
212
-
213
-	/**
214
-	 * This sets a cookie to be able to recognize the start of the download
215
-	 * the content must not be longer than 32 characters and must only contain
216
-	 * alphanumeric characters
217
-	 *
218
-	 * @param RequestInterface $request
219
-	 * @param ResponseInterface $response
220
-	 */
221
-	function handleDownloadToken(RequestInterface $request, ResponseInterface $response) {
222
-		$queryParams = $request->getQueryParameters();
223
-
224
-		/**
225
-		 * this sets a cookie to be able to recognize the start of the download
226
-		 * the content must not be longer than 32 characters and must only contain
227
-		 * alphanumeric characters
228
-		 */
229
-		if (isset($queryParams['downloadStartSecret'])) {
230
-			$token = $queryParams['downloadStartSecret'];
231
-			if (!isset($token[32])
232
-				&& preg_match('!^[a-zA-Z0-9]+$!', $token) === 1) {
233
-				// FIXME: use $response->setHeader() instead
234
-				setcookie('ocDownloadStarted', $token, time() + 20, '/');
235
-			}
236
-		}
237
-	}
238
-
239
-	/**
240
-	 * Add headers to file download
241
-	 *
242
-	 * @param RequestInterface $request
243
-	 * @param ResponseInterface $response
244
-	 */
245
-	function httpGet(RequestInterface $request, ResponseInterface $response) {
246
-		// Only handle valid files
247
-		$node = $this->tree->getNodeForPath($request->getPath());
248
-		if (!($node instanceof IFile)) return;
249
-
250
-		// adds a 'Content-Disposition: attachment' header in case no disposition
251
-		// header has been set before
252
-		if ($this->downloadAttachment &&
253
-			$response->getHeader('Content-Disposition') === null) {
254
-			$filename = $node->getName();
255
-			if ($this->request->isUserAgent(
256
-				[
257
-					\OC\AppFramework\Http\Request::USER_AGENT_IE,
258
-					\OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME,
259
-					\OC\AppFramework\Http\Request::USER_AGENT_FREEBOX,
260
-				])) {
261
-				$response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"');
262
-			} else {
263
-				$response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename)
264
-													 . '; filename="' . rawurlencode($filename) . '"');
265
-			}
266
-		}
267
-
268
-		if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
269
-			//Add OC-Checksum header
270
-			/** @var $node File */
271
-			$checksum = $node->getChecksum();
272
-			if ($checksum !== null && $checksum !== '') {
273
-				$response->addHeader('OC-Checksum', $checksum);
274
-			}
275
-		}
276
-	}
277
-
278
-	/**
279
-	 * Adds all ownCloud-specific properties
280
-	 *
281
-	 * @param PropFind $propFind
282
-	 * @param \Sabre\DAV\INode $node
283
-	 * @return void
284
-	 */
285
-	public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) {
286
-
287
-		$httpRequest = $this->server->httpRequest;
288
-
289
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
290
-
291
-			$propFind->handle(self::FILEID_PROPERTYNAME, function() use ($node) {
292
-				return $node->getFileId();
293
-			});
294
-
295
-			$propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function() use ($node) {
296
-				return $node->getInternalFileId();
297
-			});
298
-
299
-			$propFind->handle(self::PERMISSIONS_PROPERTYNAME, function() use ($node) {
300
-				$perms = $node->getDavPermissions();
301
-				if ($this->isPublic) {
302
-					// remove mount information
303
-					$perms = str_replace(['S', 'M'], '', $perms);
304
-				}
305
-				return $perms;
306
-			});
307
-
308
-			$propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest) {
309
-				return $node->getSharePermissions(
310
-					$httpRequest->getRawServerValue('PHP_AUTH_USER')
311
-				);
312
-			});
313
-
314
-			$propFind->handle(self::GETETAG_PROPERTYNAME, function() use ($node) {
315
-				return $node->getETag();
316
-			});
317
-
318
-			$propFind->handle(self::OWNER_ID_PROPERTYNAME, function() use ($node) {
319
-				$owner = $node->getOwner();
320
-				if (!$owner) {
321
-					return null;
322
-				} else {
323
-					return $owner->getUID();
324
-				}
325
-			});
326
-			$propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function() use ($node) {
327
-				$owner = $node->getOwner();
328
-				if (!$owner) {
329
-					return null;
330
-				} else {
331
-					return $owner->getDisplayName();
332
-				}
333
-			});
334
-
335
-			$propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) {
336
-				return json_encode($this->previewManager->isAvailable($node->getFileInfo()));
337
-			});
338
-			$propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
339
-				return $node->getSize();
340
-			});
341
-		}
342
-
343
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
344
-			$propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() use ($node) {
345
-				return $this->config->getSystemValue('data-fingerprint', '');
346
-			});
347
-		}
348
-
349
-		if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
350
-			$propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function() use ($node) {
351
-				/** @var $node \OCA\DAV\Connector\Sabre\File */
352
-				try {
353
-					$directDownloadUrl = $node->getDirectDownload();
354
-					if (isset($directDownloadUrl['url'])) {
355
-						return $directDownloadUrl['url'];
356
-					}
357
-				} catch (StorageNotAvailableException $e) {
358
-					return false;
359
-				} catch (ForbiddenException $e) {
360
-					return false;
361
-				}
362
-				return false;
363
-			});
364
-
365
-			$propFind->handle(self::CHECKSUMS_PROPERTYNAME, function() use ($node) {
366
-				$checksum = $node->getChecksum();
367
-				if ($checksum === NULL || $checksum === '') {
368
-					return null;
369
-				}
370
-
371
-				return new ChecksumList($checksum);
372
-			});
373
-
374
-		}
375
-
376
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Directory) {
377
-			$propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
378
-				return $node->getSize();
379
-			});
380
-		}
381
-	}
382
-
383
-	/**
384
-	 * Update ownCloud-specific properties
385
-	 *
386
-	 * @param string $path
387
-	 * @param PropPatch $propPatch
388
-	 *
389
-	 * @return void
390
-	 */
391
-	public function handleUpdateProperties($path, PropPatch $propPatch) {
392
-		$propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function($time) use ($path) {
393
-			if (empty($time)) {
394
-				return false;
395
-			}
396
-			$node = $this->tree->getNodeForPath($path);
397
-			if (is_null($node)) {
398
-				return 404;
399
-			}
400
-			$node->touch($time);
401
-			return true;
402
-		});
403
-		$propPatch->handle(self::GETETAG_PROPERTYNAME, function($etag) use ($path) {
404
-			if (empty($etag)) {
405
-				return false;
406
-			}
407
-			$node = $this->tree->getNodeForPath($path);
408
-			if (is_null($node)) {
409
-				return 404;
410
-			}
411
-			if ($node->setEtag($etag) !== -1) {
412
-				return true;
413
-			}
414
-			return false;
415
-		});
416
-	}
417
-
418
-	/**
419
-	 * @param string $filePath
420
-	 * @param \Sabre\DAV\INode $node
421
-	 * @throws \Sabre\DAV\Exception\BadRequest
422
-	 */
423
-	public function sendFileIdHeader($filePath, \Sabre\DAV\INode $node = null) {
424
-		// chunked upload handling
425
-		if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
426
-			list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($filePath);
427
-			$info = \OC_FileChunking::decodeName($name);
428
-			if (!empty($info)) {
429
-				$filePath = $path . '/' . $info['name'];
430
-			}
431
-		}
432
-
433
-		// we get the node for the given $filePath here because in case of afterCreateFile $node is the parent folder
434
-		if (!$this->server->tree->nodeExists($filePath)) {
435
-			return;
436
-		}
437
-		$node = $this->server->tree->getNodeForPath($filePath);
438
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
439
-			$fileId = $node->getFileId();
440
-			if (!is_null($fileId)) {
441
-				$this->server->httpResponse->setHeader('OC-FileId', $fileId);
442
-			}
443
-		}
444
-	}
54
+    // namespace
55
+    const NS_OWNCLOUD = 'http://owncloud.org/ns';
56
+    const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
57
+    const FILEID_PROPERTYNAME = '{http://owncloud.org/ns}id';
58
+    const INTERNAL_FILEID_PROPERTYNAME = '{http://owncloud.org/ns}fileid';
59
+    const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions';
60
+    const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions';
61
+    const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
62
+    const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
63
+    const GETETAG_PROPERTYNAME = '{DAV:}getetag';
64
+    const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
65
+    const OWNER_ID_PROPERTYNAME = '{http://owncloud.org/ns}owner-id';
66
+    const OWNER_DISPLAY_NAME_PROPERTYNAME = '{http://owncloud.org/ns}owner-display-name';
67
+    const CHECKSUMS_PROPERTYNAME = '{http://owncloud.org/ns}checksums';
68
+    const DATA_FINGERPRINT_PROPERTYNAME = '{http://owncloud.org/ns}data-fingerprint';
69
+    const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview';
70
+
71
+    /**
72
+     * Reference to main server object
73
+     *
74
+     * @var \Sabre\DAV\Server
75
+     */
76
+    private $server;
77
+
78
+    /**
79
+     * @var Tree
80
+     */
81
+    private $tree;
82
+
83
+    /**
84
+     * Whether this is public webdav.
85
+     * If true, some returned information will be stripped off.
86
+     *
87
+     * @var bool
88
+     */
89
+    private $isPublic;
90
+
91
+    /**
92
+     * @var View
93
+     */
94
+    private $fileView;
95
+
96
+    /**
97
+     * @var bool
98
+     */
99
+    private $downloadAttachment;
100
+
101
+    /**
102
+     * @var IConfig
103
+     */
104
+    private $config;
105
+
106
+    /**
107
+     * @var IRequest
108
+     */
109
+    private $request;
110
+
111
+    /**
112
+     * @var IPreview
113
+     */
114
+    private $previewManager;
115
+
116
+    /**
117
+     * @param Tree $tree
118
+     * @param IConfig $config
119
+     * @param IRequest $request
120
+     * @param IPreview $previewManager
121
+     * @param bool $isPublic
122
+     * @param bool $downloadAttachment
123
+     */
124
+    public function __construct(Tree $tree,
125
+                                IConfig $config,
126
+                                IRequest $request,
127
+                                IPreview $previewManager,
128
+                                $isPublic = false,
129
+                                $downloadAttachment = true) {
130
+        $this->tree = $tree;
131
+        $this->config = $config;
132
+        $this->request = $request;
133
+        $this->isPublic = $isPublic;
134
+        $this->downloadAttachment = $downloadAttachment;
135
+        $this->previewManager = $previewManager;
136
+    }
137
+
138
+    /**
139
+     * This initializes the plugin.
140
+     *
141
+     * This function is called by \Sabre\DAV\Server, after
142
+     * addPlugin is called.
143
+     *
144
+     * This method should set up the required event subscriptions.
145
+     *
146
+     * @param \Sabre\DAV\Server $server
147
+     * @return void
148
+     */
149
+    public function initialize(\Sabre\DAV\Server $server) {
150
+
151
+        $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
152
+        $server->xml->namespaceMap[self::NS_NEXTCLOUD] = 'nc';
153
+        $server->protectedProperties[] = self::FILEID_PROPERTYNAME;
154
+        $server->protectedProperties[] = self::INTERNAL_FILEID_PROPERTYNAME;
155
+        $server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME;
156
+        $server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME;
157
+        $server->protectedProperties[] = self::SIZE_PROPERTYNAME;
158
+        $server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
159
+        $server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME;
160
+        $server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME;
161
+        $server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME;
162
+        $server->protectedProperties[] = self::DATA_FINGERPRINT_PROPERTYNAME;
163
+        $server->protectedProperties[] = self::HAS_PREVIEW_PROPERTYNAME;
164
+
165
+        // normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH
166
+        $allowedProperties = ['{DAV:}getetag'];
167
+        $server->protectedProperties = array_diff($server->protectedProperties, $allowedProperties);
168
+
169
+        $this->server = $server;
170
+        $this->server->on('propFind', array($this, 'handleGetProperties'));
171
+        $this->server->on('propPatch', array($this, 'handleUpdateProperties'));
172
+        $this->server->on('afterBind', array($this, 'sendFileIdHeader'));
173
+        $this->server->on('afterWriteContent', array($this, 'sendFileIdHeader'));
174
+        $this->server->on('afterMethod:GET', [$this,'httpGet']);
175
+        $this->server->on('afterMethod:GET', array($this, 'handleDownloadToken'));
176
+        $this->server->on('afterResponse', function($request, ResponseInterface $response) {
177
+            $body = $response->getBody();
178
+            if (is_resource($body)) {
179
+                fclose($body);
180
+            }
181
+        });
182
+        $this->server->on('beforeMove', [$this, 'checkMove']);
183
+    }
184
+
185
+    /**
186
+     * Plugin that checks if a move can actually be performed.
187
+     *
188
+     * @param string $source source path
189
+     * @param string $destination destination path
190
+     * @throws Forbidden
191
+     * @throws NotFound
192
+     */
193
+    function checkMove($source, $destination) {
194
+        $sourceNode = $this->tree->getNodeForPath($source);
195
+        if (!$sourceNode instanceof Node) {
196
+            return;
197
+        }
198
+        list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($source);
199
+        list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destination);
200
+
201
+        if ($sourceDir !== $destinationDir) {
202
+            $sourceNodeFileInfo = $sourceNode->getFileInfo();
203
+            if (is_null($sourceNodeFileInfo)) {
204
+                throw new NotFound($source . ' does not exist');
205
+            }
206
+
207
+            if (!$sourceNodeFileInfo->isDeletable()) {
208
+                throw new Forbidden($source . " cannot be deleted");
209
+            }
210
+        }
211
+    }
212
+
213
+    /**
214
+     * This sets a cookie to be able to recognize the start of the download
215
+     * the content must not be longer than 32 characters and must only contain
216
+     * alphanumeric characters
217
+     *
218
+     * @param RequestInterface $request
219
+     * @param ResponseInterface $response
220
+     */
221
+    function handleDownloadToken(RequestInterface $request, ResponseInterface $response) {
222
+        $queryParams = $request->getQueryParameters();
223
+
224
+        /**
225
+         * this sets a cookie to be able to recognize the start of the download
226
+         * the content must not be longer than 32 characters and must only contain
227
+         * alphanumeric characters
228
+         */
229
+        if (isset($queryParams['downloadStartSecret'])) {
230
+            $token = $queryParams['downloadStartSecret'];
231
+            if (!isset($token[32])
232
+                && preg_match('!^[a-zA-Z0-9]+$!', $token) === 1) {
233
+                // FIXME: use $response->setHeader() instead
234
+                setcookie('ocDownloadStarted', $token, time() + 20, '/');
235
+            }
236
+        }
237
+    }
238
+
239
+    /**
240
+     * Add headers to file download
241
+     *
242
+     * @param RequestInterface $request
243
+     * @param ResponseInterface $response
244
+     */
245
+    function httpGet(RequestInterface $request, ResponseInterface $response) {
246
+        // Only handle valid files
247
+        $node = $this->tree->getNodeForPath($request->getPath());
248
+        if (!($node instanceof IFile)) return;
249
+
250
+        // adds a 'Content-Disposition: attachment' header in case no disposition
251
+        // header has been set before
252
+        if ($this->downloadAttachment &&
253
+            $response->getHeader('Content-Disposition') === null) {
254
+            $filename = $node->getName();
255
+            if ($this->request->isUserAgent(
256
+                [
257
+                    \OC\AppFramework\Http\Request::USER_AGENT_IE,
258
+                    \OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME,
259
+                    \OC\AppFramework\Http\Request::USER_AGENT_FREEBOX,
260
+                ])) {
261
+                $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"');
262
+            } else {
263
+                $response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename)
264
+                                                        . '; filename="' . rawurlencode($filename) . '"');
265
+            }
266
+        }
267
+
268
+        if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
269
+            //Add OC-Checksum header
270
+            /** @var $node File */
271
+            $checksum = $node->getChecksum();
272
+            if ($checksum !== null && $checksum !== '') {
273
+                $response->addHeader('OC-Checksum', $checksum);
274
+            }
275
+        }
276
+    }
277
+
278
+    /**
279
+     * Adds all ownCloud-specific properties
280
+     *
281
+     * @param PropFind $propFind
282
+     * @param \Sabre\DAV\INode $node
283
+     * @return void
284
+     */
285
+    public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) {
286
+
287
+        $httpRequest = $this->server->httpRequest;
288
+
289
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
290
+
291
+            $propFind->handle(self::FILEID_PROPERTYNAME, function() use ($node) {
292
+                return $node->getFileId();
293
+            });
294
+
295
+            $propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function() use ($node) {
296
+                return $node->getInternalFileId();
297
+            });
298
+
299
+            $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function() use ($node) {
300
+                $perms = $node->getDavPermissions();
301
+                if ($this->isPublic) {
302
+                    // remove mount information
303
+                    $perms = str_replace(['S', 'M'], '', $perms);
304
+                }
305
+                return $perms;
306
+            });
307
+
308
+            $propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest) {
309
+                return $node->getSharePermissions(
310
+                    $httpRequest->getRawServerValue('PHP_AUTH_USER')
311
+                );
312
+            });
313
+
314
+            $propFind->handle(self::GETETAG_PROPERTYNAME, function() use ($node) {
315
+                return $node->getETag();
316
+            });
317
+
318
+            $propFind->handle(self::OWNER_ID_PROPERTYNAME, function() use ($node) {
319
+                $owner = $node->getOwner();
320
+                if (!$owner) {
321
+                    return null;
322
+                } else {
323
+                    return $owner->getUID();
324
+                }
325
+            });
326
+            $propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function() use ($node) {
327
+                $owner = $node->getOwner();
328
+                if (!$owner) {
329
+                    return null;
330
+                } else {
331
+                    return $owner->getDisplayName();
332
+                }
333
+            });
334
+
335
+            $propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) {
336
+                return json_encode($this->previewManager->isAvailable($node->getFileInfo()));
337
+            });
338
+            $propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
339
+                return $node->getSize();
340
+            });
341
+        }
342
+
343
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
344
+            $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() use ($node) {
345
+                return $this->config->getSystemValue('data-fingerprint', '');
346
+            });
347
+        }
348
+
349
+        if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
350
+            $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function() use ($node) {
351
+                /** @var $node \OCA\DAV\Connector\Sabre\File */
352
+                try {
353
+                    $directDownloadUrl = $node->getDirectDownload();
354
+                    if (isset($directDownloadUrl['url'])) {
355
+                        return $directDownloadUrl['url'];
356
+                    }
357
+                } catch (StorageNotAvailableException $e) {
358
+                    return false;
359
+                } catch (ForbiddenException $e) {
360
+                    return false;
361
+                }
362
+                return false;
363
+            });
364
+
365
+            $propFind->handle(self::CHECKSUMS_PROPERTYNAME, function() use ($node) {
366
+                $checksum = $node->getChecksum();
367
+                if ($checksum === NULL || $checksum === '') {
368
+                    return null;
369
+                }
370
+
371
+                return new ChecksumList($checksum);
372
+            });
373
+
374
+        }
375
+
376
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Directory) {
377
+            $propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
378
+                return $node->getSize();
379
+            });
380
+        }
381
+    }
382
+
383
+    /**
384
+     * Update ownCloud-specific properties
385
+     *
386
+     * @param string $path
387
+     * @param PropPatch $propPatch
388
+     *
389
+     * @return void
390
+     */
391
+    public function handleUpdateProperties($path, PropPatch $propPatch) {
392
+        $propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function($time) use ($path) {
393
+            if (empty($time)) {
394
+                return false;
395
+            }
396
+            $node = $this->tree->getNodeForPath($path);
397
+            if (is_null($node)) {
398
+                return 404;
399
+            }
400
+            $node->touch($time);
401
+            return true;
402
+        });
403
+        $propPatch->handle(self::GETETAG_PROPERTYNAME, function($etag) use ($path) {
404
+            if (empty($etag)) {
405
+                return false;
406
+            }
407
+            $node = $this->tree->getNodeForPath($path);
408
+            if (is_null($node)) {
409
+                return 404;
410
+            }
411
+            if ($node->setEtag($etag) !== -1) {
412
+                return true;
413
+            }
414
+            return false;
415
+        });
416
+    }
417
+
418
+    /**
419
+     * @param string $filePath
420
+     * @param \Sabre\DAV\INode $node
421
+     * @throws \Sabre\DAV\Exception\BadRequest
422
+     */
423
+    public function sendFileIdHeader($filePath, \Sabre\DAV\INode $node = null) {
424
+        // chunked upload handling
425
+        if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
426
+            list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($filePath);
427
+            $info = \OC_FileChunking::decodeName($name);
428
+            if (!empty($info)) {
429
+                $filePath = $path . '/' . $info['name'];
430
+            }
431
+        }
432
+
433
+        // we get the node for the given $filePath here because in case of afterCreateFile $node is the parent folder
434
+        if (!$this->server->tree->nodeExists($filePath)) {
435
+            return;
436
+        }
437
+        $node = $this->server->tree->getNodeForPath($filePath);
438
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
439
+            $fileId = $node->getFileId();
440
+            if (!is_null($fileId)) {
441
+                $this->server->httpResponse->setHeader('OC-FileId', $fileId);
442
+            }
443
+        }
444
+    }
445 445
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -171,7 +171,7 @@  discard block
 block discarded – undo
171 171
 		$this->server->on('propPatch', array($this, 'handleUpdateProperties'));
172 172
 		$this->server->on('afterBind', array($this, 'sendFileIdHeader'));
173 173
 		$this->server->on('afterWriteContent', array($this, 'sendFileIdHeader'));
174
-		$this->server->on('afterMethod:GET', [$this,'httpGet']);
174
+		$this->server->on('afterMethod:GET', [$this, 'httpGet']);
175 175
 		$this->server->on('afterMethod:GET', array($this, 'handleDownloadToken'));
176 176
 		$this->server->on('afterResponse', function($request, ResponseInterface $response) {
177 177
 			$body = $response->getBody();
@@ -201,11 +201,11 @@  discard block
 block discarded – undo
201 201
 		if ($sourceDir !== $destinationDir) {
202 202
 			$sourceNodeFileInfo = $sourceNode->getFileInfo();
203 203
 			if (is_null($sourceNodeFileInfo)) {
204
-				throw new NotFound($source . ' does not exist');
204
+				throw new NotFound($source.' does not exist');
205 205
 			}
206 206
 
207 207
 			if (!$sourceNodeFileInfo->isDeletable()) {
208
-				throw new Forbidden($source . " cannot be deleted");
208
+				throw new Forbidden($source." cannot be deleted");
209 209
 			}
210 210
 		}
211 211
 	}
@@ -258,10 +258,10 @@  discard block
 block discarded – undo
258 258
 					\OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME,
259 259
 					\OC\AppFramework\Http\Request::USER_AGENT_FREEBOX,
260 260
 				])) {
261
-				$response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"');
261
+				$response->addHeader('Content-Disposition', 'attachment; filename="'.rawurlencode($filename).'"');
262 262
 			} else {
263
-				$response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename)
264
-													 . '; filename="' . rawurlencode($filename) . '"');
263
+				$response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\''.rawurlencode($filename)
264
+													 . '; filename="'.rawurlencode($filename).'"');
265 265
 			}
266 266
 		}
267 267
 
@@ -332,7 +332,7 @@  discard block
 block discarded – undo
332 332
 				}
333 333
 			});
334 334
 
335
-			$propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) {
335
+			$propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function() use ($node) {
336 336
 				return json_encode($this->previewManager->isAvailable($node->getFileInfo()));
337 337
 			});
338 338
 			$propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
@@ -426,7 +426,7 @@  discard block
 block discarded – undo
426 426
 			list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($filePath);
427 427
 			$info = \OC_FileChunking::decodeName($name);
428 428
 			if (!empty($info)) {
429
-				$filePath = $path . '/' . $info['name'];
429
+				$filePath = $path.'/'.$info['name'];
430 430
 			}
431 431
 		}
432 432
 
Please login to merge, or discard this patch.