Passed
Push — master ( 2be893...8e65f0 )
by Morris
29:15 queued 17:51
created
lib/private/Files/ObjectStore/Swift.php 1 patch
Indentation   +94 added lines, -94 removed lines patch added patch discarded remove patch
@@ -33,99 +33,99 @@
 block discarded – undo
33 33
 use OpenStack\Common\Error\BadResponseError;
34 34
 
35 35
 class Swift implements IObjectStore {
36
-	/**
37
-	 * @var array
38
-	 */
39
-	private $params;
40
-
41
-	/** @var SwiftFactory */
42
-	private $swiftFactory;
43
-
44
-	public function __construct($params, SwiftFactory $connectionFactory = null) {
45
-		$this->swiftFactory = $connectionFactory ?: new SwiftFactory(
46
-			\OC::$server->getMemCacheFactory()->createDistributed('swift::'),
47
-			$params,
48
-			\OC::$server->getLogger()
49
-		);
50
-		$this->params = $params;
51
-	}
52
-
53
-	/**
54
-	 * @return \OpenStack\ObjectStore\v1\Models\Container
55
-	 * @throws StorageAuthException
56
-	 * @throws \OCP\Files\StorageNotAvailableException
57
-	 */
58
-	private function getContainer() {
59
-		return $this->swiftFactory->getContainer();
60
-	}
61
-
62
-	/**
63
-	 * @return string the container name where objects are stored
64
-	 */
65
-	public function getStorageId() {
66
-		if (isset($this->params['bucket'])) {
67
-			return $this->params['bucket'];
68
-		}
69
-
70
-		return $this->params['container'];
71
-	}
72
-
73
-	/**
74
-	 * @param string $urn the unified resource name used to identify the object
75
-	 * @param resource $stream stream with the data to write
76
-	 * @throws \Exception from openstack lib when something goes wrong
77
-	 */
78
-	public function writeObject($urn, $stream) {
79
-		$this->getContainer()->createObject([
80
-			'name' => $urn,
81
-			'stream' => stream_for($stream)
82
-		]);
83
-	}
84
-
85
-	/**
86
-	 * @param string $urn the unified resource name used to identify the object
87
-	 * @return resource stream with the read data
88
-	 * @throws \Exception from openstack lib when something goes wrong
89
-	 * @throws NotFoundException if file does not exist
90
-	 */
91
-	public function readObject($urn) {
92
-		try {
93
-			$object = $this->getContainer()->getObject($urn);
94
-
95
-			// we need to keep a reference to objectContent or
96
-			// the stream will be closed before we can do anything with it
97
-			$objectContent = $object->download();
98
-		} catch (BadResponseError $e) {
99
-			if ($e->getResponse()->getStatusCode() === 404) {
100
-				throw new NotFoundException("object $urn not found in object store");
101
-			} else {
102
-				throw $e;
103
-			}
104
-		}
105
-		$objectContent->rewind();
106
-
107
-		$stream = $objectContent->detach();
108
-		// save the object content in the context of the stream to prevent it being gc'd until the stream is closed
109
-		stream_context_set_option($stream, 'swift', 'content', $objectContent);
110
-
111
-		return RetryWrapper::wrap($stream);
112
-	}
113
-
114
-	/**
115
-	 * @param string $urn Unified Resource Name
116
-	 * @return void
117
-	 * @throws \Exception from openstack lib when something goes wrong
118
-	 */
119
-	public function deleteObject($urn) {
120
-		$this->getContainer()->getObject($urn)->delete();
121
-	}
122
-
123
-	/**
124
-	 * @return void
125
-	 * @throws \Exception from openstack lib when something goes wrong
126
-	 */
127
-	public function deleteContainer() {
128
-		$this->getContainer()->delete();
129
-	}
36
+    /**
37
+     * @var array
38
+     */
39
+    private $params;
40
+
41
+    /** @var SwiftFactory */
42
+    private $swiftFactory;
43
+
44
+    public function __construct($params, SwiftFactory $connectionFactory = null) {
45
+        $this->swiftFactory = $connectionFactory ?: new SwiftFactory(
46
+            \OC::$server->getMemCacheFactory()->createDistributed('swift::'),
47
+            $params,
48
+            \OC::$server->getLogger()
49
+        );
50
+        $this->params = $params;
51
+    }
52
+
53
+    /**
54
+     * @return \OpenStack\ObjectStore\v1\Models\Container
55
+     * @throws StorageAuthException
56
+     * @throws \OCP\Files\StorageNotAvailableException
57
+     */
58
+    private function getContainer() {
59
+        return $this->swiftFactory->getContainer();
60
+    }
61
+
62
+    /**
63
+     * @return string the container name where objects are stored
64
+     */
65
+    public function getStorageId() {
66
+        if (isset($this->params['bucket'])) {
67
+            return $this->params['bucket'];
68
+        }
69
+
70
+        return $this->params['container'];
71
+    }
72
+
73
+    /**
74
+     * @param string $urn the unified resource name used to identify the object
75
+     * @param resource $stream stream with the data to write
76
+     * @throws \Exception from openstack lib when something goes wrong
77
+     */
78
+    public function writeObject($urn, $stream) {
79
+        $this->getContainer()->createObject([
80
+            'name' => $urn,
81
+            'stream' => stream_for($stream)
82
+        ]);
83
+    }
84
+
85
+    /**
86
+     * @param string $urn the unified resource name used to identify the object
87
+     * @return resource stream with the read data
88
+     * @throws \Exception from openstack lib when something goes wrong
89
+     * @throws NotFoundException if file does not exist
90
+     */
91
+    public function readObject($urn) {
92
+        try {
93
+            $object = $this->getContainer()->getObject($urn);
94
+
95
+            // we need to keep a reference to objectContent or
96
+            // the stream will be closed before we can do anything with it
97
+            $objectContent = $object->download();
98
+        } catch (BadResponseError $e) {
99
+            if ($e->getResponse()->getStatusCode() === 404) {
100
+                throw new NotFoundException("object $urn not found in object store");
101
+            } else {
102
+                throw $e;
103
+            }
104
+        }
105
+        $objectContent->rewind();
106
+
107
+        $stream = $objectContent->detach();
108
+        // save the object content in the context of the stream to prevent it being gc'd until the stream is closed
109
+        stream_context_set_option($stream, 'swift', 'content', $objectContent);
110
+
111
+        return RetryWrapper::wrap($stream);
112
+    }
113
+
114
+    /**
115
+     * @param string $urn Unified Resource Name
116
+     * @return void
117
+     * @throws \Exception from openstack lib when something goes wrong
118
+     */
119
+    public function deleteObject($urn) {
120
+        $this->getContainer()->getObject($urn)->delete();
121
+    }
122
+
123
+    /**
124
+     * @return void
125
+     * @throws \Exception from openstack lib when something goes wrong
126
+     */
127
+    public function deleteContainer() {
128
+        $this->getContainer()->delete();
129
+    }
130 130
 
131 131
 }
Please login to merge, or discard this patch.
lib/private/Files/ObjectStore/ObjectStoreStorage.php 2 patches
Indentation   +431 added lines, -431 removed lines patch added patch discarded remove patch
@@ -33,435 +33,435 @@
 block discarded – undo
33 33
 use OCP\Files\ObjectStore\IObjectStore;
34 34
 
35 35
 class ObjectStoreStorage extends \OC\Files\Storage\Common {
36
-	/**
37
-	 * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
38
-	 */
39
-	protected $objectStore;
40
-	/**
41
-	 * @var string $id
42
-	 */
43
-	protected $id;
44
-	/**
45
-	 * @var \OC\User\User $user
46
-	 */
47
-	protected $user;
48
-
49
-	private $objectPrefix = 'urn:oid:';
50
-
51
-	private $logger;
52
-
53
-	public function __construct($params) {
54
-		if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
55
-			$this->objectStore = $params['objectstore'];
56
-		} else {
57
-			throw new \Exception('missing IObjectStore instance');
58
-		}
59
-		if (isset($params['storageid'])) {
60
-			$this->id = 'object::store:' . $params['storageid'];
61
-		} else {
62
-			$this->id = 'object::store:' . $this->objectStore->getStorageId();
63
-		}
64
-		if (isset($params['objectPrefix'])) {
65
-			$this->objectPrefix = $params['objectPrefix'];
66
-		}
67
-		//initialize cache with root directory in cache
68
-		if (!$this->is_dir('/')) {
69
-			$this->mkdir('/');
70
-		}
71
-
72
-		$this->logger = \OC::$server->getLogger();
73
-	}
74
-
75
-	public function mkdir($path) {
76
-		$path = $this->normalizePath($path);
77
-
78
-		if ($this->file_exists($path)) {
79
-			return false;
80
-		}
81
-
82
-		$mTime = time();
83
-		$data = [
84
-			'mimetype' => 'httpd/unix-directory',
85
-			'size' => 0,
86
-			'mtime' => $mTime,
87
-			'storage_mtime' => $mTime,
88
-			'permissions' => \OCP\Constants::PERMISSION_ALL,
89
-		];
90
-		if ($path === '') {
91
-			//create root on the fly
92
-			$data['etag'] = $this->getETag('');
93
-			$this->getCache()->put('', $data);
94
-			return true;
95
-		} else {
96
-			// if parent does not exist, create it
97
-			$parent = $this->normalizePath(dirname($path));
98
-			$parentType = $this->filetype($parent);
99
-			if ($parentType === false) {
100
-				if (!$this->mkdir($parent)) {
101
-					// something went wrong
102
-					return false;
103
-				}
104
-			} else if ($parentType === 'file') {
105
-				// parent is a file
106
-				return false;
107
-			}
108
-			// finally create the new dir
109
-			$mTime = time(); // update mtime
110
-			$data['mtime'] = $mTime;
111
-			$data['storage_mtime'] = $mTime;
112
-			$data['etag'] = $this->getETag($path);
113
-			$this->getCache()->put($path, $data);
114
-			return true;
115
-		}
116
-	}
117
-
118
-	/**
119
-	 * @param string $path
120
-	 * @return string
121
-	 */
122
-	private function normalizePath($path) {
123
-		$path = trim($path, '/');
124
-		//FIXME why do we sometimes get a path like 'files//username'?
125
-		$path = str_replace('//', '/', $path);
126
-
127
-		// dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
128
-		if (!$path || $path === '.') {
129
-			$path = '';
130
-		}
131
-
132
-		return $path;
133
-	}
134
-
135
-	/**
136
-	 * Object Stores use a NoopScanner because metadata is directly stored in
137
-	 * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
138
-	 *
139
-	 * @param string $path
140
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
141
-	 * @return \OC\Files\ObjectStore\NoopScanner
142
-	 */
143
-	public function getScanner($path = '', $storage = null) {
144
-		if (!$storage) {
145
-			$storage = $this;
146
-		}
147
-		if (!isset($this->scanner)) {
148
-			$this->scanner = new NoopScanner($storage);
149
-		}
150
-		return $this->scanner;
151
-	}
152
-
153
-	public function getId() {
154
-		return $this->id;
155
-	}
156
-
157
-	public function rmdir($path) {
158
-		$path = $this->normalizePath($path);
159
-
160
-		if (!$this->is_dir($path)) {
161
-			return false;
162
-		}
163
-
164
-		$this->rmObjects($path);
165
-
166
-		$this->getCache()->remove($path);
167
-
168
-		return true;
169
-	}
170
-
171
-	private function rmObjects($path) {
172
-		$children = $this->getCache()->getFolderContents($path);
173
-		foreach ($children as $child) {
174
-			if ($child['mimetype'] === 'httpd/unix-directory') {
175
-				$this->rmObjects($child['path']);
176
-			} else {
177
-				$this->unlink($child['path']);
178
-			}
179
-		}
180
-	}
181
-
182
-	public function unlink($path) {
183
-		$path = $this->normalizePath($path);
184
-		$stat = $this->stat($path);
185
-
186
-		if ($stat && isset($stat['fileid'])) {
187
-			if ($stat['mimetype'] === 'httpd/unix-directory') {
188
-				return $this->rmdir($path);
189
-			}
190
-			try {
191
-				$this->objectStore->deleteObject($this->getURN($stat['fileid']));
192
-			} catch (\Exception $ex) {
193
-				if ($ex->getCode() !== 404) {
194
-					$this->logger->logException($ex, [
195
-						'app' => 'objectstore',
196
-						'message' => 'Could not delete object ' . $this->getURN($stat['fileid']) . ' for ' . $path,
197
-					]);
198
-					return false;
199
-				}
200
-				//removing from cache is ok as it does not exist in the objectstore anyway
201
-			}
202
-			$this->getCache()->remove($path);
203
-			return true;
204
-		}
205
-		return false;
206
-	}
207
-
208
-	public function stat($path) {
209
-		$path = $this->normalizePath($path);
210
-		$cacheEntry = $this->getCache()->get($path);
211
-		if ($cacheEntry instanceof CacheEntry) {
212
-			return $cacheEntry->getData();
213
-		} else {
214
-			return false;
215
-		}
216
-	}
217
-
218
-	/**
219
-	 * Override this method if you need a different unique resource identifier for your object storage implementation.
220
-	 * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
221
-	 * You may need a mapping table to store your URN if it cannot be generated from the fileid.
222
-	 *
223
-	 * @param int $fileId the fileid
224
-	 * @return null|string the unified resource name used to identify the object
225
-	 */
226
-	protected function getURN($fileId) {
227
-		if (is_numeric($fileId)) {
228
-			return $this->objectPrefix . $fileId;
229
-		}
230
-		return null;
231
-	}
232
-
233
-	public function opendir($path) {
234
-		$path = $this->normalizePath($path);
235
-
236
-		try {
237
-			$files = array();
238
-			$folderContents = $this->getCache()->getFolderContents($path);
239
-			foreach ($folderContents as $file) {
240
-				$files[] = $file['name'];
241
-			}
242
-
243
-			return IteratorDirectory::wrap($files);
244
-		} catch (\Exception $e) {
245
-			$this->logger->logException($e);
246
-			return false;
247
-		}
248
-	}
249
-
250
-	public function filetype($path) {
251
-		$path = $this->normalizePath($path);
252
-		$stat = $this->stat($path);
253
-		if ($stat) {
254
-			if ($stat['mimetype'] === 'httpd/unix-directory') {
255
-				return 'dir';
256
-			}
257
-			return 'file';
258
-		} else {
259
-			return false;
260
-		}
261
-	}
262
-
263
-	public function fopen($path, $mode) {
264
-		$path = $this->normalizePath($path);
265
-
266
-		if (strrpos($path, '.') !== false) {
267
-			$ext = substr($path, strrpos($path, '.'));
268
-		} else {
269
-			$ext = '';
270
-		}
271
-
272
-		switch ($mode) {
273
-			case 'r':
274
-			case 'rb':
275
-				$stat = $this->stat($path);
276
-				if (is_array($stat)) {
277
-					try {
278
-						return $this->objectStore->readObject($this->getURN($stat['fileid']));
279
-					} catch (NotFoundException $e) {
280
-						$this->logger->logException($e, [
281
-							'app' => 'objectstore',
282
-							'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
283
-						]);
284
-						throw $e;
285
-					} catch (\Exception $ex) {
286
-						$this->logger->logException($ex, [
287
-							'app' => 'objectstore',
288
-							'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
289
-						]);
290
-						return false;
291
-					}
292
-				} else {
293
-					return false;
294
-				}
295
-			case 'w':
296
-			case 'wb':
297
-			case 'w+':
298
-			case 'wb+':
299
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
300
-				$handle = fopen($tmpFile, $mode);
301
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
302
-					$this->writeBack($tmpFile, $path);
303
-				});
304
-			case 'a':
305
-			case 'ab':
306
-			case 'r+':
307
-			case 'a+':
308
-			case 'x':
309
-			case 'x+':
310
-			case 'c':
311
-			case 'c+':
312
-				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
313
-				if ($this->file_exists($path)) {
314
-					$source = $this->fopen($path, 'r');
315
-					file_put_contents($tmpFile, $source);
316
-				}
317
-				$handle = fopen($tmpFile, $mode);
318
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
319
-					$this->writeBack($tmpFile, $path);
320
-				});
321
-		}
322
-		return false;
323
-	}
324
-
325
-	public function file_exists($path) {
326
-		$path = $this->normalizePath($path);
327
-		return (bool)$this->stat($path);
328
-	}
329
-
330
-	public function rename($source, $target) {
331
-		$source = $this->normalizePath($source);
332
-		$target = $this->normalizePath($target);
333
-		$this->remove($target);
334
-		$this->getCache()->move($source, $target);
335
-		$this->touch(dirname($target));
336
-		return true;
337
-	}
338
-
339
-	public function getMimeType($path) {
340
-		$path = $this->normalizePath($path);
341
-		$stat = $this->stat($path);
342
-		if (is_array($stat)) {
343
-			return $stat['mimetype'];
344
-		} else {
345
-			return false;
346
-		}
347
-	}
348
-
349
-	public function touch($path, $mtime = null) {
350
-		if (is_null($mtime)) {
351
-			$mtime = time();
352
-		}
353
-
354
-		$path = $this->normalizePath($path);
355
-		$dirName = dirname($path);
356
-		$parentExists = $this->is_dir($dirName);
357
-		if (!$parentExists) {
358
-			return false;
359
-		}
360
-
361
-		$stat = $this->stat($path);
362
-		if (is_array($stat)) {
363
-			// update existing mtime in db
364
-			$stat['mtime'] = $mtime;
365
-			$this->getCache()->update($stat['fileid'], $stat);
366
-		} else {
367
-			try {
368
-				//create a empty file, need to have at least on char to make it
369
-				// work with all object storage implementations
370
-				$this->file_put_contents($path, ' ');
371
-				$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
372
-				$stat = array(
373
-					'etag' => $this->getETag($path),
374
-					'mimetype' => $mimeType,
375
-					'size' => 0,
376
-					'mtime' => $mtime,
377
-					'storage_mtime' => $mtime,
378
-					'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
379
-				);
380
-				$this->getCache()->put($path, $stat);
381
-			} catch (\Exception $ex) {
382
-				$this->logger->logException($ex, [
383
-					'app' => 'objectstore',
384
-					'message' => 'Could not create object for ' . $path,
385
-				]);
386
-				throw $ex;
387
-			}
388
-		}
389
-		return true;
390
-	}
391
-
392
-	public function writeBack($tmpFile, $path) {
393
-		$size = filesize($tmpFile);
394
-		$this->writeStream($path, fopen($tmpFile, 'r'), $size);
395
-	}
396
-
397
-	/**
398
-	 * external changes are not supported, exclusive access to the object storage is assumed
399
-	 *
400
-	 * @param string $path
401
-	 * @param int $time
402
-	 * @return false
403
-	 */
404
-	public function hasUpdated($path, $time) {
405
-		return false;
406
-	}
407
-
408
-	public function needsPartFile() {
409
-		return false;
410
-	}
411
-
412
-	public function file_put_contents($path, $data) {
413
-		$stream = fopen('php://temp', 'r+');
414
-		fwrite($stream, $data);
415
-		rewind($stream);
416
-		return $this->writeStream($path, $stream, strlen($data)) > 0;
417
-	}
418
-
419
-	public function writeStream(string $path, $stream, int $size = null): int {
420
-		$stat = $this->stat($path);
421
-		if (empty($stat)) {
422
-			// create new file
423
-			$stat = [
424
-				'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
425
-			];
426
-		}
427
-		// update stat with new data
428
-		$mTime = time();
429
-		$stat['size'] = (int)$size;
430
-		$stat['mtime'] = $mTime;
431
-		$stat['storage_mtime'] = $mTime;
432
-
433
-		$mimetypeDetector = \OC::$server->getMimeTypeDetector();
434
-		$mimetype = $mimetypeDetector->detectPath($path);
435
-
436
-		$stat['mimetype'] = $mimetype;
437
-		$stat['etag'] = $this->getETag($path);
438
-
439
-		$fileId = $this->getCache()->put($path, $stat);
440
-		try {
441
-			//upload to object storage
442
-			if ($size === null) {
443
-				$countStream = CountReadStream::wrap($stream, function ($writtenSize) use ($fileId, &$size) {
444
-					$this->getCache()->update($fileId, [
445
-						'size' => $writtenSize
446
-					]);
447
-					$size = $writtenSize;
448
-				});
449
-				$this->objectStore->writeObject($this->getURN($fileId), $countStream);
450
-				if (is_resource($countStream)) {
451
-					fclose($countStream);
452
-				}
453
-			} else {
454
-				$this->objectStore->writeObject($this->getURN($fileId), $stream);
455
-			}
456
-		} catch (\Exception $ex) {
457
-			$this->getCache()->remove($path);
458
-			$this->logger->logException($ex, [
459
-				'app' => 'objectstore',
460
-				'message' => 'Could not create object ' . $this->getURN($fileId) . ' for ' . $path,
461
-			]);
462
-			throw $ex; // make this bubble up
463
-		}
464
-
465
-		return $size;
466
-	}
36
+    /**
37
+     * @var \OCP\Files\ObjectStore\IObjectStore $objectStore
38
+     */
39
+    protected $objectStore;
40
+    /**
41
+     * @var string $id
42
+     */
43
+    protected $id;
44
+    /**
45
+     * @var \OC\User\User $user
46
+     */
47
+    protected $user;
48
+
49
+    private $objectPrefix = 'urn:oid:';
50
+
51
+    private $logger;
52
+
53
+    public function __construct($params) {
54
+        if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) {
55
+            $this->objectStore = $params['objectstore'];
56
+        } else {
57
+            throw new \Exception('missing IObjectStore instance');
58
+        }
59
+        if (isset($params['storageid'])) {
60
+            $this->id = 'object::store:' . $params['storageid'];
61
+        } else {
62
+            $this->id = 'object::store:' . $this->objectStore->getStorageId();
63
+        }
64
+        if (isset($params['objectPrefix'])) {
65
+            $this->objectPrefix = $params['objectPrefix'];
66
+        }
67
+        //initialize cache with root directory in cache
68
+        if (!$this->is_dir('/')) {
69
+            $this->mkdir('/');
70
+        }
71
+
72
+        $this->logger = \OC::$server->getLogger();
73
+    }
74
+
75
+    public function mkdir($path) {
76
+        $path = $this->normalizePath($path);
77
+
78
+        if ($this->file_exists($path)) {
79
+            return false;
80
+        }
81
+
82
+        $mTime = time();
83
+        $data = [
84
+            'mimetype' => 'httpd/unix-directory',
85
+            'size' => 0,
86
+            'mtime' => $mTime,
87
+            'storage_mtime' => $mTime,
88
+            'permissions' => \OCP\Constants::PERMISSION_ALL,
89
+        ];
90
+        if ($path === '') {
91
+            //create root on the fly
92
+            $data['etag'] = $this->getETag('');
93
+            $this->getCache()->put('', $data);
94
+            return true;
95
+        } else {
96
+            // if parent does not exist, create it
97
+            $parent = $this->normalizePath(dirname($path));
98
+            $parentType = $this->filetype($parent);
99
+            if ($parentType === false) {
100
+                if (!$this->mkdir($parent)) {
101
+                    // something went wrong
102
+                    return false;
103
+                }
104
+            } else if ($parentType === 'file') {
105
+                // parent is a file
106
+                return false;
107
+            }
108
+            // finally create the new dir
109
+            $mTime = time(); // update mtime
110
+            $data['mtime'] = $mTime;
111
+            $data['storage_mtime'] = $mTime;
112
+            $data['etag'] = $this->getETag($path);
113
+            $this->getCache()->put($path, $data);
114
+            return true;
115
+        }
116
+    }
117
+
118
+    /**
119
+     * @param string $path
120
+     * @return string
121
+     */
122
+    private function normalizePath($path) {
123
+        $path = trim($path, '/');
124
+        //FIXME why do we sometimes get a path like 'files//username'?
125
+        $path = str_replace('//', '/', $path);
126
+
127
+        // dirname('/folder') returns '.' but internally (in the cache) we store the root as ''
128
+        if (!$path || $path === '.') {
129
+            $path = '';
130
+        }
131
+
132
+        return $path;
133
+    }
134
+
135
+    /**
136
+     * Object Stores use a NoopScanner because metadata is directly stored in
137
+     * the file cache and cannot really scan the filesystem. The storage passed in is not used anywhere.
138
+     *
139
+     * @param string $path
140
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner
141
+     * @return \OC\Files\ObjectStore\NoopScanner
142
+     */
143
+    public function getScanner($path = '', $storage = null) {
144
+        if (!$storage) {
145
+            $storage = $this;
146
+        }
147
+        if (!isset($this->scanner)) {
148
+            $this->scanner = new NoopScanner($storage);
149
+        }
150
+        return $this->scanner;
151
+    }
152
+
153
+    public function getId() {
154
+        return $this->id;
155
+    }
156
+
157
+    public function rmdir($path) {
158
+        $path = $this->normalizePath($path);
159
+
160
+        if (!$this->is_dir($path)) {
161
+            return false;
162
+        }
163
+
164
+        $this->rmObjects($path);
165
+
166
+        $this->getCache()->remove($path);
167
+
168
+        return true;
169
+    }
170
+
171
+    private function rmObjects($path) {
172
+        $children = $this->getCache()->getFolderContents($path);
173
+        foreach ($children as $child) {
174
+            if ($child['mimetype'] === 'httpd/unix-directory') {
175
+                $this->rmObjects($child['path']);
176
+            } else {
177
+                $this->unlink($child['path']);
178
+            }
179
+        }
180
+    }
181
+
182
+    public function unlink($path) {
183
+        $path = $this->normalizePath($path);
184
+        $stat = $this->stat($path);
185
+
186
+        if ($stat && isset($stat['fileid'])) {
187
+            if ($stat['mimetype'] === 'httpd/unix-directory') {
188
+                return $this->rmdir($path);
189
+            }
190
+            try {
191
+                $this->objectStore->deleteObject($this->getURN($stat['fileid']));
192
+            } catch (\Exception $ex) {
193
+                if ($ex->getCode() !== 404) {
194
+                    $this->logger->logException($ex, [
195
+                        'app' => 'objectstore',
196
+                        'message' => 'Could not delete object ' . $this->getURN($stat['fileid']) . ' for ' . $path,
197
+                    ]);
198
+                    return false;
199
+                }
200
+                //removing from cache is ok as it does not exist in the objectstore anyway
201
+            }
202
+            $this->getCache()->remove($path);
203
+            return true;
204
+        }
205
+        return false;
206
+    }
207
+
208
+    public function stat($path) {
209
+        $path = $this->normalizePath($path);
210
+        $cacheEntry = $this->getCache()->get($path);
211
+        if ($cacheEntry instanceof CacheEntry) {
212
+            return $cacheEntry->getData();
213
+        } else {
214
+            return false;
215
+        }
216
+    }
217
+
218
+    /**
219
+     * Override this method if you need a different unique resource identifier for your object storage implementation.
220
+     * The default implementations just appends the fileId to 'urn:oid:'. Make sure the URN is unique over all users.
221
+     * You may need a mapping table to store your URN if it cannot be generated from the fileid.
222
+     *
223
+     * @param int $fileId the fileid
224
+     * @return null|string the unified resource name used to identify the object
225
+     */
226
+    protected function getURN($fileId) {
227
+        if (is_numeric($fileId)) {
228
+            return $this->objectPrefix . $fileId;
229
+        }
230
+        return null;
231
+    }
232
+
233
+    public function opendir($path) {
234
+        $path = $this->normalizePath($path);
235
+
236
+        try {
237
+            $files = array();
238
+            $folderContents = $this->getCache()->getFolderContents($path);
239
+            foreach ($folderContents as $file) {
240
+                $files[] = $file['name'];
241
+            }
242
+
243
+            return IteratorDirectory::wrap($files);
244
+        } catch (\Exception $e) {
245
+            $this->logger->logException($e);
246
+            return false;
247
+        }
248
+    }
249
+
250
+    public function filetype($path) {
251
+        $path = $this->normalizePath($path);
252
+        $stat = $this->stat($path);
253
+        if ($stat) {
254
+            if ($stat['mimetype'] === 'httpd/unix-directory') {
255
+                return 'dir';
256
+            }
257
+            return 'file';
258
+        } else {
259
+            return false;
260
+        }
261
+    }
262
+
263
+    public function fopen($path, $mode) {
264
+        $path = $this->normalizePath($path);
265
+
266
+        if (strrpos($path, '.') !== false) {
267
+            $ext = substr($path, strrpos($path, '.'));
268
+        } else {
269
+            $ext = '';
270
+        }
271
+
272
+        switch ($mode) {
273
+            case 'r':
274
+            case 'rb':
275
+                $stat = $this->stat($path);
276
+                if (is_array($stat)) {
277
+                    try {
278
+                        return $this->objectStore->readObject($this->getURN($stat['fileid']));
279
+                    } catch (NotFoundException $e) {
280
+                        $this->logger->logException($e, [
281
+                            'app' => 'objectstore',
282
+                            'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
283
+                        ]);
284
+                        throw $e;
285
+                    } catch (\Exception $ex) {
286
+                        $this->logger->logException($ex, [
287
+                            'app' => 'objectstore',
288
+                            'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
289
+                        ]);
290
+                        return false;
291
+                    }
292
+                } else {
293
+                    return false;
294
+                }
295
+            case 'w':
296
+            case 'wb':
297
+            case 'w+':
298
+            case 'wb+':
299
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
300
+                $handle = fopen($tmpFile, $mode);
301
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
302
+                    $this->writeBack($tmpFile, $path);
303
+                });
304
+            case 'a':
305
+            case 'ab':
306
+            case 'r+':
307
+            case 'a+':
308
+            case 'x':
309
+            case 'x+':
310
+            case 'c':
311
+            case 'c+':
312
+                $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
313
+                if ($this->file_exists($path)) {
314
+                    $source = $this->fopen($path, 'r');
315
+                    file_put_contents($tmpFile, $source);
316
+                }
317
+                $handle = fopen($tmpFile, $mode);
318
+                return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
319
+                    $this->writeBack($tmpFile, $path);
320
+                });
321
+        }
322
+        return false;
323
+    }
324
+
325
+    public function file_exists($path) {
326
+        $path = $this->normalizePath($path);
327
+        return (bool)$this->stat($path);
328
+    }
329
+
330
+    public function rename($source, $target) {
331
+        $source = $this->normalizePath($source);
332
+        $target = $this->normalizePath($target);
333
+        $this->remove($target);
334
+        $this->getCache()->move($source, $target);
335
+        $this->touch(dirname($target));
336
+        return true;
337
+    }
338
+
339
+    public function getMimeType($path) {
340
+        $path = $this->normalizePath($path);
341
+        $stat = $this->stat($path);
342
+        if (is_array($stat)) {
343
+            return $stat['mimetype'];
344
+        } else {
345
+            return false;
346
+        }
347
+    }
348
+
349
+    public function touch($path, $mtime = null) {
350
+        if (is_null($mtime)) {
351
+            $mtime = time();
352
+        }
353
+
354
+        $path = $this->normalizePath($path);
355
+        $dirName = dirname($path);
356
+        $parentExists = $this->is_dir($dirName);
357
+        if (!$parentExists) {
358
+            return false;
359
+        }
360
+
361
+        $stat = $this->stat($path);
362
+        if (is_array($stat)) {
363
+            // update existing mtime in db
364
+            $stat['mtime'] = $mtime;
365
+            $this->getCache()->update($stat['fileid'], $stat);
366
+        } else {
367
+            try {
368
+                //create a empty file, need to have at least on char to make it
369
+                // work with all object storage implementations
370
+                $this->file_put_contents($path, ' ');
371
+                $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
372
+                $stat = array(
373
+                    'etag' => $this->getETag($path),
374
+                    'mimetype' => $mimeType,
375
+                    'size' => 0,
376
+                    'mtime' => $mtime,
377
+                    'storage_mtime' => $mtime,
378
+                    'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
379
+                );
380
+                $this->getCache()->put($path, $stat);
381
+            } catch (\Exception $ex) {
382
+                $this->logger->logException($ex, [
383
+                    'app' => 'objectstore',
384
+                    'message' => 'Could not create object for ' . $path,
385
+                ]);
386
+                throw $ex;
387
+            }
388
+        }
389
+        return true;
390
+    }
391
+
392
+    public function writeBack($tmpFile, $path) {
393
+        $size = filesize($tmpFile);
394
+        $this->writeStream($path, fopen($tmpFile, 'r'), $size);
395
+    }
396
+
397
+    /**
398
+     * external changes are not supported, exclusive access to the object storage is assumed
399
+     *
400
+     * @param string $path
401
+     * @param int $time
402
+     * @return false
403
+     */
404
+    public function hasUpdated($path, $time) {
405
+        return false;
406
+    }
407
+
408
+    public function needsPartFile() {
409
+        return false;
410
+    }
411
+
412
+    public function file_put_contents($path, $data) {
413
+        $stream = fopen('php://temp', 'r+');
414
+        fwrite($stream, $data);
415
+        rewind($stream);
416
+        return $this->writeStream($path, $stream, strlen($data)) > 0;
417
+    }
418
+
419
+    public function writeStream(string $path, $stream, int $size = null): int {
420
+        $stat = $this->stat($path);
421
+        if (empty($stat)) {
422
+            // create new file
423
+            $stat = [
424
+                'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
425
+            ];
426
+        }
427
+        // update stat with new data
428
+        $mTime = time();
429
+        $stat['size'] = (int)$size;
430
+        $stat['mtime'] = $mTime;
431
+        $stat['storage_mtime'] = $mTime;
432
+
433
+        $mimetypeDetector = \OC::$server->getMimeTypeDetector();
434
+        $mimetype = $mimetypeDetector->detectPath($path);
435
+
436
+        $stat['mimetype'] = $mimetype;
437
+        $stat['etag'] = $this->getETag($path);
438
+
439
+        $fileId = $this->getCache()->put($path, $stat);
440
+        try {
441
+            //upload to object storage
442
+            if ($size === null) {
443
+                $countStream = CountReadStream::wrap($stream, function ($writtenSize) use ($fileId, &$size) {
444
+                    $this->getCache()->update($fileId, [
445
+                        'size' => $writtenSize
446
+                    ]);
447
+                    $size = $writtenSize;
448
+                });
449
+                $this->objectStore->writeObject($this->getURN($fileId), $countStream);
450
+                if (is_resource($countStream)) {
451
+                    fclose($countStream);
452
+                }
453
+            } else {
454
+                $this->objectStore->writeObject($this->getURN($fileId), $stream);
455
+            }
456
+        } catch (\Exception $ex) {
457
+            $this->getCache()->remove($path);
458
+            $this->logger->logException($ex, [
459
+                'app' => 'objectstore',
460
+                'message' => 'Could not create object ' . $this->getURN($fileId) . ' for ' . $path,
461
+            ]);
462
+            throw $ex; // make this bubble up
463
+        }
464
+
465
+        return $size;
466
+    }
467 467
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -57,9 +57,9 @@  discard block
 block discarded – undo
57 57
 			throw new \Exception('missing IObjectStore instance');
58 58
 		}
59 59
 		if (isset($params['storageid'])) {
60
-			$this->id = 'object::store:' . $params['storageid'];
60
+			$this->id = 'object::store:'.$params['storageid'];
61 61
 		} else {
62
-			$this->id = 'object::store:' . $this->objectStore->getStorageId();
62
+			$this->id = 'object::store:'.$this->objectStore->getStorageId();
63 63
 		}
64 64
 		if (isset($params['objectPrefix'])) {
65 65
 			$this->objectPrefix = $params['objectPrefix'];
@@ -193,7 +193,7 @@  discard block
 block discarded – undo
193 193
 				if ($ex->getCode() !== 404) {
194 194
 					$this->logger->logException($ex, [
195 195
 						'app' => 'objectstore',
196
-						'message' => 'Could not delete object ' . $this->getURN($stat['fileid']) . ' for ' . $path,
196
+						'message' => 'Could not delete object '.$this->getURN($stat['fileid']).' for '.$path,
197 197
 					]);
198 198
 					return false;
199 199
 				}
@@ -225,7 +225,7 @@  discard block
 block discarded – undo
225 225
 	 */
226 226
 	protected function getURN($fileId) {
227 227
 		if (is_numeric($fileId)) {
228
-			return $this->objectPrefix . $fileId;
228
+			return $this->objectPrefix.$fileId;
229 229
 		}
230 230
 		return null;
231 231
 	}
@@ -279,13 +279,13 @@  discard block
 block discarded – undo
279 279
 					} catch (NotFoundException $e) {
280 280
 						$this->logger->logException($e, [
281 281
 							'app' => 'objectstore',
282
-							'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
282
+							'message' => 'Could not get object '.$this->getURN($stat['fileid']).' for file '.$path,
283 283
 						]);
284 284
 						throw $e;
285 285
 					} catch (\Exception $ex) {
286 286
 						$this->logger->logException($ex, [
287 287
 							'app' => 'objectstore',
288
-							'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path,
288
+							'message' => 'Could not get object '.$this->getURN($stat['fileid']).' for file '.$path,
289 289
 						]);
290 290
 						return false;
291 291
 					}
@@ -298,7 +298,7 @@  discard block
 block discarded – undo
298 298
 			case 'wb+':
299 299
 				$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
300 300
 				$handle = fopen($tmpFile, $mode);
301
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
301
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
302 302
 					$this->writeBack($tmpFile, $path);
303 303
 				});
304 304
 			case 'a':
@@ -315,7 +315,7 @@  discard block
 block discarded – undo
315 315
 					file_put_contents($tmpFile, $source);
316 316
 				}
317 317
 				$handle = fopen($tmpFile, $mode);
318
-				return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
318
+				return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) {
319 319
 					$this->writeBack($tmpFile, $path);
320 320
 				});
321 321
 		}
@@ -324,7 +324,7 @@  discard block
 block discarded – undo
324 324
 
325 325
 	public function file_exists($path) {
326 326
 		$path = $this->normalizePath($path);
327
-		return (bool)$this->stat($path);
327
+		return (bool) $this->stat($path);
328 328
 	}
329 329
 
330 330
 	public function rename($source, $target) {
@@ -381,7 +381,7 @@  discard block
 block discarded – undo
381 381
 			} catch (\Exception $ex) {
382 382
 				$this->logger->logException($ex, [
383 383
 					'app' => 'objectstore',
384
-					'message' => 'Could not create object for ' . $path,
384
+					'message' => 'Could not create object for '.$path,
385 385
 				]);
386 386
 				throw $ex;
387 387
 			}
@@ -426,7 +426,7 @@  discard block
 block discarded – undo
426 426
 		}
427 427
 		// update stat with new data
428 428
 		$mTime = time();
429
-		$stat['size'] = (int)$size;
429
+		$stat['size'] = (int) $size;
430 430
 		$stat['mtime'] = $mTime;
431 431
 		$stat['storage_mtime'] = $mTime;
432 432
 
@@ -440,7 +440,7 @@  discard block
 block discarded – undo
440 440
 		try {
441 441
 			//upload to object storage
442 442
 			if ($size === null) {
443
-				$countStream = CountReadStream::wrap($stream, function ($writtenSize) use ($fileId, &$size) {
443
+				$countStream = CountReadStream::wrap($stream, function($writtenSize) use ($fileId, &$size) {
444 444
 					$this->getCache()->update($fileId, [
445 445
 						'size' => $writtenSize
446 446
 					]);
@@ -457,7 +457,7 @@  discard block
 block discarded – undo
457 457
 			$this->getCache()->remove($path);
458 458
 			$this->logger->logException($ex, [
459 459
 				'app' => 'objectstore',
460
-				'message' => 'Could not create object ' . $this->getURN($fileId) . ' for ' . $path,
460
+				'message' => 'Could not create object '.$this->getURN($fileId).' for '.$path,
461 461
 			]);
462 462
 			throw $ex; // make this bubble up
463 463
 		}
Please login to merge, or discard this patch.
lib/public/Files/ObjectStore/IObjectStore.php 1 patch
Indentation   +27 added lines, -27 removed lines patch added patch discarded remove patch
@@ -33,34 +33,34 @@
 block discarded – undo
33 33
  */
34 34
 interface IObjectStore {
35 35
 
36
-	/**
37
-	 * @return string the container or bucket name where objects are stored
38
-	 * @since 7.0.0
39
-	 */
40
-	public function getStorageId();
36
+    /**
37
+     * @return string the container or bucket name where objects are stored
38
+     * @since 7.0.0
39
+     */
40
+    public function getStorageId();
41 41
 
42
-	/**
43
-	 * @param string $urn the unified resource name used to identify the object
44
-	 * @return resource stream with the read data
45
-	 * @throws \Exception when something goes wrong, message will be logged
46
-	 * @throws NotFoundException if file does not exist
47
-	 * @since 7.0.0
48
-	 */
49
-	public function readObject($urn);
42
+    /**
43
+     * @param string $urn the unified resource name used to identify the object
44
+     * @return resource stream with the read data
45
+     * @throws \Exception when something goes wrong, message will be logged
46
+     * @throws NotFoundException if file does not exist
47
+     * @since 7.0.0
48
+     */
49
+    public function readObject($urn);
50 50
 
51
-	/**
52
-	 * @param string $urn the unified resource name used to identify the object
53
-	 * @param resource $stream stream with the data to write
54
-	 * @throws \Exception when something goes wrong, message will be logged
55
-	 * @since 7.0.0
56
-	 */
57
-	public function writeObject($urn, $stream);
51
+    /**
52
+     * @param string $urn the unified resource name used to identify the object
53
+     * @param resource $stream stream with the data to write
54
+     * @throws \Exception when something goes wrong, message will be logged
55
+     * @since 7.0.0
56
+     */
57
+    public function writeObject($urn, $stream);
58 58
 
59
-	/**
60
-	 * @param string $urn the unified resource name used to identify the object
61
-	 * @return void
62
-	 * @throws \Exception when something goes wrong, message will be logged
63
-	 * @since 7.0.0
64
-	 */
65
-	public function deleteObject($urn);
59
+    /**
60
+     * @param string $urn the unified resource name used to identify the object
61
+     * @return void
62
+     * @throws \Exception when something goes wrong, message will be logged
63
+     * @since 7.0.0
64
+     */
65
+    public function deleteObject($urn);
66 66
 }
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/File.php 2 patches
Indentation   +545 added lines, -545 removed lines patch added patch discarded remove patch
@@ -67,549 +67,549 @@
 block discarded – undo
67 67
 
68 68
 class File extends Node implements IFile {
69 69
 
70
-	protected $request;
71
-
72
-	/**
73
-	 * Sets up the node, expects a full path name
74
-	 *
75
-	 * @param \OC\Files\View $view
76
-	 * @param \OCP\Files\FileInfo $info
77
-	 * @param \OCP\Share\IManager $shareManager
78
-	 * @param \OC\AppFramework\Http\Request $request
79
-	 */
80
-	public function __construct(View $view, FileInfo $info, IManager $shareManager = null, Request $request = null) {
81
-		parent::__construct($view, $info, $shareManager);
82
-
83
-		if (isset($request)) {
84
-			$this->request = $request;
85
-		} else {
86
-			$this->request = \OC::$server->getRequest();
87
-		}
88
-	}
89
-
90
-	/**
91
-	 * Updates the data
92
-	 *
93
-	 * The data argument is a readable stream resource.
94
-	 *
95
-	 * After a successful put operation, you may choose to return an ETag. The
96
-	 * etag must always be surrounded by double-quotes. These quotes must
97
-	 * appear in the actual string you're returning.
98
-	 *
99
-	 * Clients may use the ETag from a PUT request to later on make sure that
100
-	 * when they update the file, the contents haven't changed in the mean
101
-	 * time.
102
-	 *
103
-	 * If you don't plan to store the file byte-by-byte, and you return a
104
-	 * different object on a subsequent GET you are strongly recommended to not
105
-	 * return an ETag, and just return null.
106
-	 *
107
-	 * @param resource $data
108
-	 *
109
-	 * @throws Forbidden
110
-	 * @throws UnsupportedMediaType
111
-	 * @throws BadRequest
112
-	 * @throws Exception
113
-	 * @throws EntityTooLarge
114
-	 * @throws ServiceUnavailable
115
-	 * @throws FileLocked
116
-	 * @return string|null
117
-	 */
118
-	public function put($data) {
119
-		try {
120
-			$exists = $this->fileView->file_exists($this->path);
121
-			if ($this->info && $exists && !$this->info->isUpdateable()) {
122
-				throw new Forbidden();
123
-			}
124
-		} catch (StorageNotAvailableException $e) {
125
-			throw new ServiceUnavailable("File is not updatable: " . $e->getMessage());
126
-		}
127
-
128
-		// verify path of the target
129
-		$this->verifyPath();
130
-
131
-		// chunked handling
132
-		if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
133
-			try {
134
-				return $this->createFileChunked($data);
135
-			} catch (\Exception $e) {
136
-				$this->convertToSabreException($e);
137
-			}
138
-		}
139
-
140
-		/** @var Storage $partStorage */
141
-		list($partStorage) = $this->fileView->resolvePath($this->path);
142
-		$needsPartFile = $partStorage->needsPartFile() && (strlen($this->path) > 1);
143
-
144
-		$view = \OC\Files\Filesystem::getView();
145
-
146
-		if ($needsPartFile) {
147
-			// mark file as partial while uploading (ignored by the scanner)
148
-			$partFilePath = $this->getPartFileBasePath($this->path) . '.ocTransferId' . rand() . '.part';
149
-		} else {
150
-			// upload file directly as the final path
151
-			$partFilePath = $this->path;
152
-
153
-			if ($view && !$this->emitPreHooks($exists)) {
154
-				throw new Exception('Could not write to final file, canceled by hook');
155
-			}
156
-		}
157
-
158
-		// the part file and target file might be on a different storage in case of a single file storage (e.g. single file share)
159
-		/** @var \OC\Files\Storage\Storage $partStorage */
160
-		list($partStorage, $internalPartPath) = $this->fileView->resolvePath($partFilePath);
161
-		/** @var \OC\Files\Storage\Storage $storage */
162
-		list($storage, $internalPath) = $this->fileView->resolvePath($this->path);
163
-		try {
164
-			if (!$needsPartFile) {
165
-				$this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
166
-			}
167
-
168
-			if ($partStorage->instanceOfStorage(Storage\IWriteStreamStorage::class)) {
169
-				$count = $partStorage->writeStream($internalPartPath, $data);
170
-				$result = $count > 0;
171
-				if ($result === false) {
172
-					$result = feof($data);
173
-				}
174
-
175
-			} else {
176
-				$target = $partStorage->fopen($internalPartPath, 'wb');
177
-				if ($target === false) {
178
-					\OC::$server->getLogger()->error('\OC\Files\Filesystem::fopen() failed', ['app' => 'webdav']);
179
-					// because we have no clue about the cause we can only throw back a 500/Internal Server Error
180
-					throw new Exception('Could not write file contents');
181
-				}
182
-				list($count, $result) = \OC_Helper::streamCopy($data, $target);
183
-				fclose($target);
184
-			}
185
-
186
-			if ($result === false) {
187
-				$expected = -1;
188
-				if (isset($_SERVER['CONTENT_LENGTH'])) {
189
-					$expected = $_SERVER['CONTENT_LENGTH'];
190
-				}
191
-				throw new Exception('Error while copying file to target location (copied bytes: ' . $count . ', expected filesize: ' . $expected . ' )');
192
-			}
193
-
194
-			// if content length is sent by client:
195
-			// double check if the file was fully received
196
-			// compare expected and actual size
197
-			if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
198
-				$expected = (int)$_SERVER['CONTENT_LENGTH'];
199
-				if ($count !== $expected) {
200
-					throw new BadRequest('expected filesize ' . $expected . ' got ' . $count);
201
-				}
202
-			}
203
-
204
-		} catch (\Exception $e) {
205
-			\OC::$server->getLogger()->logException($e);
206
-			if ($needsPartFile) {
207
-				$partStorage->unlink($internalPartPath);
208
-			}
209
-			$this->convertToSabreException($e);
210
-		}
211
-
212
-		try {
213
-			if ($needsPartFile) {
214
-				if ($view && !$this->emitPreHooks($exists)) {
215
-					$partStorage->unlink($internalPartPath);
216
-					throw new Exception('Could not rename part file to final file, canceled by hook');
217
-				}
218
-				try {
219
-					$this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
220
-				} catch (LockedException $e) {
221
-					if ($needsPartFile) {
222
-						$partStorage->unlink($internalPartPath);
223
-					}
224
-					throw new FileLocked($e->getMessage(), $e->getCode(), $e);
225
-				}
226
-
227
-				// rename to correct path
228
-				try {
229
-					$renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath);
230
-					$fileExists = $storage->file_exists($internalPath);
231
-					if ($renameOkay === false || $fileExists === false) {
232
-						\OC::$server->getLogger()->error('renaming part file to final file failed $renameOkay: ' . ($renameOkay ? 'true' : 'false') . ', $fileExists: ' . ($fileExists ? 'true' : 'false') . ')', ['app' => 'webdav']);
233
-						throw new Exception('Could not rename part file to final file');
234
-					}
235
-				} catch (ForbiddenException $ex) {
236
-					throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
237
-				} catch (\Exception $e) {
238
-					$partStorage->unlink($internalPartPath);
239
-					$this->convertToSabreException($e);
240
-				}
241
-			}
242
-
243
-			// since we skipped the view we need to scan and emit the hooks ourselves
244
-			$storage->getUpdater()->update($internalPath);
245
-
246
-			try {
247
-				$this->changeLock(ILockingProvider::LOCK_SHARED);
248
-			} catch (LockedException $e) {
249
-				throw new FileLocked($e->getMessage(), $e->getCode(), $e);
250
-			}
251
-
252
-			// allow sync clients to send the mtime along in a header
253
-			if (isset($this->request->server['HTTP_X_OC_MTIME'])) {
254
-				$mtime = $this->sanitizeMtime($this->request->server['HTTP_X_OC_MTIME']);
255
-				if ($this->fileView->touch($this->path, $mtime)) {
256
-					$this->header('X-OC-MTime: accepted');
257
-				}
258
-			}
259
-
260
-			if ($view) {
261
-				$this->emitPostHooks($exists);
262
-			}
263
-
264
-			$this->refreshInfo();
265
-
266
-			if (isset($this->request->server['HTTP_OC_CHECKSUM'])) {
267
-				$checksum = trim($this->request->server['HTTP_OC_CHECKSUM']);
268
-				$this->fileView->putFileInfo($this->path, ['checksum' => $checksum]);
269
-				$this->refreshInfo();
270
-			} else if ($this->getChecksum() !== null && $this->getChecksum() !== '') {
271
-				$this->fileView->putFileInfo($this->path, ['checksum' => '']);
272
-				$this->refreshInfo();
273
-			}
274
-
275
-		} catch (StorageNotAvailableException $e) {
276
-			throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage());
277
-		}
278
-
279
-		return '"' . $this->info->getEtag() . '"';
280
-	}
281
-
282
-	private function getPartFileBasePath($path) {
283
-		$partFileInStorage = \OC::$server->getConfig()->getSystemValue('part_file_in_storage', true);
284
-		if ($partFileInStorage) {
285
-			return $path;
286
-		} else {
287
-			return md5($path); // will place it in the root of the view with a unique name
288
-		}
289
-	}
290
-
291
-	/**
292
-	 * @param string $path
293
-	 */
294
-	private function emitPreHooks($exists, $path = null) {
295
-		if (is_null($path)) {
296
-			$path = $this->path;
297
-		}
298
-		$hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path));
299
-		$run = true;
300
-
301
-		if (!$exists) {
302
-			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_create, array(
303
-				\OC\Files\Filesystem::signal_param_path => $hookPath,
304
-				\OC\Files\Filesystem::signal_param_run => &$run,
305
-			));
306
-		} else {
307
-			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_update, array(
308
-				\OC\Files\Filesystem::signal_param_path => $hookPath,
309
-				\OC\Files\Filesystem::signal_param_run => &$run,
310
-			));
311
-		}
312
-		\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_write, array(
313
-			\OC\Files\Filesystem::signal_param_path => $hookPath,
314
-			\OC\Files\Filesystem::signal_param_run => &$run,
315
-		));
316
-		return $run;
317
-	}
318
-
319
-	/**
320
-	 * @param string $path
321
-	 */
322
-	private function emitPostHooks($exists, $path = null) {
323
-		if (is_null($path)) {
324
-			$path = $this->path;
325
-		}
326
-		$hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path));
327
-		if (!$exists) {
328
-			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array(
329
-				\OC\Files\Filesystem::signal_param_path => $hookPath
330
-			));
331
-		} else {
332
-			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_update, array(
333
-				\OC\Files\Filesystem::signal_param_path => $hookPath
334
-			));
335
-		}
336
-		\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_write, array(
337
-			\OC\Files\Filesystem::signal_param_path => $hookPath
338
-		));
339
-	}
340
-
341
-	/**
342
-	 * Returns the data
343
-	 *
344
-	 * @return resource
345
-	 * @throws Forbidden
346
-	 * @throws ServiceUnavailable
347
-	 */
348
-	public function get() {
349
-		//throw exception if encryption is disabled but files are still encrypted
350
-		try {
351
-			if (!$this->info->isReadable()) {
352
-				// do a if the file did not exist
353
-				throw new NotFound();
354
-			}
355
-			try {
356
-				$res = $this->fileView->fopen(ltrim($this->path, '/'), 'rb');
357
-			} catch (\Exception $e) {
358
-				$this->convertToSabreException($e);
359
-			}
360
-			if ($res === false) {
361
-				throw new ServiceUnavailable("Could not open file");
362
-			}
363
-			return $res;
364
-		} catch (GenericEncryptionException $e) {
365
-			// returning 503 will allow retry of the operation at a later point in time
366
-			throw new ServiceUnavailable("Encryption not ready: " . $e->getMessage());
367
-		} catch (StorageNotAvailableException $e) {
368
-			throw new ServiceUnavailable("Failed to open file: " . $e->getMessage());
369
-		} catch (ForbiddenException $ex) {
370
-			throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
371
-		} catch (LockedException $e) {
372
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
373
-		}
374
-	}
375
-
376
-	/**
377
-	 * Delete the current file
378
-	 *
379
-	 * @throws Forbidden
380
-	 * @throws ServiceUnavailable
381
-	 */
382
-	public function delete() {
383
-		if (!$this->info->isDeletable()) {
384
-			throw new Forbidden();
385
-		}
386
-
387
-		try {
388
-			if (!$this->fileView->unlink($this->path)) {
389
-				// assume it wasn't possible to delete due to permissions
390
-				throw new Forbidden();
391
-			}
392
-		} catch (StorageNotAvailableException $e) {
393
-			throw new ServiceUnavailable("Failed to unlink: " . $e->getMessage());
394
-		} catch (ForbiddenException $ex) {
395
-			throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
396
-		} catch (LockedException $e) {
397
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
398
-		}
399
-	}
400
-
401
-	/**
402
-	 * Returns the mime-type for a file
403
-	 *
404
-	 * If null is returned, we'll assume application/octet-stream
405
-	 *
406
-	 * @return string
407
-	 */
408
-	public function getContentType() {
409
-		$mimeType = $this->info->getMimetype();
410
-
411
-		// PROPFIND needs to return the correct mime type, for consistency with the web UI
412
-		if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
413
-			return $mimeType;
414
-		}
415
-		return \OC::$server->getMimeTypeDetector()->getSecureMimeType($mimeType);
416
-	}
417
-
418
-	/**
419
-	 * @return array|false
420
-	 */
421
-	public function getDirectDownload() {
422
-		if (\OCP\App::isEnabled('encryption')) {
423
-			return [];
424
-		}
425
-		/** @var \OCP\Files\Storage $storage */
426
-		list($storage, $internalPath) = $this->fileView->resolvePath($this->path);
427
-		if (is_null($storage)) {
428
-			return [];
429
-		}
430
-
431
-		return $storage->getDirectDownload($internalPath);
432
-	}
433
-
434
-	/**
435
-	 * @param resource $data
436
-	 * @return null|string
437
-	 * @throws Exception
438
-	 * @throws BadRequest
439
-	 * @throws NotImplemented
440
-	 * @throws ServiceUnavailable
441
-	 */
442
-	private function createFileChunked($data) {
443
-		list($path, $name) = \Sabre\Uri\split($this->path);
444
-
445
-		$info = \OC_FileChunking::decodeName($name);
446
-		if (empty($info)) {
447
-			throw new NotImplemented('Invalid chunk name');
448
-		}
449
-
450
-		$chunk_handler = new \OC_FileChunking($info);
451
-		$bytesWritten = $chunk_handler->store($info['index'], $data);
452
-
453
-		//detect aborted upload
454
-		if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
455
-			if (isset($_SERVER['CONTENT_LENGTH'])) {
456
-				$expected = (int)$_SERVER['CONTENT_LENGTH'];
457
-				if ($bytesWritten !== $expected) {
458
-					$chunk_handler->remove($info['index']);
459
-					throw new BadRequest(
460
-						'expected filesize ' . $expected . ' got ' . $bytesWritten);
461
-				}
462
-			}
463
-		}
464
-
465
-		if ($chunk_handler->isComplete()) {
466
-			/** @var Storage $storage */
467
-			list($storage,) = $this->fileView->resolvePath($path);
468
-			$needsPartFile = $storage->needsPartFile();
469
-			$partFile = null;
470
-
471
-			$targetPath = $path . '/' . $info['name'];
472
-			/** @var \OC\Files\Storage\Storage $targetStorage */
473
-			list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath);
474
-
475
-			$exists = $this->fileView->file_exists($targetPath);
476
-
477
-			try {
478
-				$this->fileView->lockFile($targetPath, ILockingProvider::LOCK_SHARED);
479
-
480
-				$this->emitPreHooks($exists, $targetPath);
481
-				$this->fileView->changeLock($targetPath, ILockingProvider::LOCK_EXCLUSIVE);
482
-				/** @var \OC\Files\Storage\Storage $targetStorage */
483
-				list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath);
484
-
485
-				if ($needsPartFile) {
486
-					// we first assembly the target file as a part file
487
-					$partFile = $this->getPartFileBasePath($path . '/' . $info['name']) . '.ocTransferId' . $info['transferid'] . '.part';
488
-					/** @var \OC\Files\Storage\Storage $targetStorage */
489
-					list($partStorage, $partInternalPath) = $this->fileView->resolvePath($partFile);
490
-
491
-
492
-					$chunk_handler->file_assemble($partStorage, $partInternalPath);
493
-
494
-					// here is the final atomic rename
495
-					$renameOkay = $targetStorage->moveFromStorage($partStorage, $partInternalPath, $targetInternalPath);
496
-					$fileExists = $targetStorage->file_exists($targetInternalPath);
497
-					if ($renameOkay === false || $fileExists === false) {
498
-						\OC::$server->getLogger()->error('\OC\Files\Filesystem::rename() failed', ['app' => 'webdav']);
499
-						// only delete if an error occurred and the target file was already created
500
-						if ($fileExists) {
501
-							// set to null to avoid double-deletion when handling exception
502
-							// stray part file
503
-							$partFile = null;
504
-							$targetStorage->unlink($targetInternalPath);
505
-						}
506
-						$this->fileView->changeLock($targetPath, ILockingProvider::LOCK_SHARED);
507
-						throw new Exception('Could not rename part file assembled from chunks');
508
-					}
509
-				} else {
510
-					// assemble directly into the final file
511
-					$chunk_handler->file_assemble($targetStorage, $targetInternalPath);
512
-				}
513
-
514
-				// allow sync clients to send the mtime along in a header
515
-				if (isset($this->request->server['HTTP_X_OC_MTIME'])) {
516
-					$mtime = $this->sanitizeMtime($this->request->server['HTTP_X_OC_MTIME']);
517
-					if ($targetStorage->touch($targetInternalPath, $mtime)) {
518
-						$this->header('X-OC-MTime: accepted');
519
-					}
520
-				}
521
-
522
-				// since we skipped the view we need to scan and emit the hooks ourselves
523
-				$targetStorage->getUpdater()->update($targetInternalPath);
524
-
525
-				$this->fileView->changeLock($targetPath, ILockingProvider::LOCK_SHARED);
526
-
527
-				$this->emitPostHooks($exists, $targetPath);
528
-
529
-				// FIXME: should call refreshInfo but can't because $this->path is not the of the final file
530
-				$info = $this->fileView->getFileInfo($targetPath);
531
-
532
-				if (isset($this->request->server['HTTP_OC_CHECKSUM'])) {
533
-					$checksum = trim($this->request->server['HTTP_OC_CHECKSUM']);
534
-					$this->fileView->putFileInfo($targetPath, ['checksum' => $checksum]);
535
-				} else if ($info->getChecksum() !== null && $info->getChecksum() !== '') {
536
-					$this->fileView->putFileInfo($this->path, ['checksum' => '']);
537
-				}
538
-
539
-				$this->fileView->unlockFile($targetPath, ILockingProvider::LOCK_SHARED);
540
-
541
-				return $info->getEtag();
542
-			} catch (\Exception $e) {
543
-				if ($partFile !== null) {
544
-					$targetStorage->unlink($targetInternalPath);
545
-				}
546
-				$this->convertToSabreException($e);
547
-			}
548
-		}
549
-
550
-		return null;
551
-	}
552
-
553
-	/**
554
-	 * Convert the given exception to a SabreException instance
555
-	 *
556
-	 * @param \Exception $e
557
-	 *
558
-	 * @throws \Sabre\DAV\Exception
559
-	 */
560
-	private function convertToSabreException(\Exception $e) {
561
-		if ($e instanceof \Sabre\DAV\Exception) {
562
-			throw $e;
563
-		}
564
-		if ($e instanceof NotPermittedException) {
565
-			// a more general case - due to whatever reason the content could not be written
566
-			throw new Forbidden($e->getMessage(), 0, $e);
567
-		}
568
-		if ($e instanceof ForbiddenException) {
569
-			// the path for the file was forbidden
570
-			throw new DAVForbiddenException($e->getMessage(), $e->getRetry(), $e);
571
-		}
572
-		if ($e instanceof EntityTooLargeException) {
573
-			// the file is too big to be stored
574
-			throw new EntityTooLarge($e->getMessage(), 0, $e);
575
-		}
576
-		if ($e instanceof InvalidContentException) {
577
-			// the file content is not permitted
578
-			throw new UnsupportedMediaType($e->getMessage(), 0, $e);
579
-		}
580
-		if ($e instanceof InvalidPathException) {
581
-			// the path for the file was not valid
582
-			// TODO: find proper http status code for this case
583
-			throw new Forbidden($e->getMessage(), 0, $e);
584
-		}
585
-		if ($e instanceof LockedException || $e instanceof LockNotAcquiredException) {
586
-			// the file is currently being written to by another process
587
-			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
588
-		}
589
-		if ($e instanceof GenericEncryptionException) {
590
-			// returning 503 will allow retry of the operation at a later point in time
591
-			throw new ServiceUnavailable('Encryption not ready: ' . $e->getMessage(), 0, $e);
592
-		}
593
-		if ($e instanceof StorageNotAvailableException) {
594
-			throw new ServiceUnavailable('Failed to write file contents: ' . $e->getMessage(), 0, $e);
595
-		}
596
-		if ($e instanceof NotFoundException) {
597
-			throw new NotFound('File not found: ' . $e->getMessage(), 0, $e);
598
-		}
599
-
600
-		throw new \Sabre\DAV\Exception($e->getMessage(), 0, $e);
601
-	}
602
-
603
-	/**
604
-	 * Get the checksum for this file
605
-	 *
606
-	 * @return string
607
-	 */
608
-	public function getChecksum() {
609
-		return $this->info->getChecksum();
610
-	}
611
-
612
-	protected function header($string) {
613
-		\header($string);
614
-	}
70
+    protected $request;
71
+
72
+    /**
73
+     * Sets up the node, expects a full path name
74
+     *
75
+     * @param \OC\Files\View $view
76
+     * @param \OCP\Files\FileInfo $info
77
+     * @param \OCP\Share\IManager $shareManager
78
+     * @param \OC\AppFramework\Http\Request $request
79
+     */
80
+    public function __construct(View $view, FileInfo $info, IManager $shareManager = null, Request $request = null) {
81
+        parent::__construct($view, $info, $shareManager);
82
+
83
+        if (isset($request)) {
84
+            $this->request = $request;
85
+        } else {
86
+            $this->request = \OC::$server->getRequest();
87
+        }
88
+    }
89
+
90
+    /**
91
+     * Updates the data
92
+     *
93
+     * The data argument is a readable stream resource.
94
+     *
95
+     * After a successful put operation, you may choose to return an ETag. The
96
+     * etag must always be surrounded by double-quotes. These quotes must
97
+     * appear in the actual string you're returning.
98
+     *
99
+     * Clients may use the ETag from a PUT request to later on make sure that
100
+     * when they update the file, the contents haven't changed in the mean
101
+     * time.
102
+     *
103
+     * If you don't plan to store the file byte-by-byte, and you return a
104
+     * different object on a subsequent GET you are strongly recommended to not
105
+     * return an ETag, and just return null.
106
+     *
107
+     * @param resource $data
108
+     *
109
+     * @throws Forbidden
110
+     * @throws UnsupportedMediaType
111
+     * @throws BadRequest
112
+     * @throws Exception
113
+     * @throws EntityTooLarge
114
+     * @throws ServiceUnavailable
115
+     * @throws FileLocked
116
+     * @return string|null
117
+     */
118
+    public function put($data) {
119
+        try {
120
+            $exists = $this->fileView->file_exists($this->path);
121
+            if ($this->info && $exists && !$this->info->isUpdateable()) {
122
+                throw new Forbidden();
123
+            }
124
+        } catch (StorageNotAvailableException $e) {
125
+            throw new ServiceUnavailable("File is not updatable: " . $e->getMessage());
126
+        }
127
+
128
+        // verify path of the target
129
+        $this->verifyPath();
130
+
131
+        // chunked handling
132
+        if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
133
+            try {
134
+                return $this->createFileChunked($data);
135
+            } catch (\Exception $e) {
136
+                $this->convertToSabreException($e);
137
+            }
138
+        }
139
+
140
+        /** @var Storage $partStorage */
141
+        list($partStorage) = $this->fileView->resolvePath($this->path);
142
+        $needsPartFile = $partStorage->needsPartFile() && (strlen($this->path) > 1);
143
+
144
+        $view = \OC\Files\Filesystem::getView();
145
+
146
+        if ($needsPartFile) {
147
+            // mark file as partial while uploading (ignored by the scanner)
148
+            $partFilePath = $this->getPartFileBasePath($this->path) . '.ocTransferId' . rand() . '.part';
149
+        } else {
150
+            // upload file directly as the final path
151
+            $partFilePath = $this->path;
152
+
153
+            if ($view && !$this->emitPreHooks($exists)) {
154
+                throw new Exception('Could not write to final file, canceled by hook');
155
+            }
156
+        }
157
+
158
+        // the part file and target file might be on a different storage in case of a single file storage (e.g. single file share)
159
+        /** @var \OC\Files\Storage\Storage $partStorage */
160
+        list($partStorage, $internalPartPath) = $this->fileView->resolvePath($partFilePath);
161
+        /** @var \OC\Files\Storage\Storage $storage */
162
+        list($storage, $internalPath) = $this->fileView->resolvePath($this->path);
163
+        try {
164
+            if (!$needsPartFile) {
165
+                $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
166
+            }
167
+
168
+            if ($partStorage->instanceOfStorage(Storage\IWriteStreamStorage::class)) {
169
+                $count = $partStorage->writeStream($internalPartPath, $data);
170
+                $result = $count > 0;
171
+                if ($result === false) {
172
+                    $result = feof($data);
173
+                }
174
+
175
+            } else {
176
+                $target = $partStorage->fopen($internalPartPath, 'wb');
177
+                if ($target === false) {
178
+                    \OC::$server->getLogger()->error('\OC\Files\Filesystem::fopen() failed', ['app' => 'webdav']);
179
+                    // because we have no clue about the cause we can only throw back a 500/Internal Server Error
180
+                    throw new Exception('Could not write file contents');
181
+                }
182
+                list($count, $result) = \OC_Helper::streamCopy($data, $target);
183
+                fclose($target);
184
+            }
185
+
186
+            if ($result === false) {
187
+                $expected = -1;
188
+                if (isset($_SERVER['CONTENT_LENGTH'])) {
189
+                    $expected = $_SERVER['CONTENT_LENGTH'];
190
+                }
191
+                throw new Exception('Error while copying file to target location (copied bytes: ' . $count . ', expected filesize: ' . $expected . ' )');
192
+            }
193
+
194
+            // if content length is sent by client:
195
+            // double check if the file was fully received
196
+            // compare expected and actual size
197
+            if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
198
+                $expected = (int)$_SERVER['CONTENT_LENGTH'];
199
+                if ($count !== $expected) {
200
+                    throw new BadRequest('expected filesize ' . $expected . ' got ' . $count);
201
+                }
202
+            }
203
+
204
+        } catch (\Exception $e) {
205
+            \OC::$server->getLogger()->logException($e);
206
+            if ($needsPartFile) {
207
+                $partStorage->unlink($internalPartPath);
208
+            }
209
+            $this->convertToSabreException($e);
210
+        }
211
+
212
+        try {
213
+            if ($needsPartFile) {
214
+                if ($view && !$this->emitPreHooks($exists)) {
215
+                    $partStorage->unlink($internalPartPath);
216
+                    throw new Exception('Could not rename part file to final file, canceled by hook');
217
+                }
218
+                try {
219
+                    $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
220
+                } catch (LockedException $e) {
221
+                    if ($needsPartFile) {
222
+                        $partStorage->unlink($internalPartPath);
223
+                    }
224
+                    throw new FileLocked($e->getMessage(), $e->getCode(), $e);
225
+                }
226
+
227
+                // rename to correct path
228
+                try {
229
+                    $renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath);
230
+                    $fileExists = $storage->file_exists($internalPath);
231
+                    if ($renameOkay === false || $fileExists === false) {
232
+                        \OC::$server->getLogger()->error('renaming part file to final file failed $renameOkay: ' . ($renameOkay ? 'true' : 'false') . ', $fileExists: ' . ($fileExists ? 'true' : 'false') . ')', ['app' => 'webdav']);
233
+                        throw new Exception('Could not rename part file to final file');
234
+                    }
235
+                } catch (ForbiddenException $ex) {
236
+                    throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
237
+                } catch (\Exception $e) {
238
+                    $partStorage->unlink($internalPartPath);
239
+                    $this->convertToSabreException($e);
240
+                }
241
+            }
242
+
243
+            // since we skipped the view we need to scan and emit the hooks ourselves
244
+            $storage->getUpdater()->update($internalPath);
245
+
246
+            try {
247
+                $this->changeLock(ILockingProvider::LOCK_SHARED);
248
+            } catch (LockedException $e) {
249
+                throw new FileLocked($e->getMessage(), $e->getCode(), $e);
250
+            }
251
+
252
+            // allow sync clients to send the mtime along in a header
253
+            if (isset($this->request->server['HTTP_X_OC_MTIME'])) {
254
+                $mtime = $this->sanitizeMtime($this->request->server['HTTP_X_OC_MTIME']);
255
+                if ($this->fileView->touch($this->path, $mtime)) {
256
+                    $this->header('X-OC-MTime: accepted');
257
+                }
258
+            }
259
+
260
+            if ($view) {
261
+                $this->emitPostHooks($exists);
262
+            }
263
+
264
+            $this->refreshInfo();
265
+
266
+            if (isset($this->request->server['HTTP_OC_CHECKSUM'])) {
267
+                $checksum = trim($this->request->server['HTTP_OC_CHECKSUM']);
268
+                $this->fileView->putFileInfo($this->path, ['checksum' => $checksum]);
269
+                $this->refreshInfo();
270
+            } else if ($this->getChecksum() !== null && $this->getChecksum() !== '') {
271
+                $this->fileView->putFileInfo($this->path, ['checksum' => '']);
272
+                $this->refreshInfo();
273
+            }
274
+
275
+        } catch (StorageNotAvailableException $e) {
276
+            throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage());
277
+        }
278
+
279
+        return '"' . $this->info->getEtag() . '"';
280
+    }
281
+
282
+    private function getPartFileBasePath($path) {
283
+        $partFileInStorage = \OC::$server->getConfig()->getSystemValue('part_file_in_storage', true);
284
+        if ($partFileInStorage) {
285
+            return $path;
286
+        } else {
287
+            return md5($path); // will place it in the root of the view with a unique name
288
+        }
289
+    }
290
+
291
+    /**
292
+     * @param string $path
293
+     */
294
+    private function emitPreHooks($exists, $path = null) {
295
+        if (is_null($path)) {
296
+            $path = $this->path;
297
+        }
298
+        $hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path));
299
+        $run = true;
300
+
301
+        if (!$exists) {
302
+            \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_create, array(
303
+                \OC\Files\Filesystem::signal_param_path => $hookPath,
304
+                \OC\Files\Filesystem::signal_param_run => &$run,
305
+            ));
306
+        } else {
307
+            \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_update, array(
308
+                \OC\Files\Filesystem::signal_param_path => $hookPath,
309
+                \OC\Files\Filesystem::signal_param_run => &$run,
310
+            ));
311
+        }
312
+        \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_write, array(
313
+            \OC\Files\Filesystem::signal_param_path => $hookPath,
314
+            \OC\Files\Filesystem::signal_param_run => &$run,
315
+        ));
316
+        return $run;
317
+    }
318
+
319
+    /**
320
+     * @param string $path
321
+     */
322
+    private function emitPostHooks($exists, $path = null) {
323
+        if (is_null($path)) {
324
+            $path = $this->path;
325
+        }
326
+        $hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path));
327
+        if (!$exists) {
328
+            \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array(
329
+                \OC\Files\Filesystem::signal_param_path => $hookPath
330
+            ));
331
+        } else {
332
+            \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_update, array(
333
+                \OC\Files\Filesystem::signal_param_path => $hookPath
334
+            ));
335
+        }
336
+        \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_write, array(
337
+            \OC\Files\Filesystem::signal_param_path => $hookPath
338
+        ));
339
+    }
340
+
341
+    /**
342
+     * Returns the data
343
+     *
344
+     * @return resource
345
+     * @throws Forbidden
346
+     * @throws ServiceUnavailable
347
+     */
348
+    public function get() {
349
+        //throw exception if encryption is disabled but files are still encrypted
350
+        try {
351
+            if (!$this->info->isReadable()) {
352
+                // do a if the file did not exist
353
+                throw new NotFound();
354
+            }
355
+            try {
356
+                $res = $this->fileView->fopen(ltrim($this->path, '/'), 'rb');
357
+            } catch (\Exception $e) {
358
+                $this->convertToSabreException($e);
359
+            }
360
+            if ($res === false) {
361
+                throw new ServiceUnavailable("Could not open file");
362
+            }
363
+            return $res;
364
+        } catch (GenericEncryptionException $e) {
365
+            // returning 503 will allow retry of the operation at a later point in time
366
+            throw new ServiceUnavailable("Encryption not ready: " . $e->getMessage());
367
+        } catch (StorageNotAvailableException $e) {
368
+            throw new ServiceUnavailable("Failed to open file: " . $e->getMessage());
369
+        } catch (ForbiddenException $ex) {
370
+            throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
371
+        } catch (LockedException $e) {
372
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
373
+        }
374
+    }
375
+
376
+    /**
377
+     * Delete the current file
378
+     *
379
+     * @throws Forbidden
380
+     * @throws ServiceUnavailable
381
+     */
382
+    public function delete() {
383
+        if (!$this->info->isDeletable()) {
384
+            throw new Forbidden();
385
+        }
386
+
387
+        try {
388
+            if (!$this->fileView->unlink($this->path)) {
389
+                // assume it wasn't possible to delete due to permissions
390
+                throw new Forbidden();
391
+            }
392
+        } catch (StorageNotAvailableException $e) {
393
+            throw new ServiceUnavailable("Failed to unlink: " . $e->getMessage());
394
+        } catch (ForbiddenException $ex) {
395
+            throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
396
+        } catch (LockedException $e) {
397
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
398
+        }
399
+    }
400
+
401
+    /**
402
+     * Returns the mime-type for a file
403
+     *
404
+     * If null is returned, we'll assume application/octet-stream
405
+     *
406
+     * @return string
407
+     */
408
+    public function getContentType() {
409
+        $mimeType = $this->info->getMimetype();
410
+
411
+        // PROPFIND needs to return the correct mime type, for consistency with the web UI
412
+        if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
413
+            return $mimeType;
414
+        }
415
+        return \OC::$server->getMimeTypeDetector()->getSecureMimeType($mimeType);
416
+    }
417
+
418
+    /**
419
+     * @return array|false
420
+     */
421
+    public function getDirectDownload() {
422
+        if (\OCP\App::isEnabled('encryption')) {
423
+            return [];
424
+        }
425
+        /** @var \OCP\Files\Storage $storage */
426
+        list($storage, $internalPath) = $this->fileView->resolvePath($this->path);
427
+        if (is_null($storage)) {
428
+            return [];
429
+        }
430
+
431
+        return $storage->getDirectDownload($internalPath);
432
+    }
433
+
434
+    /**
435
+     * @param resource $data
436
+     * @return null|string
437
+     * @throws Exception
438
+     * @throws BadRequest
439
+     * @throws NotImplemented
440
+     * @throws ServiceUnavailable
441
+     */
442
+    private function createFileChunked($data) {
443
+        list($path, $name) = \Sabre\Uri\split($this->path);
444
+
445
+        $info = \OC_FileChunking::decodeName($name);
446
+        if (empty($info)) {
447
+            throw new NotImplemented('Invalid chunk name');
448
+        }
449
+
450
+        $chunk_handler = new \OC_FileChunking($info);
451
+        $bytesWritten = $chunk_handler->store($info['index'], $data);
452
+
453
+        //detect aborted upload
454
+        if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
455
+            if (isset($_SERVER['CONTENT_LENGTH'])) {
456
+                $expected = (int)$_SERVER['CONTENT_LENGTH'];
457
+                if ($bytesWritten !== $expected) {
458
+                    $chunk_handler->remove($info['index']);
459
+                    throw new BadRequest(
460
+                        'expected filesize ' . $expected . ' got ' . $bytesWritten);
461
+                }
462
+            }
463
+        }
464
+
465
+        if ($chunk_handler->isComplete()) {
466
+            /** @var Storage $storage */
467
+            list($storage,) = $this->fileView->resolvePath($path);
468
+            $needsPartFile = $storage->needsPartFile();
469
+            $partFile = null;
470
+
471
+            $targetPath = $path . '/' . $info['name'];
472
+            /** @var \OC\Files\Storage\Storage $targetStorage */
473
+            list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath);
474
+
475
+            $exists = $this->fileView->file_exists($targetPath);
476
+
477
+            try {
478
+                $this->fileView->lockFile($targetPath, ILockingProvider::LOCK_SHARED);
479
+
480
+                $this->emitPreHooks($exists, $targetPath);
481
+                $this->fileView->changeLock($targetPath, ILockingProvider::LOCK_EXCLUSIVE);
482
+                /** @var \OC\Files\Storage\Storage $targetStorage */
483
+                list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath);
484
+
485
+                if ($needsPartFile) {
486
+                    // we first assembly the target file as a part file
487
+                    $partFile = $this->getPartFileBasePath($path . '/' . $info['name']) . '.ocTransferId' . $info['transferid'] . '.part';
488
+                    /** @var \OC\Files\Storage\Storage $targetStorage */
489
+                    list($partStorage, $partInternalPath) = $this->fileView->resolvePath($partFile);
490
+
491
+
492
+                    $chunk_handler->file_assemble($partStorage, $partInternalPath);
493
+
494
+                    // here is the final atomic rename
495
+                    $renameOkay = $targetStorage->moveFromStorage($partStorage, $partInternalPath, $targetInternalPath);
496
+                    $fileExists = $targetStorage->file_exists($targetInternalPath);
497
+                    if ($renameOkay === false || $fileExists === false) {
498
+                        \OC::$server->getLogger()->error('\OC\Files\Filesystem::rename() failed', ['app' => 'webdav']);
499
+                        // only delete if an error occurred and the target file was already created
500
+                        if ($fileExists) {
501
+                            // set to null to avoid double-deletion when handling exception
502
+                            // stray part file
503
+                            $partFile = null;
504
+                            $targetStorage->unlink($targetInternalPath);
505
+                        }
506
+                        $this->fileView->changeLock($targetPath, ILockingProvider::LOCK_SHARED);
507
+                        throw new Exception('Could not rename part file assembled from chunks');
508
+                    }
509
+                } else {
510
+                    // assemble directly into the final file
511
+                    $chunk_handler->file_assemble($targetStorage, $targetInternalPath);
512
+                }
513
+
514
+                // allow sync clients to send the mtime along in a header
515
+                if (isset($this->request->server['HTTP_X_OC_MTIME'])) {
516
+                    $mtime = $this->sanitizeMtime($this->request->server['HTTP_X_OC_MTIME']);
517
+                    if ($targetStorage->touch($targetInternalPath, $mtime)) {
518
+                        $this->header('X-OC-MTime: accepted');
519
+                    }
520
+                }
521
+
522
+                // since we skipped the view we need to scan and emit the hooks ourselves
523
+                $targetStorage->getUpdater()->update($targetInternalPath);
524
+
525
+                $this->fileView->changeLock($targetPath, ILockingProvider::LOCK_SHARED);
526
+
527
+                $this->emitPostHooks($exists, $targetPath);
528
+
529
+                // FIXME: should call refreshInfo but can't because $this->path is not the of the final file
530
+                $info = $this->fileView->getFileInfo($targetPath);
531
+
532
+                if (isset($this->request->server['HTTP_OC_CHECKSUM'])) {
533
+                    $checksum = trim($this->request->server['HTTP_OC_CHECKSUM']);
534
+                    $this->fileView->putFileInfo($targetPath, ['checksum' => $checksum]);
535
+                } else if ($info->getChecksum() !== null && $info->getChecksum() !== '') {
536
+                    $this->fileView->putFileInfo($this->path, ['checksum' => '']);
537
+                }
538
+
539
+                $this->fileView->unlockFile($targetPath, ILockingProvider::LOCK_SHARED);
540
+
541
+                return $info->getEtag();
542
+            } catch (\Exception $e) {
543
+                if ($partFile !== null) {
544
+                    $targetStorage->unlink($targetInternalPath);
545
+                }
546
+                $this->convertToSabreException($e);
547
+            }
548
+        }
549
+
550
+        return null;
551
+    }
552
+
553
+    /**
554
+     * Convert the given exception to a SabreException instance
555
+     *
556
+     * @param \Exception $e
557
+     *
558
+     * @throws \Sabre\DAV\Exception
559
+     */
560
+    private function convertToSabreException(\Exception $e) {
561
+        if ($e instanceof \Sabre\DAV\Exception) {
562
+            throw $e;
563
+        }
564
+        if ($e instanceof NotPermittedException) {
565
+            // a more general case - due to whatever reason the content could not be written
566
+            throw new Forbidden($e->getMessage(), 0, $e);
567
+        }
568
+        if ($e instanceof ForbiddenException) {
569
+            // the path for the file was forbidden
570
+            throw new DAVForbiddenException($e->getMessage(), $e->getRetry(), $e);
571
+        }
572
+        if ($e instanceof EntityTooLargeException) {
573
+            // the file is too big to be stored
574
+            throw new EntityTooLarge($e->getMessage(), 0, $e);
575
+        }
576
+        if ($e instanceof InvalidContentException) {
577
+            // the file content is not permitted
578
+            throw new UnsupportedMediaType($e->getMessage(), 0, $e);
579
+        }
580
+        if ($e instanceof InvalidPathException) {
581
+            // the path for the file was not valid
582
+            // TODO: find proper http status code for this case
583
+            throw new Forbidden($e->getMessage(), 0, $e);
584
+        }
585
+        if ($e instanceof LockedException || $e instanceof LockNotAcquiredException) {
586
+            // the file is currently being written to by another process
587
+            throw new FileLocked($e->getMessage(), $e->getCode(), $e);
588
+        }
589
+        if ($e instanceof GenericEncryptionException) {
590
+            // returning 503 will allow retry of the operation at a later point in time
591
+            throw new ServiceUnavailable('Encryption not ready: ' . $e->getMessage(), 0, $e);
592
+        }
593
+        if ($e instanceof StorageNotAvailableException) {
594
+            throw new ServiceUnavailable('Failed to write file contents: ' . $e->getMessage(), 0, $e);
595
+        }
596
+        if ($e instanceof NotFoundException) {
597
+            throw new NotFound('File not found: ' . $e->getMessage(), 0, $e);
598
+        }
599
+
600
+        throw new \Sabre\DAV\Exception($e->getMessage(), 0, $e);
601
+    }
602
+
603
+    /**
604
+     * Get the checksum for this file
605
+     *
606
+     * @return string
607
+     */
608
+    public function getChecksum() {
609
+        return $this->info->getChecksum();
610
+    }
611
+
612
+    protected function header($string) {
613
+        \header($string);
614
+    }
615 615
 }
Please login to merge, or discard this patch.
Spacing   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -122,7 +122,7 @@  discard block
 block discarded – undo
122 122
 				throw new Forbidden();
123 123
 			}
124 124
 		} catch (StorageNotAvailableException $e) {
125
-			throw new ServiceUnavailable("File is not updatable: " . $e->getMessage());
125
+			throw new ServiceUnavailable("File is not updatable: ".$e->getMessage());
126 126
 		}
127 127
 
128 128
 		// verify path of the target
@@ -145,7 +145,7 @@  discard block
 block discarded – undo
145 145
 
146 146
 		if ($needsPartFile) {
147 147
 			// mark file as partial while uploading (ignored by the scanner)
148
-			$partFilePath = $this->getPartFileBasePath($this->path) . '.ocTransferId' . rand() . '.part';
148
+			$partFilePath = $this->getPartFileBasePath($this->path).'.ocTransferId'.rand().'.part';
149 149
 		} else {
150 150
 			// upload file directly as the final path
151 151
 			$partFilePath = $this->path;
@@ -188,16 +188,16 @@  discard block
 block discarded – undo
188 188
 				if (isset($_SERVER['CONTENT_LENGTH'])) {
189 189
 					$expected = $_SERVER['CONTENT_LENGTH'];
190 190
 				}
191
-				throw new Exception('Error while copying file to target location (copied bytes: ' . $count . ', expected filesize: ' . $expected . ' )');
191
+				throw new Exception('Error while copying file to target location (copied bytes: '.$count.', expected filesize: '.$expected.' )');
192 192
 			}
193 193
 
194 194
 			// if content length is sent by client:
195 195
 			// double check if the file was fully received
196 196
 			// compare expected and actual size
197 197
 			if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
198
-				$expected = (int)$_SERVER['CONTENT_LENGTH'];
198
+				$expected = (int) $_SERVER['CONTENT_LENGTH'];
199 199
 				if ($count !== $expected) {
200
-					throw new BadRequest('expected filesize ' . $expected . ' got ' . $count);
200
+					throw new BadRequest('expected filesize '.$expected.' got '.$count);
201 201
 				}
202 202
 			}
203 203
 
@@ -229,7 +229,7 @@  discard block
 block discarded – undo
229 229
 					$renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath);
230 230
 					$fileExists = $storage->file_exists($internalPath);
231 231
 					if ($renameOkay === false || $fileExists === false) {
232
-						\OC::$server->getLogger()->error('renaming part file to final file failed $renameOkay: ' . ($renameOkay ? 'true' : 'false') . ', $fileExists: ' . ($fileExists ? 'true' : 'false') . ')', ['app' => 'webdav']);
232
+						\OC::$server->getLogger()->error('renaming part file to final file failed $renameOkay: '.($renameOkay ? 'true' : 'false').', $fileExists: '.($fileExists ? 'true' : 'false').')', ['app' => 'webdav']);
233 233
 						throw new Exception('Could not rename part file to final file');
234 234
 					}
235 235
 				} catch (ForbiddenException $ex) {
@@ -273,10 +273,10 @@  discard block
 block discarded – undo
273 273
 			}
274 274
 
275 275
 		} catch (StorageNotAvailableException $e) {
276
-			throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage());
276
+			throw new ServiceUnavailable("Failed to check file size: ".$e->getMessage());
277 277
 		}
278 278
 
279
-		return '"' . $this->info->getEtag() . '"';
279
+		return '"'.$this->info->getEtag().'"';
280 280
 	}
281 281
 
282 282
 	private function getPartFileBasePath($path) {
@@ -363,9 +363,9 @@  discard block
 block discarded – undo
363 363
 			return $res;
364 364
 		} catch (GenericEncryptionException $e) {
365 365
 			// returning 503 will allow retry of the operation at a later point in time
366
-			throw new ServiceUnavailable("Encryption not ready: " . $e->getMessage());
366
+			throw new ServiceUnavailable("Encryption not ready: ".$e->getMessage());
367 367
 		} catch (StorageNotAvailableException $e) {
368
-			throw new ServiceUnavailable("Failed to open file: " . $e->getMessage());
368
+			throw new ServiceUnavailable("Failed to open file: ".$e->getMessage());
369 369
 		} catch (ForbiddenException $ex) {
370 370
 			throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
371 371
 		} catch (LockedException $e) {
@@ -390,7 +390,7 @@  discard block
 block discarded – undo
390 390
 				throw new Forbidden();
391 391
 			}
392 392
 		} catch (StorageNotAvailableException $e) {
393
-			throw new ServiceUnavailable("Failed to unlink: " . $e->getMessage());
393
+			throw new ServiceUnavailable("Failed to unlink: ".$e->getMessage());
394 394
 		} catch (ForbiddenException $ex) {
395 395
 			throw new DAVForbiddenException($ex->getMessage(), $ex->getRetry());
396 396
 		} catch (LockedException $e) {
@@ -453,11 +453,11 @@  discard block
 block discarded – undo
453 453
 		//detect aborted upload
454 454
 		if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
455 455
 			if (isset($_SERVER['CONTENT_LENGTH'])) {
456
-				$expected = (int)$_SERVER['CONTENT_LENGTH'];
456
+				$expected = (int) $_SERVER['CONTENT_LENGTH'];
457 457
 				if ($bytesWritten !== $expected) {
458 458
 					$chunk_handler->remove($info['index']);
459 459
 					throw new BadRequest(
460
-						'expected filesize ' . $expected . ' got ' . $bytesWritten);
460
+						'expected filesize '.$expected.' got '.$bytesWritten);
461 461
 				}
462 462
 			}
463 463
 		}
@@ -468,7 +468,7 @@  discard block
 block discarded – undo
468 468
 			$needsPartFile = $storage->needsPartFile();
469 469
 			$partFile = null;
470 470
 
471
-			$targetPath = $path . '/' . $info['name'];
471
+			$targetPath = $path.'/'.$info['name'];
472 472
 			/** @var \OC\Files\Storage\Storage $targetStorage */
473 473
 			list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath);
474 474
 
@@ -484,7 +484,7 @@  discard block
 block discarded – undo
484 484
 
485 485
 				if ($needsPartFile) {
486 486
 					// we first assembly the target file as a part file
487
-					$partFile = $this->getPartFileBasePath($path . '/' . $info['name']) . '.ocTransferId' . $info['transferid'] . '.part';
487
+					$partFile = $this->getPartFileBasePath($path.'/'.$info['name']).'.ocTransferId'.$info['transferid'].'.part';
488 488
 					/** @var \OC\Files\Storage\Storage $targetStorage */
489 489
 					list($partStorage, $partInternalPath) = $this->fileView->resolvePath($partFile);
490 490
 
@@ -588,13 +588,13 @@  discard block
 block discarded – undo
588 588
 		}
589 589
 		if ($e instanceof GenericEncryptionException) {
590 590
 			// returning 503 will allow retry of the operation at a later point in time
591
-			throw new ServiceUnavailable('Encryption not ready: ' . $e->getMessage(), 0, $e);
591
+			throw new ServiceUnavailable('Encryption not ready: '.$e->getMessage(), 0, $e);
592 592
 		}
593 593
 		if ($e instanceof StorageNotAvailableException) {
594
-			throw new ServiceUnavailable('Failed to write file contents: ' . $e->getMessage(), 0, $e);
594
+			throw new ServiceUnavailable('Failed to write file contents: '.$e->getMessage(), 0, $e);
595 595
 		}
596 596
 		if ($e instanceof NotFoundException) {
597
-			throw new NotFound('File not found: ' . $e->getMessage(), 0, $e);
597
+			throw new NotFound('File not found: '.$e->getMessage(), 0, $e);
598 598
 		}
599 599
 
600 600
 		throw new \Sabre\DAV\Exception($e->getMessage(), 0, $e);
Please login to merge, or discard this patch.