Passed
Push — master ( 53330c...d4eb84 )
by Joas
19:18 queued 04:45
created

Share::tryHttpPostToShareEndpoint()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 32
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

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

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

92
			/** @scrutinizer ignore-deprecated */ \OCP\Util::writeLog('OCP\Share',

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

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

Loading history...
93
				'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class']
94
				.' is already registered for '.$itemType,
95
				ILogger::WARN);
96
		}
97
		return false;
98
	}
99
100
	/**
101
	 * Get the items of item type shared with the current user
102
	 * @param string $itemType
103
	 * @param int $format (optional) Format type must be defined by the backend
104
	 * @param mixed $parameters (optional)
105
	 * @param int $limit Number of items to return (optional) Returns all by default
106
	 * @param boolean $includeCollections (optional)
107
	 * @return mixed Return depends on format
108
	 * @deprecated TESTS ONLY - this methods is only used by tests
109
	 * called like this:
110
	 * \OC\Share\Share::getItemsSharedWith('folder'); (apps/files_sharing/tests/UpdaterTest.php)
111
	 */
112
	public static function getItemsSharedWith() {
113
		return self::getItems('folder', null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, self::FORMAT_NONE,
114
			null, -1, false);
115
	}
116
117
	/**
118
	 * Get the items of item type shared with a user
119
	 * @param string $itemType
120
	 * @param string $user id for which user we want the shares
121
	 * @param int $format (optional) Format type must be defined by the backend
122
	 * @param mixed $parameters (optional)
123
	 * @param int $limit Number of items to return (optional) Returns all by default
124
	 * @param boolean $includeCollections (optional)
125
	 * @return mixed Return depends on format
126
	 */
127
	public static function getItemsSharedWithUser($itemType, $user, $format = self::FORMAT_NONE,
128
												  $parameters = null, $limit = -1, $includeCollections = false) {
129
		return self::getItems($itemType, null, self::$shareTypeUserAndGroups, $user, null, $format,
130
			$parameters, $limit, $includeCollections);
131
	}
132
133
	/**
134
	 * Get the item of item type shared with a given user by source
135
	 * @param string $itemType
136
	 * @param string $itemSource
137
	 * @param string $user User to whom the item was shared
138
	 * @param string $owner Owner of the share
139
	 * @param int $shareType only look for a specific share type
140
	 * @return array Return list of items with file_target, permissions and expiration
141
	 */
142
	public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null, $shareType = null) {
143
		$shares = array();
144
		$fileDependent = false;
145
146
		$where = 'WHERE';
147
		$fileDependentWhere = '';
148
		if ($itemType === 'file' || $itemType === 'folder') {
149
			$fileDependent = true;
150
			$column = 'file_source';
151
			$fileDependentWhere = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
152
			$fileDependentWhere .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
153
		} else {
154
			$column = 'item_source';
155
		}
156
157
		$select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent);
158
159
		$where .= ' `' . $column . '` = ? AND `item_type` = ? ';
160
		$arguments = array($itemSource, $itemType);
161
		// for link shares $user === null
162
		if ($user !== null) {
0 ignored issues
show
introduced by
The condition $user !== null is always true.
Loading history...
163
			$where .= ' AND `share_with` = ? ';
164
			$arguments[] = $user;
165
		}
166
167
		if ($shareType !== null) {
168
			$where .= ' AND `share_type` = ? ';
169
			$arguments[] = $shareType;
170
		}
171
172
		if ($owner !== null) {
173
			$where .= ' AND `uid_owner` = ? ';
174
			$arguments[] = $owner;
175
		}
176
177
		$query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` '. $fileDependentWhere . $where);
178
179
		$result = \OC_DB::executeAudited($query, $arguments);
180
181
		while ($row = $result->fetchRow()) {
182
			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
183
				continue;
184
			}
185
			if ($fileDependent && (int)$row['file_parent'] === -1) {
186
				// if it is a mount point we need to get the path from the mount manager
187
				$mountManager = \OC\Files\Filesystem::getMountManager();
188
				$mountPoint = $mountManager->findByStorageId($row['storage_id']);
189
				if (!empty($mountPoint)) {
190
					$path = $mountPoint[0]->getMountPoint();
191
					$path = trim($path, '/');
192
					$path = substr($path, strlen($owner) + 1); //normalize path to 'files/foo.txt`
193
					$row['path'] = $path;
194
				} else {
195
					\OC::$server->getLogger()->warning(
196
						'Could not resolve mount point for ' . $row['storage_id'],
197
						['app' => 'OCP\Share']
198
					);
199
				}
200
			}
201
			$shares[] = $row;
202
		}
203
204
		//if didn't found a result than let's look for a group share.
205
		if(empty($shares) && $user !== null) {
206
			$userObject = \OC::$server->getUserManager()->get($user);
207
			$groups = [];
208
			if ($userObject) {
209
				$groups = \OC::$server->getGroupManager()->getUserGroupIds($userObject);
210
			}
211
212
			if (!empty($groups)) {
213
				$where = $fileDependentWhere . ' WHERE `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)';
214
				$arguments = array($itemSource, $itemType, $groups);
215
				$types = array(null, null, IQueryBuilder::PARAM_STR_ARRAY);
216
217
				if ($owner !== null) {
218
					$where .= ' AND `uid_owner` = ?';
219
					$arguments[] = $owner;
220
					$types[] = null;
221
				}
222
223
				// TODO: inject connection, hopefully one day in the future when this
224
				// class isn't static anymore...
225
				$conn = \OC::$server->getDatabaseConnection();
226
				$result = $conn->executeQuery(
227
					'SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where,
228
					$arguments,
229
					$types
230
				);
231
232
				while ($row = $result->fetch()) {
233
					$shares[] = $row;
234
				}
235
			}
236
		}
237
238
		return $shares;
239
240
	}
241
242
	/**
243
	 * Get the item of item type shared with the current user by source
244
	 * @param string $itemType
245
	 * @param string $itemSource
246
	 * @param int $format (optional) Format type must be defined by the backend
247
	 * @param mixed $parameters
248
	 * @param boolean $includeCollections
249
	 * @param string $shareWith (optional) define against which user should be checked, default: current user
250
	 * @return array
251
	 */
252
	public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE,
253
													 $parameters = null, $includeCollections = false, $shareWith = null) {
254
		$shareWith = ($shareWith === null) ? \OC_User::getUser() : $shareWith;
255
		return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, $shareWith, null, $format,
256
			$parameters, 1, $includeCollections, true);
257
	}
258
259
	/**
260
	 * Get the shared item of item type owned by the current user
261
	 * @param string $itemType
262
	 * @param string $itemSource
263
	 * @param int $format (optional) Format type must be defined by the backend
264
	 * @param mixed $parameters
265
	 * @param boolean $includeCollections
266
	 * @return mixed Return depends on format
267
	 */
268
	public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE,
269
										 $parameters = null, $includeCollections = false) {
270
		return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format,
271
			$parameters, -1, $includeCollections);
272
	}
273
274
	/**
275
	 * Share an item with a user, group, or via private link
276
	 * @param string $itemType
277
	 * @param string $itemSource
278
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
279
	 * @param string $shareWith User or group the item is being shared with
280
	 * @param int $permissions CRUDS
281
	 * @param string $itemSourceName
282
	 * @param \DateTime|null $expirationDate
283
	 * @return boolean|string Returns true on success or false on failure, Returns token on success for links
284
	 * @throws \OC\HintException when the share type is remote and the shareWith is invalid
285
	 * @throws \Exception
286
	 * @since 5.0.0 - parameter $itemSourceName was added in 6.0.0, parameter $expirationDate was added in 7.0.0, parameter $passwordChanged added in 9.0.0
287
	 * @deprecated 14.0.0 TESTS ONLY - this methods is as of 2018-06 only used by tests
288
	 * called like this:
289
	 * \OC\Share\Share::shareItem('test', 1, \OCP\Share::SHARE_TYPE_USER, $otherUserId, \OCP\Constants::PERMISSION_READ);
290
	 */
291
	public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions) {
292
		$backend = self::getBackend($itemType);
293
294
		if ($backend->isShareTypeAllowed($shareType) === false) {
295
			$message = 'Sharing failed, because the backend does not allow shares from type %i';
296
			throw new \Exception(sprintf($message, $shareType));
297
		}
298
299
		$uidOwner = \OC_User::getUser();
300
301
		// Verify share type and sharing conditions are met
302
		if ($shareType === self::SHARE_TYPE_USER) {
303
			if ($shareWith == $uidOwner) {
304
				$message = 'Sharing failed, because you can not share with yourself';
305
				throw new \Exception($message);
306
			}
307
			if (!\OC::$server->getUserManager()->userExists($shareWith)) {
308
				$message = 'Sharing failed, because the user %s does not exist';
309
				throw new \Exception(sprintf($message, $shareWith));
310
			}
311
			// Check if the item source is already shared with the user, either from the same owner or a different user
312
			if ($checkExists = self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups,
313
				$shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
314
				// Only allow the same share to occur again if it is the same
315
				// owner and is not a user share, this use case is for increasing
316
				// permissions for a specific user
317
				if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
318
					$message = 'Sharing failed, because this item is already shared with %s';
319
					throw new \Exception(sprintf($message, $shareWith));
320
				}
321
			}
322
			if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_USER,
323
				$shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
324
				// Only allow the same share to occur again if it is the same
325
				// owner and is not a user share, this use case is for increasing
326
				// permissions for a specific user
327
				if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
328
					$message = 'Sharing failed, because this item is already shared with user %s';
329
					throw new \Exception(sprintf($message, $shareWith));
330
				}
331
			}
332
		}
333
334
		// Put the item into the database
335
		$result = self::put('test', $itemSource, self::SHARE_TYPE_USER, $shareWith, $uidOwner, $permissions);
0 ignored issues
show
Deprecated Code introduced by
The function OC\Share\Share::put() has been deprecated: TESTS ONLY - this methods is only used by tests called like this: self::put('test', $itemSource, self::SHARE_TYPE_USER, $shareWith, $uidOwner, $permissions); ( Ignorable by Annotation )

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

335
		$result = /** @scrutinizer ignore-deprecated */ self::put('test', $itemSource, self::SHARE_TYPE_USER, $shareWith, $uidOwner, $permissions);

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

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

Loading history...
336
337
		return $result ? true : false;
338
	}
339
340
	/**
341
	 * Unshare an item from a user, group, or delete a private link
342
	 * @param string $itemType
343
	 * @param string $itemSource
344
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
345
	 * @param string $shareWith User or group the item is being shared with
346
	 * @param string $owner owner of the share, if null the current user is used
347
	 * @return boolean true on success or false on failure
348
	 */
349
	public static function unshare($itemType, $itemSource, $shareType, $shareWith, $owner = null) {
350
351
		// check if it is a valid itemType
352
		self::getBackend($itemType);
353
354
		$items = self::getItemSharedWithUser($itemType, $itemSource, $shareWith, $owner, $shareType);
355
356
		$toDelete = array();
357
		$newParent = null;
358
		$currentUser = $owner ? $owner : \OC_User::getUser();
359
		foreach ($items as $item) {
360
			// delete the item with the expected share_type and owner
361
			if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) {
362
				$toDelete = $item;
363
				// if there is more then one result we don't have to delete the children
364
				// but update their parent. For group shares the new parent should always be
365
				// the original group share and not the db entry with the unique name
366
			} else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
367
				$newParent = $item['parent'];
368
			} else {
369
				$newParent = $item['id'];
370
			}
371
		}
372
373
		if (!empty($toDelete)) {
374
			self::unshareItem($toDelete, $newParent);
375
			return true;
376
		}
377
		return false;
378
	}
379
380
	/**
381
	 * Checks whether a share has expired, calls unshareItem() if yes.
382
	 * @param array $item Share data (usually database row)
383
	 * @return boolean True if item was expired, false otherwise.
384
	 */
385
	protected static function expireItem(array $item) {
386
387
		$result = false;
388
389
		// only use default expiration date for link shares
390
		if ((int) $item['share_type'] === self::SHARE_TYPE_LINK) {
391
392
			// calculate expiration date
393
			if (!empty($item['expiration'])) {
394
				$userDefinedExpire = new \DateTime($item['expiration']);
395
				$expires = $userDefinedExpire->getTimestamp();
396
			} else {
397
				$expires = null;
398
			}
399
400
401
			// get default expiration settings
402
			$defaultSettings = Helper::getDefaultExpireSetting();
403
			$expires = Helper::calculateExpireDate($defaultSettings, $item['stime'], $expires);
404
405
406
			if (is_int($expires)) {
407
				$now = time();
408
				if ($now > $expires) {
409
					self::unshareItem($item);
410
					$result = true;
411
				}
412
			}
413
		}
414
		return $result;
415
	}
416
417
	/**
418
	 * Unshares a share given a share data array
419
	 * @param array $item Share data (usually database row)
420
	 * @param int $newParent parent ID
421
	 * @return null
422
	 */
423
	protected static function unshareItem(array $item, $newParent = null) {
424
425
		$shareType = (int)$item['share_type'];
426
		$shareWith = null;
427
		if ($shareType !== \OCP\Share::SHARE_TYPE_LINK) {
428
			$shareWith = $item['share_with'];
429
		}
430
431
		// Pass all the vars we have for now, they may be useful
432
		$hookParams = array(
433
			'id'            => $item['id'],
434
			'itemType'      => $item['item_type'],
435
			'itemSource'    => $item['item_source'],
436
			'shareType'     => $shareType,
437
			'shareWith'     => $shareWith,
438
			'itemParent'    => $item['parent'],
439
			'uidOwner'      => $item['uid_owner'],
440
		);
441
		if($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
442
			$hookParams['fileSource'] = $item['file_source'];
443
			$hookParams['fileTarget'] = $item['file_target'];
444
		}
445
446
		\OC_Hook::emit(\OCP\Share::class, 'pre_unshare', $hookParams);
447
		$deletedShares = Helper::delete($item['id'], false, null, $newParent);
448
		$deletedShares[] = $hookParams;
449
		$hookParams['deletedShares'] = $deletedShares;
450
		\OC_Hook::emit(\OCP\Share::class, 'post_unshare', $hookParams);
451
		if ((int)$item['share_type'] === \OCP\Share::SHARE_TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
452
			list(, $remote) = Helper::splitUserRemote($item['share_with']);
453
			self::sendRemoteUnshare($remote, $item['id'], $item['token']);
454
		}
455
	}
456
457
	/**
458
	 * Get the backend class for the specified item type
459
	 * @param string $itemType
460
	 * @throws \Exception
461
	 * @return \OCP\Share_Backend
462
	 */
463
	public static function getBackend($itemType) {
464
		$l = \OC::$server->getL10N('lib');
465
		if (isset(self::$backends[$itemType])) {
466
			return self::$backends[$itemType];
467
		} else if (isset(self::$backendTypes[$itemType]['class'])) {
468
			$class = self::$backendTypes[$itemType]['class'];
469
			if (class_exists($class)) {
470
				self::$backends[$itemType] = new $class;
471
				if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) {
472
					$message = 'Sharing backend %s must implement the interface OCP\Share_Backend';
473
					$message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', array($class));
474
					\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), ILogger::ERROR);
0 ignored issues
show
Deprecated Code introduced by
The function OCP\Util::writeLog() has been deprecated: 13.0.0 use log of \OCP\ILogger ( Ignorable by Annotation )

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

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

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

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

Loading history...
475
					throw new \Exception($message_t);
476
				}
477
				return self::$backends[$itemType];
478
			} else {
479
				$message = 'Sharing backend %s not found';
480
				$message_t = $l->t('Sharing backend %s not found', array($class));
481
				\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), ILogger::ERROR);
0 ignored issues
show
Deprecated Code introduced by
The function OCP\Util::writeLog() has been deprecated: 13.0.0 use log of \OCP\ILogger ( Ignorable by Annotation )

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

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

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

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

Loading history...
482
				throw new \Exception($message_t);
483
			}
484
		}
485
		$message = 'Sharing backend for %s not found';
486
		$message_t = $l->t('Sharing backend for %s not found', array($itemType));
487
		\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemType), ILogger::ERROR);
0 ignored issues
show
Deprecated Code introduced by
The function OCP\Util::writeLog() has been deprecated: 13.0.0 use log of \OCP\ILogger ( Ignorable by Annotation )

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

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

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

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

Loading history...
488
		throw new \Exception($message_t);
489
	}
490
491
	/**
492
	 * Check if resharing is allowed
493
	 * @return boolean true if allowed or false
494
	 *
495
	 * Resharing is allowed by default if not configured
496
	 */
497
	public static function isResharingAllowed() {
498
		if (!isset(self::$isResharingAllowed)) {
499
			if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') {
500
				self::$isResharingAllowed = true;
501
			} else {
502
				self::$isResharingAllowed = false;
503
			}
504
		}
505
		return self::$isResharingAllowed;
506
	}
507
508
	/**
509
	 * Get a list of collection item types for the specified item type
510
	 * @param string $itemType
511
	 * @return array
512
	 */
513
	private static function getCollectionItemTypes($itemType) {
514
		$collectionTypes = array($itemType);
515
		foreach (self::$backendTypes as $type => $backend) {
516
			if (in_array($backend['collectionOf'], $collectionTypes)) {
517
				$collectionTypes[] = $type;
518
			}
519
		}
520
		// TODO Add option for collections to be collection of themselves, only 'folder' does it now...
521
		if (isset(self::$backendTypes[$itemType]) && (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder')) {
522
			unset($collectionTypes[0]);
523
		}
524
		// Return array if collections were found or the item type is a
525
		// collection itself - collections can be inside collections
526
		if (count($collectionTypes) > 0) {
527
			return $collectionTypes;
528
		}
529
		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...
530
	}
531
532
	/**
533
	 * Get the owners of items shared with a user.
534
	 *
535
	 * @param string $user The user the items are shared with.
536
	 * @param string $type The type of the items shared with the user.
537
	 * @param boolean $includeCollections Include collection item types (optional)
538
	 * @param boolean $includeOwner include owner in the list of users the item is shared with (optional)
539
	 * @return array
540
	 * @deprecated TESTS ONLY - this methods is only used by tests
541
	 * called like this:
542
	 * \OC\Share\Share::getSharedItemsOwners($this->user, $this->type, true)
543
	 */
544
	public static function getSharedItemsOwners($user, $type, $includeCollections = false, $includeOwner = false) {
545
		// First, we find out if $type is part of a collection (and if that collection is part of
546
		// another one and so on).
547
		$collectionTypes = array();
548
		if (!$includeCollections || !$collectionTypes = self::getCollectionItemTypes($type)) {
549
			$collectionTypes[] = $type;
550
		}
551
552
		// Of these collection types, along with our original $type, we make a
553
		// list of the ones for which a sharing backend has been registered.
554
		// FIXME: Ideally, we wouldn't need to nest getItemsSharedWith in this loop but just call it
555
		// with its $includeCollections parameter set to true. Unfortunately, this fails currently.
556
		$allMaybeSharedItems = array();
557
		foreach ($collectionTypes as $collectionType) {
558
			if (isset(self::$backends[$collectionType])) {
559
				$allMaybeSharedItems[$collectionType] = self::getItemsSharedWithUser(
560
					$collectionType,
561
					$user,
562
					self::FORMAT_NONE
563
				);
564
			}
565
		}
566
567
		$owners = array();
568
		if ($includeOwner) {
569
			$owners[] = $user;
570
		}
571
572
		// We take a look at all shared items of the given $type (or of the collections it is part of)
573
		// and find out their owners. Then, we gather the tags for the original $type from all owners,
574
		// and return them as elements of a list that look like "Tag (owner)".
575
		foreach ($allMaybeSharedItems as $collectionType => $maybeSharedItems) {
576
			foreach ($maybeSharedItems as $sharedItem) {
577
				if (isset($sharedItem['id'])) { //workaround for https://github.com/owncloud/core/issues/2814
578
					$owners[] = $sharedItem['uid_owner'];
579
				}
580
			}
581
		}
582
583
		return $owners;
584
	}
585
586
	/**
587
	 * Get shared items from the database
588
	 * @param string $itemType
589
	 * @param string $item Item source or target (optional)
590
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique
591
	 * @param string $shareWith User or group the item is being shared with
592
	 * @param string $uidOwner User that is the owner of shared items (optional)
593
	 * @param int $format Format to convert items to with formatItems() (optional)
594
	 * @param mixed $parameters to pass to formatItems() (optional)
595
	 * @param int $limit Number of items to return, -1 to return all matches (optional)
596
	 * @param boolean $includeCollections Include collection item types (optional)
597
	 * @param boolean $itemShareWithBySource (optional)
598
	 * @param boolean $checkExpireDate
599
	 * @return array
600
	 *
601
	 * See public functions getItem(s)... for parameter usage
602
	 *
603
	 */
604
	public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null,
605
									$uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1,
606
									$includeCollections = false, $itemShareWithBySource = false, $checkExpireDate  = true) {
607
		if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_enabled', 'yes') != 'yes') {
608
			return array();
609
		}
610
		$backend = self::getBackend($itemType);
611
		$collectionTypes = false;
612
		// Get filesystem root to add it to the file target and remove from the
613
		// file source, match file_source with the file cache
614
		if ($itemType == 'file' || $itemType == 'folder') {
615
			if(!is_null($uidOwner)) {
616
				$root = \OC\Files\Filesystem::getRoot();
617
			} else {
618
				$root = '';
619
			}
620
			$where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
621
			if (!isset($item)) {
622
				$where .= ' AND `file_target` IS NOT NULL ';
623
			}
624
			$where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
625
			$fileDependent = true;
626
			$queryArgs = array();
627
		} else {
628
			$fileDependent = false;
629
			$root = '';
630
			$collectionTypes = self::getCollectionItemTypes($itemType);
631
			if ($includeCollections && !isset($item) && $collectionTypes) {
632
				// If includeCollections is true, find collections of this item type, e.g. a music album contains songs
633
				if (!in_array($itemType, $collectionTypes)) {
634
					$itemTypes = array_merge(array($itemType), $collectionTypes);
635
				} else {
636
					$itemTypes = $collectionTypes;
637
				}
638
				$placeholders = implode(',', array_fill(0, count($itemTypes), '?'));
639
				$where = ' WHERE `item_type` IN ('.$placeholders.'))';
640
				$queryArgs = $itemTypes;
641
			} else {
642
				$where = ' WHERE `item_type` = ?';
643
				$queryArgs = array($itemType);
644
			}
645
		}
646
		if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
647
			$where .= ' AND `share_type` != ?';
648
			$queryArgs[] = self::SHARE_TYPE_LINK;
649
		}
650
		if (isset($shareType)) {
651
			// Include all user and group items
652
			if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
653
				$where .= ' AND ((`share_type` in (?, ?) AND `share_with` = ?) ';
654
				$queryArgs[] = self::SHARE_TYPE_USER;
655
				$queryArgs[] = self::$shareTypeGroupUserUnique;
656
				$queryArgs[] = $shareWith;
657
658
				$user = \OC::$server->getUserManager()->get($shareWith);
659
				$groups = [];
660
				if ($user) {
661
					$groups = \OC::$server->getGroupManager()->getUserGroupIds($user);
662
				}
663
				if (!empty($groups)) {
664
					$placeholders = implode(',', array_fill(0, count($groups), '?'));
665
					$where .= ' OR (`share_type` = ? AND `share_with` IN ('.$placeholders.')) ';
666
					$queryArgs[] = self::SHARE_TYPE_GROUP;
667
					$queryArgs = array_merge($queryArgs, $groups);
668
				}
669
				$where .= ')';
670
				// Don't include own group shares
671
				$where .= ' AND `uid_owner` != ?';
672
				$queryArgs[] = $shareWith;
673
			} else {
674
				$where .= ' AND `share_type` = ?';
675
				$queryArgs[] = $shareType;
676
				if (isset($shareWith)) {
677
					$where .= ' AND `share_with` = ?';
678
					$queryArgs[] = $shareWith;
679
				}
680
			}
681
		}
682
		if (isset($uidOwner)) {
683
			$where .= ' AND `uid_owner` = ?';
684
			$queryArgs[] = $uidOwner;
685
			if (!isset($shareType)) {
686
				// Prevent unique user targets for group shares from being selected
687
				$where .= ' AND `share_type` != ?';
688
				$queryArgs[] = self::$shareTypeGroupUserUnique;
689
			}
690
			if ($fileDependent) {
691
				$column = 'file_source';
692
			} else {
693
				$column = 'item_source';
694
			}
695
		} else {
696
			if ($fileDependent) {
697
				$column = 'file_target';
698
			} else {
699
				$column = 'item_target';
700
			}
701
		}
702
		if (isset($item)) {
703
			$collectionTypes = self::getCollectionItemTypes($itemType);
704
			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
705
				$where .= ' AND (';
706
			} else {
707
				$where .= ' AND';
708
			}
709
			// If looking for own shared items, check item_source else check item_target
710
			if (isset($uidOwner) || $itemShareWithBySource) {
711
				// If item type is a file, file source needs to be checked in case the item was converted
712
				if ($fileDependent) {
713
					$where .= ' `file_source` = ?';
714
					$column = 'file_source';
715
				} else {
716
					$where .= ' `item_source` = ?';
717
					$column = 'item_source';
718
				}
719
			} else {
720
				if ($fileDependent) {
721
					$where .= ' `file_target` = ?';
722
					$item = \OC\Files\Filesystem::normalizePath($item);
723
				} else {
724
					$where .= ' `item_target` = ?';
725
				}
726
			}
727
			$queryArgs[] = $item;
728
			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
729
				$placeholders = implode(',', array_fill(0, count($collectionTypes), '?'));
730
				$where .= ' OR `item_type` IN ('.$placeholders.'))';
731
				$queryArgs = array_merge($queryArgs, $collectionTypes);
732
			}
733
		}
734
735
		if ($shareType == self::$shareTypeUserAndGroups && $limit === 1) {
736
			// Make sure the unique user target is returned if it exists,
737
			// unique targets should follow the group share in the database
738
			// If the limit is not 1, the filtering can be done later
739
			$where .= ' ORDER BY `*PREFIX*share`.`id` DESC';
740
		} else {
741
			$where .= ' ORDER BY `*PREFIX*share`.`id` ASC';
742
		}
743
744
		if ($limit != -1 && !$includeCollections) {
745
			// The limit must be at least 3, because filtering needs to be done
746
			if ($limit < 3) {
747
				$queryLimit = 3;
748
			} else {
749
				$queryLimit = $limit;
750
			}
751
		} else {
752
			$queryLimit = null;
753
		}
754
		$select = self::createSelectStatement($format, $fileDependent, $uidOwner);
755
		$root = strlen($root);
756
		$query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit);
757
		$result = $query->execute($queryArgs);
758
		if ($result === false) {
759
			\OCP\Util::writeLog('OCP\Share',
0 ignored issues
show
Deprecated Code introduced by
The function OCP\Util::writeLog() has been deprecated: 13.0.0 use log of \OCP\ILogger ( Ignorable by Annotation )

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

759
			/** @scrutinizer ignore-deprecated */ \OCP\Util::writeLog('OCP\Share',

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

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

Loading history...
760
				\OC_DB::getErrorMessage() . ', select=' . $select . ' where=',
761
				ILogger::ERROR);
762
		}
763
		$items = array();
764
		$targets = array();
765
		$switchedItems = array();
766
		$mounts = array();
767
		while ($row = $result->fetchRow()) {
768
			self::transformDBResults($row);
769
			// Filter out duplicate group shares for users with unique targets
770
			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
771
				continue;
772
			}
773
			if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) {
774
				$row['share_type'] = self::SHARE_TYPE_GROUP;
775
				$row['unique_name'] = true; // remember that we use a unique name for this user
776
				$row['share_with'] = $items[$row['parent']]['share_with'];
777
				// if the group share was unshared from the user we keep the permission, otherwise
778
				// we take the permission from the parent because this is always the up-to-date
779
				// permission for the group share
780
				if ($row['permissions'] > 0) {
781
					$row['permissions'] = $items[$row['parent']]['permissions'];
782
				}
783
				// Remove the parent group share
784
				unset($items[$row['parent']]);
785
				if ($row['permissions'] == 0) {
786
					continue;
787
				}
788
			} else if (!isset($uidOwner)) {
789
				// Check if the same target already exists
790
				if (isset($targets[$row['id']])) {
791
					// Check if the same owner shared with the user twice
792
					// through a group and user share - this is allowed
793
					$id = $targets[$row['id']];
794
					if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
795
						// Switch to group share type to ensure resharing conditions aren't bypassed
796
						if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) {
797
							$items[$id]['share_type'] = self::SHARE_TYPE_GROUP;
798
							$items[$id]['share_with'] = $row['share_with'];
799
						}
800
						// Switch ids if sharing permission is granted on only
801
						// one share to ensure correct parent is used if resharing
802
						if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
803
							&& (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
804
							$items[$row['id']] = $items[$id];
805
							$switchedItems[$id] = $row['id'];
806
							unset($items[$id]);
807
							$id = $row['id'];
808
						}
809
						$items[$id]['permissions'] |= (int)$row['permissions'];
810
811
					}
812
					continue;
813
				} elseif (!empty($row['parent'])) {
814
					$targets[$row['parent']] = $row['id'];
815
				}
816
			}
817
			// Remove root from file source paths if retrieving own shared items
818
			if (isset($uidOwner) && isset($row['path'])) {
819
				if (isset($row['parent'])) {
820
					$query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?');
821
					$parentResult = $query->execute(array($row['parent']));
822
					if ($result === false) {
823
						\OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: ' .
0 ignored issues
show
Deprecated Code introduced by
The function OCP\Util::writeLog() has been deprecated: 13.0.0 use log of \OCP\ILogger ( Ignorable by Annotation )

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

823
						/** @scrutinizer ignore-deprecated */ \OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: ' .

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

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

Loading history...
824
							\OC_DB::getErrorMessage() . ', select=' . $select . ' where=' . $where,
825
							ILogger::ERROR);
826
					} else {
827
						$parentRow = $parentResult->fetchRow();
828
						$tmpPath = $parentRow['file_target'];
829
						// find the right position where the row path continues from the target path
830
						$pos = strrpos($row['path'], $parentRow['file_target']);
831
						$subPath = substr($row['path'], $pos);
832
						$splitPath = explode('/', $subPath);
833
						foreach (array_slice($splitPath, 2) as $pathPart) {
834
							$tmpPath = $tmpPath . '/' . $pathPart;
835
						}
836
						$row['path'] = $tmpPath;
837
					}
838
				} else {
839
					if (!isset($mounts[$row['storage']])) {
840
						$mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']);
841
						if (is_array($mountPoints) && !empty($mountPoints)) {
842
							$mounts[$row['storage']] = current($mountPoints);
843
						}
844
					}
845
					if (!empty($mounts[$row['storage']])) {
846
						$path = $mounts[$row['storage']]->getMountPoint().$row['path'];
847
						$relPath = substr($path, $root); // path relative to data/user
848
						$row['path'] = rtrim($relPath, '/');
849
					}
850
				}
851
			}
852
853
			if($checkExpireDate) {
854
				if (self::expireItem($row)) {
855
					continue;
856
				}
857
			}
858
			// Check if resharing is allowed, if not remove share permission
859
			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...
Deprecated Code introduced by
The function OCP\Util::isSharingDisabledForUser() has been deprecated: 9.1.0 Use \OC::$server->getShareManager()->sharingDisabledForUser ( Ignorable by Annotation )

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

859
			if (isset($row['permissions']) && (!self::isResharingAllowed() | /** @scrutinizer ignore-deprecated */ \OCP\Util::isSharingDisabledForUser())) {

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

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

Loading history...
860
				$row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE;
861
			}
862
			// Add display names to result
863
			$row['share_with_displayname'] = $row['share_with'];
864
			if ( isset($row['share_with']) && $row['share_with'] != '' &&
865
				$row['share_type'] === self::SHARE_TYPE_USER) {
866
				$shareWithUser = \OC::$server->getUserManager()->get($row['share_with']);
867
				$row['share_with_displayname'] = $shareWithUser === null ? $row['share_with'] : $shareWithUser->getDisplayName();
868
			} else if(isset($row['share_with']) && $row['share_with'] != '' &&
869
				$row['share_type'] === self::SHARE_TYPE_REMOTE) {
870
				$addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD']);
871
				foreach ($addressBookEntries as $entry) {
872
					foreach ($entry['CLOUD'] as $cloudID) {
873
						if ($cloudID === $row['share_with']) {
874
							$row['share_with_displayname'] = $entry['FN'];
875
						}
876
					}
877
				}
878
			}
879
			if ( isset($row['uid_owner']) && $row['uid_owner'] != '') {
880
				$ownerUser = \OC::$server->getUserManager()->get($row['uid_owner']);
881
				$row['displayname_owner'] = $ownerUser === null ? $row['uid_owner'] : $ownerUser->getDisplayName();
882
			}
883
884
			if ($row['permissions'] > 0) {
885
				$items[$row['id']] = $row;
886
			}
887
888
		}
889
890
		// group items if we are looking for items shared with the current user
891
		if (isset($shareWith) && $shareWith === \OCP\User::getUser()) {
0 ignored issues
show
Deprecated Code introduced by
The function OCP\User::getUser() has been deprecated: 8.0.0 Use \OC::$server->getUserSession()->getUser()->getUID() ( Ignorable by Annotation )

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

891
		if (isset($shareWith) && $shareWith === /** @scrutinizer ignore-deprecated */ \OCP\User::getUser()) {

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

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

Loading history...
892
			$items = self::groupItems($items, $itemType);
893
		}
894
895
		if (!empty($items)) {
896
			$collectionItems = array();
897
			foreach ($items as &$row) {
898
				// Return only the item instead of a 2-dimensional array
899
				if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) {
900
					if ($format == self::FORMAT_NONE) {
901
						return $row;
902
					} else {
903
						break;
904
					}
905
				}
906
				// Check if this is a collection of the requested item type
907
				if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) {
908
					if (($collectionBackend = self::getBackend($row['item_type']))
909
						&& $collectionBackend instanceof \OCP\Share_Backend_Collection) {
910
						// Collections can be inside collections, check if the item is a collection
911
						if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
912
							$collectionItems[] = $row;
913
						} else {
914
							$collection = array();
915
							$collection['item_type'] = $row['item_type'];
916
							if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
917
								$collection['path'] = basename($row['path']);
918
							}
919
							$row['collection'] = $collection;
920
							// Fetch all of the children sources
921
							$children = $collectionBackend->getChildren($row[$column]);
922
							foreach ($children as $child) {
923
								$childItem = $row;
924
								$childItem['item_type'] = $itemType;
925
								if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
926
									$childItem['item_source'] = $child['source'];
927
									$childItem['item_target'] = $child['target'];
928
								}
929
								if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
930
									if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
931
										$childItem['file_source'] = $child['source'];
932
									} else { // TODO is this really needed if we already know that we use the file backend?
933
										$meta = \OC\Files\Filesystem::getFileInfo($child['file_path']);
934
										$childItem['file_source'] = $meta['fileid'];
935
									}
936
									$childItem['file_target'] =
937
										\OC\Files\Filesystem::normalizePath($child['file_path']);
938
								}
939
								if (isset($item)) {
940
									if ($childItem[$column] == $item) {
941
										// Return only the item instead of a 2-dimensional array
942
										if ($limit == 1) {
943
											if ($format == self::FORMAT_NONE) {
944
												return $childItem;
945
											} else {
946
												// Unset the items array and break out of both loops
947
												$items = array();
948
												$items[] = $childItem;
949
												break 2;
950
											}
951
										} else {
952
											$collectionItems[] = $childItem;
953
										}
954
									}
955
								} else {
956
									$collectionItems[] = $childItem;
957
								}
958
							}
959
						}
960
					}
961
					// Remove collection item
962
					$toRemove = $row['id'];
963
					if (array_key_exists($toRemove, $switchedItems)) {
964
						$toRemove = $switchedItems[$toRemove];
965
					}
966
					unset($items[$toRemove]);
967
				} elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
968
					// FIXME: Thats a dirty hack to improve file sharing performance,
969
					// see github issue #10588 for more details
970
					// Need to find a solution which works for all back-ends
971
					$collectionBackend = self::getBackend($row['item_type']);
972
					$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

972
					/** @scrutinizer ignore-call */ 
973
     $sharedParents = $collectionBackend->getParents($row['item_source']);
Loading history...
973
					foreach ($sharedParents as $parent) {
974
						$collectionItems[] = $parent;
975
					}
976
				}
977
			}
978
			if (!empty($collectionItems)) {
979
				$collectionItems = array_unique($collectionItems, SORT_REGULAR);
980
				$items = array_merge($items, $collectionItems);
981
			}
982
983
			// filter out invalid items, these can appear when subshare entries exist
984
			// for a group in which the requested user isn't a member any more
985
			$items = array_filter($items, function($item) {
986
				return $item['share_type'] !== self::$shareTypeGroupUserUnique;
987
			});
988
989
			return self::formatResult($items, $column, $backend, $format, $parameters);
990
		} elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) {
991
			// FIXME: Thats a dirty hack to improve file sharing performance,
992
			// see github issue #10588 for more details
993
			// Need to find a solution which works for all back-ends
994
			$collectionItems = array();
995
			$collectionBackend = self::getBackend('folder');
996
			$sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner);
997
			foreach ($sharedParents as $parent) {
998
				$collectionItems[] = $parent;
999
			}
1000
			if ($limit === 1) {
1001
				return reset($collectionItems);
1002
			}
1003
			return self::formatResult($collectionItems, $column, $backend, $format, $parameters);
1004
		}
1005
1006
		return array();
1007
	}
1008
1009
	/**
1010
	 * group items with link to the same source
1011
	 *
1012
	 * @param array $items
1013
	 * @param string $itemType
1014
	 * @return array of grouped items
1015
	 */
1016
	protected static function groupItems($items, $itemType) {
1017
1018
		$fileSharing = $itemType === 'file' || $itemType === 'folder';
1019
1020
		$result = array();
1021
1022
		foreach ($items as $item) {
1023
			$grouped = false;
1024
			foreach ($result as $key => $r) {
1025
				// for file/folder shares we need to compare file_source, otherwise we compare item_source
1026
				// only group shares if they already point to the same target, otherwise the file where shared
1027
				// before grouping of shares was added. In this case we don't group them toi avoid confusions
1028
				if (( $fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
1029
					(!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
1030
					// add the first item to the list of grouped shares
1031
					if (!isset($result[$key]['grouped'])) {
1032
						$result[$key]['grouped'][] = $result[$key];
1033
					}
1034
					$result[$key]['permissions'] = (int) $item['permissions'] | (int) $r['permissions'];
1035
					$result[$key]['grouped'][] = $item;
1036
					$grouped = true;
1037
					break;
1038
				}
1039
			}
1040
1041
			if (!$grouped) {
1042
				$result[] = $item;
1043
			}
1044
1045
		}
1046
1047
		return $result;
1048
	}
1049
1050
	/**
1051
	 * Put shared item into the database
1052
	 * @param string $itemType Item type
1053
	 * @param string $itemSource Item source
1054
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1055
	 * @param string $shareWith User or group the item is being shared with
1056
	 * @param string $uidOwner User that is the owner of shared item
1057
	 * @param int $permissions CRUDS permissions
1058
	 * @throws \Exception
1059
	 * @return mixed id of the new share or false
1060
	 * @deprecated TESTS ONLY - this methods is only used by tests
1061
	 * called like this:
1062
	 * self::put('test', $itemSource, self::SHARE_TYPE_USER, $shareWith, $uidOwner, $permissions);
1063
	 */
1064
	private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
1065
								$permissions) {
1066
1067
		$queriesToExecute = array();
1068
		$suggestedItemTarget = null;
1069
		$groupFileTarget = $fileTarget = $suggestedFileTarget = $filePath = '';
1070
		$groupItemTarget = $itemTarget = $fileSource = $parent = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $itemTarget is dead and can be removed.
Loading history...
1071
1072
		$result = self::checkReshare('test', $itemSource, self::SHARE_TYPE_USER, $shareWith, $uidOwner, $permissions, null, null);
0 ignored issues
show
Deprecated Code introduced by
The function OC\Share\Share::checkReshare() has been deprecated: TESTS ONLY - this methods is only used by tests called like this: self::checkReshare('test', $itemSource, self::SHARE_TYPE_USER, $shareWith, $uidOwner, $permissions, null, null); ( Ignorable by Annotation )

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

1072
		$result = /** @scrutinizer ignore-deprecated */ self::checkReshare('test', $itemSource, self::SHARE_TYPE_USER, $shareWith, $uidOwner, $permissions, null, null);

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

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

Loading history...
1073
		if(!empty($result)) {
1074
			$parent = $result['parent'];
1075
			$itemSource = $result['itemSource'];
1076
			$fileSource = $result['fileSource'];
1077
			$suggestedItemTarget = $result['suggestedItemTarget'];
1078
			$suggestedFileTarget = $result['suggestedFileTarget'];
1079
			$filePath = $result['filePath'];
1080
		}
1081
1082
		$isGroupShare = false;
1083
			$users = array($shareWith);
1084
			$itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
1085
				$suggestedItemTarget);
1086
1087
		$run = true;
1088
		$error = '';
1089
		$preHookData = array(
1090
			'itemType' => $itemType,
1091
			'itemSource' => $itemSource,
1092
			'shareType' => $shareType,
1093
			'uidOwner' => $uidOwner,
1094
			'permissions' => $permissions,
1095
			'fileSource' => $fileSource,
1096
			'expiration' => null,
1097
			'token' => null,
1098
			'run' => &$run,
1099
			'error' => &$error
1100
		);
1101
1102
		$preHookData['itemTarget'] = $itemTarget;
1103
		$preHookData['shareWith'] = $shareWith;
1104
1105
		\OC_Hook::emit(\OCP\Share::class, 'pre_shared', $preHookData);
1106
1107
		if ($run === false) {
0 ignored issues
show
introduced by
The condition $run === false is always false.
Loading history...
1108
			throw new \Exception($error);
1109
		}
1110
1111
		foreach ($users as $user) {
1112
			$sourceId = ($itemType === 'file' || $itemType === 'folder') ? $fileSource : $itemSource;
1113
			$sourceExists = self::getItemSharedWithBySource($itemType, $sourceId, self::FORMAT_NONE, null, true, $user);
1114
1115
			$userShareType = $shareType;
1116
1117
			if ($sourceExists && $sourceExists['item_source'] === $itemSource) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sourceExists of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1118
				$fileTarget = $sourceExists['file_target'];
1119
				$itemTarget = $sourceExists['item_target'];
1120
1121
			} elseif(!$sourceExists)  {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sourceExists of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1122
1123
				$itemTarget = Helper::generateTarget($itemType, $itemSource, $userShareType, $user,
1124
					$uidOwner, $suggestedItemTarget, $parent);
1125
				if (isset($fileSource)) {
1126
						$fileTarget = Helper::generateTarget('file', $filePath, $userShareType,
1127
							$user, $uidOwner, $suggestedFileTarget, $parent);
1128
				} else {
1129
					$fileTarget = null;
1130
				}
1131
1132
			} else {
1133
1134
				// group share which doesn't exists until now, check if we need a unique target for this user
1135
1136
				$itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $user,
1137
					$uidOwner, $suggestedItemTarget, $parent);
1138
1139
				// do we also need a file target
1140
				if (isset($fileSource)) {
1141
					$fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $user,
1142
						$uidOwner, $suggestedFileTarget, $parent);
1143
				} else {
1144
					$fileTarget = null;
1145
				}
1146
1147
				if (($itemTarget === $groupItemTarget) &&
1148
					(!isset($fileSource) || $fileTarget === $groupFileTarget)) {
1149
					continue;
1150
				}
1151
			}
1152
1153
			$queriesToExecute[] = array(
1154
				'itemType'			=> $itemType,
1155
				'itemSource'		=> $itemSource,
1156
				'itemTarget'		=> $itemTarget,
1157
				'shareType'			=> $userShareType,
1158
				'shareWith'			=> $user,
1159
				'uidOwner'			=> $uidOwner,
1160
				'permissions'		=> $permissions,
1161
				'shareTime'			=> time(),
1162
				'fileSource'		=> $fileSource,
1163
				'fileTarget'		=> $fileTarget,
1164
				'token'				=> null,
1165
				'parent'			=> $parent,
1166
				'expiration'		=> null,
1167
			);
1168
1169
		}
1170
1171
		$id = false;
1172
1173
		foreach ($queriesToExecute as $shareQuery) {
1174
			$shareQuery['parent'] = $parent;
1175
			$id = self::insertShare($shareQuery);
1176
		}
1177
1178
		$postHookData = array(
1179
			'itemType' => $itemType,
1180
			'itemSource' => $itemSource,
1181
			'parent' => $parent,
1182
			'shareType' => $shareType,
1183
			'uidOwner' => $uidOwner,
1184
			'permissions' => $permissions,
1185
			'fileSource' => $fileSource,
1186
			'id' => $parent,
1187
			'token' => null,
1188
			'expirationDate' => null,
1189
		);
1190
1191
		$postHookData['shareWith'] = $isGroupShare ? $shareWith['group'] : $shareWith;
0 ignored issues
show
introduced by
The condition $isGroupShare is always false.
Loading history...
1192
		$postHookData['itemTarget'] = $isGroupShare ? $groupItemTarget : $itemTarget;
0 ignored issues
show
introduced by
The condition $isGroupShare is always false.
Loading history...
1193
		$postHookData['fileTarget'] = $isGroupShare ? $groupFileTarget : $fileTarget;
0 ignored issues
show
introduced by
The condition $isGroupShare is always false.
Loading history...
1194
1195
		\OC_Hook::emit(\OCP\Share::class, 'post_shared', $postHookData);
1196
1197
1198
		return $id ? $id : false;
1199
	}
1200
1201
	/**
1202
	 * @param string $itemType
1203
	 * @param string $itemSource
1204
	 * @param int $shareType
1205
	 * @param string $shareWith
1206
	 * @param string $uidOwner
1207
	 * @param int $permissions
1208
	 * @param string|null $itemSourceName
1209
	 * @param null|\DateTime $expirationDate
1210
	 * @deprecated TESTS ONLY - this methods is only used by tests
1211
	 * called like this:
1212
	 * self::checkReshare('test', $itemSource, self::SHARE_TYPE_USER, $shareWith, $uidOwner, $permissions, null, null);
1213
	 */
1214
	private static function checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate) {
0 ignored issues
show
Unused Code introduced by
The parameter $shareType 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

1214
	private static function checkReshare($itemType, $itemSource, /** @scrutinizer ignore-unused */ $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate) {

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...
1215
		$backend = self::getBackend($itemType);
1216
1217
		$result = array();
1218
1219
		$column = ($itemType === 'file' || $itemType === 'folder') ? 'file_source' : 'item_source';
1220
1221
		$checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true);
1222
		if ($checkReshare) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $checkReshare of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1223
			// Check if attempting to share back to owner
1224
			if ($checkReshare['uid_owner'] == $shareWith) {
1225
				$message = 'Sharing %1$s failed, because the user %2$s is the original sharer';
1226
				throw new \Exception(sprintf($message, $itemSourceName, $shareWith));
1227
			}
1228
		}
1229
1230
		if ($checkReshare && $checkReshare['uid_owner'] !== \OC_User::getUser()) {
1231
			// Check if share permissions is granted
1232
			if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
1233
				if (~(int)$checkReshare['permissions'] & $permissions) {
1234
					$message = 'Sharing %1$s failed, because the permissions exceed permissions granted to %2$s';
1235
					throw new \Exception(sprintf($message, $itemSourceName, $uidOwner));
1236
				} else {
1237
					// TODO Don't check if inside folder
1238
					$result['parent'] = $checkReshare['id'];
1239
1240
					$result['expirationDate'] = $expirationDate;
1241
					// $checkReshare['expiration'] could be null and then is always less than any value
1242
					if(isset($checkReshare['expiration']) && $checkReshare['expiration'] < $expirationDate) {
1243
						$result['expirationDate'] = $checkReshare['expiration'];
1244
					}
1245
1246
					// only suggest the same name as new target if it is a reshare of the
1247
					// same file/folder and not the reshare of a child
1248
					if ($checkReshare[$column] === $itemSource) {
1249
						$result['filePath'] = $checkReshare['file_target'];
1250
						$result['itemSource'] = $checkReshare['item_source'];
1251
						$result['fileSource'] = $checkReshare['file_source'];
1252
						$result['suggestedItemTarget'] = $checkReshare['item_target'];
1253
						$result['suggestedFileTarget'] = $checkReshare['file_target'];
1254
					} else {
1255
						$result['filePath'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $backend->getFilePath($itemSource, $uidOwner) : null;
1256
						$result['suggestedItemTarget'] = null;
1257
						$result['suggestedFileTarget'] = null;
1258
						$result['itemSource'] = $itemSource;
1259
						$result['fileSource'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $itemSource : null;
1260
					}
1261
				}
1262
			} else {
1263
				$message = 'Sharing %s failed, because resharing is not allowed';
1264
				throw new \Exception(sprintf($message, $itemSourceName));
1265
			}
1266
		} else {
1267
			$result['parent'] = null;
1268
			$result['suggestedItemTarget'] = null;
1269
			$result['suggestedFileTarget'] = null;
1270
			$result['itemSource'] = $itemSource;
1271
			$result['expirationDate'] = $expirationDate;
1272
			if (!$backend->isValidSource($itemSource, $uidOwner)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $backend->isValidSource($itemSource, $uidOwner) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
1273
				$message = 'Sharing %1$s failed, because the sharing backend for '
1274
					.'%2$s could not find its source';
1275
				throw new \Exception(sprintf($message, $itemSource, $itemType));
1276
			}
1277
			if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
1278
				$result['filePath'] = $backend->getFilePath($itemSource, $uidOwner);
1279
					$meta = \OC\Files\Filesystem::getFileInfo($result['filePath']);
1280
					$result['fileSource'] = $meta['fileid'];
1281
				if ($result['fileSource'] == -1) {
1282
					$message = 'Sharing %s failed, because the file could not be found in the file cache';
1283
					throw new \Exception(sprintf($message, $itemSource));
1284
				}
1285
			} else {
1286
				$result['filePath'] = null;
1287
				$result['fileSource'] = null;
1288
			}
1289
		}
1290
1291
		return $result;
1292
	}
1293
1294
	/**
1295
	 *
1296
	 * @param array $shareData
1297
	 * @return mixed false in case of a failure or the id of the new share
1298
	 */
1299
	private static function insertShare(array $shareData) {
1300
1301
		$query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` ('
1302
			.' `item_type`, `item_source`, `item_target`, `share_type`,'
1303
			.' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,'
1304
			.' `file_target`, `token`, `parent`, `expiration`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)');
1305
		$query->bindValue(1, $shareData['itemType']);
1306
		$query->bindValue(2, $shareData['itemSource']);
1307
		$query->bindValue(3, $shareData['itemTarget']);
1308
		$query->bindValue(4, $shareData['shareType']);
1309
		$query->bindValue(5, $shareData['shareWith']);
1310
		$query->bindValue(6, $shareData['uidOwner']);
1311
		$query->bindValue(7, $shareData['permissions']);
1312
		$query->bindValue(8, $shareData['shareTime']);
1313
		$query->bindValue(9, $shareData['fileSource']);
1314
		$query->bindValue(10, $shareData['fileTarget']);
1315
		$query->bindValue(11, $shareData['token']);
1316
		$query->bindValue(12, $shareData['parent']);
1317
		$query->bindValue(13, $shareData['expiration'], 'datetime');
0 ignored issues
show
Bug introduced by
'datetime' of type string is incompatible with the type integer expected by parameter $type of OC_DB_StatementWrapper::bindValue(). ( Ignorable by Annotation )

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

1317
		$query->bindValue(13, $shareData['expiration'], /** @scrutinizer ignore-type */ 'datetime');
Loading history...
1318
		$result = $query->execute();
1319
1320
		$id = false;
1321
		if ($result) {
1322
			$id =  \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
1323
		}
1324
1325
		return $id;
1326
1327
	}
1328
1329
	/**
1330
	 * construct select statement
1331
	 * @param int $format
1332
	 * @param boolean $fileDependent ist it a file/folder share or a generla share
1333
	 * @param string $uidOwner
1334
	 * @return string select statement
1335
	 */
1336
	private static function createSelectStatement($format, $fileDependent, $uidOwner = null) {
1337
		$select = '*';
1338
		if ($format == self::FORMAT_STATUSES) {
1339
			if ($fileDependent) {
1340
				$select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, '
1341
					. '`share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`, '
1342
					. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`, '
1343
					. '`uid_initiator`';
1344
			} else {
1345
				$select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`, `*PREFIX*share`.`permissions`';
1346
			}
1347
		} else {
1348
			if (isset($uidOwner)) {
1349
				if ($fileDependent) {
1350
					$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,'
1351
						. ' `share_type`, `share_with`, `file_source`, `file_target`, `path`, `*PREFIX*share`.`permissions`, `stime`,'
1352
						. ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`, '
1353
						. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
1354
				} else {
1355
					$select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `*PREFIX*share`.`permissions`,'
1356
						. ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`';
1357
				}
1358
			} else {
1359
				if ($fileDependent) {
1360
					if ($format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_GET_FOLDER_CONTENTS || $format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_FILE_APP_ROOT) {
1361
						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, '
1362
							. '`share_type`, `share_with`, `file_source`, `path`, `file_target`, `stime`, '
1363
							. '`*PREFIX*share`.`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
1364
							. '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `mail_send`';
1365
					} else {
1366
						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,'
1367
							. '`*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,'
1368
							. '`file_source`, `path`, `file_target`, `*PREFIX*share`.`permissions`,'
1369
						    . '`stime`, `expiration`, `token`, `storage`, `mail_send`,'
1370
							. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
1371
					}
1372
				}
1373
			}
1374
		}
1375
		return $select;
1376
	}
1377
1378
1379
	/**
1380
	 * transform db results
1381
	 * @param array $row result
1382
	 */
1383
	private static function transformDBResults(&$row) {
1384
		if (isset($row['id'])) {
1385
			$row['id'] = (int) $row['id'];
1386
		}
1387
		if (isset($row['share_type'])) {
1388
			$row['share_type'] = (int) $row['share_type'];
1389
		}
1390
		if (isset($row['parent'])) {
1391
			$row['parent'] = (int) $row['parent'];
1392
		}
1393
		if (isset($row['file_parent'])) {
1394
			$row['file_parent'] = (int) $row['file_parent'];
1395
		}
1396
		if (isset($row['file_source'])) {
1397
			$row['file_source'] = (int) $row['file_source'];
1398
		}
1399
		if (isset($row['permissions'])) {
1400
			$row['permissions'] = (int) $row['permissions'];
1401
		}
1402
		if (isset($row['storage'])) {
1403
			$row['storage'] = (int) $row['storage'];
1404
		}
1405
		if (isset($row['stime'])) {
1406
			$row['stime'] = (int) $row['stime'];
1407
		}
1408
		if (isset($row['expiration']) && $row['share_type'] !== self::SHARE_TYPE_LINK) {
1409
			// discard expiration date for non-link shares, which might have been
1410
			// set by ancient bugs
1411
			$row['expiration'] = null;
1412
		}
1413
	}
1414
1415
	/**
1416
	 * format result
1417
	 * @param array $items result
1418
	 * @param string $column is it a file share or a general share ('file_target' or 'item_target')
1419
	 * @param \OCP\Share_Backend $backend sharing backend
1420
	 * @param int $format
1421
	 * @param array $parameters additional format parameters
1422
	 * @return array format result
1423
	 */
1424
	private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) {
1425
		if ($format === self::FORMAT_NONE) {
1426
			return $items;
1427
		} else if ($format === self::FORMAT_STATUSES) {
1428
			$statuses = array();
1429
			foreach ($items as $item) {
1430
				if ($item['share_type'] === self::SHARE_TYPE_LINK) {
1431
					if ($item['uid_initiator'] !== \OC::$server->getUserSession()->getUser()->getUID()) {
1432
						continue;
1433
					}
1434
					$statuses[$item[$column]]['link'] = true;
1435
				} else if (!isset($statuses[$item[$column]])) {
1436
					$statuses[$item[$column]]['link'] = false;
1437
				}
1438
				if (!empty($item['file_target'])) {
1439
					$statuses[$item[$column]]['path'] = $item['path'];
1440
				}
1441
			}
1442
			return $statuses;
1443
		} else {
1444
			return $backend->formatItems($items, $format, $parameters);
1445
		}
1446
	}
1447
1448
	/**
1449
	 * remove protocol from URL
1450
	 *
1451
	 * @param string $url
1452
	 * @return string
1453
	 */
1454
	public static function removeProtocolFromUrl($url) {
1455
		if (strpos($url, 'https://') === 0) {
1456
			return substr($url, strlen('https://'));
1457
		} else if (strpos($url, 'http://') === 0) {
1458
			return substr($url, strlen('http://'));
1459
		}
1460
1461
		return $url;
1462
	}
1463
1464
	/**
1465
	 * try http post first with https and then with http as a fallback
1466
	 *
1467
	 * @param string $remoteDomain
1468
	 * @param string $urlSuffix
1469
	 * @param array $fields post parameters
1470
	 * @return array
1471
	 */
1472
	private static function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) {
1473
		$protocol = 'https://';
1474
		$result = [
1475
			'success' => false,
1476
			'result' => '',
1477
		];
1478
		$try = 0;
1479
		$discoveryService = \OC::$server->query(\OCP\OCS\IDiscoveryService::class);
1480
		while ($result['success'] === false && $try < 2) {
1481
			$federationEndpoints = $discoveryService->discover($protocol . $remoteDomain, 'FEDERATED_SHARING');
1482
			$endpoint = isset($federationEndpoints['share']) ? $federationEndpoints['share'] : '/ocs/v2.php/cloud/shares';
1483
			$client = \OC::$server->getHTTPClientService()->newClient();
1484
1485
			try {
1486
				$response = $client->post(
1487
					$protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT,
1488
					[
1489
						'body' => $fields,
1490
						'connect_timeout' => 10,
1491
					]
1492
				);
1493
1494
				$result = ['success' => true, 'result' => $response->getBody()];
1495
			} catch (\Exception $e) {
1496
				$result = ['success' => false, 'result' => $e->getMessage()];
1497
			}
1498
1499
			$try++;
1500
			$protocol = 'http://';
1501
		}
1502
1503
		return $result;
1504
	}
1505
1506
	/**
1507
	 * send server-to-server unshare to remote server
1508
	 *
1509
	 * @param string $remote url
1510
	 * @param int $id share id
1511
	 * @param string $token
1512
	 * @return bool
1513
	 */
1514
	private static function sendRemoteUnshare($remote, $id, $token) {
1515
		$url = rtrim($remote, '/');
1516
		$fields = array('token' => $token, 'format' => 'json');
1517
		$url = self::removeProtocolFromUrl($url);
1518
		$result = self::tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields);
1519
		$status = json_decode($result['result'], true);
1520
1521
		return ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200));
1522
	}
1523
1524
	/**
1525
	 * @return int
1526
	 */
1527
	public static function getExpireInterval() {
1528
		return (int)\OC::$server->getConfig()->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1529
	}
1530
1531
	/**
1532
	 * Checks whether the given path is reachable for the given owner
1533
	 *
1534
	 * @param string $path path relative to files
1535
	 * @param string $ownerStorageId storage id of the owner
1536
	 *
1537
	 * @return boolean true if file is reachable, false otherwise
1538
	 */
1539
	private static function isFileReachable($path, $ownerStorageId) {
1540
		// if outside the home storage, file is always considered reachable
1541
		if (!(substr($ownerStorageId, 0, 6) === 'home::' ||
1542
			substr($ownerStorageId, 0, 13) === 'object::user:'
1543
		)) {
1544
			return true;
1545
		}
1546
1547
		// if inside the home storage, the file has to be under "/files/"
1548
		$path = ltrim($path, '/');
1549
		if (substr($path, 0, 6) === 'files/') {
1550
			return true;
1551
		}
1552
1553
		return false;
1554
	}
1555
}
1556