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