Completed
Push — stable8.2 ( 66f1ca...821663 )
by Morris
55:04 queued 36s
created

File::needsPartFile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
cc 2
eloc 3
nc 2
nop 1
crap 2
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 44
	public function put($data) {
86
		try {
87 44
			$exists = $this->fileView->file_exists($this->path);
88 44
			if ($this->info && $exists && !$this->info->isUpdateable()) {
89
				throw new Forbidden();
90
			}
91 44
		} catch (StorageNotAvailableException $e) {
92
			throw new ServiceUnavailable("File is not updatable: " . $e->getMessage());
93
		}
94
95
		// verify path of the target
96 44
		$this->verifyPath();
97
98
		// chunked handling
99 43
		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 24
		list($partStorage) = $this->fileView->resolvePath($this->path);
108 24
		$needsPartFile = $this->needsPartFile($partStorage) && (strlen($this->path) > 1);
109
110 24
		if ($needsPartFile) {
111
			// mark file as partial while uploading (ignored by the scanner)
112 24
			$partFilePath = $this->path . '.ocTransferId' . rand() . '.part';
113 24
		} 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 24
		list($partStorage, $internalPartPath) = $this->fileView->resolvePath($partFilePath);
121
		/** @var \OC\Files\Storage\Storage $storage */
122 24
		list($storage, $internalPath) = $this->fileView->resolvePath($this->path);
123
		try {
124 24
			$target = $partStorage->fopen($internalPartPath, 'wb');
125 14
			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 13
			list($count, $result) = \OC_Helper::streamCopy($data, $target);
131 13
			fclose($target);
132
133 13
			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 13 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 24
		} catch (\Exception $e) {
152 13
			if ($needsPartFile) {
153 13
				$partStorage->unlink($internalPartPath);
154 13
			}
155 13
			$this->convertToSabreException($e);
156
		}
157
158
		try {
159 11
			$view = \OC\Files\Filesystem::getView();
160 11
			if ($view) {
161 11
				$run = $this->emitPreHooks($exists);
162 11
			} else {
163
				$run = true;
164
			}
165
166
			try {
167 11
				$this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
168 11
			} 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 10
			if ($needsPartFile) {
176
				// rename to correct path
177
				try {
178 10
					if ($run) {
179 9
						$renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath);
180 9
						$fileExists = $storage->file_exists($internalPath);
181 9
					}
182 10
					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 10
				} catch (\Exception $e) {
187 1
					$partStorage->unlink($internalPartPath);
188 1
					$this->convertToSabreException($e);
189
				}
190 9
			}
191
192
			try {
193 9
				$this->changeLock(ILockingProvider::LOCK_SHARED);
194 9
			} catch (LockedException $e) {
195
				throw new FileLocked($e->getMessage(), $e->getCode(), $e);
196
			}
197
198
			// since we skipped the view we need to scan and emit the hooks ourselves
199 9
			$this->fileView->getUpdater()->update($this->path);
200
201 9
			if ($view) {
202 9
				$this->emitPostHooks($exists);
203 9
			}
204
205
			// allow sync clients to send the mtime along in a header
206 9
			$request = \OC::$server->getRequest();
207 9
			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 9
			$this->refreshInfo();
213 11
		} catch (StorageNotAvailableException $e) {
214
			throw new ServiceUnavailable("Failed to check file size: " . $e->getMessage());
215
		}
216
217 9
		return '"' . $this->info->getEtag() . '"';
218
	}
219
220 30
	private function emitPreHooks($exists, $path = null) {
221 30
		if (is_null($path)) {
222 11
			$path = $this->path;
223 11
		}
224 30
		$hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path));
225 30
		$run = true;
226
227 30 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...
228 25
			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_create, array(
229 25
				\OC\Files\Filesystem::signal_param_path => $hookPath,
230 25
				\OC\Files\Filesystem::signal_param_run => &$run,
231 25
			));
232 25
		} else {
233 5
			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_update, array(
234 5
				\OC\Files\Filesystem::signal_param_path => $hookPath,
235 5
				\OC\Files\Filesystem::signal_param_run => &$run,
236 5
			));
237
		}
238 30
		\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_write, array(
239 30
			\OC\Files\Filesystem::signal_param_path => $hookPath,
240 30
			\OC\Files\Filesystem::signal_param_run => &$run,
241 30
		));
242 30
		return $run;
243
	}
244
245 16
	private function emitPostHooks($exists, $path = null) {
246 16
		if (is_null($path)) {
247 9
			$path = $this->path;
248 9
		}
249 16
		$hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path));
250 16 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...
251 11
			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array(
252 11
				\OC\Files\Filesystem::signal_param_path => $hookPath
253 11
			));
254 11
		} else {
255 5
			\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_update, array(
256 5
				\OC\Files\Filesystem::signal_param_path => $hookPath
257 5
			));
258
		}
259 16
		\OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_write, array(
260 16
			\OC\Files\Filesystem::signal_param_path => $hookPath
261 16
		));
262 16
	}
263
264
	/**
265
	 * Returns the data
266
	 *
267
	 * @return string|resource
268
	 * @throws Forbidden
269
	 * @throws ServiceUnavailable
270
	 */
271 4
	public function get() {
272
		//throw exception if encryption is disabled but files are still encrypted
273
		try {
274 4
			$res = $this->fileView->fopen(ltrim($this->path, '/'), 'rb');
275 3
			if ($res === false) {
276 1
				throw new ServiceUnavailable("Could not open file");
277
			}
278 2
			return $res;
279 2
		} catch (GenericEncryptionException $e) {
280
			// returning 503 will allow retry of the operation at a later point in time
281
			throw new ServiceUnavailable("Encryption not ready: " . $e->getMessage());
282 2
		} catch (StorageNotAvailableException $e) {
283
			throw new ServiceUnavailable("Failed to open file: " . $e->getMessage());
284 2
		} catch (LockedException $e) {
285 1
			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
286
		}
287
	}
288
289
	/**
290
	 * Delete the current file
291
	 *
292
	 * @throws Forbidden
293
	 * @throws ServiceUnavailable
294
	 */
295 3
	public function delete() {
296 3
		if (!$this->info->isDeletable()) {
297 1
			throw new Forbidden();
298
		}
299
300
		try {
301 2
			if (!$this->fileView->unlink($this->path)) {
302
				// assume it wasn't possible to delete due to permissions
303 1
				throw new Forbidden();
304
			}
305 2
		} catch (StorageNotAvailableException $e) {
306
			throw new ServiceUnavailable("Failed to unlink: " . $e->getMessage());
307 1
		} catch (LockedException $e) {
308
			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
309
		}
310 1
	}
311
312
	/**
313
	 * Returns the mime-type for a file
314
	 *
315
	 * If null is returned, we'll assume application/octet-stream
316
	 *
317
	 * @return mixed
318
	 */
319 2
	public function getContentType() {
320 2
		$mimeType = $this->info->getMimetype();
321
322
		// PROPFIND needs to return the correct mime type, for consistency with the web UI
323 2
		if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
324
			return $mimeType;
325
		}
326 2
		return \OC_Helper::getSecureMimeType($mimeType);
327
	}
328
329
	/**
330
	 * @return array|false
331
	 */
332
	public function getDirectDownload() {
333
		if (\OCP\App::isEnabled('encryption')) {
334
			return [];
335
		}
336
		/** @var \OCP\Files\Storage $storage */
337
		list($storage, $internalPath) = $this->fileView->resolvePath($this->path);
338
		if (is_null($storage)) {
339
			return [];
340
		}
341
342
		return $storage->getDirectDownload($internalPath);
343
	}
344
345
	/**
346
	 * @param resource $data
347
	 * @return null|string
348
	 * @throws Exception
349
	 * @throws BadRequest
350
	 * @throws NotImplemented
351
	 * @throws ServiceUnavailable
352
	 */
353 19
	private function createFileChunked($data) {
354 19
		list($path, $name) = \Sabre\HTTP\URLUtil::splitPath($this->path);
355
356 19
		$info = \OC_FileChunking::decodeName($name);
357 19
		if (empty($info)) {
358
			throw new NotImplemented('Invalid chunk name');
359
		}
360 19
		$chunk_handler = new \OC_FileChunking($info);
361 19
		$bytesWritten = $chunk_handler->store($info['index'], $data);
362
363
		//detect aborted upload
364 19
		if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') {
365 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...
366
				$expected = $_SERVER['CONTENT_LENGTH'];
367
				if ($bytesWritten != $expected) {
368
					$chunk_handler->remove($info['index']);
369
					throw new BadRequest(
370
						'expected filesize ' . $expected . ' got ' . $bytesWritten);
371
				}
372
			}
373
		}
374
375 19
		if ($chunk_handler->isComplete()) {
376 19
			list($storage,) = $this->fileView->resolvePath($path);
377 19
			$needsPartFile = $this->needsPartFile($storage);
378 19
			$partFile = null;
379
380 19
			$targetPath = $path . '/' . $info['name'];
381
			/** @var \OC\Files\Storage\Storage $targetStorage */
382 19
			list($targetStorage, $targetInternalPath) = $this->fileView->resolvePath($targetPath);
383
384 19
			$exists = $this->fileView->file_exists($targetPath);
385
386
			try {
387 19
				$this->emitPreHooks($exists, $targetPath);
388
389 19
				$this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
390
391 18
				if ($needsPartFile) {
392
					// we first assembly the target file as a part file
393 18
					$partFile = $path . '/' . $info['name'] . '.ocTransferId' . $info['transferid'] . '.part';
394
395
396 18
					list($partStorage, $partInternalPath) = $this->fileView->resolvePath($partFile);
397
398
399 18
					$chunk_handler->file_assemble($partStorage, $partInternalPath, $this->fileView->getAbsolutePath($targetPath));
400
401
					// here is the final atomic rename
402 8
					$renameOkay = $targetStorage->moveFromStorage($partStorage, $partInternalPath, $targetInternalPath);
403
404 8
					$fileExists = $this->fileView->file_exists($targetPath);
405 8
					if ($renameOkay === false || $fileExists === false) {
406 1
						\OCP\Util::writeLog('webdav', '\OC\Files\Filesystem::rename() failed', \OCP\Util::ERROR);
407
						// only delete if an error occurred and the target file was already created
408 1
						if ($fileExists) {
409
							// set to null to avoid double-deletion when handling exception
410
							// stray part file
411
							$partFile = null;
412
							$targetStorage->unlink($targetInternalPath);
413
						}
414 1
						$this->changeLock(ILockingProvider::LOCK_SHARED);
415 1
						throw new Exception('Could not rename part file assembled from chunks');
416
					}
417 7
				} else {
418
					// assemble directly into the final file
419
					$chunk_handler->file_assemble($targetStorage, $targetInternalPath, $this->fileView->getAbsolutePath($targetPath));
420
				}
421
422
				// allow sync clients to send the mtime along in a header
423 7
				$request = \OC::$server->getRequest();
424 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...
425
					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...
426
						header('X-OC-MTime: accepted');
427
					}
428
				}
429
430 7
				$this->changeLock(ILockingProvider::LOCK_SHARED);
431
432
				// since we skipped the view we need to scan and emit the hooks ourselves
433 7
				$this->fileView->getUpdater()->update($targetPath);
434
435 7
				$this->emitPostHooks($exists, $targetPath);
436
437 7
				$info = $this->fileView->getFileInfo($targetPath);
438 7
				return $info->getEtag();
439 12
			} catch (\Exception $e) {
440 12
				if ($partFile !== null) {
441 11
					$targetStorage->unlink($targetInternalPath);
442 11
				}
443 12
				$this->convertToSabreException($e);
444
			}
445
		}
446
447 19
		return null;
448
	}
449
450
	/**
451
	 * Returns whether a part file is needed for the given storage
452
	 * or whether the file can be assembled/uploaded directly on the
453
	 * target storage.
454
	 *
455
	 * @param \OCP\Files\Storage $storage
456
	 * @return bool true if the storage needs part file handling
457
	 */
458 43
	private function needsPartFile($storage) {
459
		// TODO: in the future use ChunkHandler provided by storage
460
		// and/or add method on Storage called "needsPartFile()"
461 43
		return !$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage') &&
462 43
		!$storage->instanceOfStorage('OC\Files\Storage\OwnCloud');
463
	}
464
465
	/**
466
	 * Convert the given exception to a SabreException instance
467
	 *
468
	 * @param \Exception $e
469
	 *
470
	 * @throws \Sabre\DAV\Exception
471
	 */
472 26
	private function convertToSabreException(\Exception $e) {
473 26
		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...
474 17
			throw $e;
475
		}
476 19
		if ($e instanceof NotPermittedException) {
477
			// a more general case - due to whatever reason the content could not be written
478 2
			throw new Forbidden($e->getMessage(), 0, $e);
479
		}
480 17
		if ($e instanceof EntityTooLargeException) {
481
			// the file is too big to be stored
482 2
			throw new EntityTooLarge($e->getMessage(), 0, $e);
483
		}
484 15
		if ($e instanceof InvalidContentException) {
485
			// the file content is not permitted
486 2
			throw new UnsupportedMediaType($e->getMessage(), 0, $e);
487
		}
488 13
		if ($e instanceof InvalidPathException) {
489
			// the path for the file was not valid
490
			// TODO: find proper http status code for this case
491 2
			throw new Forbidden($e->getMessage(), 0, $e);
492
		}
493 11
		if ($e instanceof LockedException || $e instanceof LockNotAcquiredException) {
494
			// the file is currently being written to by another process
495 5
			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
496
		}
497 6
		if ($e instanceof GenericEncryptionException) {
498
			// returning 503 will allow retry of the operation at a later point in time
499 2
			throw new ServiceUnavailable('Encryption not ready: ' . $e->getMessage(), 0, $e);
500
		}
501 4
		if ($e instanceof StorageNotAvailableException) {
502 2
			throw new ServiceUnavailable('Failed to write file contents: ' . $e->getMessage(), 0, $e);
503
		}
504
505 2
		throw new \Sabre\DAV\Exception($e->getMessage(), 0, $e);
506
	}
507
}
508