Completed
Push — stable9 ( 212ff8...07c437 )
by Morris
26:13 queued 09:36
created

ObjectTree::move()   F

Complexity

Conditions 23
Paths 713

Size

Total Lines 74
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 23
eloc 47
c 1
b 0
f 1
nc 713
nop 2
dl 0
loc 74
rs 2.7027

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 Joas Schilling <[email protected]>
5
 * @author Morris Jobke <[email protected]>
6
 * @author Robin Appelman <[email protected]>
7
 * @author Thomas Müller <[email protected]>
8
 * @author Vincent Petry <[email protected]>
9
 *
10
 * @copyright Copyright (c) 2016, ownCloud, Inc.
11
 * @license AGPL-3.0
12
 *
13
 * This code is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License, version 3,
15
 * as published by the Free Software Foundation.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License, version 3,
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
24
 *
25
 */
26
27
namespace OCA\DAV\Connector\Sabre;
28
29
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
30
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
31
use OCA\DAV\Connector\Sabre\Exception\FileLocked;
32
use OC\Files\FileInfo;
33
use OC\Files\Mount\MoveableMount;
34
use OCP\Files\ForbiddenException;
35
use OCP\Files\StorageInvalidException;
36
use OCP\Files\StorageNotAvailableException;
37
use OCP\Lock\LockedException;
38
39
class ObjectTree extends \Sabre\DAV\Tree {
40
41
	/**
42
	 * @var \OC\Files\View
43
	 */
44
	protected $fileView;
45
46
	/**
47
	 * @var  \OCP\Files\Mount\IMountManager
48
	 */
49
	protected $mountManager;
50
51
	/**
52
	 * Creates the object
53
	 */
54
	public function __construct() {
55
	}
56
57
	/**
58
	 * @param \Sabre\DAV\INode $rootNode
59
	 * @param \OC\Files\View $view
60
	 * @param  \OCP\Files\Mount\IMountManager $mountManager
61
	 */
62
	public function init(\Sabre\DAV\INode $rootNode, \OC\Files\View $view, \OCP\Files\Mount\IMountManager $mountManager) {
63
		$this->rootNode = $rootNode;
64
		$this->fileView = $view;
65
		$this->mountManager = $mountManager;
66
	}
67
68
	/**
69
	 * If the given path is a chunked file name, converts it
70
	 * to the real file name. Only applies if the OC-CHUNKED header
71
	 * is present.
72
	 *
73
	 * @param string $path chunk file path to convert
74
	 *
75
	 * @return string path to real file
76
	 */
77
	private function resolveChunkFile($path) {
78
		if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
79
			// resolve to real file name to find the proper node
80
			list($dir, $name) = \Sabre\HTTP\URLUtil::splitPath($path);
81
			if ($dir == '/' || $dir == '.') {
82
				$dir = '';
83
			}
84
85
			$info = \OC_FileChunking::decodeName($name);
86
			// only replace path if it was really the chunked file
87
			if (isset($info['transferid'])) {
88
				// getNodePath is called for multiple nodes within a chunk
89
				// upload call
90
				$path = $dir . '/' . $info['name'];
91
				$path = ltrim($path, '/');
92
			}
93
		}
94
		return $path;
95
	}
96
97
	public function cacheNode(Node $node) {
98
		$this->cache[trim($node->getPath(), '/')] = $node;
99
	}
100
101
	/**
102
	 * Returns the INode object for the requested path
103
	 *
104
	 * @param string $path
105
	 * @return \Sabre\DAV\INode
106
	 * @throws InvalidPath
107
	 * @throws \Sabre\DAV\Exception\Locked
108
	 * @throws \Sabre\DAV\Exception\NotFound
109
	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
110
	 */
111
	public function getNodeForPath($path) {
112
		if (!$this->fileView) {
113
			throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup');
114
		}
115
116
		$path = trim($path, '/');
117
118
		if (isset($this->cache[$path])) {
119
			return $this->cache[$path];
120
		}
121
122
		if ($path) {
123
			try {
124
				$this->fileView->verifyPath($path, basename($path));
125
			} catch (\OCP\Files\InvalidPathException $ex) {
126
				throw new InvalidPath($ex->getMessage());
127
			}
128
		}
129
130
		// Is it the root node?
131
		if (!strlen($path)) {
132
			return $this->rootNode;
133
		}
134
135
		if (pathinfo($path, PATHINFO_EXTENSION) === 'part') {
136
			// read from storage
137
			$absPath = $this->fileView->getAbsolutePath($path);
138
			$mount = $this->fileView->getMount($path);
139
			$storage = $mount->getStorage();
140
			$internalPath = $mount->getInternalPath($absPath);
141
			if ($storage && $storage->file_exists($internalPath)) {
142
				/**
143
				 * @var \OC\Files\Storage\Storage $storage
144
				 */
145
				// get data directly
146
				$data = $storage->getMetaData($internalPath);
147
				$info = new FileInfo($absPath, $storage, $internalPath, $data, $mount);
0 ignored issues
show
Bug introduced by
It seems like $mount defined by $this->fileView->getMount($path) on line 138 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...
148
			} else {
149
				$info = null;
150
			}
151
		} else {
152
			// resolve chunk file name to real name, if applicable
153
			$path = $this->resolveChunkFile($path);
154
155
			// read from cache
156
			try {
157
				$info = $this->fileView->getFileInfo($path);
158
			} catch (StorageNotAvailableException $e) {
159
				throw new \Sabre\DAV\Exception\ServiceUnavailable('Storage not available');
160
			} catch (StorageInvalidException $e) {
161
				throw new \Sabre\DAV\Exception\NotFound('Storage ' . $path . ' is invalid');
162
			} catch (LockedException $e) {
163
				throw new \Sabre\DAV\Exception\Locked();
164
			}
165
		}
166
167
		if (!$info) {
168
			throw new \Sabre\DAV\Exception\NotFound('File with name ' . $path . ' could not be located');
169
		}
170
171 View Code Duplication
		if ($info->getType() === 'dir') {
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...
172
			$node = new \OCA\DAV\Connector\Sabre\Directory($this->fileView, $info, $this);
173
		} else {
174
			$node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info);
175
		}
176
177
		$this->cache[$path] = $node;
178
		return $node;
179
180
	}
181
182
	/**
183
	 * Moves a file from one location to another
184
	 *
185
	 * @param string $sourcePath The path to the file which should be moved
186
	 * @param string $destinationPath The full destination path, so not just the destination parent node
187
	 * @return int
188
	 * @throws FileLocked
189
	 * @throws Forbidden
190
	 * @throws InvalidPath
191
	 * @throws \Sabre\DAV\Exception\Forbidden
192
	 * @throws \Sabre\DAV\Exception\Locked
193
	 * @throws \Sabre\DAV\Exception\NotFound
194
	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
195
	 */
196
	public function move($sourcePath, $destinationPath) {
197
		if (!$this->fileView) {
198
			throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup');
199
		}
200
201
		$infoDestination = $this->fileView->getFileInfo(dirname($destinationPath));
202
		$infoSource = $this->fileView->getFileInfo($sourcePath);
203
		$destinationPermission = $infoDestination && $infoDestination->isUpdateable();
204
		$sourcePermission =  $infoSource && $infoSource->isDeletable();
205
206
		if (!$destinationPermission || !$sourcePermission) {
207
			throw new Forbidden('No permissions to move object.');
208
		}
209
210
		$targetNodeExists = $this->nodeExists($destinationPath);
211
		$sourceNode = $this->getNodeForPath($sourcePath);
212
		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...
213
			throw new \Sabre\DAV\Exception\Forbidden('Could not copy directory ' . $sourceNode->getName() . ', target exists');
214
		}
215
		list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($sourcePath);
216
		list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destinationPath);
217
218
		$isMovableMount = false;
219
		$sourceMount = $this->mountManager->find($this->fileView->getAbsolutePath($sourcePath));
220
		$internalPath = $sourceMount->getInternalPath($this->fileView->getAbsolutePath($sourcePath));
221
		if ($sourceMount instanceof MoveableMount && $internalPath === '') {
222
			$isMovableMount = true;
223
		}
224
225
		try {
226
			$sameFolder = ($sourceDir === $destinationDir);
227
			// if we're overwriting or same folder
228
			if ($targetNodeExists || $sameFolder) {
229
				// note that renaming a share mount point is always allowed
230
				if (!$this->fileView->isUpdatable($destinationDir) && !$isMovableMount) {
231
					throw new \Sabre\DAV\Exception\Forbidden();
232
				}
233
			} else {
234
				if (!$this->fileView->isCreatable($destinationDir)) {
235
					throw new \Sabre\DAV\Exception\Forbidden();
236
				}
237
			}
238
239
			if (!$sameFolder) {
240
				// moving to a different folder, source will be gone, like a deletion
241
				// note that moving a share mount point is always allowed
242
				if (!$this->fileView->isDeletable($sourcePath) && !$isMovableMount) {
243
					throw new \Sabre\DAV\Exception\Forbidden();
244
				}
245
			}
246
247
			$fileName = basename($destinationPath);
248
			try {
249
				$this->fileView->verifyPath($destinationDir, $fileName);
250
			} catch (\OCP\Files\InvalidPathException $ex) {
251
				throw new InvalidPath($ex->getMessage());
252
			}
253
254
			$renameOkay = $this->fileView->rename($sourcePath, $destinationPath);
255
			if (!$renameOkay) {
256
				throw new \Sabre\DAV\Exception\Forbidden('');
257
			}
258
		} catch (StorageNotAvailableException $e) {
259
			throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
260
		} catch (ForbiddenException $ex) {
261
			throw new Forbidden($ex->getMessage(), $ex->getRetry());
262
		} catch (LockedException $e) {
263
			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
264
		}
265
266
		$this->markDirty($sourceDir);
267
		$this->markDirty($destinationDir);
268
269
	}
270
271
	/**
272
	 * Copies a file or directory.
273
	 *
274
	 * This method must work recursively and delete the destination
275
	 * if it exists
276
	 *
277
	 * @param string $source
278
	 * @param string $destination
279
	 * @throws FileLocked
280
	 * @throws Forbidden
281
	 * @throws InvalidPath
282
	 * @throws \Exception
283
	 * @throws \Sabre\DAV\Exception\Locked
284
	 * @throws \Sabre\DAV\Exception\NotFound
285
	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
286
	 */
287
	public function copy($source, $destination) {
288
		if (!$this->fileView) {
289
			throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup');
290
		}
291
292
		$info = $this->fileView->getFileInfo(dirname($destination));
293
		if ($info && !$info->isUpdateable()) {
294
			throw new Forbidden('No permissions to copy object.');
295
		}
296
		
297
		// this will trigger existence check
298
		$this->getNodeForPath($source);
299
300
		list($destinationDir, $destinationName) = \Sabre\HTTP\URLUtil::splitPath($destination);
301
		try {
302
			$this->fileView->verifyPath($destinationDir, $destinationName);
303
		} catch (\OCP\Files\InvalidPathException $ex) {
304
			throw new InvalidPath($ex->getMessage());
305
		}
306
307
		try {
308
			$this->fileView->copy($source, $destination);
309
		} catch (StorageNotAvailableException $e) {
310
			throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
311
		} catch (ForbiddenException $ex) {
312
			throw new Forbidden($ex->getMessage(), $ex->getRetry());
313
		} catch (LockedException $e) {
314
			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
315
		}
316
317
		list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destination);
318
		$this->markDirty($destinationDir);
319
	}
320
}
321