Completed
Pull Request — stable8.2 (#24656)
by Joas
12:22
created

File::put()   F

Complexity

Conditions 31
Paths > 20000

Size

Total Lines 134
Code Lines 79

Duplication

Lines 6
Ratio 4.48 %

Code Coverage

Tests 65
CRAP Score 39.5608

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 6
loc 134
ccs 65
cts 82
cp 0.7927
rs 2
cc 31
eloc 79
nc 84868
nop 1
crap 39.5608

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @author Bart Visscher <[email protected]>
4
 * @author Björn Schießle <[email protected]>
5
 * @author Jakob Sack <[email protected]>
6
 * @author Jörn Friedrich Dreyer <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Owen Winkler <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 * @author Robin McCorkell <[email protected]>
12
 * @author Thomas Müller <[email protected]>
13
 * @author Thomas Tanghus <[email protected]>
14
 * @author Vincent Petry <[email protected]>
15
 *
16
 * @copyright Copyright (c) 2015, ownCloud, Inc.
17
 * @license AGPL-3.0
18
 *
19
 * This code is free software: you can redistribute it and/or modify
20
 * it under the terms of the GNU Affero General Public License, version 3,
21
 * as published by the Free Software Foundation.
22
 *
23
 * This program is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
 * GNU Affero General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU Affero General Public License, version 3,
29
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
30
 *
31
 */
32
33
namespace OC\Connector\Sabre;
34
35
use OC\Connector\Sabre\Exception\EntityTooLarge;
36
use OC\Connector\Sabre\Exception\FileLocked;
37
use OC\Connector\Sabre\Exception\UnsupportedMediaType;
38
use OC\Files\Filesystem;
39
use OCP\Encryption\Exceptions\GenericEncryptionException;
40
use OCP\Files\EntityTooLargeException;
41
use OCP\Files\InvalidContentException;
42
use OCP\Files\InvalidPathException;
43
use OCP\Files\LockNotAcquiredException;
44
use OCP\Files\NotPermittedException;
45
use OCP\Files\StorageNotAvailableException;
46
use OCP\Lock\ILockingProvider;
47
use OCP\Lock\LockedException;
48
use Sabre\DAV\Exception;
49
use Sabre\DAV\Exception\BadRequest;
50
use Sabre\DAV\Exception\Forbidden;
51
use Sabre\DAV\Exception\NotImplemented;
52
use Sabre\DAV\Exception\ServiceUnavailable;
53
use Sabre\DAV\IFile;
54
55
class File extends Node implements IFile {
56
57
	/**
58
	 * Updates the data
59
	 *
60
	 * The data argument is a readable stream resource.
61
	 *
62
	 * After a successful put operation, you may choose to return an ETag. The
63
	 * etag must always be surrounded by double-quotes. These quotes must
64
	 * appear in the actual string you're returning.
65
	 *
66
	 * Clients may use the ETag from a PUT request to later on make sure that
67
	 * when they update the file, the contents haven't changed in the mean
68
	 * time.
69
	 *
70
	 * If you don't plan to store the file byte-by-byte, and you return a
71
	 * different object on a subsequent GET you are strongly recommended to not
72
	 * return an ETag, and just return null.
73
	 *
74
	 * @param resource $data
75
	 *
76
	 * @throws Forbidden
77
	 * @throws UnsupportedMediaType
78
	 * @throws BadRequest
79
	 * @throws Exception
80
	 * @throws EntityTooLarge
81
	 * @throws ServiceUnavailable
82
	 * @throws FileLocked
83
	 * @return string|null
84
	 */
85 45
	public function put($data) {
86
		try {
87 45
			$exists = $this->fileView->file_exists($this->path);
88 45
			if ($this->info && $exists && !$this->info->isUpdateable()) {
89
				throw new Forbidden();
90
			}
91 45
		} catch (StorageNotAvailableException $e) {
92
			throw new ServiceUnavailable("File is not updatable: " . $e->getMessage());
93
		}
94
95
		// verify path of the target
96 45
		$this->verifyPath();
97
98
		// chunked handling
99 44
		if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
100
			try {
101 19
				return $this->createFileChunked($data);
102 12
			} catch (\Exception $e) {
103 12
				$this->convertToSabreException($e);
104
			}
105
		}
106
107 25
		list($partStorage) = $this->fileView->resolvePath($this->path);
108 25
		$needsPartFile = $this->needsPartFile($partStorage) && (strlen($this->path) > 1);
109
110 25
		if ($needsPartFile) {
111
			// mark file as partial while uploading (ignored by the scanner)
112 25
			$partFilePath = $this->getPartFileBasePath($this->path) . '.ocTransferId' . rand() . '.part';
113 25
		} else {
114
			// upload file directly as the final path
115
			$partFilePath = $this->path;
116
		}
117
118
		// the part file and target file might be on a different storage in case of a single file storage (e.g. single file share)
119
		/** @var \OC\Files\Storage\Storage $partStorage */
120 25
		list($partStorage, $internalPartPath) = $this->fileView->resolvePath($partFilePath);
121
		/** @var \OC\Files\Storage\Storage $storage */
122 25
		list($storage, $internalPath) = $this->fileView->resolvePath($this->path);
123
		try {
124 25
			$target = $partStorage->fopen($internalPartPath, 'wb');
125 15
			if ($target === false) {
126 1
				\OCP\Util::writeLog('webdav', '\OC\Files\Filesystem::fopen() failed', \OCP\Util::ERROR);
127
				// because we have no clue about the cause we can only throw back a 500/Internal Server Error
128 1
				throw new Exception('Could not write file contents');
129
			}
130 14
			list($count, $result) = \OC_Helper::streamCopy($data, $target);
131 14
			fclose($target);
132
133 14
			if ($result === false) {
134
				$expected = -1;
135
				if (isset($_SERVER['CONTENT_LENGTH'])) {
136
					$expected = $_SERVER['CONTENT_LENGTH'];
137
				}
138
				throw new Exception('Error while copying file to target location (copied bytes: ' . $count . ', expected filesize: ' . $expected . ' )');
139
			}
140
141
			// if content length is sent by client:
142
			// double check if the file was fully received
143
			// compare expected and actual size
144 14 View Code Duplication
			if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] !== 'LOCK') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
145 2
				$expected = $_SERVER['CONTENT_LENGTH'];
146 2
				if ($count != $expected) {
147 2
					throw new BadRequest('expected filesize ' . $expected . ' got ' . $count);
148
				}
149
			}
150
151 25
		} catch (\Exception $e) {
152 13
			if ($needsPartFile) {
153 13
				$partStorage->unlink($internalPartPath);
154 13
			}
155 13
			$this->convertToSabreException($e);
156
		}
157
158
		try {
159 12
			$view = \OC\Files\Filesystem::getView();
160 12
			if ($view) {
161 12
				$run = $this->emitPreHooks($exists);
162 12
			} else {
163
				$run = true;
164
			}
165
166
			try {
167 12
				$this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
168 12
			} catch (LockedException $e) {
169 1
				if ($needsPartFile) {
170 1
					$partStorage->unlink($internalPartPath);
171 1
				}
172 1
				throw new FileLocked($e->getMessage(), $e->getCode(), $e);
173
			}
174
175 11
			if ($needsPartFile) {
176
				// rename to correct path
177
				try {
178 11
					if ($run) {
179 10
						$renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath);
180 10
						$fileExists = $storage->file_exists($internalPath);
181 10
					}
182 11
					if (!$run || $renameOkay === false || $fileExists === false) {
0 ignored issues
show
Bug introduced by
The variable $renameOkay does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $fileExists does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
183 1
						\OCP\Util::writeLog('webdav', 'renaming part file to final file failed', \OCP\Util::ERROR);
184 1
						throw new Exception('Could not rename part file to final file');
185
					}
186 11
				} catch (\Exception $e) {
187 1
					$partStorage->unlink($internalPartPath);
188 1
					$this->convertToSabreException($e);
189
				}
190 10
			}
191
192
			// since we skipped the view we need to scan and emit the hooks ourselves
193 10
			$this->fileView->getUpdater()->update($this->path);
194 10
195
			try {
196
				$this->changeLock(ILockingProvider::LOCK_SHARED);
197
			} catch (LockedException $e) {
198
				throw new FileLocked($e->getMessage(), $e->getCode(), $e);
199 10
			}
200
201 10
			if ($view) {
202 10
				$this->emitPostHooks($exists);
203 10
			}
204
205
			// allow sync clients to send the mtime along in a header
206 10
			$request = \OC::$server->getRequest();
207 10
			if (isset($request->server['HTTP_X_OC_MTIME'])) {
0 ignored issues
show
Bug introduced by
Accessing server on the interface OCP\IRequest suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
208
				if ($this->fileView->touch($this->path, $request->server['HTTP_X_OC_MTIME'])) {
0 ignored issues
show
Bug introduced by
Accessing server on the interface OCP\IRequest suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
209
					header('X-OC-MTime: accepted');
210
				}
211
			}
212 10
			$this->refreshInfo();
213 12
		} catch (StorageNotAvailableException $e) {
214
			throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage());
215
		}
216
217 10
		return '"' . $this->info->getEtag() . '"';
218
	}
219
220 31
	private function getPartFileBasePath($path) {
221 31
		$partFileInStorage = \OC::$server->getConfig()->getSystemValue('part_file_in_storage', true);
222 12
		if ($partFileInStorage) {
223 12
			return $path;
224 31
		} else {
225 31
			return md5($path); // will place it in the root of the view with a unique name
226
		}
227 31
	}
228 26
229 26
	/**
230 26
	 * @param string $path
231 26
	 */
232 26
	private function emitPreHooks($exists, $path = null) {
233 5
		if (is_null($path)) {
234 5
			$path = $this->path;
235 5
		}
236 5
		$hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path));
237
		$run = true;
238 31
239 31 View Code Duplication
		if (!$exists) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
240 31
			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_create, array(
241 31
				\OC\Files\Filesystem::signal_param_path => $hookPath,
242 31
				\OC\Files\Filesystem::signal_param_run => &$run,
243
			));
244
		} else {
245 17
			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_update, array(
246 17
				\OC\Files\Filesystem::signal_param_path => $hookPath,
247 10
				\OC\Files\Filesystem::signal_param_run => &$run,
248 10
			));
249 17
		}
250 17
		\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_write, array(
251 12
			\OC\Files\Filesystem::signal_param_path => $hookPath,
252 12
			\OC\Files\Filesystem::signal_param_run => &$run,
253 12
		));
254 12
		return $run;
255 5
	}
256 5
257 5
	private function emitPostHooks($exists, $path = null) {
258
		if (is_null($path)) {
259 17
			$path = $this->path;
260 17
		}
261 17
		$hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path));
262 17 View Code Duplication
		if (!$exists) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
263
			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array(
264
				\OC\Files\Filesystem::signal_param_path => $hookPath
265
			));
266
		} else {
267
			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_update, array(
268
				\OC\Files\Filesystem::signal_param_path => $hookPath
269
			));
270
		}
271 4
		\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_write, array(
272
			\OC\Files\Filesystem::signal_param_path => $hookPath
273
		));
274 4
	}
275 3
276 1
	/**
277
	 * Returns the data
278 2
	 *
279 2
	 * @return string|resource
280
	 * @throws Forbidden
281
	 * @throws ServiceUnavailable
282 2
	 */
283
	public function get() {
284 2
		//throw exception if encryption is disabled but files are still encrypted
285 1
		try {
286
			$res = $this->fileView->fopen(ltrim($this->path, '/'), 'rb');
287
			if ($res === false) {
288
				throw new ServiceUnavailable("Could not open file");
289
			}
290
			return $res;
291
		} catch (GenericEncryptionException $e) {
292
			// returning 503 will allow retry of the operation at a later point in time
293
			throw new ServiceUnavailable("Encryption not ready: " . $e->getMessage());
294
		} catch (StorageNotAvailableException $e) {
295 3
			throw new ServiceUnavailable("Failed to open file: " . $e->getMessage());
296 3
		} catch (LockedException $e) {
297 1
			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
298
		}
299
	}
300
301 2
	/**
302
	 * Delete the current file
303 1
	 *
304
	 * @throws Forbidden
305 2
	 * @throws ServiceUnavailable
306
	 */
307 1
	public function delete() {
308
		if (!$this->info->isDeletable()) {
309
			throw new Forbidden();
310 1
		}
311
312
		try {
313
			if (!$this->fileView->unlink($this->path)) {
314
				// assume it wasn't possible to delete due to permissions
315
				throw new Forbidden();
316
			}
317
		} catch (StorageNotAvailableException $e) {
318
			throw new ServiceUnavailable("Failed to unlink: " . $e->getMessage());
319 2
		} catch (LockedException $e) {
320 2
			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
321
		}
322
	}
323 2
324
	/**
325
	 * Returns the mime-type for a file
326 2
	 *
327
	 * If null is returned, we'll assume application/octet-stream
328
	 *
329
	 * @return mixed
330
	 */
331
	public function getContentType() {
332
		$mimeType = $this->info->getMimetype();
333
334
		// PROPFIND needs to return the correct mime type, for consistency with the web UI
335
		if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
336
			return $mimeType;
337
		}
338
		return \OC_Helper::getSecureMimeType($mimeType);
339
	}
340
341
	/**
342
	 * @return array|false
343
	 */
344
	public function getDirectDownload() {
345
		if (\OCP\App::isEnabled('encryption')) {
346
			return [];
347
		}
348
		/** @var \OCP\Files\Storage $storage */
349
		list($storage, $internalPath) = $this->fileView->resolvePath($this->path);
350
		if (is_null($storage)) {
351
			return [];
352
		}
353 19
354 19
		return $storage->getDirectDownload($internalPath);
355
	}
356 19
357 19
	/**
358
	 * @param resource $data
359
	 * @return null|string
360 19
	 * @throws Exception
361 19
	 * @throws BadRequest
362
	 * @throws NotImplemented
363
	 * @throws ServiceUnavailable
364 19
	 */
365
	private function createFileChunked($data) {
366
		list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($this->path);
367
368
		$info = \OC_FileChunking::decodeName($name);
369
		if (empty($info)) {
370
			throw new NotImplemented('Invalid chunk name');
371
		}
372
		$chunk_handler = new \OC_FileChunking($info);
373
		$bytesWritten = $chunk_handler->store($info['index'], $data);
374
375 19
		//detect aborted upload
376 19
		if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
377 19 View Code Duplication
			if (isset($_SERVER['CONTENT_LENGTH'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
378 19
				$expected = $_SERVER['CONTENT_LENGTH'];
379
				if ($bytesWritten != $expected) {
380 19
					$chunk_handler->remove($info['index']);
381
					throw new BadRequest(
382 19
						'expected filesize ' . $expected . ' got ' . $bytesWritten);
383
				}
384 19
			}
385
		}
386
387 19
		if ($chunk_handler->isComplete()) {
388
			list($storage,) = $this->fileView->resolvePath($path);
389 19
			$needsPartFile = $this->needsPartFile($storage);
390
			$partFile = null;
391 18
392
			$targetPath = $path . '/' . $info['name'];
393 18
			/** @var \OC\Files\Storage\Storage $targetStorage */
394
			list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath);
395
396 18
			$exists = $this->fileView->file_exists($targetPath);
397
398
			try {
399 18
				$this->emitPreHooks($exists, $targetPath);
400
401
				$this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
402 8
403
				if ($needsPartFile) {
404 8
					// we first assembly the target file as a part file
405 8
					$partFile = $this->getPartFileBasePath($path . '/' . $info['name']) . '.ocTransferId' . $info['transferid'] . '.part';
406 1
					/** @var \OC\Files\Storage\Storage $targetStorage */
407
					list($partStorage, $partInternalPath) = $this->fileView->resolvePath($partFile);
408 1
409
410
					$chunk_handler->file_assemble($partStorage, $partInternalPath, $this->fileView->getAbsolutePath($targetPath));
411
412
					// here is the final atomic rename
413
					$renameOkay = $targetStorage->moveFromStorage($partStorage, $partInternalPath, $targetInternalPath);
414 1
415 1
					$fileExists = $this->fileView->file_exists($targetPath);
416
					if ($renameOkay === false || $fileExists === false) {
417 7
						\OCP\Util::writeLog('webdav', '\OC\Files\Filesystem::rename() failed', \OCP\Util::ERROR);
418
						// only delete if an error occurred and the target file was already created
419
						if ($fileExists) {
420
							// set to null to avoid double-deletion when handling exception
421
							// stray part file
422
							$partFile = null;
423 7
							$targetStorage->unlink($targetInternalPath);
424 7
						}
425
						$this->changeLock(ILockingProvider::LOCK_SHARED);
426
						throw new Exception('Could not rename part file assembled from chunks');
427
					}
428
				} else {
429
					// assemble directly into the final file
430 7
					$chunk_handler->file_assemble($targetStorage, $targetInternalPath, $this->fileView->getAbsolutePath($targetPath));
431
				}
432
433 7
				// allow sync clients to send the mtime along in a header
434
				$request = \OC::$server->getRequest();
435 7
				if (isset($request->server['HTTP_X_OC_MTIME'])) {
0 ignored issues
show
Bug introduced by
Accessing server on the interface OCP\IRequest suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
436
					if ($targetStorage->touch($targetInternalPath, $request->server['HTTP_X_OC_MTIME'])) {
0 ignored issues
show
Bug introduced by
Accessing server on the interface OCP\IRequest suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
437 7
						header('X-OC-MTime: accepted');
438 7
					}
439 12
				}
440 12
441 11
				// since we skipped the view we need to scan and emit the hooks ourselves
442 11
				$this->fileView->getUpdater()->update($targetPath);
443 12
444
				$this->changeLock(ILockingProvider::LOCK_SHARED);
445
446
				$this->emitPostHooks($exists, $targetPath);
447 19
448
				$info = $this->fileView->getFileInfo($targetPath);
449
				return $info->getEtag();
450
			} catch (\Exception $e) {
451
				if ($partFile !== null) {
452
					$targetStorage->unlink($targetInternalPath);
453
				}
454
				$this->convertToSabreException($e);
455
			}
456
		}
457
458 44
		return null;
459
	}
460
461 44
	/**
462 44
	 * Returns whether a part file is needed for the given storage
463
	 * or whether the file can be assembled/uploaded directly on the
464
	 * target storage.
465
	 *
466
	 * @param \OCP\Files\Storage $storage
467
	 * @return bool true if the storage needs part file handling
468
	 */
469
	private function needsPartFile($storage) {
470
		// TODO: in the future use ChunkHandler provided by storage
471
		// and/or add method on Storage called "needsPartFile()"
472 26
		return !$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage') &&
473 26
		!$storage->instanceOfStorage('OC\Files\Storage\OwnCloud');
474 17
	}
475
476 19
	/**
477
	 * Convert the given exception to a SabreException instance
478 2
	 *
479
	 * @param \Exception $e
480 17
	 *
481
	 * @throws \Sabre\DAV\Exception
482 2
	 */
483
	private function convertToSabreException(\Exception $e) {
484 15
		if ($e instanceof \Sabre\DAV\Exception) {
0 ignored issues
show
Bug introduced by
The class Sabre\DAV\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
485
			throw $e;
486 2
		}
487
		if ($e instanceof NotPermittedException) {
488 13
			// a more general case - due to whatever reason the content could not be written
489
			throw new Forbidden($e->getMessage(), 0, $e);
490
		}
491 2
		if ($e instanceof EntityTooLargeException) {
492
			// the file is too big to be stored
493 11
			throw new EntityTooLarge($e->getMessage(), 0, $e);
494
		}
495 5
		if ($e instanceof InvalidContentException) {
496
			// the file content is not permitted
497 6
			throw new UnsupportedMediaType($e->getMessage(), 0, $e);
498
		}
499 2
		if ($e instanceof InvalidPathException) {
500
			// the path for the file was not valid
501 4
			// TODO: find proper http status code for this case
502 2
			throw new Forbidden($e->getMessage(), 0, $e);
503
		}
504
		if ($e instanceof LockedException || $e instanceof LockNotAcquiredException) {
505 2
			// the file is currently being written to by another process
506
			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
507
		}
508
		if ($e instanceof GenericEncryptionException) {
509
			// returning 503 will allow retry of the operation at a later point in time
510
			throw new ServiceUnavailable('Encryption not ready: ' . $e->getMessage(), 0, $e);
511
		}
512
		if ($e instanceof StorageNotAvailableException) {
513
			throw new ServiceUnavailable('Failed to write file contents: ' . $e->getMessage(), 0, $e);
514
		}
515
516
		throw new \Sabre\DAV\Exception($e->getMessage(), 0, $e);
517
	}
518
}
519