Passed
Push — master ( b0cc93...6f88f2 )
by Roeland
15:34 queued 03:56
created

Node::getMTime()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Bernhard Posselt <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Morris Jobke <[email protected]>
8
 * @author Robin Appelman <[email protected]>
9
 * @author Roeland Jago Douma <[email protected]>
10
 * @author Vincent Petry <[email protected]>
11
 *
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OC\Files\Node;
29
30
use OC\Files\Filesystem;
31
use OC\Files\Mount\MoveableMount;
32
use OCP\Files\FileInfo;
33
use OCP\Files\InvalidPathException;
34
use OCP\Files\NotFoundException;
35
use OCP\Files\NotPermittedException;
36
37
// FIXME: this class really should be abstract
38
class Node implements \OCP\Files\Node {
39
	/**
40
	 * @var \OC\Files\View $view
41
	 */
42
	protected $view;
43
44
	/**
45
	 * @var \OC\Files\Node\Root $root
46
	 */
47
	protected $root;
48
49
	/**
50
	 * @var string $path
51
	 */
52
	protected $path;
53
54
	/**
55
	 * @var \OCP\Files\FileInfo
56
	 */
57
	protected $fileInfo;
58
59
	/**
60
	 * @param \OC\Files\View $view
61
	 * @param \OCP\Files\IRootFolder $root
62
	 * @param string $path
63
	 * @param FileInfo $fileInfo
64
	 */
65
	public function __construct($root, $view, $path, $fileInfo = null) {
66
		$this->view = $view;
67
		$this->root = $root;
0 ignored issues
show
Documentation Bug introduced by
$root is of type OCP\Files\IRootFolder, but the property $root was declared to be of type OC\Files\Node\Root. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
68
		$this->path = $path;
69
		$this->fileInfo = $fileInfo;
70
	}
71
72
	/**
73
	 * Creates a Node of the same type that represents a non-existing path
74
	 *
75
	 * @param string $path path
76
	 * @return string non-existing node class
77
	 */
78
	protected function createNonExistingNode($path) {
79
		throw new \Exception('Must be implemented by subclasses');
80
	}
81
82
	/**
83
	 * Returns the matching file info
84
	 *
85
	 * @return FileInfo
86
	 * @throws InvalidPathException
87
	 * @throws NotFoundException
88
	 */
89
	public function getFileInfo() {
90
		if (!Filesystem::isValidPath($this->path)) {
91
			throw new InvalidPathException();
92
		}
93
		if (!$this->fileInfo) {
94
			$fileInfo = $this->view->getFileInfo($this->path);
95
			if ($fileInfo instanceof FileInfo) {
96
				$this->fileInfo = $fileInfo;
97
			} else {
98
				throw new NotFoundException();
99
			}
100
		}
101
		return $this->fileInfo;
102
	}
103
104
	/**
105
	 * @param string[] $hooks
106
	 */
107
	protected function sendHooks($hooks) {
108
		foreach ($hooks as $hook) {
109
			$this->root->emit('\OC\Files', $hook, array($this));
110
		}
111
	}
112
113
	/**
114
	 * @param int $permissions
115
	 * @return bool
116
	 */
117
	protected function checkPermissions($permissions) {
118
		return ($this->getPermissions() & $permissions) === $permissions;
119
	}
120
121
	public function delete() {
122
	}
123
124
	/**
125
	 * @param int $mtime
126
	 * @throws \OCP\Files\NotPermittedException
127
	 */
128
	public function touch($mtime = null) {
129
		if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) {
130
			$this->sendHooks(array('preTouch'));
131
			$this->view->touch($this->path, $mtime);
132
			$this->sendHooks(array('postTouch'));
133
			if ($this->fileInfo) {
134
				if (is_null($mtime)) {
135
					$mtime = time();
136
				}
137
				$this->fileInfo['mtime'] = $mtime;
138
			}
139
		} else {
140
			throw new NotPermittedException();
141
		}
142
	}
143
144
	/**
145
	 * @return \OC\Files\Storage\Storage
146
	 * @throws \OCP\Files\NotFoundException
147
	 */
148
	public function getStorage() {
149
		list($storage,) = $this->view->resolvePath($this->path);
150
		return $storage;
151
	}
152
153
	/**
154
	 * @return string
155
	 */
156
	public function getPath() {
157
		return $this->path;
158
	}
159
160
	/**
161
	 * @return string
162
	 */
163
	public function getInternalPath() {
164
		list(, $internalPath) = $this->view->resolvePath($this->path);
165
		return $internalPath;
166
	}
167
168
	/**
169
	 * @return int
170
	 * @throws InvalidPathException
171
	 * @throws NotFoundException
172
	 */
173
	public function getId() {
174
		return $this->getFileInfo()->getId();
175
	}
176
177
	/**
178
	 * @return array
179
	 */
180
	public function stat() {
181
		return $this->view->stat($this->path);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->view->stat($this->path) could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
182
	}
183
184
	/**
185
	 * @return int
186
	 * @throws InvalidPathException
187
	 * @throws NotFoundException
188
	 */
189
	public function getMTime() {
190
		return $this->getFileInfo()->getMTime();
191
	}
192
193
	/**
194
	 * @param bool $includeMounts
195
	 * @return int
196
	 * @throws InvalidPathException
197
	 * @throws NotFoundException
198
	 */
199
	public function getSize($includeMounts = true) {
200
		return $this->getFileInfo()->getSize($includeMounts);
201
	}
202
203
	/**
204
	 * @return string
205
	 * @throws InvalidPathException
206
	 * @throws NotFoundException
207
	 */
208
	public function getEtag() {
209
		return $this->getFileInfo()->getEtag();
210
	}
211
212
	/**
213
	 * @return int
214
	 * @throws InvalidPathException
215
	 * @throws NotFoundException
216
	 */
217
	public function getPermissions() {
218
		return $this->getFileInfo()->getPermissions();
219
	}
220
221
	/**
222
	 * @return bool
223
	 * @throws InvalidPathException
224
	 * @throws NotFoundException
225
	 */
226
	public function isReadable() {
227
		return $this->getFileInfo()->isReadable();
228
	}
229
230
	/**
231
	 * @return bool
232
	 * @throws InvalidPathException
233
	 * @throws NotFoundException
234
	 */
235
	public function isUpdateable() {
236
		return $this->getFileInfo()->isUpdateable();
237
	}
238
239
	/**
240
	 * @return bool
241
	 * @throws InvalidPathException
242
	 * @throws NotFoundException
243
	 */
244
	public function isDeletable() {
245
		return $this->getFileInfo()->isDeletable();
246
	}
247
248
	/**
249
	 * @return bool
250
	 * @throws InvalidPathException
251
	 * @throws NotFoundException
252
	 */
253
	public function isShareable() {
254
		return $this->getFileInfo()->isShareable();
255
	}
256
257
	/**
258
	 * @return bool
259
	 * @throws InvalidPathException
260
	 * @throws NotFoundException
261
	 */
262
	public function isCreatable() {
263
		return $this->getFileInfo()->isCreatable();
264
	}
265
266
	/**
267
	 * @return Node
268
	 */
269
	public function getParent() {
270
		$newPath = dirname($this->path);
271
		if ($newPath === '' || $newPath === '.' || $newPath === '/') {
272
			return $this->root;
273
		}
274
		return $this->root->get($newPath);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->root->get($newPath) returns the type string which is incompatible with the documented return type OC\Files\Node\Node.
Loading history...
275
	}
276
277
	/**
278
	 * @return string
279
	 */
280
	public function getName() {
281
		return basename($this->path);
282
	}
283
284
	/**
285
	 * @param string $path
286
	 * @return string
287
	 */
288
	protected function normalizePath($path) {
289
		if ($path === '' or $path === '/') {
290
			return '/';
291
		}
292
		//no windows style slashes
293
		$path = str_replace('\\', '/', $path);
294
		//add leading slash
295
		if ($path[0] !== '/') {
296
			$path = '/' . $path;
297
		}
298
		//remove duplicate slashes
299
		while (strpos($path, '//') !== false) {
300
			$path = str_replace('//', '/', $path);
301
		}
302
		//remove trailing slash
303
		$path = rtrim($path, '/');
304
305
		return $path;
306
	}
307
308
	/**
309
	 * check if the requested path is valid
310
	 *
311
	 * @param string $path
312
	 * @return bool
313
	 */
314
	public function isValidPath($path) {
315
		if (!$path || $path[0] !== '/') {
316
			$path = '/' . $path;
317
		}
318
		if (strstr($path, '/../') || strrchr($path, '/') === '/..') {
319
			return false;
320
		}
321
		return true;
322
	}
323
324
	public function isMounted() {
325
		return $this->getFileInfo()->isMounted();
326
	}
327
328
	public function isShared() {
329
		return $this->getFileInfo()->isShared();
330
	}
331
332
	public function getMimeType() {
333
		return $this->getFileInfo()->getMimetype();
334
	}
335
336
	public function getMimePart() {
337
		return $this->getFileInfo()->getMimePart();
338
	}
339
340
	public function getType() {
341
		return $this->getFileInfo()->getType();
342
	}
343
344
	public function isEncrypted() {
345
		return $this->getFileInfo()->isEncrypted();
346
	}
347
348
	public function getMountPoint() {
349
		return $this->getFileInfo()->getMountPoint();
350
	}
351
352
	public function getOwner() {
353
		return $this->getFileInfo()->getOwner();
354
	}
355
356
	public function getChecksum() {
357
	}
358
359
	public function getExtension(): string {
360
		return $this->getFileInfo()->getExtension();
361
	}
362
363
	/**
364
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
365
	 * @throws \OCP\Lock\LockedException
366
	 */
367
	public function lock($type) {
368
		$this->view->lockFile($this->path, $type);
369
	}
370
371
	/**
372
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
373
	 * @throws \OCP\Lock\LockedException
374
	 */
375
	public function changeLock($type) {
376
		$this->view->changeLock($this->path, $type);
377
	}
378
379
	/**
380
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
381
	 * @throws \OCP\Lock\LockedException
382
	 */
383
	public function unlock($type) {
384
		$this->view->unlockFile($this->path, $type);
385
	}
386
387
	/**
388
	 * @param string $targetPath
389
	 * @throws \OCP\Files\NotPermittedException if copy not allowed or failed
390
	 * @return \OC\Files\Node\Node
391
	 */
392
	public function copy($targetPath) {
393
		$targetPath = $this->normalizePath($targetPath);
394
		$parent = $this->root->get(dirname($targetPath));
395
		if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) {
0 ignored issues
show
introduced by
$parent is never a sub-type of OC\Files\Node\Folder.
Loading history...
396
			$nonExisting = $this->createNonExistingNode($targetPath);
397
			$this->root->emit('\OC\Files', 'preCopy', [$this, $nonExisting]);
398
			$this->root->emit('\OC\Files', 'preWrite', [$nonExisting]);
399
			if (!$this->view->copy($this->path, $targetPath)) {
400
				throw new NotPermittedException('Could not copy ' . $this->path . ' to ' . $targetPath);
401
			}
402
			$targetNode = $this->root->get($targetPath);
403
			$this->root->emit('\OC\Files', 'postCopy', [$this, $targetNode]);
404
			$this->root->emit('\OC\Files', 'postWrite', [$targetNode]);
405
			return $targetNode;
406
		} else {
407
			throw new NotPermittedException('No permission to copy to path ' . $targetPath);
408
		}
409
	}
410
411
	/**
412
	 * @param string $targetPath
413
	 * @throws \OCP\Files\NotPermittedException if move not allowed or failed
414
	 * @return \OC\Files\Node\Node
415
	 */
416
	public function move($targetPath) {
417
		$targetPath = $this->normalizePath($targetPath);
418
		$parent = $this->root->get(dirname($targetPath));
419
		if (
420
			$parent instanceof Folder and
0 ignored issues
show
introduced by
$parent is never a sub-type of OC\Files\Node\Folder.
Loading history...
421
			$this->isValidPath($targetPath) and
422
			(
423
				$parent->isCreatable() ||
424
				($parent->getInternalPath() === '' && $parent->getMountPoint() instanceof MoveableMount)
425
			)
426
		) {
427
			$nonExisting = $this->createNonExistingNode($targetPath);
428
			$this->root->emit('\OC\Files', 'preRename', [$this, $nonExisting]);
429
			$this->root->emit('\OC\Files', 'preWrite', [$nonExisting]);
430
			if (!$this->view->rename($this->path, $targetPath)) {
431
				throw new NotPermittedException('Could not move ' . $this->path . ' to ' . $targetPath);
432
			}
433
			$targetNode = $this->root->get($targetPath);
434
			$this->root->emit('\OC\Files', 'postRename', [$this, $targetNode]);
435
			$this->root->emit('\OC\Files', 'postWrite', [$targetNode]);
436
			$this->path = $targetPath;
437
			return $targetNode;
438
		} else {
439
			throw new NotPermittedException('No permission to move to path ' . $targetPath);
440
		}
441
	}
442
443
}
444