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