Completed
Push — stable8.2 ( e9036a...6707df )
by
unknown
59:39
created

ObjectTree::move()   C

Complexity

Conditions 18
Paths 122

Size

Total Lines 63
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 34
CRAP Score 19.0935
Metric Value
dl 0
loc 63
rs 5.5129
ccs 34
cts 40
cp 0.85
cc 18
eloc 39
nc 122
nop 2
crap 19.0935

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 Björn Schießle <[email protected]>
4
 * @author Morris Jobke <[email protected]>
5
 * @author Robin Appelman <[email protected]>
6
 * @author Thomas Müller <[email protected]>
7
 * @author Vincent Petry <[email protected]>
8
 *
9
 * @copyright Copyright (c) 2015, ownCloud, Inc.
10
 * @license AGPL-3.0
11
 *
12
 * This code is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License, version 3,
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License, version 3,
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
23
 *
24
 */
25
26
namespace OC\Connector\Sabre;
27
28
use OC\Connector\Sabre\Exception\InvalidPath;
29
use OC\Connector\Sabre\Exception\FileLocked;
30
use OC\Files\FileInfo;
31
use OC\Files\Mount\MoveableMount;
32
use OCP\Files\StorageInvalidException;
33
use OCP\Files\StorageNotAvailableException;
34
use OCP\Lock\LockedException;
35
36
class ObjectTree extends \Sabre\DAV\Tree {
37
38
	/**
39
	 * @var \OC\Files\View
40
	 */
41
	protected $fileView;
42
43
	/**
44
	 * @var  \OCP\Files\Mount\IMountManager
45
	 */
46
	protected $mountManager;
47
48
	/**
49
	 * Creates the object
50
	 */
51 33
	public function __construct() {
52 33
	}
53
54
	/**
55
	 * @param \Sabre\DAV\INode $rootNode
56
	 * @param \OC\Files\View $view
57
	 * @param  \OCP\Files\Mount\IMountManager $mountManager
58
	 */
59 33
	public function init(\Sabre\DAV\INode $rootNode, \OC\Files\View $view, \OCP\Files\Mount\IMountManager $mountManager) {
60 33
		$this->rootNode = $rootNode;
61 33
		$this->fileView = $view;
62 33
		$this->mountManager = $mountManager;
63 33
	}
64
65
	/**
66
	 * If the given path is a chunked file name, converts it
67
	 * to the real file name. Only applies if the OC-CHUNKED header
68
	 * is present.
69
	 *
70
	 * @param string $path chunk file path to convert
71
	 * 
72
	 * @return string path to real file
73
	 */
74 21
	private function resolveChunkFile($path) {
75 21
		if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
76
			// resolve to real file name to find the proper node
77 10
			list($dir, $name) = \Sabre\HTTP\URLUtil::splitPath($path);
78 10
			if ($dir == '/' || $dir == '.') {
79
				$dir = '';
80
			}
81
82 10
			$info = \OC_FileChunking::decodeName($name);
83
			// only replace path if it was really the chunked file
84 10
			if (isset($info['transferid'])) {
85
				// getNodePath is called for multiple nodes within a chunk
86
				// upload call
87 8
				$path = $dir . '/' . $info['name'];
88 8
				$path = ltrim($path, '/');
89 8
			}
90 10
		}
91 21
		return $path;
92
	}
93
94
	/**
95
	 * Returns the INode object for the requested path
96
	 *
97
	 * @param string $path
98
	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
99
	 * @throws \Sabre\DAV\Exception\NotFound
100
	 * @return \Sabre\DAV\INode
101
	 */
102 23
	public function getNodeForPath($path) {
103 23
		if (!$this->fileView) {
104
			throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup');
105
		}
106
107 23
		$path = trim($path, '/');
108 23
		if ($path) {
109
			try {
110 22
				$this->fileView->verifyPath($path, basename($path));
111 22
			} catch (\OCP\Files\InvalidPathException $ex) {
112 1
				throw new InvalidPath($ex->getMessage());
113
			}
114 21
		}
115
116 22
		if (isset($this->cache[$path])) {
117 8
			return $this->cache[$path];
118
		}
119
120
		// Is it the root node?
121 22
		if (!strlen($path)) {
122 11
			return $this->rootNode;
123
		}
124
125 21
		if (pathinfo($path, PATHINFO_EXTENSION) === 'part') {
126
			// read from storage
127
			$absPath = $this->fileView->getAbsolutePath($path);
128
			$mount = $this->fileView->getMount($path);
129
			$storage = $mount->getStorage();
130
			$internalPath = $mount->getInternalPath($absPath);
131
			if ($storage) {
132
				/**
133
				 * @var \OC\Files\Storage\Storage $storage
134
				 */
135
				$scanner = $storage->getScanner($internalPath);
136
				// get data directly
137
				$data = $scanner->getData($internalPath);
138
				$info = new FileInfo($absPath, $storage, $internalPath, $data, $mount);
0 ignored issues
show
Bug introduced by
It seems like $data defined by $scanner->getData($internalPath) on line 137 can also be of type null; however, OC\Files\FileInfo::__construct() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $mount defined by $this->fileView->getMount($path) on line 128 can be null; however, OC\Files\FileInfo::__construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
139
			} else {
140
				$info = null;
141
			}
142
		} else {
143
			// resolve chunk file name to real name, if applicable
144 21
			$path = $this->resolveChunkFile($path);
145
146
			// read from cache
147
			try {
148 21
				$info = $this->fileView->getFileInfo($path);
149 21
			} catch (StorageNotAvailableException $e) {
150
				throw new \Sabre\DAV\Exception\ServiceUnavailable('Storage not available');
151
			} catch (StorageInvalidException $e) {
152
				throw new \Sabre\DAV\Exception\NotFound('Storage ' . $path . ' is invalid');
153
			} catch (LockedException $e) {
154
				throw new \Sabre\DAV\Exception\Locked();
155
			}
156
		}
157
158 21
		if (!$info) {
159 6
			throw new \Sabre\DAV\Exception\NotFound('File with name ' . $path . ' could not be located');
160
		}
161
162 21
		if ($info->getType() === 'dir') {
163 3
			$node = new \OC\Connector\Sabre\Directory($this->fileView, $info);
164 3
		} else {
165 18
			$node = new \OC\Connector\Sabre\File($this->fileView, $info);
166
		}
167
168 21
		$this->cache[$path] = $node;
169 21
		return $node;
170
171
	}
172
173
	/**
174
	 * Moves a file from one location to another
175
	 *
176
	 * @param string $sourcePath The path to the file which should be moved
177
	 * @param string $destinationPath The full destination path, so not just the destination parent node
178
	 * @throws \Sabre\DAV\Exception\BadRequest
179
	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
180
	 * @throws \Sabre\DAV\Exception\Forbidden
181
	 * @return int
182
	 */
183 10
	public function move($sourcePath, $destinationPath) {
184 10
		if (!$this->fileView) {
185
			throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup');
186
		}
187
188 10
		$targetNodeExists = $this->nodeExists($destinationPath);
189 10
		$sourceNode = $this->getNodeForPath($sourcePath);
190 10
		if ($sourceNode instanceof \Sabre\DAV\ICollection && $targetNodeExists) {
0 ignored issues
show
Bug introduced by
The class Sabre\DAV\ICollection 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...
191 1
			throw new \Sabre\DAV\Exception\Forbidden('Could not copy directory ' . $sourceNode->getName() . ', target exists');
192
		}
193 9
		list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($sourcePath);
194 9
		list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destinationPath);
195
196 9
		$isMovableMount = false;
197 9
		$sourceMount = $this->mountManager->find($this->fileView->getAbsolutePath($sourcePath));
198 9
		$internalPath = $sourceMount->getInternalPath($this->fileView->getAbsolutePath($sourcePath));
199 9
		if ($sourceMount instanceof MoveableMount && $internalPath === '') {
200
			$isMovableMount = true;
201
		}
202
203
		try {
204 9
			$sameFolder = ($sourceDir === $destinationDir);
205
			// if we're overwriting or same folder
206 9
			if ($targetNodeExists || $sameFolder) {
207
				// note that renaming a share mount point is always allowed
208 3
				if (!$this->fileView->isUpdatable($destinationDir) && !$isMovableMount) {
209 2
					throw new \Sabre\DAV\Exception\Forbidden();
210
				}
211 1
			} else {
212 6
				if (!$this->fileView->isCreatable($destinationDir)) {
213 3
					throw new \Sabre\DAV\Exception\Forbidden();
214
				}
215
			}
216
217 4
			if (!$sameFolder) {
218
				// moving to a different folder, source will be gone, like a deletion
219
				// note that moving a share mount point is always allowed
220 3
				if (!$this->fileView->isDeletable($sourcePath) && !$isMovableMount) {
221 1
					throw new \Sabre\DAV\Exception\Forbidden();
222
				}
223 2
			}
224
225 3
			$fileName = basename($destinationPath);
226
			try {
227 3
				$this->fileView->verifyPath($destinationDir, $fileName);
228 3
			} catch (\OCP\Files\InvalidPathException $ex) {
229 1
				throw new InvalidPath($ex->getMessage());
230
			}
231
232 2
			$renameOkay = $this->fileView->rename($sourcePath, $destinationPath);
233 2
			if (!$renameOkay) {
234
				throw new \Sabre\DAV\Exception\Forbidden('');
235
			}
236 9
		} catch (StorageNotAvailableException $e) {
237
			throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
238 7
		} catch (LockedException $e) {
239
			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
240
		}
241
242 2
		$this->markDirty($sourceDir);
243 2
		$this->markDirty($destinationDir);
244
245 2
	}
246
247
	/**
248
	 * Copies a file or directory.
249
	 *
250
	 * This method must work recursively and delete the destination
251
	 * if it exists
252
	 *
253
	 * @param string $source
254
	 * @param string $destination
255
	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
256
	 * @return void
257
	 */
258
	public function copy($source, $destination) {
259
		if (!$this->fileView) {
260
			throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup');
261
		}
262
263
		// this will trigger existence check
264
		$this->getNodeForPath($source);
265
266
		list($destinationDir, $destinationName) = \Sabre\HTTP\URLUtil::splitPath($destination);
267
		try {
268
			$this->fileView->verifyPath($destinationDir, $destinationName);
269
		} catch (\OCP\Files\InvalidPathException $ex) {
270
			throw new InvalidPath($ex->getMessage());
271
		}
272
273
		try {
274
			$this->fileView->copy($source, $destination);
275
		} catch (StorageNotAvailableException $e) {
276
			throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
277
		} catch (LockedException $e) {
278
			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
279
		}
280
281
		list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destination);
282
		$this->markDirty($destinationDir);
283
	}
284
}
285