Passed
Push — master ( c1161e...940996 )
by John
12:59 queued 11s
created

Share::isFileReachable()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 3
nop 2
dl 0
loc 15
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 Bernhard Reiter <[email protected]>
8
 * @author Bjoern Schiessle <[email protected]>
9
 * @author Björn Schießle <[email protected]>
10
 * @author Christoph Wurst <[email protected]>
11
 * @author Joas Schilling <[email protected]>
12
 * @author Lukas Reschke <[email protected]>
13
 * @author Morris Jobke <[email protected]>
14
 * @author Robin Appelman <[email protected]>
15
 * @author Robin McCorkell <[email protected]>
16
 * @author Roeland Jago Douma <[email protected]>
17
 * @author Sebastian Döll <[email protected]>
18
 * @author Thomas Müller <[email protected]>
19
 * @author Vincent Petry <[email protected]>
20
 * @author Volkan Gezer <[email protected]>
21
 *
22
 * @license AGPL-3.0
23
 *
24
 * This code is free software: you can redistribute it and/or modify
25
 * it under the terms of the GNU Affero General Public License, version 3,
26
 * as published by the Free Software Foundation.
27
 *
28
 * This program is distributed in the hope that it will be useful,
29
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31
 * GNU Affero General Public License for more details.
32
 *
33
 * You should have received a copy of the GNU Affero General Public License, version 3,
34
 * along with this program. If not, see <http://www.gnu.org/licenses/>
35
 *
36
 */
37
38
namespace OC\Share;
39
40
use OCP\DB\QueryBuilder\IQueryBuilder;
41
use OCP\ILogger;
42
use OCP\Share\IShare;
43
44
/**
45
 * This class provides the ability for apps to share their content between users.
46
 * Apps must create a backend class that implements OCP\Share_Backend and register it with this class.
47
 *
48
 * It provides the following hooks:
49
 *  - post_shared
50
 */
51
class Share extends Constants {
52
53
	/** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask
54
	 * Construct permissions for share() and setPermissions with Or (|) e.g.
55
	 * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE
56
	 *
57
	 * Check if permission is granted with And (&) e.g. Check if delete is
58
	 * granted: if ($permissions & PERMISSION_DELETE)
59
	 *
60
	 * Remove permissions with And (&) and Not (~) e.g. Remove the update
61
	 * permission: $permissions &= ~PERMISSION_UPDATE
62
	 *
63
	 * Apps are required to handle permissions on their own, this class only
64
	 * stores and manages the permissions of shares
65
	 * @see lib/public/constants.php
66
	 */
67
68
	/**
69
	 * Register a sharing backend class that implements OCP\Share_Backend for an item type
70
	 * @param string $itemType Item type
71
	 * @param string $class Backend class
72
	 * @param string $collectionOf (optional) Depends on item type
73
	 * @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files
74
	 * @return boolean true if backend is registered or false if error
75
	 */
76
	public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) {
77
		if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_enabled', 'yes') == 'yes') {
78
			if (!isset(self::$backendTypes[$itemType])) {
79
				self::$backendTypes[$itemType] = [
80
					'class' => $class,
81
					'collectionOf' => $collectionOf,
82
					'supportedFileExtensions' => $supportedFileExtensions
83
				];
84
				return true;
85
			}
86
			\OCP\Util::writeLog('OCP\Share',
87
				'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class']
88
				.' is already registered for '.$itemType,
89
				ILogger::WARN);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::WARN has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

89
				/** @scrutinizer ignore-deprecated */ ILogger::WARN);

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...
90
		}
91
		return false;
92
	}
93
94
	/**
95
	 * Get the items of item type shared with the current user
96
	 * @param string $itemType
97
	 * @param int $format (optional) Format type must be defined by the backend
98
	 * @param mixed $parameters (optional)
99
	 * @param int $limit Number of items to return (optional) Returns all by default
100
	 * @param boolean $includeCollections (optional)
101
	 * @return mixed Return depends on format
102
	 * @deprecated TESTS ONLY - this methods is only used by tests
103
	 * called like this:
104
	 * \OC\Share\Share::getItemsSharedWith('folder'); (apps/files_sharing/tests/UpdaterTest.php)
105
	 */
106
	public static function getItemsSharedWith() {
107
		return self::getItems('folder', null, self::$shareTypeUserAndGroups, \OC_User::getUser());
108
	}
109
110
	/**
111
	 * Get the items of item type shared with a user
112
	 * @param string $itemType
113
	 * @param string $user id for which user we want the shares
114
	 * @param int $format (optional) Format type must be defined by the backend
115
	 * @param mixed $parameters (optional)
116
	 * @param int $limit Number of items to return (optional) Returns all by default
117
	 * @param boolean $includeCollections (optional)
118
	 * @return mixed Return depends on format
119
	 * @deprecated TESTS ONLY - this methods is only used by tests
120
	 * called like this:
121
	 * \OC\Share\Share::getItemsSharedWithUser('test', $shareWith); (tests/lib/Share/Backend.php)
122
	 */
123
	public static function getItemsSharedWithUser($itemType, $user) {
0 ignored issues
show
Unused Code introduced by
The parameter $itemType is not used and could be removed. ( Ignorable by Annotation )

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

123
	public static function getItemsSharedWithUser(/** @scrutinizer ignore-unused */ $itemType, $user) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
124
		return self::getItems('test', null, self::$shareTypeUserAndGroups, $user);
125
	}
126
127
	/**
128
	 * Get the item of item type shared with a given user by source
129
	 * @param string $itemType
130
	 * @param string $itemSource
131
	 * @param string $user User to whom the item was shared
132
	 * @param string $owner Owner of the share
133
	 * @param int $shareType only look for a specific share type
134
	 * @return array Return list of items with file_target, permissions and expiration
135
	 */
136
	public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null, $shareType = null) {
137
		$shares = [];
138
		$fileDependent = false;
139
140
		$where = 'WHERE';
141
		$fileDependentWhere = '';
142
		if ($itemType === 'file' || $itemType === 'folder') {
143
			$fileDependent = true;
144
			$column = 'file_source';
145
			$fileDependentWhere = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
146
			$fileDependentWhere .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
147
		} else {
148
			$column = 'item_source';
149
		}
150
151
		$select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent);
152
153
		$where .= ' `' . $column . '` = ? AND `item_type` = ? ';
154
		$arguments = [$itemSource, $itemType];
155
		// for link shares $user === null
156
		if ($user !== null) {
0 ignored issues
show
introduced by
The condition $user !== null is always true.
Loading history...
157
			$where .= ' AND `share_with` = ? ';
158
			$arguments[] = $user;
159
		}
160
161
		if ($shareType !== null) {
162
			$where .= ' AND `share_type` = ? ';
163
			$arguments[] = $shareType;
164
		}
165
166
		if ($owner !== null) {
167
			$where .= ' AND `uid_owner` = ? ';
168
			$arguments[] = $owner;
169
		}
170
171
		$query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` '. $fileDependentWhere . $where);
172
173
		$result = \OC_DB::executeAudited($query, $arguments);
174
175
		while ($row = $result->fetchRow()) {
176
			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
177
				continue;
178
			}
179
			if ($fileDependent && (int)$row['file_parent'] === -1) {
180
				// if it is a mount point we need to get the path from the mount manager
181
				$mountManager = \OC\Files\Filesystem::getMountManager();
182
				$mountPoint = $mountManager->findByStorageId($row['storage_id']);
183
				if (!empty($mountPoint)) {
184
					$path = $mountPoint[0]->getMountPoint();
185
					$path = trim($path, '/');
186
					$path = substr($path, strlen($owner) + 1); //normalize path to 'files/foo.txt`
187
					$row['path'] = $path;
188
				} else {
189
					\OC::$server->getLogger()->warning(
190
						'Could not resolve mount point for ' . $row['storage_id'],
191
						['app' => 'OCP\Share']
192
					);
193
				}
194
			}
195
			$shares[] = $row;
196
		}
197
		$result->closeCursor();
198
199
		//if didn't found a result than let's look for a group share.
200
		if (empty($shares) && $user !== null) {
201
			$userObject = \OC::$server->getUserManager()->get($user);
202
			$groups = [];
203
			if ($userObject) {
204
				$groups = \OC::$server->getGroupManager()->getUserGroupIds($userObject);
205
			}
206
207
			if (!empty($groups)) {
208
				$where = $fileDependentWhere . ' WHERE `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)';
209
				$arguments = [$itemSource, $itemType, $groups];
210
				$types = [null, null, IQueryBuilder::PARAM_STR_ARRAY];
211
212
				if ($owner !== null) {
213
					$where .= ' AND `uid_owner` = ?';
214
					$arguments[] = $owner;
215
					$types[] = null;
216
				}
217
218
				// TODO: inject connection, hopefully one day in the future when this
219
				// class isn't static anymore...
220
				$conn = \OC::$server->getDatabaseConnection();
221
				$result = $conn->executeQuery(
222
					'SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where,
223
					$arguments,
224
					$types
225
				);
226
227
				while ($row = $result->fetch()) {
228
					$shares[] = $row;
229
				}
230
			}
231
		}
232
233
		return $shares;
234
	}
235
236
	/**
237
	 * Get the shared item of item type owned by the current user
238
	 * @param string $itemType
239
	 * @param string $itemSource
240
	 * @param int $format (optional) Format type must be defined by the backend
241
	 * @param mixed $parameters
242
	 * @param boolean $includeCollections
243
	 * @return mixed Return depends on format
244
	 *
245
	 * Refactoring notes:
246
	 *   * defacto $parameters and $format is always the default and therefore is removed in the subsequent call
247
	 */
248
	public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE,
0 ignored issues
show
Unused Code introduced by
The parameter $format is not used and could be removed. ( Ignorable by Annotation )

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

248
	public static function getItemShared($itemType, $itemSource, /** @scrutinizer ignore-unused */ $format = self::FORMAT_NONE,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
249
										 $parameters = null, $includeCollections = false) {
0 ignored issues
show
Unused Code introduced by
The parameter $parameters is not used and could be removed. ( Ignorable by Annotation )

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

249
										 /** @scrutinizer ignore-unused */ $parameters = null, $includeCollections = false) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
250
		return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), self::FORMAT_NONE,
251
			null, -1, $includeCollections);
252
	}
253
254
	/**
255
	 * Unshare an item from a user, group, or delete a private link
256
	 * @param string $itemType
257
	 * @param string $itemSource
258
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
259
	 * @param string $shareWith User or group the item is being shared with
260
	 * @param string $owner owner of the share, if null the current user is used
261
	 * @return boolean true on success or false on failure
262
	 */
263
	public static function unshare($itemType, $itemSource, $shareType, $shareWith, $owner = null) {
264
265
		// check if it is a valid itemType
266
		self::getBackend($itemType);
267
268
		$items = self::getItemSharedWithUser($itemType, $itemSource, $shareWith, $owner, $shareType);
269
270
		$toDelete = [];
271
		$newParent = null;
272
		$currentUser = $owner ? $owner : \OC_User::getUser();
273
		foreach ($items as $item) {
274
			// delete the item with the expected share_type and owner
275
			if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) {
276
				$toDelete = $item;
277
			// if there is more then one result we don't have to delete the children
278
				// but update their parent. For group shares the new parent should always be
279
				// the original group share and not the db entry with the unique name
280
			} elseif ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
281
				$newParent = $item['parent'];
282
			} else {
283
				$newParent = $item['id'];
284
			}
285
		}
286
287
		if (!empty($toDelete)) {
288
			self::unshareItem($toDelete, $newParent);
289
			return true;
290
		}
291
		return false;
292
	}
293
294
	/**
295
	 * Unshares a share given a share data array
296
	 * @param array $item Share data (usually database row)
297
	 * @param int $newParent parent ID
298
	 * @return null
299
	 */
300
	protected static function unshareItem(array $item, $newParent = null) {
301
		$shareType = (int)$item['share_type'];
302
		$shareWith = null;
303
		if ($shareType !== IShare::TYPE_LINK) {
304
			$shareWith = $item['share_with'];
305
		}
306
307
		// Pass all the vars we have for now, they may be useful
308
		$hookParams = [
309
			'id' => $item['id'],
310
			'itemType' => $item['item_type'],
311
			'itemSource' => $item['item_source'],
312
			'shareType' => $shareType,
313
			'shareWith' => $shareWith,
314
			'itemParent' => $item['parent'],
315
			'uidOwner' => $item['uid_owner'],
316
		];
317
		if ($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
318
			$hookParams['fileSource'] = $item['file_source'];
319
			$hookParams['fileTarget'] = $item['file_target'];
320
		}
321
322
		\OC_Hook::emit(\OCP\Share::class, 'pre_unshare', $hookParams);
323
		$deletedShares = Helper::delete($item['id'], false, null, $newParent);
324
		$deletedShares[] = $hookParams;
325
		$hookParams['deletedShares'] = $deletedShares;
326
		\OC_Hook::emit(\OCP\Share::class, 'post_unshare', $hookParams);
327
		if ((int)$item['share_type'] === IShare::TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
328
			list(, $remote) = Helper::splitUserRemote($item['share_with']);
329
			self::sendRemoteUnshare($remote, $item['id'], $item['token']);
330
		}
331
	}
332
333
	/**
334
	 * Get the backend class for the specified item type
335
	 * @param string $itemType
336
	 * @throws \Exception
337
	 * @return \OCP\Share_Backend
338
	 */
339
	public static function getBackend($itemType) {
340
		$l = \OC::$server->getL10N('lib');
341
		if (isset(self::$backends[$itemType])) {
342
			return self::$backends[$itemType];
343
		} elseif (isset(self::$backendTypes[$itemType]['class'])) {
344
			$class = self::$backendTypes[$itemType]['class'];
345
			if (class_exists($class)) {
346
				self::$backends[$itemType] = new $class;
347
				if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) {
348
					$message = 'Sharing backend %s must implement the interface OCP\Share_Backend';
349
					$message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', [$class]);
350
					\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), ILogger::ERROR);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

350
					\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), /** @scrutinizer ignore-deprecated */ ILogger::ERROR);

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...
351
					throw new \Exception($message_t);
352
				}
353
				return self::$backends[$itemType];
354
			} else {
355
				$message = 'Sharing backend %s not found';
356
				$message_t = $l->t('Sharing backend %s not found', [$class]);
357
				\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), ILogger::ERROR);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

357
				\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), /** @scrutinizer ignore-deprecated */ ILogger::ERROR);

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...
358
				throw new \Exception($message_t);
359
			}
360
		}
361
		$message = 'Sharing backend for %s not found';
362
		$message_t = $l->t('Sharing backend for %s not found', [$itemType]);
363
		\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemType), ILogger::ERROR);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

363
		\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemType), /** @scrutinizer ignore-deprecated */ ILogger::ERROR);

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...
364
		throw new \Exception($message_t);
365
	}
366
367
	/**
368
	 * Check if resharing is allowed
369
	 * @return boolean true if allowed or false
370
	 *
371
	 * Resharing is allowed by default if not configured
372
	 */
373
	public static function isResharingAllowed() {
374
		if (!isset(self::$isResharingAllowed)) {
375
			if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') {
376
				self::$isResharingAllowed = true;
377
			} else {
378
				self::$isResharingAllowed = false;
379
			}
380
		}
381
		return self::$isResharingAllowed;
382
	}
383
384
	/**
385
	 * Get a list of collection item types for the specified item type
386
	 * @param string $itemType
387
	 * @return array
388
	 */
389
	private static function getCollectionItemTypes($itemType) {
390
		$collectionTypes = [$itemType];
391
		foreach (self::$backendTypes as $type => $backend) {
392
			if (in_array($backend['collectionOf'], $collectionTypes)) {
393
				$collectionTypes[] = $type;
394
			}
395
		}
396
		// TODO Add option for collections to be collection of themselves, only 'folder' does it now...
397
		if (isset(self::$backendTypes[$itemType]) && (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder')) {
398
			unset($collectionTypes[0]);
399
		}
400
		// Return array if collections were found or the item type is a
401
		// collection itself - collections can be inside collections
402
		if (count($collectionTypes) > 0) {
403
			return $collectionTypes;
404
		}
405
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
406
	}
407
408
	/**
409
	 * Get shared items from the database
410
	 * @param string $itemType
411
	 * @param string $item Item source or target (optional)
412
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique
413
	 * @param string $shareWith User or group the item is being shared with
414
	 * @param string $uidOwner User that is the owner of shared items (optional)
415
	 * @param int $format Format to convert items to with formatItems() (optional)
416
	 * @param mixed $parameters to pass to formatItems() (optional)
417
	 * @param int $limit Number of items to return, -1 to return all matches (optional)
418
	 * @param boolean $includeCollections Include collection item types (optional)
419
	 * @param boolean $itemShareWithBySource (optional)
420
	 * @param boolean $checkExpireDate
421
	 * @return array
422
	 *
423
	 * See public functions getItem(s)... for parameter usage
424
	 *
425
	 * Refactoring notes:
426
	 *   * defacto $limit, $itemsShareWithBySource, $checkExpireDate, $parameters and $format is always the default and therefore is removed in the subsequent call
427
	 */
428
	public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null,
429
									$uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1,
0 ignored issues
show
Unused Code introduced by
The parameter $limit is not used and could be removed. ( Ignorable by Annotation )

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

429
									$uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, /** @scrutinizer ignore-unused */ $limit = -1,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $format is not used and could be removed. ( Ignorable by Annotation )

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

429
									$uidOwner = null, /** @scrutinizer ignore-unused */ $format = self::FORMAT_NONE, $parameters = null, $limit = -1,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $parameters is not used and could be removed. ( Ignorable by Annotation )

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

429
									$uidOwner = null, $format = self::FORMAT_NONE, /** @scrutinizer ignore-unused */ $parameters = null, $limit = -1,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
430
									$includeCollections = false, $itemShareWithBySource = false, $checkExpireDate = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $itemShareWithBySource is not used and could be removed. ( Ignorable by Annotation )

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

430
									$includeCollections = false, /** @scrutinizer ignore-unused */ $itemShareWithBySource = false, $checkExpireDate = true) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $checkExpireDate is not used and could be removed. ( Ignorable by Annotation )

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

430
									$includeCollections = false, $itemShareWithBySource = false, /** @scrutinizer ignore-unused */ $checkExpireDate = true) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
431
		if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_enabled', 'yes') != 'yes') {
432
			return [];
433
		}
434
		$backend = self::getBackend($itemType);
435
		$collectionTypes = false;
436
		// Get filesystem root to add it to the file target and remove from the
437
		// file source, match file_source with the file cache
438
		if ($itemType == 'file' || $itemType == 'folder') {
439
			if (!is_null($uidOwner)) {
440
				$root = \OC\Files\Filesystem::getRoot();
441
			} else {
442
				$root = '';
443
			}
444
			$where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
445
			if (!isset($item)) {
446
				$where .= ' AND `file_target` IS NOT NULL ';
447
			}
448
			$where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
449
			$fileDependent = true;
450
			$queryArgs = [];
451
		} else {
452
			$fileDependent = false;
453
			$root = '';
454
			$collectionTypes = self::getCollectionItemTypes($itemType);
455
			if ($includeCollections && !isset($item) && $collectionTypes) {
456
				// If includeCollections is true, find collections of this item type, e.g. a music album contains songs
457
				if (!in_array($itemType, $collectionTypes)) {
458
					$itemTypes = array_merge([$itemType], $collectionTypes);
459
				} else {
460
					$itemTypes = $collectionTypes;
461
				}
462
				$placeholders = implode(',', array_fill(0, count($itemTypes), '?'));
463
				$where = ' WHERE `item_type` IN ('.$placeholders.'))';
464
				$queryArgs = $itemTypes;
465
			} else {
466
				$where = ' WHERE `item_type` = ?';
467
				$queryArgs = [$itemType];
468
			}
469
		}
470
		if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
471
			$where .= ' AND `share_type` != ?';
472
			$queryArgs[] = IShare::TYPE_LINK;
473
		}
474
		if (isset($shareType)) {
475
			// Include all user and group items
476
			if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
477
				$where .= ' AND ((`share_type` in (?, ?) AND `share_with` = ?) ';
478
				$queryArgs[] = IShare::TYPE_USER;
479
				$queryArgs[] = self::$shareTypeGroupUserUnique;
480
				$queryArgs[] = $shareWith;
481
482
				$user = \OC::$server->getUserManager()->get($shareWith);
483
				$groups = [];
484
				if ($user) {
485
					$groups = \OC::$server->getGroupManager()->getUserGroupIds($user);
486
				}
487
				if (!empty($groups)) {
488
					$placeholders = implode(',', array_fill(0, count($groups), '?'));
489
					$where .= ' OR (`share_type` = ? AND `share_with` IN ('.$placeholders.')) ';
490
					$queryArgs[] = IShare::TYPE_GROUP;
491
					$queryArgs = array_merge($queryArgs, $groups);
492
				}
493
				$where .= ')';
494
				// Don't include own group shares
495
				$where .= ' AND `uid_owner` != ?';
496
				$queryArgs[] = $shareWith;
497
			} else {
498
				$where .= ' AND `share_type` = ?';
499
				$queryArgs[] = $shareType;
500
				if (isset($shareWith)) {
501
					$where .= ' AND `share_with` = ?';
502
					$queryArgs[] = $shareWith;
503
				}
504
			}
505
		}
506
		if (isset($uidOwner)) {
507
			$where .= ' AND `uid_owner` = ?';
508
			$queryArgs[] = $uidOwner;
509
			if (!isset($shareType)) {
510
				// Prevent unique user targets for group shares from being selected
511
				$where .= ' AND `share_type` != ?';
512
				$queryArgs[] = self::$shareTypeGroupUserUnique;
513
			}
514
			if ($fileDependent) {
515
				$column = 'file_source';
516
			} else {
517
				$column = 'item_source';
518
			}
519
		} else {
520
			if ($fileDependent) {
521
				$column = 'file_target';
522
			} else {
523
				$column = 'item_target';
524
			}
525
		}
526
		if (isset($item)) {
527
			$collectionTypes = self::getCollectionItemTypes($itemType);
528
			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
529
				$where .= ' AND (';
530
			} else {
531
				$where .= ' AND';
532
			}
533
			// If looking for own shared items, check item_source else check item_target
534
			if (isset($uidOwner)) {
535
				// If item type is a file, file source needs to be checked in case the item was converted
536
				if ($fileDependent) {
537
					$where .= ' `file_source` = ?';
538
					$column = 'file_source';
539
				} else {
540
					$where .= ' `item_source` = ?';
541
					$column = 'item_source';
542
				}
543
			} else {
544
				if ($fileDependent) {
545
					$where .= ' `file_target` = ?';
546
					$item = \OC\Files\Filesystem::normalizePath($item);
547
				} else {
548
					$where .= ' `item_target` = ?';
549
				}
550
			}
551
			$queryArgs[] = $item;
552
			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
553
				$placeholders = implode(',', array_fill(0, count($collectionTypes), '?'));
554
				$where .= ' OR `item_type` IN ('.$placeholders.'))';
555
				$queryArgs = array_merge($queryArgs, $collectionTypes);
556
			}
557
		}
558
559
		$where .= ' ORDER BY `*PREFIX*share`.`id` ASC';
560
561
		$queryLimit = null;
562
		$select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent, $uidOwner);
563
		$root = strlen($root);
564
		$query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit);
565
		$result = $query->execute($queryArgs);
566
		if ($result === false) {
567
			\OCP\Util::writeLog('OCP\Share',
568
				\OC_DB::getErrorMessage() . ', select=' . $select . ' where=',
569
				ILogger::ERROR);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

569
				/** @scrutinizer ignore-deprecated */ ILogger::ERROR);

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...
570
		}
571
		$items = [];
572
		$targets = [];
573
		$switchedItems = [];
574
		$mounts = [];
575
		while ($row = $result->fetchRow()) {
576
			self::transformDBResults($row);
577
			// Filter out duplicate group shares for users with unique targets
578
			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
579
				continue;
580
			}
581
			if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) {
582
				$row['share_type'] = IShare::TYPE_GROUP;
583
				$row['unique_name'] = true; // remember that we use a unique name for this user
584
				$row['share_with'] = $items[$row['parent']]['share_with'];
585
				// if the group share was unshared from the user we keep the permission, otherwise
586
				// we take the permission from the parent because this is always the up-to-date
587
				// permission for the group share
588
				if ($row['permissions'] > 0) {
589
					$row['permissions'] = $items[$row['parent']]['permissions'];
590
				}
591
				// Remove the parent group share
592
				unset($items[$row['parent']]);
593
				if ($row['permissions'] == 0) {
594
					continue;
595
				}
596
			} elseif (!isset($uidOwner)) {
597
				// Check if the same target already exists
598
				if (isset($targets[$row['id']])) {
599
					// Check if the same owner shared with the user twice
600
					// through a group and user share - this is allowed
601
					$id = $targets[$row['id']];
602
					if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
603
						// Switch to group share type to ensure resharing conditions aren't bypassed
604
						if ($items[$id]['share_type'] != IShare::TYPE_GROUP) {
605
							$items[$id]['share_type'] = IShare::TYPE_GROUP;
606
							$items[$id]['share_with'] = $row['share_with'];
607
						}
608
						// Switch ids if sharing permission is granted on only
609
						// one share to ensure correct parent is used if resharing
610
						if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
611
							&& (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
612
							$items[$row['id']] = $items[$id];
613
							$switchedItems[$id] = $row['id'];
614
							unset($items[$id]);
615
							$id = $row['id'];
616
						}
617
						$items[$id]['permissions'] |= (int)$row['permissions'];
618
					}
619
					continue;
620
				} elseif (!empty($row['parent'])) {
621
					$targets[$row['parent']] = $row['id'];
622
				}
623
			}
624
			// Remove root from file source paths if retrieving own shared items
625
			if (isset($uidOwner) && isset($row['path'])) {
626
				if (isset($row['parent'])) {
627
					$query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
628
					$query->select('file_target')
629
						->from('share')
630
						->where($query->expr()->eq('id', $query->createNamedParameter($row['parent'])));
631
632
					$parentResult = $query->execute();
633
					$parentRow = $parentResult->fetch();
634
					$parentResult->closeCursor();
635
636
					if ($parentRow === false) {
637
						\OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: ' .
638
							\OC_DB::getErrorMessage() . ', select=' . $select . ' where=' . $where,
639
							ILogger::ERROR);
0 ignored issues
show
Deprecated Code introduced by
The constant OCP\ILogger::ERROR has been deprecated: 20.0.0 ( Ignorable by Annotation )

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

639
							/** @scrutinizer ignore-deprecated */ ILogger::ERROR);

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...
640
					} else {
641
						$tmpPath = $parentRow['file_target'];
642
						// find the right position where the row path continues from the target path
643
						$pos = strrpos($row['path'], $parentRow['file_target']);
644
						$subPath = substr($row['path'], $pos);
645
						$splitPath = explode('/', $subPath);
646
						foreach (array_slice($splitPath, 2) as $pathPart) {
647
							$tmpPath = $tmpPath . '/' . $pathPart;
648
						}
649
						$row['path'] = $tmpPath;
650
					}
651
				} else {
652
					if (!isset($mounts[$row['storage']])) {
653
						$mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']);
654
						if (is_array($mountPoints) && !empty($mountPoints)) {
655
							$mounts[$row['storage']] = current($mountPoints);
656
						}
657
					}
658
					if (!empty($mounts[$row['storage']])) {
659
						$path = $mounts[$row['storage']]->getMountPoint().$row['path'];
660
						$relPath = substr($path, $root); // path relative to data/user
661
						$row['path'] = rtrim($relPath, '/');
662
					}
663
				}
664
			}
665
666
			// Check if resharing is allowed, if not remove share permission
667
			if (isset($row['permissions']) && (!self::isResharingAllowed() | \OCP\Util::isSharingDisabledForUser())) {
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise | or did you mean ||?
Loading history...
668
				$row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE;
669
			}
670
			// Add display names to result
671
			$row['share_with_displayname'] = $row['share_with'];
672
			if (isset($row['share_with']) && $row['share_with'] != '' &&
673
				$row['share_type'] === IShare::TYPE_USER) {
674
				$shareWithUser = \OC::$server->getUserManager()->get($row['share_with']);
675
				$row['share_with_displayname'] = $shareWithUser === null ? $row['share_with'] : $shareWithUser->getDisplayName();
676
			} elseif (isset($row['share_with']) && $row['share_with'] != '' &&
677
				$row['share_type'] === IShare::TYPE_REMOTE) {
678
				$addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD']);
679
				foreach ($addressBookEntries as $entry) {
680
					foreach ($entry['CLOUD'] as $cloudID) {
681
						if ($cloudID === $row['share_with']) {
682
							$row['share_with_displayname'] = $entry['FN'];
683
						}
684
					}
685
				}
686
			}
687
			if (isset($row['uid_owner']) && $row['uid_owner'] != '') {
688
				$ownerUser = \OC::$server->getUserManager()->get($row['uid_owner']);
689
				$row['displayname_owner'] = $ownerUser === null ? $row['uid_owner'] : $ownerUser->getDisplayName();
690
			}
691
692
			if ($row['permissions'] > 0) {
693
				$items[$row['id']] = $row;
694
			}
695
		}
696
697
		// group items if we are looking for items shared with the current user
698
		if (isset($shareWith) && $shareWith === \OCP\User::getUser()) {
699
			$items = self::groupItems($items, $itemType);
700
		}
701
702
		if (!empty($items)) {
703
			$collectionItems = [];
704
			foreach ($items as &$row) {
705
				// Check if this is a collection of the requested item type
706
				if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) {
707
					if (($collectionBackend = self::getBackend($row['item_type']))
708
						&& $collectionBackend instanceof \OCP\Share_Backend_Collection) {
709
						// Collections can be inside collections, check if the item is a collection
710
						if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
711
							$collectionItems[] = $row;
712
						} else {
713
							$collection = [];
714
							$collection['item_type'] = $row['item_type'];
715
							if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
716
								$collection['path'] = basename($row['path']);
717
							}
718
							$row['collection'] = $collection;
719
							// Fetch all of the children sources
720
							$children = $collectionBackend->getChildren($row[$column]);
721
							foreach ($children as $child) {
722
								$childItem = $row;
723
								$childItem['item_type'] = $itemType;
724
								if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
725
									$childItem['item_source'] = $child['source'];
726
									$childItem['item_target'] = $child['target'];
727
								}
728
								if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
729
									if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
730
										$childItem['file_source'] = $child['source'];
731
									} else { // TODO is this really needed if we already know that we use the file backend?
732
										$meta = \OC\Files\Filesystem::getFileInfo($child['file_path']);
733
										$childItem['file_source'] = $meta['fileid'];
734
									}
735
									$childItem['file_target'] =
736
										\OC\Files\Filesystem::normalizePath($child['file_path']);
737
								}
738
								if (isset($item)) {
739
									if ($childItem[$column] == $item) {
740
										$collectionItems[] = $childItem;
741
									}
742
								} else {
743
									$collectionItems[] = $childItem;
744
								}
745
							}
746
						}
747
					}
748
					// Remove collection item
749
					$toRemove = $row['id'];
750
					if (array_key_exists($toRemove, $switchedItems)) {
751
						$toRemove = $switchedItems[$toRemove];
752
					}
753
					unset($items[$toRemove]);
754
				} elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
755
					// FIXME: Thats a dirty hack to improve file sharing performance,
756
					// see github issue #10588 for more details
757
					// Need to find a solution which works for all back-ends
758
					$collectionBackend = self::getBackend($row['item_type']);
759
					$sharedParents = $collectionBackend->getParents($row['item_source']);
0 ignored issues
show
Bug introduced by
The method getParents() does not exist on OCP\Share_Backend. It seems like you code against a sub-type of OCP\Share_Backend such as OCA\Files_Sharing\ShareBackend\Folder or OCA\Files_Sharing\ShareBackend\Folder. ( Ignorable by Annotation )

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

759
					/** @scrutinizer ignore-call */ 
760
     $sharedParents = $collectionBackend->getParents($row['item_source']);
Loading history...
760
					foreach ($sharedParents as $parent) {
761
						$collectionItems[] = $parent;
762
					}
763
				}
764
			}
765
			if (!empty($collectionItems)) {
766
				$collectionItems = array_unique($collectionItems, SORT_REGULAR);
767
				$items = array_merge($items, $collectionItems);
768
			}
769
770
			// filter out invalid items, these can appear when subshare entries exist
771
			// for a group in which the requested user isn't a member any more
772
			$items = array_filter($items, function ($item) {
773
				return $item['share_type'] !== self::$shareTypeGroupUserUnique;
774
			});
775
776
			return self::formatResult($items, $column, $backend);
777
		} elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) {
778
			// FIXME: Thats a dirty hack to improve file sharing performance,
779
			// see github issue #10588 for more details
780
			// Need to find a solution which works for all back-ends
781
			$collectionItems = [];
782
			$collectionBackend = self::getBackend('folder');
783
			$sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner);
784
			foreach ($sharedParents as $parent) {
785
				$collectionItems[] = $parent;
786
			}
787
			return self::formatResult($collectionItems, $column, $backend);
788
		}
789
790
		return [];
791
	}
792
793
	/**
794
	 * group items with link to the same source
795
	 *
796
	 * @param array $items
797
	 * @param string $itemType
798
	 * @return array of grouped items
799
	 */
800
	protected static function groupItems($items, $itemType) {
801
		$fileSharing = $itemType === 'file' || $itemType === 'folder';
802
803
		$result = [];
804
805
		foreach ($items as $item) {
806
			$grouped = false;
807
			foreach ($result as $key => $r) {
808
				// for file/folder shares we need to compare file_source, otherwise we compare item_source
809
				// only group shares if they already point to the same target, otherwise the file where shared
810
				// before grouping of shares was added. In this case we don't group them toi avoid confusions
811
				if (($fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
812
					(!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
813
					// add the first item to the list of grouped shares
814
					if (!isset($result[$key]['grouped'])) {
815
						$result[$key]['grouped'][] = $result[$key];
816
					}
817
					$result[$key]['permissions'] = (int) $item['permissions'] | (int) $r['permissions'];
818
					$result[$key]['grouped'][] = $item;
819
					$grouped = true;
820
					break;
821
				}
822
			}
823
824
			if (!$grouped) {
825
				$result[] = $item;
826
			}
827
		}
828
829
		return $result;
830
	}
831
832
	/**
833
	 * construct select statement
834
	 * @param int $format
835
	 * @param boolean $fileDependent ist it a file/folder share or a generla share
836
	 * @param string $uidOwner
837
	 * @return string select statement
838
	 */
839
	private static function createSelectStatement($format, $fileDependent, $uidOwner = null) {
840
		$select = '*';
841
		if ($format == self::FORMAT_STATUSES) {
842
			if ($fileDependent) {
843
				$select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, '
844
					. '`share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`, '
845
					. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`, '
846
					. '`uid_initiator`';
847
			} else {
848
				$select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`, `*PREFIX*share`.`permissions`';
849
			}
850
		} else {
851
			if (isset($uidOwner)) {
852
				if ($fileDependent) {
853
					$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,'
854
						. ' `share_type`, `share_with`, `file_source`, `file_target`, `path`, `*PREFIX*share`.`permissions`, `stime`,'
855
						. ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`, '
856
						. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
857
				} else {
858
					$select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `*PREFIX*share`.`permissions`,'
859
						. ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`';
860
				}
861
			} else {
862
				if ($fileDependent) {
863
					if ($format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_GET_FOLDER_CONTENTS || $format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_FILE_APP_ROOT) {
864
						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, '
865
							. '`share_type`, `share_with`, `file_source`, `path`, `file_target`, `stime`, '
866
							. '`*PREFIX*share`.`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
867
							. '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `mail_send`';
868
					} else {
869
						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,'
870
							. '`*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,'
871
							. '`file_source`, `path`, `file_target`, `*PREFIX*share`.`permissions`,'
872
							. '`stime`, `expiration`, `token`, `storage`, `mail_send`,'
873
							. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
874
					}
875
				}
876
			}
877
		}
878
		return $select;
879
	}
880
881
882
	/**
883
	 * transform db results
884
	 * @param array $row result
885
	 */
886
	private static function transformDBResults(&$row) {
887
		if (isset($row['id'])) {
888
			$row['id'] = (int) $row['id'];
889
		}
890
		if (isset($row['share_type'])) {
891
			$row['share_type'] = (int) $row['share_type'];
892
		}
893
		if (isset($row['parent'])) {
894
			$row['parent'] = (int) $row['parent'];
895
		}
896
		if (isset($row['file_parent'])) {
897
			$row['file_parent'] = (int) $row['file_parent'];
898
		}
899
		if (isset($row['file_source'])) {
900
			$row['file_source'] = (int) $row['file_source'];
901
		}
902
		if (isset($row['permissions'])) {
903
			$row['permissions'] = (int) $row['permissions'];
904
		}
905
		if (isset($row['storage'])) {
906
			$row['storage'] = (int) $row['storage'];
907
		}
908
		if (isset($row['stime'])) {
909
			$row['stime'] = (int) $row['stime'];
910
		}
911
		if (isset($row['expiration']) && $row['share_type'] !== IShare::TYPE_LINK) {
912
			// discard expiration date for non-link shares, which might have been
913
			// set by ancient bugs
914
			$row['expiration'] = null;
915
		}
916
	}
917
918
	/**
919
	 * format result
920
	 * @param array $items result
921
	 * @param string $column is it a file share or a general share ('file_target' or 'item_target')
922
	 * @param \OCP\Share_Backend $backend sharing backend
923
	 * @param int $format
924
	 * @param array $parameters additional format parameters
925
	 * @return array format result
926
	 */
927
	private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) {
928
		if ($format === self::FORMAT_NONE) {
929
			return $items;
930
		} elseif ($format === self::FORMAT_STATUSES) {
931
			$statuses = [];
932
			foreach ($items as $item) {
933
				if ($item['share_type'] === IShare::TYPE_LINK) {
934
					if ($item['uid_initiator'] !== \OC::$server->getUserSession()->getUser()->getUID()) {
935
						continue;
936
					}
937
					$statuses[$item[$column]]['link'] = true;
938
				} elseif (!isset($statuses[$item[$column]])) {
939
					$statuses[$item[$column]]['link'] = false;
940
				}
941
				if (!empty($item['file_target'])) {
942
					$statuses[$item[$column]]['path'] = $item['path'];
943
				}
944
			}
945
			return $statuses;
946
		} else {
947
			return $backend->formatItems($items, $format, $parameters);
948
		}
949
	}
950
951
	/**
952
	 * remove protocol from URL
953
	 *
954
	 * @param string $url
955
	 * @return string
956
	 */
957
	public static function removeProtocolFromUrl($url) {
958
		if (strpos($url, 'https://') === 0) {
959
			return substr($url, strlen('https://'));
960
		} elseif (strpos($url, 'http://') === 0) {
961
			return substr($url, strlen('http://'));
962
		}
963
964
		return $url;
965
	}
966
967
	/**
968
	 * try http post first with https and then with http as a fallback
969
	 *
970
	 * @param string $remoteDomain
971
	 * @param string $urlSuffix
972
	 * @param array $fields post parameters
973
	 * @return array
974
	 */
975
	private static function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) {
976
		$protocol = 'https://';
977
		$result = [
978
			'success' => false,
979
			'result' => '',
980
		];
981
		$try = 0;
982
		$discoveryService = \OC::$server->query(\OCP\OCS\IDiscoveryService::class);
983
		while ($result['success'] === false && $try < 2) {
984
			$federationEndpoints = $discoveryService->discover($protocol . $remoteDomain, 'FEDERATED_SHARING');
985
			$endpoint = isset($federationEndpoints['share']) ? $federationEndpoints['share'] : '/ocs/v2.php/cloud/shares';
986
			$client = \OC::$server->getHTTPClientService()->newClient();
987
988
			try {
989
				$response = $client->post(
990
					$protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT,
991
					[
992
						'body' => $fields,
993
						'connect_timeout' => 10,
994
					]
995
				);
996
997
				$result = ['success' => true, 'result' => $response->getBody()];
998
			} catch (\Exception $e) {
999
				$result = ['success' => false, 'result' => $e->getMessage()];
1000
			}
1001
1002
			$try++;
1003
			$protocol = 'http://';
1004
		}
1005
1006
		return $result;
1007
	}
1008
1009
	/**
1010
	 * send server-to-server unshare to remote server
1011
	 *
1012
	 * @param string $remote url
1013
	 * @param int $id share id
1014
	 * @param string $token
1015
	 * @return bool
1016
	 */
1017
	private static function sendRemoteUnshare($remote, $id, $token) {
1018
		$url = rtrim($remote, '/');
1019
		$fields = ['token' => $token, 'format' => 'json'];
1020
		$url = self::removeProtocolFromUrl($url);
1021
		$result = self::tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields);
1022
		$status = json_decode($result['result'], true);
1023
1024
		return ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200));
1025
	}
1026
1027
	/**
1028
	 * @return int
1029
	 */
1030
	public static function getExpireInterval() {
1031
		return (int)\OC::$server->getConfig()->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1032
	}
1033
1034
	/**
1035
	 * Checks whether the given path is reachable for the given owner
1036
	 *
1037
	 * @param string $path path relative to files
1038
	 * @param string $ownerStorageId storage id of the owner
1039
	 *
1040
	 * @return boolean true if file is reachable, false otherwise
1041
	 */
1042
	private static function isFileReachable($path, $ownerStorageId) {
1043
		// if outside the home storage, file is always considered reachable
1044
		if (!(substr($ownerStorageId, 0, 6) === 'home::' ||
1045
			substr($ownerStorageId, 0, 13) === 'object::user:'
1046
		)) {
1047
			return true;
1048
		}
1049
1050
		// if inside the home storage, the file has to be under "/files/"
1051
		$path = ltrim($path, '/');
1052
		if (substr($path, 0, 6) === 'files/') {
1053
			return true;
1054
		}
1055
1056
		return false;
1057
	}
1058
}
1059