Passed
Push — master ( 814bf0...daee22 )
by Morris
13:03
created

Node::getPath()   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 Arthur Schiwon <[email protected]>
6
 * @author Bart Visscher <[email protected]>
7
 * @author Björn Schießle <[email protected]>
8
 * @author Jakob Sack <[email protected]>
9
 * @author Joas Schilling <[email protected]>
10
 * @author Jörn Friedrich Dreyer <[email protected]>
11
 * @author Klaas Freitag <[email protected]>
12
 * @author Markus Goetz <[email protected]>
13
 * @author Morris Jobke <[email protected]>
14
 * @author Robin Appelman <[email protected]>
15
 * @author Roeland Jago Douma <[email protected]>
16
 * @author Thomas Müller <[email protected]>
17
 * @author Vincent Petry <[email protected]>
18
 *
19
 * @license AGPL-3.0
20
 *
21
 * This code is free software: you can redistribute it and/or modify
22
 * it under the terms of the GNU Affero General Public License, version 3,
23
 * as published by the Free Software Foundation.
24
 *
25
 * This program is distributed in the hope that it will be useful,
26
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28
 * GNU Affero General Public License for more details.
29
 *
30
 * You should have received a copy of the GNU Affero General Public License, version 3,
31
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
32
 *
33
 */
34
35
namespace OCA\DAV\Connector\Sabre;
36
37
use OC\Files\Mount\MoveableMount;
38
use OC\Files\View;
39
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
40
use OCP\Files\FileInfo;
41
use OCP\Files\StorageNotAvailableException;
42
use OCP\Share\Exceptions\ShareNotFound;
43
use OCP\Share\IManager;
44
use OCP\Share;
45
use OCP\Share\IShare;
46
47
48
abstract class Node implements \Sabre\DAV\INode {
49
50
	/**
51
	 * @var \OC\Files\View
52
	 */
53
	protected $fileView;
54
55
	/**
56
	 * The path to the current node
57
	 *
58
	 * @var string
59
	 */
60
	protected $path;
61
62
	/**
63
	 * node properties cache
64
	 *
65
	 * @var array
66
	 */
67
	protected $property_cache = null;
68
69
	/**
70
	 * @var \OCP\Files\FileInfo
71
	 */
72
	protected $info;
73
74
	/**
75
	 * @var IManager
76
	 */
77
	protected $shareManager;
78
79
	/**
80
	 * Sets up the node, expects a full path name
81
	 *
82
	 * @param \OC\Files\View $view
83
	 * @param \OCP\Files\FileInfo $info
84
	 * @param IManager $shareManager
85
	 */
86
	public function __construct(View $view, FileInfo $info, IManager $shareManager = null) {
87
		$this->fileView = $view;
88
		$this->path = $this->fileView->getRelativePath($info->getPath());
89
		$this->info = $info;
90
		if ($shareManager) {
91
			$this->shareManager = $shareManager;
92
		} else {
93
			$this->shareManager = \OC::$server->getShareManager();
94
		}
95
	}
96
97
	protected function refreshInfo() {
98
		$this->info = $this->fileView->getFileInfo($this->path);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->fileView->getFileInfo($this->path) can also be of type false. However, the property $info is declared as type OCP\Files\FileInfo. Maybe add an additional type 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 mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
99
	}
100
101
	/**
102
	 *  Returns the name of the node
103
	 *
104
	 * @return string
105
	 */
106
	public function getName() {
107
		return $this->info->getName();
108
	}
109
110
	/**
111
	 * Returns the full path
112
	 *
113
	 * @return string
114
	 */
115
	public function getPath() {
116
		return $this->path;
117
	}
118
119
	/**
120
	 * Renames the node
121
	 *
122
	 * @param string $name The new name
123
	 * @throws \Sabre\DAV\Exception\BadRequest
124
	 * @throws \Sabre\DAV\Exception\Forbidden
125
	 */
126
	public function setName($name) {
127
128
		// rename is only allowed if the update privilege is granted
129
		if (!$this->info->isUpdateable()) {
130
			throw new \Sabre\DAV\Exception\Forbidden();
131
		}
132
133
		list($parentPath,) = \Sabre\Uri\split($this->path);
134
		list(, $newName) = \Sabre\Uri\split($name);
135
136
		// verify path of the target
137
		$this->verifyPath();
138
139
		$newPath = $parentPath . '/' . $newName;
140
141
		$this->fileView->rename($this->path, $newPath);
142
143
		$this->path = $newPath;
144
145
		$this->refreshInfo();
146
	}
147
148
	public function setPropertyCache($property_cache) {
149
		$this->property_cache = $property_cache;
150
	}
151
152
	/**
153
	 * Returns the last modification time, as a unix timestamp
154
	 *
155
	 * @return int timestamp as integer
156
	 */
157
	public function getLastModified() {
158
		$timestamp = $this->info->getMtime();
159
		if (!empty($timestamp)) {
160
			return (int)$timestamp;
161
		}
162
		return $timestamp;
163
	}
164
165
	/**
166
	 *  sets the last modification time of the file (mtime) to the value given
167
	 *  in the second parameter or to now if the second param is empty.
168
	 *  Even if the modification time is set to a custom value the access time is set to now.
169
	 */
170
	public function touch($mtime) {
171
		$mtime = $this->sanitizeMtime($mtime);
172
		$this->fileView->touch($this->path, $mtime);
173
		$this->refreshInfo();
174
	}
175
176
	/**
177
	 * Returns the ETag for a file
178
	 *
179
	 * An ETag is a unique identifier representing the current version of the
180
	 * file. If the file changes, the ETag MUST change.  The ETag is an
181
	 * arbitrary string, but MUST be surrounded by double-quotes.
182
	 *
183
	 * Return null if the ETag can not effectively be determined
184
	 *
185
	 * @return string
186
	 */
187
	public function getETag() {
188
		return '"' . $this->info->getEtag() . '"';
189
	}
190
191
	/**
192
	 * Sets the ETag
193
	 *
194
	 * @param string $etag
195
	 *
196
	 * @return int file id of updated file or -1 on failure
197
	 */
198
	public function setETag($etag) {
199
		return $this->fileView->putFileInfo($this->path, array('etag' => $etag));
200
	}
201
202
	/**
203
	 * Returns the size of the node, in bytes
204
	 *
205
	 * @return integer
206
	 */
207
	public function getSize() {
208
		return $this->info->getSize();
209
	}
210
211
	/**
212
	 * Returns the cache's file id
213
	 *
214
	 * @return int
215
	 */
216
	public function getId() {
217
		return $this->info->getId();
218
	}
219
220
	/**
221
	 * @return string|null
222
	 */
223
	public function getFileId() {
224
		if ($this->info->getId()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->info->getId() of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
225
			$instanceId = \OC_Util::getInstanceId();
226
			$id = sprintf('%08d', $this->info->getId());
227
			return $id . $instanceId;
228
		}
229
230
		return null;
231
	}
232
233
	/**
234
	 * @return integer
235
	 */
236
	public function getInternalFileId() {
237
		return $this->info->getId();
238
	}
239
240
	/**
241
	 * @param string $user
242
	 * @return int
243
	 */
244
	public function getSharePermissions($user) {
245
246
		// check of we access a federated share
247
		if ($user !== null) {
0 ignored issues
show
introduced by
The condition $user !== null is always true.
Loading history...
248
			try {
249
				$share = $this->shareManager->getShareByToken($user);
250
				return $share->getPermissions();
251
			} catch (ShareNotFound $e) {
252
				// ignore
253
			}
254
		}
255
256
		try {
257
			$storage = $this->info->getStorage();
258
		} catch (StorageNotAvailableException $e) {
259
			$storage = null;
260
		}
261
262
		if ($storage && $storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
263
			/** @var \OCA\Files_Sharing\SharedStorage $storage */
264
			$permissions = (int)$storage->getShare()->getPermissions();
265
		} else {
266
			$permissions = $this->info->getPermissions();
267
		}
268
269
		/*
270
		 * We can always share non moveable mount points with DELETE and UPDATE
271
		 * Eventually we need to do this properly
272
		 */
273
		$mountpoint = $this->info->getMountPoint();
274
		if (!($mountpoint instanceof MoveableMount)) {
275
			$mountpointpath = $mountpoint->getMountPoint();
276
			if (substr($mountpointpath, -1) === '/') {
277
				$mountpointpath = substr($mountpointpath, 0, -1);
278
			}
279
280
			if (!$mountpoint->getOption('readonly', false) && $mountpointpath === $this->info->getPath()) {
281
				$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
282
			}
283
		}
284
285
		/*
286
		 * Files can't have create or delete permissions
287
		 */
288
		if ($this->info->getType() === \OCP\Files\FileInfo::TYPE_FILE) {
289
			$permissions &= ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_DELETE);
290
		}
291
292
		return $permissions;
293
	}
294
295
	/**
296
	 * @param string $user
297
	 * @return string
298
	 */
299
	public function getNoteFromShare($user) {
300
		if ($user === null) {
0 ignored issues
show
introduced by
The condition $user === null is always false.
Loading history...
301
			return '';
302
		}
303
304
		$types = [
305
			Share::SHARE_TYPE_USER,
306
			Share::SHARE_TYPE_GROUP,
307
			Share::SHARE_TYPE_CIRCLE,
308
			Share::SHARE_TYPE_ROOM
309
		];
310
311
		foreach ($types as $shareType) {
312
			$shares = $this->shareManager->getSharedWith($user, $shareType, $this, -1);
0 ignored issues
show
Bug introduced by
$this of type OCA\DAV\Connector\Sabre\Node is incompatible with the type OCP\Files\Node|null expected by parameter $node of OCP\Share\IManager::getSharedWith(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

312
			$shares = $this->shareManager->getSharedWith($user, $shareType, /** @scrutinizer ignore-type */ $this, -1);
Loading history...
313
			foreach ($shares as $share) {
314
				$note = $share->getNote();
315
				if($share->getShareOwner() !== $user && !empty($note)) {
316
					return $note;
317
				}
318
			}
319
		}
320
321
		return '';
322
	}
323
324
	/**
325
	 * @return string
326
	 */
327
	public function getDavPermissions() {
328
		$p = '';
329
		if ($this->info->isShared()) {
330
			$p .= 'S';
331
		}
332
		if ($this->info->isShareable()) {
333
			$p .= 'R';
334
		}
335
		if ($this->info->isMounted()) {
336
			$p .= 'M';
337
		}
338
		if ($this->info->isReadable()) {
339
			$p .= 'G';
340
		}
341
		if ($this->info->isDeletable()) {
342
			$p .= 'D';
343
		}
344
		if ($this->info->isUpdateable()) {
345
			$p .= 'NV'; // Renameable, Moveable
346
		}
347
		if ($this->info->getType() === \OCP\Files\FileInfo::TYPE_FILE) {
348
			if ($this->info->isUpdateable()) {
349
				$p .= 'W';
350
			}
351
		} else {
352
			if ($this->info->isCreatable()) {
353
				$p .= 'CK';
354
			}
355
		}
356
		return $p;
357
	}
358
359
	public function getOwner() {
360
		return $this->info->getOwner();
361
	}
362
363
	protected function verifyPath() {
364
		try {
365
			$fileName = basename($this->info->getPath());
366
			$this->fileView->verifyPath($this->path, $fileName);
367
		} catch (\OCP\Files\InvalidPathException $ex) {
368
			throw new InvalidPath($ex->getMessage());
369
		}
370
	}
371
372
	/**
373
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
374
	 */
375
	public function acquireLock($type) {
376
		$this->fileView->lockFile($this->path, $type);
377
	}
378
379
	/**
380
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
381
	 */
382
	public function releaseLock($type) {
383
		$this->fileView->unlockFile($this->path, $type);
384
	}
385
386
	/**
387
	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
388
	 */
389
	public function changeLock($type) {
390
		$this->fileView->changeLock($this->path, $type);
391
	}
392
393
	public function getFileInfo() {
394
		return $this->info;
395
	}
396
397
	protected function sanitizeMtime($mtimeFromRequest) {
398
		// In PHP 5.X "is_numeric" returns true for strings in hexadecimal
399
		// notation. This is no longer the case in PHP 7.X, so this check
400
		// ensures that strings with hexadecimal notations fail too in PHP 5.X.
401
		$isHexadecimal = is_string($mtimeFromRequest) && preg_match('/^\s*0[xX]/', $mtimeFromRequest);
402
		if ($isHexadecimal || !is_numeric($mtimeFromRequest)) {
403
			throw new \InvalidArgumentException('X-OC-MTime header must be an integer (unix timestamp).');
404
		}
405
406
		return (int)$mtimeFromRequest;
407
	}
408
409
}
410