Completed
Push — master ( 9d9302...1724fe )
by Morris
17:04
created

ObjectTree   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 199
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 199
rs 9.6
wmc 32
lcom 1
cbo 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
A init() 0 5 1
B resolveChunkFile() 0 19 5
C getNodeForPath() 0 72 15
D copy() 0 39 10
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Bjoern Schiessle <[email protected]>
6
 * @author Björn Schießle <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Lukas Reschke <[email protected]>
9
 * @author Morris Jobke <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 * @author Thomas Müller <[email protected]>
12
 * @author Vincent Petry <[email protected]>
13
 *
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OCA\DAV\Connector\Sabre;
31
32
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
33
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
34
use OCA\DAV\Connector\Sabre\Exception\FileLocked;
35
use OC\Files\FileInfo;
36
use OC\Files\Mount\MoveableMount;
37
use OCP\Files\ForbiddenException;
38
use OCP\Files\StorageInvalidException;
39
use OCP\Files\StorageNotAvailableException;
40
use OCP\Lock\LockedException;
41
42
class ObjectTree extends CachingTree {
43
44
	/**
45
	 * @var \OC\Files\View
46
	 */
47
	protected $fileView;
48
49
	/**
50
	 * @var  \OCP\Files\Mount\IMountManager
51
	 */
52
	protected $mountManager;
53
54
	/**
55
	 * Creates the object
56
	 */
57
	public function __construct() {
58
	}
59
60
	/**
61
	 * @param \Sabre\DAV\INode $rootNode
62
	 * @param \OC\Files\View $view
63
	 * @param  \OCP\Files\Mount\IMountManager $mountManager
64
	 */
65
	public function init(\Sabre\DAV\INode $rootNode, \OC\Files\View $view, \OCP\Files\Mount\IMountManager $mountManager) {
66
		$this->rootNode = $rootNode;
67
		$this->fileView = $view;
68
		$this->mountManager = $mountManager;
69
	}
70
71
	/**
72
	 * If the given path is a chunked file name, converts it
73
	 * to the real file name. Only applies if the OC-CHUNKED header
74
	 * is present.
75
	 *
76
	 * @param string $path chunk file path to convert
77
	 *
78
	 * @return string path to real file
79
	 */
80
	private function resolveChunkFile($path) {
0 ignored issues
show
Coding Style introduced by
resolveChunkFile uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
81
		if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
82
			// resolve to real file name to find the proper node
83
			list($dir, $name) = \Sabre\Uri\split($path);
84
			if ($dir === '/' || $dir === '.') {
85
				$dir = '';
86
			}
87
88
			$info = \OC_FileChunking::decodeName($name);
89
			// only replace path if it was really the chunked file
90
			if (isset($info['transferid'])) {
91
				// getNodePath is called for multiple nodes within a chunk
92
				// upload call
93
				$path = $dir . '/' . $info['name'];
94
				$path = ltrim($path, '/');
95
			}
96
		}
97
		return $path;
98
	}
99
100
	/**
101
	 * Returns the INode object for the requested path
102
	 *
103
	 * @param string $path
104
	 * @return \Sabre\DAV\INode
105
	 * @throws InvalidPath
106
	 * @throws \Sabre\DAV\Exception\Locked
107
	 * @throws \Sabre\DAV\Exception\NotFound
108
	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
109
	 */
110
	public function getNodeForPath($path) {
111
		if (!$this->fileView) {
112
			throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup');
113
		}
114
115
		$path = trim($path, '/');
116
117
		if (isset($this->cache[$path])) {
118
			return $this->cache[$path];
119
		}
120
121
		if ($path) {
122
			try {
123
				$this->fileView->verifyPath($path, basename($path));
124
			} catch (\OCP\Files\InvalidPathException $ex) {
125
				throw new InvalidPath($ex->getMessage());
126
			}
127
		}
128
129
		// Is it the root node?
130
		if (!strlen($path)) {
131
			return $this->rootNode;
132
		}
133
134
		if (pathinfo($path, PATHINFO_EXTENSION) === 'part') {
135
			// read from storage
136
			$absPath = $this->fileView->getAbsolutePath($path);
137
			$mount = $this->fileView->getMount($path);
138
			$storage = $mount->getStorage();
139
			$internalPath = $mount->getInternalPath($absPath);
140
			if ($storage && $storage->file_exists($internalPath)) {
141
				/**
142
				 * @var \OC\Files\Storage\Storage $storage
143
				 */
144
				// get data directly
145
				$data = $storage->getMetaData($internalPath);
146
				$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 137 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...
147
			} else {
148
				$info = null;
149
			}
150
		} else {
151
			// resolve chunk file name to real name, if applicable
152
			$path = $this->resolveChunkFile($path);
153
154
			// read from cache
155
			try {
156
				$info = $this->fileView->getFileInfo($path);
157
			} catch (StorageNotAvailableException $e) {
158
				throw new \Sabre\DAV\Exception\ServiceUnavailable('Storage is temporarily not available');
159
			} catch (StorageInvalidException $e) {
160
				throw new \Sabre\DAV\Exception\NotFound('Storage ' . $path . ' is invalid');
161
			} catch (LockedException $e) {
162
				throw new \Sabre\DAV\Exception\Locked();
163
			} catch (ForbiddenException $e) {
164
				throw new \Sabre\DAV\Exception\Forbidden();
165
			}
166
		}
167
168
		if (!$info) {
169
			throw new \Sabre\DAV\Exception\NotFound('File with name ' . $path . ' could not be located');
170
		}
171
172
		if ($info->getType() === 'dir') {
173
			$node = new \OCA\DAV\Connector\Sabre\Directory($this->fileView, $info, $this);
174
		} else {
175
			$node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info);
176
		}
177
178
		$this->cache[$path] = $node;
179
		return $node;
180
181
	}
182
183
	/**
184
	 * Copies a file or directory.
185
	 *
186
	 * This method must work recursively and delete the destination
187
	 * if it exists
188
	 *
189
	 * @param string $source
190
	 * @param string $destination
191
	 * @throws FileLocked
192
	 * @throws Forbidden
193
	 * @throws InvalidPath
194
	 * @throws \Exception
195
	 * @throws \Sabre\DAV\Exception\Forbidden
196
	 * @throws \Sabre\DAV\Exception\Locked
197
	 * @throws \Sabre\DAV\Exception\NotFound
198
	 * @throws \Sabre\DAV\Exception\ServiceUnavailable
199
	 * @return void
200
	 */
201
	public function copy($source, $destination) {
202
		if (!$this->fileView) {
203
			throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup');
204
		}
205
206
207
		$info = $this->fileView->getFileInfo(dirname($destination));
208
		if ($this->fileView->file_exists($destination)) {
209
			$destinationPermission = $info && $info->isUpdateable();
210
		} else {
211
			$destinationPermission = $info && $info->isCreatable();
212
		}
213
		if (!$destinationPermission) {
214
			throw new Forbidden('No permissions to copy object.');
215
		}
216
217
		// this will trigger existence check
218
		$this->getNodeForPath($source);
219
220
		list($destinationDir, $destinationName) = \Sabre\Uri\split($destination);
221
		try {
222
			$this->fileView->verifyPath($destinationDir, $destinationName);
223
		} catch (\OCP\Files\InvalidPathException $ex) {
224
			throw new InvalidPath($ex->getMessage());
225
		}
226
227
		try {
228
			$this->fileView->copy($source, $destination);
229
		} catch (StorageNotAvailableException $e) {
230
			throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
231
		} catch (ForbiddenException $ex) {
232
			throw new Forbidden($ex->getMessage(), $ex->getRetry());
233
		} catch (LockedException $e) {
234
			throw new FileLocked($e->getMessage(), $e->getCode(), $e);
235
		}
236
237
		list($destinationDir,) = \Sabre\Uri\split($destination);
238
		$this->markDirty($destinationDir);
239
	}
240
}
241