Passed
Push — master ( ccc0a5...5195be )
by Roeland
10:43 queued 11s
created

Node::getFileId()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 8
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
		if (!$this->fileView->rename($this->path, $newPath)) {
142
			throw new \Sabre\DAV\Exception('Failed to rename '. $this->path . ' to ' . $newPath);
143
		}
144
145
		$this->path = $newPath;
146
147
		$this->refreshInfo();
148
	}
149
150
	public function setPropertyCache($property_cache) {
151
		$this->property_cache = $property_cache;
152
	}
153
154
	/**
155
	 * Returns the last modification time, as a unix timestamp
156
	 *
157
	 * @return int timestamp as integer
158
	 */
159
	public function getLastModified() {
160
		$timestamp = $this->info->getMtime();
161
		if (!empty($timestamp)) {
162
			return (int)$timestamp;
163
		}
164
		return $timestamp;
165
	}
166
167
	/**
168
	 *  sets the last modification time of the file (mtime) to the value given
169
	 *  in the second parameter or to now if the second param is empty.
170
	 *  Even if the modification time is set to a custom value the access time is set to now.
171
	 */
172
	public function touch($mtime) {
173
		$mtime = $this->sanitizeMtime($mtime);
174
		$this->fileView->touch($this->path, $mtime);
175
		$this->refreshInfo();
176
	}
177
178
	/**
179
	 * Returns the ETag for a file
180
	 *
181
	 * An ETag is a unique identifier representing the current version of the
182
	 * file. If the file changes, the ETag MUST change.  The ETag is an
183
	 * arbitrary string, but MUST be surrounded by double-quotes.
184
	 *
185
	 * Return null if the ETag can not effectively be determined
186
	 *
187
	 * @return string
188
	 */
189
	public function getETag() {
190
		return '"' . $this->info->getEtag() . '"';
191
	}
192
193
	/**
194
	 * Sets the ETag
195
	 *
196
	 * @param string $etag
197
	 *
198
	 * @return int file id of updated file or -1 on failure
199
	 */
200
	public function setETag($etag) {
201
		return $this->fileView->putFileInfo($this->path, array('etag' => $etag));
202
	}
203
204
	public function setCreationTime(int $time) {
205
		return $this->fileView->putFileInfo($this->path, array('creation_time' => $time));
206
	}
207
208
	public function setUploadTime(int $time) {
209
		return $this->fileView->putFileInfo($this->path, array('upload_time' => $time));
210
	}
211
212
	/**
213
	 * Returns the size of the node, in bytes
214
	 *
215
	 * @return integer
216
	 */
217
	public function getSize() {
218
		return $this->info->getSize();
219
	}
220
221
	/**
222
	 * Returns the cache's file id
223
	 *
224
	 * @return int
225
	 */
226
	public function getId() {
227
		return $this->info->getId();
228
	}
229
230
	/**
231
	 * @return string|null
232
	 */
233
	public function getFileId() {
234
		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...
235
			$instanceId = \OC_Util::getInstanceId();
236
			$id = sprintf('%08d', $this->info->getId());
237
			return $id . $instanceId;
238
		}
239
240
		return null;
241
	}
242
243
	/**
244
	 * @return integer
245
	 */
246
	public function getInternalFileId() {
247
		return $this->info->getId();
248
	}
249
250
	/**
251
	 * @param string $user
252
	 * @return int
253
	 */
254
	public function getSharePermissions($user) {
255
256
		// check of we access a federated share
257
		if ($user !== null) {
0 ignored issues
show
introduced by
The condition $user !== null is always true.
Loading history...
258
			try {
259
				$share = $this->shareManager->getShareByToken($user);
260
				return $share->getPermissions();
261
			} catch (ShareNotFound $e) {
262
				// ignore
263
			}
264
		}
265
266
		try {
267
			$storage = $this->info->getStorage();
268
		} catch (StorageNotAvailableException $e) {
269
			$storage = null;
270
		}
271
272
		if ($storage && $storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
273
			/** @var \OCA\Files_Sharing\SharedStorage $storage */
274
			$permissions = (int)$storage->getShare()->getPermissions();
275
		} else {
276
			$permissions = $this->info->getPermissions();
277
		}
278
279
		/*
280
		 * We can always share non moveable mount points with DELETE and UPDATE
281
		 * Eventually we need to do this properly
282
		 */
283
		$mountpoint = $this->info->getMountPoint();
284
		if (!($mountpoint instanceof MoveableMount)) {
285
			$mountpointpath = $mountpoint->getMountPoint();
286
			if (substr($mountpointpath, -1) === '/') {
287
				$mountpointpath = substr($mountpointpath, 0, -1);
288
			}
289
290
			if (!$mountpoint->getOption('readonly', false) && $mountpointpath === $this->info->getPath()) {
291
				$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
292
			}
293
		}
294
295
		/*
296
		 * Files can't have create or delete permissions
297
		 */
298
		if ($this->info->getType() === \OCP\Files\FileInfo::TYPE_FILE) {
299
			$permissions &= ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_DELETE);
300
		}
301
302
		return $permissions;
303
	}
304
305
	/**
306
	 * @param string $user
307
	 * @return string
308
	 */
309
	public function getNoteFromShare($user) {
310
		if ($user === null) {
0 ignored issues
show
introduced by
The condition $user === null is always false.
Loading history...
311
			return '';
312
		}
313
314
		$types = [
315
			Share::SHARE_TYPE_USER,
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_USER has been deprecated: 17.0.0 - use IShare::TYPE_USER instead ( Ignorable by Annotation )

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

315
			/** @scrutinizer ignore-deprecated */ Share::SHARE_TYPE_USER,

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
316
			Share::SHARE_TYPE_GROUP,
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_GROUP has been deprecated: 17.0.0 - use IShare::TYPE_GROUP instead ( Ignorable by Annotation )

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

316
			/** @scrutinizer ignore-deprecated */ Share::SHARE_TYPE_GROUP,

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
317
			Share::SHARE_TYPE_CIRCLE,
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_CIRCLE has been deprecated: 17.0.0 - use IShare::TYPE_CIRCLE instead ( Ignorable by Annotation )

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

317
			/** @scrutinizer ignore-deprecated */ Share::SHARE_TYPE_CIRCLE,

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
318
			Share::SHARE_TYPE_ROOM
0 ignored issues
show
Deprecated Code introduced by
The constant OC\Share\Constants::SHARE_TYPE_ROOM has been deprecated: 17.0.0 - use IShare::TYPE_ROOM instead ( Ignorable by Annotation )

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

318
			/** @scrutinizer ignore-deprecated */ Share::SHARE_TYPE_ROOM

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
319
		];
320
321
		foreach ($types as $shareType) {
322
			$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

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