Completed
Push — master ( 4f0c6d...cc06d8 )
by Thomas
13:11
created

Share::shareWithMembershipGroupOnly()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Bart Visscher <[email protected]>
5
 * @author Bernhard Reiter <[email protected]>
6
 * @author Björn Schießle <[email protected]>
7
 * @author Christopher Schäpers <[email protected]>
8
 * @author Christoph Wurst <[email protected]>
9
 * @author Daniel Hansson <[email protected]>
10
 * @author Joas Schilling <[email protected]>
11
 * @author Jörn Friedrich Dreyer <[email protected]>
12
 * @author Lukas Reschke <[email protected]>
13
 * @author Michael Kuhn <[email protected]>
14
 * @author Morris Jobke <[email protected]>
15
 * @author Robin Appelman <[email protected]>
16
 * @author Robin McCorkell <[email protected]>
17
 * @author Roeland Jago Douma <[email protected]>
18
 * @author Sebastian Döll <[email protected]>
19
 * @author Stefan Weil <[email protected]>
20
 * @author Thomas Müller <[email protected]>
21
 * @author Torben Dannhauer <[email protected]>
22
 * @author Vincent Petry <[email protected]>
23
 * @author Volkan Gezer <[email protected]>
24
 *
25
 * @copyright Copyright (c) 2018, ownCloud GmbH
26
 * @license AGPL-3.0
27
 *
28
 * This code is free software: you can redistribute it and/or modify
29
 * it under the terms of the GNU Affero General Public License, version 3,
30
 * as published by the Free Software Foundation.
31
 *
32
 * This program is distributed in the hope that it will be useful,
33
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35
 * GNU Affero General Public License for more details.
36
 *
37
 * You should have received a copy of the GNU Affero General Public License, version 3,
38
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
39
 *
40
 */
41
42
namespace OC\Share;
43
44
use OC\Files\Filesystem;
45
use OC\Group\Group;
46
use OCA\FederatedFileSharing\DiscoveryManager;
47
use OCP\DB\QueryBuilder\IQueryBuilder;
48
use OCP\IUser;
49
use OCP\IUserSession;
50
use OCP\IDBConnection;
51
use OCP\IConfig;
52
use Symfony\Component\EventDispatcher\GenericEvent;
53
54
/**
55
 * This class provides the ability for apps to share their content between users.
56
 * Apps must create a backend class that implements OCP\Share_Backend and register it with this class.
57
 *
58
 * It provides the following hooks:
59
 *  - post_shared
60
 */
61
class Share extends Constants {
62
63
	/** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask
64
	 * Construct permissions for share() and setPermissions with Or (|) e.g.
65
	 * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE
66
	 *
67
	 * Check if permission is granted with And (&) e.g. Check if delete is
68
	 * granted: if ($permissions & PERMISSION_DELETE)
69
	 *
70
	 * Remove permissions with And (&) and Not (~) e.g. Remove the update
71
	 * permission: $permissions &= ~PERMISSION_UPDATE
72
	 *
73
	 * Apps are required to handle permissions on their own, this class only
74
	 * stores and manages the permissions of shares
75
	 * @see lib/public/constants.php
76
	 */
77
78
	/**
79
	 * Register a sharing backend class that implements OCP\Share_Backend for an item type
80
	 * @param string $itemType Item type
81
	 * @param string $class Backend class
82
	 * @param string $collectionOf (optional) Depends on item type
83
	 * @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files
84
	 * @return boolean true if backend is registered or false if error
85
	 */
86
	public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) {
87
		if (self::isEnabled()) {
88
			if (!isset(self::$backendTypes[$itemType])) {
89
				self::$backendTypes[$itemType] = [
90
					'class' => $class,
91
					'collectionOf' => $collectionOf,
92
					'supportedFileExtensions' => $supportedFileExtensions
93
				];
94
				if(count(self::$backendTypes) === 1) {
95
					\OC_Util::addScript('core', 'shareconfigmodel');
96
					\OC_Util::addScript('core', 'sharemodel');
97
					\OC_Util::addScript('core', 'sharescollection');
98
					\OC_Util::addScript('core', 'shareitemmodel');
99
					\OC_Util::addScript('core', 'sharedialogresharerinfoview');
100
					\OC_Util::addScript('core', 'sharedialoglinklistview');
101
					\OC_Util::addScript('core', 'sharedialoglinkshareview');
102
					\OC_Util::addScript('core', 'sharedialogmailview');
103
					\OC_Util::addScript('core', 'sharedialoglinksocialview');
104
					\OC_Util::addScript('core', 'sharedialogexpirationview');
105
					\OC_Util::addScript('core', 'sharedialogshareelistview');
106
					\OC_Util::addScript('core', 'sharedialogview');
107
					\OC_Util::addScript('core', 'share');
108
					\OC_Util::addStyle('core', 'share');
109
				}
110
				return true;
111
			}
112
			\OCP\Util::writeLog('OCP\Share',
113
				'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class']
114
				.' is already registered for '.$itemType,
115
				\OCP\Util::WARN);
116
		}
117
		return false;
118
	}
119
120
	/**
121
	 * Check if the Share API is enabled
122
	 * @return boolean true if enabled or false
123
	 *
124
	 * The Share API is enabled by default if not configured
125
	 */
126
	public static function isEnabled() {
127
		if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_enabled', 'yes') == 'yes') {
128
			return true;
129
		}
130
		return false;
131
	}
132
133
	/**
134
	 * Find which users can access a shared item
135
	 * @param string $path to the file
136
	 * @param string $ownerUser owner of the file
137
	 * @param boolean $includeOwner include owner to the list of users with access to the file
138
	 * @param boolean $returnUserPaths Return an array with the user => path map
139
	 * @param boolean $recursive take all parent folders into account (default true)
140
	 * @return array
141
	 * @note $path needs to be relative to user data dir, e.g. 'file.txt'
142
	 *       not '/admin/data/file.txt'
143
	 */
144
	public static function getUsersSharingFile($path, $ownerUser, $includeOwner = false, $returnUserPaths = false, $recursive = true) {
145
		$userManager = \OC::$server->getUserManager();
146
		$userObject = $userManager->get($ownerUser);
147
148 View Code Duplication
		if (is_null($ownerUser)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
149
			\OCP\Util::writeLog('files', ' Backends provided no user object for ' . $ownerUser, \OCP\Util::ERROR);
150
			throw new \OC\User\NoUserException('Backends provided no user object for ' . $ownerUser);
151
		}
152
153
		$ownerUser = $userObject->getUID();
154
155
		Filesystem::initMountPoints($ownerUser);
156
		$shares = $sharePaths = $fileTargets = [];
157
		$publicShare = false;
158
		$remoteShare = false;
159
		$source = -1;
160
		$cache = $mountPath = false;
161
162
		$view = new \OC\Files\View('/' . $ownerUser . '/files');
163
		$meta = $view->getFileInfo($path);
164
		if ($meta) {
165
			$path = substr($meta->getPath(), strlen('/' . $ownerUser . '/files'));
166
		} else {
167
			// if the file doesn't exists yet we start with the parent folder
168
			$meta = $view->getFileInfo(dirname($path));
169
		}
170
171
		if($meta !== false) {
172
			$source = $meta['fileid'];
173
			$cache = new \OC\Files\Cache\Cache($meta['storage']);
174
175
			$mountPath = $meta->getMountPoint()->getMountPoint();
176
			if ($mountPath !== false) {
177
				$mountPath = substr($mountPath, strlen('/' . $ownerUser . '/files'));
178
			}
179
		}
180
181
		$paths = [];
182
		while ($source !== -1) {
183
			// Fetch all shares with another user
184
			if (!$returnUserPaths) {
185
				$query = \OC_DB::prepare(
186
					'SELECT `share_with`, `file_source`, `file_target`
187
					FROM
188
					`*PREFIX*share`
189
					WHERE
190
					`item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
191
				);
192
				$result = $query->execute([$source, self::SHARE_TYPE_USER]);
193
			} else {
194
				$query = \OC_DB::prepare(
195
					'SELECT `share_with`, `file_source`, `file_target`
196
				FROM
197
				`*PREFIX*share`
198
				WHERE
199
				`item_source` = ? AND `share_type` IN (?, ?) AND `item_type` IN (\'file\', \'folder\')'
200
				);
201
				$result = $query->execute([$source, self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique]);
202
			}
203
204
			if (\OCP\DB::isError($result)) {
205
				\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
206
			} else {
207
				while ($row = $result->fetchRow()) {
208
					$shares[] = $row['share_with'];
209
					if ($returnUserPaths) {
210
						$fileTargets[(int) $row['file_source']][$row['share_with']] = $row;
211
					}
212
				}
213
			}
214
215
			// We also need to take group shares into account
216
			$query = \OC_DB::prepare(
217
				'SELECT `share_with`, `file_source`, `file_target`
218
				FROM
219
				`*PREFIX*share`
220
				WHERE
221
				`item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
222
			);
223
224
			$result = $query->execute([$source, self::SHARE_TYPE_GROUP]);
225
226
			if (\OCP\DB::isError($result)) {
227
				\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
228
			} else {
229
				while ($row = $result->fetchRow()) {
230
					$usersInGroup = self::usersInGroup($row['share_with']);
231
					$shares = array_merge($shares, $usersInGroup);
232
					if ($returnUserPaths) {
233
						foreach ($usersInGroup as $user) {
234
							if (!isset($fileTargets[(int) $row['file_source']][$user])) {
235
								// When the user already has an entry for this file source
236
								// the file is either shared directly with him as well, or
237
								// he has an exception entry (because of naming conflict).
238
								$fileTargets[(int) $row['file_source']][$user] = $row;
239
							}
240
						}
241
					}
242
				}
243
			}
244
245
			//check for public link shares
246 View Code Duplication
			if (!$publicShare) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
247
				$query = \OC_DB::prepare('
248
					SELECT `share_with`
249
					FROM `*PREFIX*share`
250
					WHERE `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')', 1
251
				);
252
253
				$result = $query->execute([$source, self::SHARE_TYPE_LINK]);
254
255
				if (\OCP\DB::isError($result)) {
256
					\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
257
				} else {
258
					if ($result->fetchRow()) {
259
						$publicShare = true;
260
					}
261
				}
262
			}
263
264
			//check for remote share
265 View Code Duplication
			if (!$remoteShare) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
266
				$query = \OC_DB::prepare('
267
					SELECT `share_with`
268
					FROM `*PREFIX*share`
269
					WHERE `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')', 1
270
				);
271
272
				$result = $query->execute([$source, self::SHARE_TYPE_REMOTE]);
273
274
				if (\OCP\DB::isError($result)) {
275
					\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
276
				} else {
277
					if ($result->fetchRow()) {
278
						$remoteShare = true;
279
					}
280
				}
281
			}
282
283
			// let's get the parent for the next round
284
			$meta = $cache->get((int)$source);
285
			if ($recursive === true && $meta !== false) {
286
				$paths[$source] = $meta['path'];
287
				$source = (int)$meta['parent'];
288
			} else {
289
				$source = -1;
290
			}
291
		}
292
293
		// Include owner in list of users, if requested
294
		if ($includeOwner) {
295
			$shares[] = $ownerUser;
296
		}
297
298
		if ($returnUserPaths) {
299
			$fileTargetIDs = array_keys($fileTargets);
300
			$fileTargetIDs = array_unique($fileTargetIDs);
301
302
			if (!empty($fileTargetIDs)) {
303
				$query = \OC_DB::prepare(
304
					'SELECT `fileid`, `path`
305
					FROM `*PREFIX*filecache`
306
					WHERE `fileid` IN (' . implode(',', $fileTargetIDs) . ')'
307
				);
308
				$result = $query->execute();
309
310
				if (\OCP\DB::isError($result)) {
311
					\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
312
				} else {
313
					while ($row = $result->fetchRow()) {
314
						foreach ($fileTargets[$row['fileid']] as $uid => $shareData) {
315
							if ($mountPath !== false) {
316
								$sharedPath = $shareData['file_target'];
317
								$sharedPath .= substr($path, strlen($mountPath) + strlen($paths[$row['fileid']]));
318
								$sharePaths[$uid] = $sharedPath;
319
							} else {
320
								$sharedPath = $shareData['file_target'];
321
								$sharedPath .= substr($path, strlen($row['path']) -5);
322
								$sharePaths[$uid] = $sharedPath;
323
							}
324
						}
325
					}
326
				}
327
			}
328
329
			if ($includeOwner) {
330
				$sharePaths[$ownerUser] = $path;
331
			} else {
332
				unset($sharePaths[$ownerUser]);
333
			}
334
335
			return $sharePaths;
336
		}
337
338
		return ['users' => array_unique($shares), 'public' => $publicShare, 'remote' => $remoteShare];
339
	}
340
341
	/**
342
	 * Get the items of item type shared with the current user
343
	 * @param string $itemType
344
	 * @param int $format (optional) Format type must be defined by the backend
345
	 * @param mixed $parameters (optional)
346
	 * @param int $limit Number of items to return (optional) Returns all by default
347
	 * @param boolean $includeCollections (optional)
348
	 * @return mixed Return depends on format
349
	 */
350
	public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE,
351
											  $parameters = null, $limit = -1, $includeCollections = false) {
352
		return self::getItems($itemType, null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
353
			$parameters, $limit, $includeCollections);
354
	}
355
356
	/**
357
	 * Get the items of item type shared with a user
358
	 * @param string $itemType
359
	 * @param string $user id for which user we want the shares
360
	 * @param int $format (optional) Format type must be defined by the backend
361
	 * @param mixed $parameters (optional)
362
	 * @param int $limit Number of items to return (optional) Returns all by default
363
	 * @param boolean $includeCollections (optional)
364
	 * @return mixed Return depends on format
365
	 */
366
	public static function getItemsSharedWithUser($itemType, $user, $format = self::FORMAT_NONE,
367
												  $parameters = null, $limit = -1, $includeCollections = false) {
368
		return self::getItems($itemType, null, self::$shareTypeUserAndGroups, $user, null, $format,
369
			$parameters, $limit, $includeCollections);
370
	}
371
372
	/**
373
	 * Get the item of item type shared with the current user
374
	 * @param string $itemType
375
	 * @param string $itemTarget
376
	 * @param int $format (optional) Format type must be defined by the backend
377
	 * @param mixed $parameters (optional)
378
	 * @param boolean $includeCollections (optional)
379
	 * @return mixed Return depends on format
380
	 */
381
	public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE,
382
											 $parameters = null, $includeCollections = false) {
383
		return self::getItems($itemType, $itemTarget, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
384
			$parameters, 1, $includeCollections);
385
	}
386
387
	/**
388
	 * Get the item of item type shared with a given user by source
389
	 * @param string $itemType
390
	 * @param string $itemSource
391
	 * @param string $user User to whom the item was shared
392
	 * @param string $owner Owner of the share
393
	 * @param int $shareType only look for a specific share type
394
	 * @return array Return list of items with file_target, permissions and expiration
395
	 */
396
	public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null, $shareType = null) {
397
		$shares = [];
398
		$fileDependent = false;
399
400
		$where = 'WHERE';
401
		$fileDependentWhere = '';
402
		if ($itemType === 'file' || $itemType === 'folder') {
403
			$fileDependent = true;
404
			$column = 'file_source';
405
			$fileDependentWhere = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
406
			$fileDependentWhere .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
407
		} else {
408
			$column = 'item_source';
409
		}
410
411
		$select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent);
412
413
		$where .= ' `' . $column . '` = ? AND `item_type` = ? ';
414
		$arguments = [$itemSource, $itemType];
415
		// for link shares $user === null
416
		if ($user !== null) {
417
			$where .= ' AND `share_with` = ? ';
418
			$arguments[] = $user;
419
		}
420
421
		if ($shareType !== null) {
422
			$where .= ' AND `share_type` = ? ';
423
			$arguments[] = $shareType;
424
		}
425
426
		if ($owner !== null) {
427
			$where .= ' AND `uid_owner` = ? ';
428
			$arguments[] = $owner;
429
		}
430
431
		$query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` '. $fileDependentWhere . $where);
432
433
		$result = \OC_DB::executeAudited($query, $arguments);
434
435
		while ($row = $result->fetchRow()) {
436
			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
437
				continue;
438
			}
439
			if ($fileDependent && (int)$row['file_parent'] === -1) {
440
				// if it is a mount point we need to get the path from the mount manager
441
				$mountManager = \OC\Files\Filesystem::getMountManager();
442
				$mountPoint = $mountManager->findByStorageId($row['storage_id']);
443
				if (!empty($mountPoint)) {
444
					$path = $mountPoint[0]->getMountPoint();
445
					$path = trim($path, '/');
446
					$path = substr($path, strlen($owner) + 1); //normalize path to 'files/foo.txt`
447
					$row['path'] = $path;
448
				} else {
449
					\OC::$server->getLogger()->warning(
450
						'Could not resolve mount point for ' . $row['storage_id'],
451
						['app' => 'OCP\Share']
452
					);
453
				}
454
			}
455
			$shares[] = $row;
456
		}
457
458
		//if didn't found a result than let's look for a group share.
459
		if(empty($shares) && $user !== null) {
460
			$groups = self::getGroupsForUser($user);
461
462
			if (!empty($groups)) {
463
				$where = $fileDependentWhere . ' WHERE `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)';
464
				$arguments = [$itemSource, $itemType, $groups];
465
				$types = [null, null, IQueryBuilder::PARAM_STR_ARRAY];
466
467
				if ($owner !== null) {
468
					$where .= ' AND `uid_owner` = ?';
469
					$arguments[] = $owner;
470
					$types[] = null;
471
				}
472
473
				// TODO: inject connection, hopefully one day in the future when this
474
				// class isn't static anymore...
475
				$conn = \OC::$server->getDatabaseConnection();
476
				$result = $conn->executeQuery(
477
					'SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where,
478
					$arguments,
0 ignored issues
show
Documentation introduced by
$arguments is of type array<integer,string|arr...bject<OC\Group\Group>>>, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
479
					$types
480
				);
481
482
				while ($row = $result->fetch()) {
483
					$shares[] = $row;
484
				}
485
			}
486
		}
487
488
		return $shares;
489
490
	}
491
492
	/**
493
	 * Get the item of item type shared with the current user by source
494
	 * @param string $itemType
495
	 * @param string $itemSource
496
	 * @param int $format (optional) Format type must be defined by the backend
497
	 * @param mixed $parameters
498
	 * @param boolean $includeCollections
499
	 * @param string $shareWith (optional) define against which user should be checked, default: current user
500
	 * @return array
501
	 */
502
	public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE,
503
													 $parameters = null, $includeCollections = false, $shareWith = null) {
504
		$shareWith = ($shareWith === null) ? \OC_User::getUser() : $shareWith;
505
		return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, $shareWith, null, $format,
506
			$parameters, 1, $includeCollections, true);
507
	}
508
509
	/**
510
	 * Get the item of item type shared by a link
511
	 * @param string $itemType
512
	 * @param string $itemSource
513
	 * @param string $uidOwner Owner of link
514
	 * @return array
515
	 */
516
	public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) {
517
		return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE,
518
			null, 1);
519
	}
520
521
	/**
522
	 * Based on the given token the share information will be returned - password protected shares will be verified
523
	 * @param string $token
524
	 * @param bool $checkPasswordProtection
525
	 * @return array|boolean false will be returned in case the token is unknown or unauthorized
526
	 */
527
	public static function getShareByToken($token, $checkPasswordProtection = true) {
528
		$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `token` = ?', 1);
529
		$result = $query->execute([$token]);
530 View Code Duplication
		if ($result === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
531
			\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage() . ', token=' . $token, \OCP\Util::ERROR);
532
		}
533
		$row = $result->fetchRow();
534
		if ($row === false) {
535
			return false;
536
		}
537
		if (is_array($row) and self::expireItem($row)) {
538
			return false;
539
		}
540
541
		// password protected shares need to be authenticated
542
		if ($checkPasswordProtection && !\OCP\Share::checkPasswordProtectedShare($row)) {
543
			return false;
544
		}
545
546
		return $row;
547
	}
548
549
	/**
550
	 * resolves reshares down to the last real share
551
	 * @param array $linkItem
552
	 * @return array file owner
553
	 */
554
	public static function resolveReShare($linkItem)
555
	{
556
		if (isset($linkItem['parent'])) {
557
			$parent = $linkItem['parent'];
558 View Code Duplication
			while (isset($parent)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
559
				$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `id` = ?', 1);
560
				$item = $query->execute([$parent])->fetchRow();
561
				if (isset($item['parent'])) {
562
					$parent = $item['parent'];
563
				} else {
564
					return $item;
565
				}
566
			}
567
		}
568
		return $linkItem;
569
	}
570
571
572
	/**
573
	 * Get the shared items of item type owned by the current user
574
	 * @param string $itemType
575
	 * @param int $format (optional) Format type must be defined by the backend
576
	 * @param mixed $parameters
577
	 * @param int $limit Number of items to return (optional) Returns all by default
578
	 * @param boolean $includeCollections
579
	 * @return mixed Return depends on format
580
	 */
581
	public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null,
582
										  $limit = -1, $includeCollections = false) {
583
		return self::getItems($itemType, null, null, null, \OC_User::getUser(), $format,
584
			$parameters, $limit, $includeCollections);
585
	}
586
587
	/**
588
	 * Get the shared item of item type owned by the current user
589
	 * @param string $itemType
590
	 * @param string $itemSource
591
	 * @param int $format (optional) Format type must be defined by the backend
592
	 * @param mixed $parameters
593
	 * @param boolean $includeCollections
594
	 * @return mixed Return depends on format
595
	 */
596
	public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE,
597
										 $parameters = null, $includeCollections = false) {
598
		return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format,
599
			$parameters, -1, $includeCollections);
600
	}
601
602
	/**
603
	 * Get all users an item is shared with
604
	 * @param string $itemType
605
	 * @param string $itemSource
606
	 * @param string $uidOwner
607
	 * @param boolean $includeCollections
608
	 * @param boolean $checkExpireDate
609
	 * @return array Return array of users
610
	 */
611
	public static function getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections = false, $checkExpireDate = true) {
612
613
		$users = [];
614
		$items = self::getItems($itemType, $itemSource, null, null, $uidOwner, self::FORMAT_NONE, null, -1, $includeCollections, false, $checkExpireDate);
615
		if ($items) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $items 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...
616
			foreach ($items as $item) {
617
				if ((int)$item['share_type'] === self::SHARE_TYPE_USER) {
618
					$users[] = $item['share_with'];
619
				} else if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) {
620
					$users = array_merge($users, self::usersInGroup($item['share_with']));
621
				}
622
			}
623
		}
624
		return $users;
625
	}
626
627
	/**
628
	 * Share an item with a user, group, or via private link
629
	 * @param string $itemType
630
	 * @param string $itemSource
631
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
632
	 * @param string $shareWith User or group the item is being shared with
633
	 * @param int $permissions CRUDS
634
	 * @param string $itemSourceName
635
	 * @param \DateTime $expirationDate
636
	 * @param bool $passwordChanged
637
	 * @return boolean|string Returns true on success or false on failure, Returns token on success for links
638
	 * @throws \OC\HintException when the share type is remote and the shareWith is invalid
639
	 * @throws \Exception
640
	 */
641
	public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null, $passwordChanged = null) {
642
643
		$backend = self::getBackend($itemType);
644
		$l = \OC::$server->getL10N('lib');
645
646
		if ($backend->isShareTypeAllowed($shareType) === false) {
647
			$message = 'Sharing %s failed, because the backend does not allow shares from type %i';
648
			$message_t = $l->t('Sharing %s failed, because the backend does not allow shares from type %i', [$itemSourceName, $shareType]);
649
			\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareType), \OCP\Util::DEBUG);
650
			throw new \Exception($message_t);
651
		}
652
653
		$uidOwner = \OC_User::getUser();
654
		$shareWithinGroupOnly = self::shareWithGroupMembersOnly();
655
		$shareWithMembershipGroupOnly = self::shareWithMembershipGroupOnly();
656
657
		if (is_null($itemSourceName)) {
658
			$itemSourceName = $itemSource;
659
		}
660
		$itemName = $itemSourceName;
661
662
		// check if file can be shared
663
		if ($itemType === 'file' or $itemType === 'folder') {
664
			$path = \OC\Files\Filesystem::getPath($itemSource);
665
			$itemName = $path;
666
667
			// verify that the file exists before we try to share it
668 View Code Duplication
			if (!$path) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
669
				$message = 'Sharing %s failed, because the file does not exist';
670
				$message_t = $l->t('Sharing %s failed, because the file does not exist', [$itemSourceName]);
671
				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
672
				throw new \Exception($message_t);
673
			}
674
			// verify that the user has share permission
675 View Code Duplication
			if (!\OC\Files\Filesystem::isSharable($path) || \OCP\Util::isSharingDisabledForUser()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
676
				$message = 'You are not allowed to share %s';
677
				$message_t = $l->t('You are not allowed to share %s', [$path]);
678
				\OCP\Util::writeLog('OCP\Share', sprintf($message, $path), \OCP\Util::DEBUG);
679
				throw new \Exception($message_t);
680
			}
681
		}
682
683
		//verify that we don't share a folder which already contains a share mount point
684
		if ($itemType === 'folder') {
685
			$path = '/' . $uidOwner . '/files' . \OC\Files\Filesystem::getPath($itemSource) . '/';
686
			$mountManager = \OC\Files\Filesystem::getMountManager();
687
			$mounts = $mountManager->findIn($path);
688
			foreach ($mounts as $mount) {
689
				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
690
					$message = 'Sharing "' . $itemSourceName . '" failed, because it contains files shared with you!';
691
					\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
692
					throw new \Exception($message);
693
				}
694
695
			}
696
		}
697
698
		// single file shares should never have delete permissions
699
		if ($itemType === 'file') {
700
			$permissions = (int)$permissions & ~\OCP\Constants::PERMISSION_DELETE;
701
		}
702
703
		//Validate expirationDate
704
		if ($expirationDate !== null) {
705
			try {
706
				/*
707
				 * Reuse the validateExpireDate.
708
				 * We have to pass time() since the second arg is the time
709
				 * the file was shared, since it is not shared yet we just use
710
				 * the current time.
711
				 */
712
				$expirationDate = self::validateExpireDate($expirationDate->format('Y-m-d'), time(), $itemType, $itemSource);
713
			} catch (\Exception $e) {
714
				throw new \OC\HintException($e->getMessage(), $e->getMessage(), 404);
715
			}
716
		}
717
718
		// Verify share type and sharing conditions are met
719
		if ($shareType === self::SHARE_TYPE_USER) {
720 View Code Duplication
			if ($shareWith == $uidOwner) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
721
				$message = 'Sharing %s failed, because you can not share with yourself';
722
				$message_t = $l->t('Sharing %s failed, because you can not share with yourself', [$itemName]);
723
				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
724
				throw new \Exception($message_t);
725
			}
726 View Code Duplication
			if (!\OC_User::userExists($shareWith)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
727
				$message = 'Sharing %s failed, because the user %s does not exist';
728
				$message_t = $l->t('Sharing %s failed, because the user %s does not exist', [$itemSourceName, $shareWith]);
729
				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
730
				throw new \Exception($message_t);
731
			}
732
			if ($shareWithinGroupOnly) {
733
				$inGroup = array_intersect(self::getGroupsForUser($uidOwner), self::getGroupsForUser($shareWith));
734 View Code Duplication
				if (empty($inGroup)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
735
					$message = 'Sharing %s failed, because the user '
736
						.'%s is not a member of any groups that %s is a member of';
737
					$message_t = $l->t('Sharing %s failed, because the user %s is not a member of any groups that %s is a member of', [$itemName, $shareWith, $uidOwner]);
738
					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemName, $shareWith, $uidOwner), \OCP\Util::DEBUG);
739
					throw new \Exception($message_t);
740
				}
741
			}
742
			// Check if the item source is already shared with the user, either from the same owner or a different user
743 View Code Duplication
			if ($checkExists = self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups,
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
744
				$shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
745
				// Only allow the same share to occur again if it is the same
746
				// owner and is not a user share, this use case is for increasing
747
				// permissions for a specific user
748
				if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
749
					$message = 'Sharing %s failed, because this item is already shared with %s';
750
					$message_t = $l->t('Sharing %s failed, because this item is already shared with %s', [$itemSourceName, $shareWith]);
751
					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
752
					throw new \Exception($message_t);
753
				}
754
			}
755 View Code Duplication
			if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_USER,
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
756
				$shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
757
				// Only allow the same share to occur again if it is the same
758
				// owner and is not a user share, this use case is for increasing
759
				// permissions for a specific user
760
				if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
761
					$message = 'Sharing %s failed, because this item is already shared with user %s';
762
					$message_t = $l->t('Sharing %s failed, because this item is already shared with user %s', [$itemSourceName, $shareWith]);
763
					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::ERROR);
764
					throw new \Exception($message_t);
765
				}
766
			}
767
		} else if ($shareType === self::SHARE_TYPE_GROUP) {
768
			if (!\OC::$server->getGroupManager()->groupExists($shareWith)) {
769
				$message = 'Sharing %s failed, because the group %s does not exist';
770
				$message_t = $l->t('Sharing %s failed, because the group %s does not exist', [$itemSourceName, $shareWith]);
771
				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
772
				throw new \Exception($message_t);
773
			}
774 View Code Duplication
			if ($shareWithMembershipGroupOnly && !\OC::$server->getGroupManager()->inGroup($uidOwner, $shareWith)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
775
				$message = 'Sharing %s failed, because '
776
					.'%s is not a member of the group %s';
777
				$message_t = $l->t('Sharing %s failed, because %s is not a member of the group %s', [$itemSourceName, $uidOwner, $shareWith]);
778
				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $uidOwner, $shareWith), \OCP\Util::DEBUG);
779
				throw new \Exception($message_t);
780
			}
781
			// Check if the item source is already shared with the group, either from the same owner or a different user
782
			// The check for each user in the group is done inside the put() function
783
			if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $shareWith,
784
				null, self::FORMAT_NONE, null, 1, true, true)) {
785
786
				if ($checkExists['share_with'] === $shareWith && $checkExists['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
787
					$message = 'Sharing %s failed, because this item is already shared with %s';
788
					$message_t = $l->t('Sharing %s failed, because this item is already shared with %s', [$itemSourceName, $shareWith]);
789
					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
790
					throw new \Exception($message_t);
791
				}
792
			}
793
			// Convert share with into an array with the keys group and users
794
			$group = $shareWith;
795
			$usersInGroup = \OC::$server->getGroupManager()->get($group)->getUsers();
796
			$usersInGroup = array_values(array_map(function(IUser $u) {
797
				return $u->getUID();
798
			}, $usersInGroup));
799
			$shareWith = [];
800
			$shareWith['group'] = $group;
801
			$shareWith['users'] = array_diff($usersInGroup, [$uidOwner]);
802
		} else if ($shareType === self::SHARE_TYPE_LINK) {
803
			$updateExistingShare = false;
804
			if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') == 'yes') {
805
806
				// IF the password is changed via the old ajax endpoint verify it before deleting the old share
807
				if ($passwordChanged === true) {
808
					self::verifyPassword($shareWith);
809
				}
810
811
				// when updating a link share
812
				// FIXME Don't delete link if we update it
813
				if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null,
814
					$uidOwner, self::FORMAT_NONE, null, 1)) {
815
					// remember old token
816
					$oldToken = $checkExists['token'];
817
					$oldPermissions = $checkExists['permissions'];
818
					//delete the old share
819
					Helper::delete($checkExists['id']);
820
					$updateExistingShare = true;
821
				}
822
823
				if ($passwordChanged === null) {
824
					// Generate hash of password - same method as user passwords
825
					if (is_string($shareWith) && $shareWith !== '') {
826
						self::verifyPassword($shareWith);
827
						$shareWith = \OC::$server->getHasher()->hash($shareWith);
828
					} else {
829
						// reuse the already set password, but only if we change permissions
830
						// otherwise the user disabled the password protection
831
						if ($checkExists && (int)$permissions !== (int)$oldPermissions) {
0 ignored issues
show
Bug introduced by
The variable $oldPermissions does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug Best Practice introduced by
The expression $checkExists 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...
832
							$shareWith = $checkExists['share_with'];
833
						}
834
					}
835
				} else {
836
					if ($passwordChanged === true) {
837
						if (is_string($shareWith) && $shareWith !== '') {
838
							self::verifyPassword($shareWith);
839
							$shareWith = \OC::$server->getHasher()->hash($shareWith);
840
						}
841
					} else if ($updateExistingShare) {
842
						$shareWith = $checkExists['share_with'];
843
					}
844
				}
845
846 View Code Duplication
				if (\OCP\Util::isPublicLinkPasswordRequired() && empty($shareWith)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Deprecated Code introduced by
The method OCP\Util::isPublicLinkPasswordRequired() has been deprecated.

This method has been deprecated.

Loading history...
847
					$message = 'You need to provide a password to create a public link, only protected links are allowed';
848
					$message_t = $l->t('You need to provide a password to create a public link, only protected links are allowed');
849
					\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
850
					throw new \Exception($message_t);
851
				}
852
853
				if ($updateExistingShare === false &&
854
					self::isDefaultExpireDateEnabled() &&
855
					empty($expirationDate)) {
856
					$expirationDate = Helper::calcExpireDate();
857
				}
858
859
				// Generate token
860
				if (isset($oldToken)) {
861
					$token = $oldToken;
862
				} else {
863
					$token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH,
864
						\OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_UPPER.
865
						\OCP\Security\ISecureRandom::CHAR_DIGITS
866
					);
867
				}
868
				$result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions,
869
					null, $token, $itemSourceName, $expirationDate);
870
				if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
871
					return $token;
872
				} else {
873
					return false;
874
				}
875
			}
876
			$message = 'Sharing %s failed, because sharing with links is not allowed';
877
			$message_t = $l->t('Sharing %s failed, because sharing with links is not allowed', [$itemSourceName]);
878
			\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
879
			throw new \Exception($message_t);
880
		} else if ($shareType === self::SHARE_TYPE_REMOTE) {
881
882
			/*
883
			 * Check if file is not already shared with the remote user
884
			 */
885
			if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_REMOTE,
886
				$shareWith, $uidOwner, self::FORMAT_NONE, null, 1, true, true)) {
887
					$message = 'Sharing %s failed, because this item is already shared with %s';
888
					$message_t = $l->t('Sharing %s failed, because this item is already shared with %s', [$itemSourceName, $shareWith]);
889
					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
890
					throw new \Exception($message_t);
891
			}
892
893
			// don't allow federated shares if source and target server are the same
894
			list($user, $remote) = Helper::splitUserRemote($shareWith);
895
			$currentServer = self::removeProtocolFromUrl(\OC::$server->getURLGenerator()->getAbsoluteURL('/'));
896
			$currentUser = \OC::$server->getUserSession()->getUser()->getUID();
897 View Code Duplication
			if (Helper::isSameUserOnSameServer($user, $remote, $currentUser, $currentServer)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
898
				$message = 'Not allowed to create a federated share with the same user.';
899
				$message_t = $l->t('Not allowed to create a federated share with the same user');
900
				\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
901
				throw new \Exception($message_t);
902
			}
903
904
			$token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH, \OCP\Security\ISecureRandom::CHAR_LOWER . \OCP\Security\ISecureRandom::CHAR_UPPER .
905
				\OCP\Security\ISecureRandom::CHAR_DIGITS);
906
907
			$shareWith = $user . '@' . $remote;
908
			$shareId = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, $token, $itemSourceName);
909
910
			$send = false;
911
			if ($shareId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $shareId of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
912
				$send = self::sendRemoteShare($token, $shareWith, $itemSourceName, $shareId, $uidOwner);
913
			}
914
915
			if ($send === false) {
916
				$currentUser = \OC::$server->getUserSession()->getUser()->getUID();
917
				self::unshare($itemType, $itemSource, $shareType, $shareWith, $currentUser);
918
				$message_t = $l->t('Sharing %s failed, could not find %s, check spelling and server availability.', [$itemSourceName, $shareWith]);
919
				throw new \Exception($message_t);
920
			}
921
922
			return $send;
923 View Code Duplication
		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
924
			// Future share types need to include their own conditions
925
			$message = 'Share type %s is not valid for %s';
926
			$message_t = $l->t('Share type %s is not valid for %s', [$shareType, $itemSource]);
927
			\OCP\Util::writeLog('OCP\Share', sprintf($message, $shareType, $itemSource), \OCP\Util::DEBUG);
928
			throw new \Exception($message_t);
929
		}
930
931
		// Put the item into the database
932
		$result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, null, $itemSourceName, $expirationDate);
0 ignored issues
show
Bug introduced by
It seems like $shareWith defined by array() on line 799 can also be of type array<string,?,{"users":"array<integer,?>"}>; however, OC\Share\Share::put() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
933
934
		return $result ? true : false;
935
	}
936
937
	/**
938
	 * Unshare an item from a user, group, or delete a private link
939
	 * @param string $itemType
940
	 * @param string $itemSource
941
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
942
	 * @param string $shareWith User or group the item is being shared with
943
	 * @param string $owner owner of the share, if null the current user is used
944
	 * @return boolean true on success or false on failure
945
	 */
946
	public static function unshare($itemType, $itemSource, $shareType, $shareWith, $owner = null) {
947
948
		// check if it is a valid itemType
949
		self::getBackend($itemType);
950
951
		$items = self::getItemSharedWithUser($itemType, $itemSource, $shareWith, $owner, $shareType);
952
953
		$toDelete = [];
954
		$newParent = null;
955
		$currentUser = $owner ? $owner : \OC_User::getUser();
956
		foreach ($items as $item) {
957
			// delete the item with the expected share_type and owner
958
			if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) {
959
				$toDelete = $item;
960
				// if there is more then one result we don't have to delete the children
961
				// but update their parent. For group shares the new parent should always be
962
				// the original group share and not the db entry with the unique name
963
			} else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
964
				$newParent = $item['parent'];
965
			} else {
966
				$newParent = $item['id'];
967
			}
968
		}
969
970
		if (!empty($toDelete)) {
971
			self::unshareItem($toDelete, $newParent);
972
			return true;
973
		}
974
		return false;
975
	}
976
977
	/**
978
	 * Unshare an item from all users, groups, and remove all links
979
	 * @param string $itemType
980
	 * @param string $itemSource
981
	 * @return boolean true on success or false on failure
982
	 */
983
	public static function unshareAll($itemType, $itemSource) {
984
		// Get all of the owners of shares of this item.
985
		$query = \OC_DB::prepare( 'SELECT `uid_owner` from `*PREFIX*share` WHERE `item_type`=? AND `item_source`=?' );
986
		$result = $query->execute([$itemType, $itemSource]);
987
		$shares = [];
988
		// Add each owner's shares to the array of all shares for this item.
989
		while ($row = $result->fetchRow()) {
990
			$shares = array_merge($shares, self::getItems($itemType, $itemSource, null, null, $row['uid_owner']));
991
		}
992
		if (!empty($shares)) {
993
			// Pass all the vars we have for now, they may be useful
994
			$hookParams = [
995
				'itemType' => $itemType,
996
				'itemSource' => $itemSource,
997
				'shares' => $shares,
998
			];
999
			\OC_Hook::emit('OCP\Share', 'pre_unshareAll', $hookParams);
1000
			foreach ($shares as $share) {
1001
				self::unshareItem($share);
1002
			}
1003
			\OC_Hook::emit('OCP\Share', 'post_unshareAll', $hookParams);
1004
			return true;
1005
		}
1006
		return false;
1007
	}
1008
1009
	/**
1010
	 * Unshare an item shared with the current user
1011
	 * @param string $itemType
1012
	 * @param string $itemOrigin Item target or source
1013
	 * @param boolean $originIsSource true if $itemOrigin is the source, false if $itemOrigin is the target (optional)
1014
	 * @return boolean true on success or false on failure
1015
	 *
1016
	 * Unsharing from self is not allowed for items inside collections
1017
	 */
1018
	public static function unshareFromSelf($itemType, $itemOrigin, $originIsSource = false) {
1019
		$originType = ($originIsSource) ? 'source' : 'target';
1020
		$uid = \OCP\User::getUser();
1021
1022
		if ($itemType === 'file' || $itemType === 'folder') {
1023
			$statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `file_' . $originType . '` = ?';
1024
		} else {
1025
			$statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `item_' . $originType . '` = ?';
1026
		}
1027
1028
		$query = \OCP\DB::prepare($statement);
1029
		$result = $query->execute([$itemType, $itemOrigin]);
1030
1031
		$shares = $result->fetchAll();
1032
1033
		$listOfUnsharedItems = [];
1034
1035
		$itemUnshared = false;
1036
		foreach ($shares as $share) {
1037
			if ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_USER &&
1038
				$share['share_with'] === $uid) {
1039
				$deletedShares = Helper::delete($share['id']);
1040
				$shareTmp = [
1041
					'id' => $share['id'],
1042
					'shareWith' => $share['share_with'],
1043
					'itemTarget' => $share['item_target'],
1044
					'itemType' => $share['item_type'],
1045
					'shareType' => (int)$share['share_type'],
1046
				];
1047
				if (isset($share['file_target'])) {
1048
					$shareTmp['fileTarget'] = $share['file_target'];
1049
				}
1050
				$listOfUnsharedItems = array_merge($listOfUnsharedItems, $deletedShares, [$shareTmp]);
1051
				$itemUnshared = true;
1052
				break;
1053
			} elseif ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
1054
				if (\OC::$server->getGroupManager()->inGroup($uid, $share['share_with'])) {
1055
					$groupShare = $share;
1056
				}
1057
			} elseif ((int)$share['share_type'] === self::$shareTypeGroupUserUnique &&
1058
				$share['share_with'] === $uid) {
1059
				$uniqueGroupShare = $share;
1060
			}
1061
		}
1062
1063
		if (!$itemUnshared && isset($groupShare) && !isset($uniqueGroupShare)) {
1064
			$query = \OC_DB::prepare('INSERT INTO `*PREFIX*share`'
1065
				.' (`item_type`, `item_source`, `item_target`, `parent`, `share_type`,'
1066
				.' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`, `file_target`)'
1067
				.' VALUES (?,?,?,?,?,?,?,?,?,?,?)');
1068
			$query->execute([$groupShare['item_type'], $groupShare['item_source'], $groupShare['item_target'],
1069
				$groupShare['id'], self::$shareTypeGroupUserUnique,
1070
				\OC_User::getUser(), $groupShare['uid_owner'], 0, $groupShare['stime'], $groupShare['file_source'],
1071
				$groupShare['file_target']]);
1072
			$shareTmp = [
1073
				'id' => $groupShare['id'],
1074
				'shareWith' => $groupShare['share_with'],
1075
				'itemTarget' => $groupShare['item_target'],
1076
				'itemType' => $groupShare['item_type'],
1077
				'shareType' => (int)$groupShare['share_type'],
1078
			];
1079
			if (isset($groupShare['file_target'])) {
1080
				$shareTmp['fileTarget'] = $groupShare['file_target'];
1081
			}
1082
			$listOfUnsharedItems = array_merge($listOfUnsharedItems, [$shareTmp]);
1083
			$itemUnshared = true;
1084
		} elseif (!$itemUnshared && isset($uniqueGroupShare)) {
1085
			$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?');
1086
			$query->execute([0, $uniqueGroupShare['id']]);
1087
			$shareTmp = [
1088
				'id' => $uniqueGroupShare['id'],
1089
				'shareWith' => $uniqueGroupShare['share_with'],
1090
				'itemTarget' => $uniqueGroupShare['item_target'],
1091
				'itemType' => $uniqueGroupShare['item_type'],
1092
				'shareType' => (int)$uniqueGroupShare['share_type'],
1093
			];
1094
			if (isset($uniqueGroupShare['file_target'])) {
1095
				$shareTmp['fileTarget'] = $uniqueGroupShare['file_target'];
1096
			}
1097
			$listOfUnsharedItems = array_merge($listOfUnsharedItems, [$shareTmp]);
1098
			$itemUnshared = true;
1099
		}
1100
1101
		if ($itemUnshared) {
1102
			\OC_Hook::emit('OCP\Share', 'post_unshareFromSelf',
1103
				['unsharedItems' => $listOfUnsharedItems, 'itemType' => $itemType]);
1104
		}
1105
1106
		return $itemUnshared;
1107
	}
1108
1109
	/**
1110
	 * sent status if users got informed by mail about share
1111
	 * @param string $itemType
1112
	 * @param string $itemSource
1113
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1114
	 * @param string $recipient with whom was the file shared
1115
	 * @param boolean $status
1116
	 */
1117
	public static function setSendMailStatus($itemType, $itemSource, $shareType, $recipient, $status) {
1118
		$status = $status ? 1 : 0;
1119
1120
		$query = \OC_DB::prepare(
1121
			'UPDATE `*PREFIX*share`
1122
					SET `mail_send` = ?
1123
					WHERE `item_type` = ? AND `item_source` = ? AND `share_type` = ? AND `share_with` = ?');
1124
1125
		$result = $query->execute([$status, $itemType, $itemSource, $shareType, $recipient]);
1126
1127
		if($result === false) {
1128
			\OCP\Util::writeLog('OCP\Share', 'Couldn\'t set send mail status', \OCP\Util::ERROR);
1129
		}
1130
	}
1131
1132
	/**
1133
	 * Set the permissions of an item for a specific user or group
1134
	 * @param string $itemType
1135
	 * @param string $itemSource
1136
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1137
	 * @param string $shareWith User or group the item is being shared with
1138
	 * @param int $permissions CRUDS permissions
1139
	 * @return boolean true on success or false on failure
1140
	 * @throws \Exception when trying to grant more permissions then the user has himself
1141
	 */
1142
	public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) {
1143
		$l = \OC::$server->getL10N('lib');
1144
		$connection = \OC::$server->getDatabaseConnection();
1145
1146
		$intArrayToLiteralArray = function($intArray, $eb) {
1147
			return array_map(function($int) use ($eb) {
1148
				return $eb->literal((int)$int, 'integer');
1149
			}, $intArray);
1150
		};
1151
		$sanitizeItem = function($item) {
1152
			$item['id'] = (int)$item['id'];
1153
			$item['premissions'] = (int)$item['permissions'];
1154
			return $item;
1155
		};
1156
1157
		if ($rootItem = self::getItems($itemType, $itemSource, $shareType, $shareWith,
1158
			\OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) {
1159
			// Check if this item is a reshare and verify that the permissions
1160
			// granted don't exceed the parent shared item
1161
			if (isset($rootItem['parent'])) {
1162
				$qb = $connection->getQueryBuilder();
1163
				$qb->select('permissions')
1164
					->from('share')
1165
					->where($qb->expr()->eq('id', $qb->createParameter('id')))
1166
					->setParameter(':id', $rootItem['parent']);
1167
				$dbresult = $qb->execute();
1168
1169
				$result = $dbresult->fetch();
1170
				$dbresult->closeCursor();
1171
				if (~(int)$result['permissions'] & $permissions) {
1172
					$message = 'Setting permissions for %s failed,'
1173
						.' because the permissions exceed permissions granted to %s';
1174
					$message_t = $l->t('Setting permissions for %s failed, because the permissions exceed permissions granted to %s', [$itemSource, \OC_User::getUser()]);
1175
					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource, \OC_User::getUser()), \OCP\Util::DEBUG);
1176
					throw new \Exception($message_t);
1177
				}
1178
			}
1179
			$qb = $connection->getQueryBuilder();
1180
			$qb->update('share')
1181
				->set('permissions', $qb->createParameter('permissions'))
1182
				->where($qb->expr()->eq('id', $qb->createParameter('id')))
1183
				->setParameter(':id', $rootItem['id'])
1184
				->setParameter(':permissions', $permissions);
1185
			$qb->execute();
1186
			if ($itemType === 'file' || $itemType === 'folder') {
1187
				\OC_Hook::emit('OCP\Share', 'post_update_permissions', [
1188
					'itemType' => $itemType,
1189
					'itemSource' => $itemSource,
1190
					'shareType' => $shareType,
1191
					'shareWith' => $shareWith,
1192
					'uidOwner' => \OC_User::getUser(),
1193
					'permissions' => $permissions,
1194
					'path' => $rootItem['path'],
1195
					'share' => $rootItem
1196
				]);
1197
			}
1198
1199
			// Share id's to update with the new permissions
1200
			$ids = [];
1201
			$items = [];
1202
1203
			// Check if permissions were removed
1204
			if ((int)$rootItem['permissions'] & ~$permissions) {
1205
				// If share permission is removed all reshares must be deleted
1206
				if (($rootItem['permissions'] & \OCP\Constants::PERMISSION_SHARE) && (~$permissions & \OCP\Constants::PERMISSION_SHARE)) {
1207
					// delete all shares, keep parent and group children
1208
					Helper::delete($rootItem['id'], true, null, null, true);
1209
				}
1210
1211
				// Remove permission from all children
1212
				$parents = [$rootItem['id']];
1213
				while (!empty($parents)) {
1214
					$parents = $intArrayToLiteralArray($parents, $qb->expr());
1215
					$qb = $connection->getQueryBuilder();
1216
					$qb->select('id', 'permissions', 'item_type')
0 ignored issues
show
Unused Code introduced by
The call to IQueryBuilder::select() has too many arguments starting with 'permissions'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1217
						->from('share')
1218
						->where($qb->expr()->in('parent', $parents));
1219
					$result = $qb->execute();
1220
					// Reset parents array, only go through loop again if
1221
					// items are found that need permissions removed
1222
					$parents = [];
1223
					while ($item = $result->fetch()) {
1224
						$item = $sanitizeItem($item);
1225
1226
						$items[] = $item;
1227
						// Check if permissions need to be removed
1228
						if ($item['permissions'] & ~$permissions) {
1229
							// Add to list of items that need permissions removed
1230
							$ids[] = $item['id'];
1231
							$parents[] = $item['id'];
1232
						}
1233
					}
1234
					$result->closeCursor();
1235
				}
1236
1237
				// Remove the permissions for all reshares of this item
1238
				if (!empty($ids)) {
1239
					$ids = "'".implode("','", $ids)."'";
1240
					// TODO this should be done with Doctrine platform objects
1241
					if (\OC::$server->getConfig()->getSystemValue("dbtype") === 'oci') {
1242
						$andOp = 'BITAND(`permissions`, ?)';
1243
					} else {
1244
						$andOp = '`permissions` & ?';
1245
					}
1246
					$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = '.$andOp
1247
						.' WHERE `id` IN ('.$ids.')');
1248
					$query->execute([$permissions]);
1249
				}
1250
1251
			}
1252
1253
			/*
1254
			 * Permissions were added
1255
			 * Update all USERGROUP shares. (So group shares where the user moved their mountpoint).
1256
			 */
1257
			if ($permissions & ~(int)$rootItem['permissions']) {
1258
				$qb = $connection->getQueryBuilder();
1259
				$qb->select('id', 'permissions', 'item_type')
0 ignored issues
show
Unused Code introduced by
The call to IQueryBuilder::select() has too many arguments starting with 'permissions'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1260
					->from('share')
1261
					->where($qb->expr()->eq('parent', $qb->createParameter('parent')))
1262
					->andWhere($qb->expr()->eq('share_type', $qb->createParameter('share_type')))
1263
					->andWhere($qb->expr()->neq('permissions', $qb->createParameter('shareDeleted')))
1264
					->setParameter(':parent', (int)$rootItem['id'])
1265
					->setParameter(':share_type', 2)
1266
					->setParameter(':shareDeleted', 0);
1267
				$result = $qb->execute();
1268
1269
				$ids = [];
1270
				while ($item = $result->fetch()) {
1271
					$item = $sanitizeItem($item);
1272
					$items[] = $item;
1273
					$ids[] = $item['id'];
1274
				}
1275
				$result->closeCursor();
1276
1277
				// Add permssions for all USERGROUP shares of this item
1278
				if (!empty($ids)) {
1279
					$ids = $intArrayToLiteralArray($ids, $qb->expr());
1280
1281
					$qb = $connection->getQueryBuilder();
1282
					$qb->update('share')
1283
						->set('permissions', $qb->createParameter('permissions'))
1284
						->where($qb->expr()->in('id', $ids))
1285
						->setParameter(':permissions', $permissions);
1286
					$qb->execute();
1287
				}
1288
			}
1289
1290
			foreach ($items as $item) {
1291
				\OC_Hook::emit('OCP\Share', 'post_update_permissions', ['share' => $item]);
1292
			}
1293
1294
			return true;
1295
		}
1296
		$message = 'Setting permissions for %s failed, because the item was not found';
1297
		$message_t = $l->t('Setting permissions for %s failed, because the item was not found', [$itemSource]);
1298
1299
		\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource), \OCP\Util::DEBUG);
1300
		throw new \Exception($message_t);
1301
	}
1302
1303
	/**
1304
	 * validate expiration date if it meets all constraints
1305
	 *
1306
	 * @param string $expireDate well formatted date string, e.g. "DD-MM-YYYY"
1307
	 * @param string $shareTime timestamp when the file was shared
1308
	 * @param string $itemType
1309
	 * @param string $itemSource
1310
	 * @return \DateTime validated date
1311
	 * @throws \Exception when the expire date is in the past or further in the future then the enforced date
1312
	 */
1313
	private static function validateExpireDate($expireDate, $shareTime, $itemType, $itemSource) {
1314
		$l = \OC::$server->getL10N('lib');
1315
		$date = new \DateTime($expireDate);
1316
		$today = new \DateTime('now');
1317
1318
		// if the user doesn't provide a share time we need to get it from the database
1319
		// fall-back mode to keep API stable, because the $shareTime parameter was added later
1320
		$defaultExpireDateEnforced = \OCP\Util::isDefaultExpireDateEnforced();
1321
		if ($defaultExpireDateEnforced && $shareTime === null) {
1322
			$items = self::getItemShared($itemType, $itemSource);
1323
			$firstItem = reset($items);
1324
			$shareTime = (int)$firstItem['stime'];
1325
		}
1326
1327
		if ($defaultExpireDateEnforced) {
1328
			// initialize max date with share time
1329
			$maxDate = new \DateTime();
1330
			$maxDate->setTimestamp($shareTime);
1331
			$maxDays = \OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
1332
			$maxDate->add(new \DateInterval('P' . $maxDays . 'D'));
1333
			if ($date > $maxDate) {
1334
				$warning = 'Cannot set expiration date. Shares cannot expire later than ' . $maxDays . ' after they have been shared';
1335
				$warning_t = $l->t('Cannot set expiration date. Shares cannot expire later than %s after they have been shared', [$maxDays]);
1336
				\OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN);
1337
				throw new \Exception($warning_t);
1338
			}
1339
		}
1340
1341 View Code Duplication
		if ($date < $today) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1342
			$message = 'Cannot set expiration date. Expiration date is in the past';
1343
			$message_t = $l->t('Cannot set expiration date. Expiration date is in the past');
1344
			\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::WARN);
1345
			throw new \Exception($message_t);
1346
		}
1347
1348
		return $date;
1349
	}
1350
1351
	/**
1352
	 * Set expiration date for a share
1353
	 * @param string $itemType
1354
	 * @param string $itemSource
1355
	 * @param string $date expiration date
1356
	 * @param int $shareTime timestamp from when the file was shared
1357
	 * @return boolean
1358
	 * @throws \Exception when the expire date is not set, in the past or further in the future then the enforced date
1359
	 */
1360
	public static function setExpirationDate($itemType, $itemSource, $date, $shareTime = null) {
1361
		$user = \OC_User::getUser();
1362
		$l = \OC::$server->getL10N('lib');
1363
1364
		if ($date == '') {
1365 View Code Duplication
			if (\OCP\Util::isDefaultExpireDateEnforced()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1366
				$warning = 'Cannot clear expiration date. Shares are required to have an expiration date.';
1367
				$warning_t = $l->t('Cannot clear expiration date. Shares are required to have an expiration date.');
1368
				\OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN);
1369
				throw new \Exception($warning_t);
1370
			} else {
1371
				$date = null;
1372
			}
1373
		} else {
1374
			$date = self::validateExpireDate($date, $shareTime, $itemType, $itemSource);
1375
		}
1376
		$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `item_type` = ? AND `item_source` = ?  AND `uid_owner` = ? AND `share_type` = ?');
1377
		$query->bindValue(1, $date, 'datetime');
1378
		$query->bindValue(2, $itemType);
1379
		$query->bindValue(3, $itemSource);
1380
		$query->bindValue(4, $user);
1381
		$query->bindValue(5, \OCP\Share::SHARE_TYPE_LINK);
1382
1383
		$query->execute();
1384
1385
		\OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [
1386
			'itemType' => $itemType,
1387
			'itemSource' => $itemSource,
1388
			'date' => $date,
1389
			'uidOwner' => $user
1390
		]);
1391
1392
		return true;
1393
	}
1394
1395
	/**
1396
	 * Retrieve the owner of a connection
1397
	 *
1398
	 * @param IDBConnection $connection
1399
	 * @param int $shareId
1400
	 * @throws \Exception
1401
	 * @return string uid of share owner
1402
	 */
1403
	private static function getShareOwner(IDBConnection $connection, $shareId) {
1404
		$qb = $connection->getQueryBuilder();
1405
1406
		$qb->select('uid_owner')
1407
			->from('share')
1408
			->where($qb->expr()->eq('id', $qb->createParameter('shareId')))
1409
			->setParameter(':shareId', $shareId);
1410
		$result = $qb->execute();
1411
		$result = $result->fetch();
1412
1413
		if (empty($result)) {
1414
			throw new \Exception('Share not found');
1415
		}
1416
1417
		return $result['uid_owner'];
1418
	}
1419
1420
	/**
1421
	 * Set password for a public link share
1422
	 *
1423
	 * @param IUserSession $userSession
1424
	 * @param IDBConnection $connection
1425
	 * @param IConfig $config
1426
	 * @param int $shareId
1427
	 * @param string $password
1428
	 * @throws \Exception
1429
	 * @return boolean
1430
	 */
1431
	public static function setPassword(IUserSession $userSession,
1432
	                                   IDBConnection $connection,
1433
	                                   IConfig $config,
1434
	                                   $shareId, $password) {
1435
		$user = $userSession->getUser();
1436
		if (is_null($user)) {
1437
			throw new \Exception("User not logged in");
1438
		}
1439
1440
		$uid = self::getShareOwner($connection, $shareId);
1441
1442
		if ($uid !== $user->getUID()) {
1443
			throw new \Exception('Cannot update share of a different user');
1444
		}
1445
1446
		if ($password === '') {
1447
			$password = null;
1448
		}
1449
1450
		//If passwords are enforced the password can't be null
1451
		if (self::enforcePassword($config) && is_null($password)) {
1452
			throw new \Exception('Cannot remove password');
1453
		}
1454
1455
		self::verifyPassword($password);
1456
1457
		$qb = $connection->getQueryBuilder();
1458
		$qb->update('share')
1459
			->set('share_with', $qb->createParameter('pass'))
1460
			->where($qb->expr()->eq('id', $qb->createParameter('shareId')))
1461
			->setParameter(':pass', is_null($password) ? null : \OC::$server->getHasher()->hash($password))
1462
			->setParameter(':shareId', $shareId);
1463
1464
		$qb->execute();
1465
1466
		return true;
1467
	}
1468
1469
	/**
1470
	 * Checks whether a share has expired, calls unshareItem() if yes.
1471
	 * @param array $item Share data (usually database row)
1472
	 * @return boolean True if item was expired, false otherwise.
1473
	 */
1474
	protected static function expireItem(array $item) {
1475
1476
		$result = false;
1477
1478
		// only use default expiration date for link shares
1479
		if ((int) $item['share_type'] === self::SHARE_TYPE_LINK) {
1480
1481
			// calculate expiration date
1482
			if (!empty($item['expiration'])) {
1483
				$userDefinedExpire = new \DateTime($item['expiration']);
1484
				$expires = $userDefinedExpire->getTimestamp();
1485
			} else {
1486
				$expires = null;
1487
			}
1488
1489
1490
			// get default expiration settings
1491
			$defaultSettings = Helper::getDefaultExpireSetting();
1492
			$expires = Helper::calculateExpireDate($defaultSettings, $item['stime'], $expires);
1493
1494
1495
			if (is_int($expires)) {
1496
				$now = time();
1497
				if ($now > $expires) {
1498
					self::unshareItem($item);
1499
					$result = true;
1500
				}
1501
			}
1502
		}
1503
		return $result;
1504
	}
1505
1506
	/**
1507
	 * Unshares a share given a share data array
1508
	 * @param array $item Share data (usually database row)
1509
	 * @param int $newParent parent ID
1510
	 * @return null
1511
	 */
1512
	protected static function unshareItem(array $item, $newParent = null) {
1513
1514
		$shareType = (int)$item['share_type'];
1515
		$shareWith = null;
1516
		if ($shareType !== \OCP\Share::SHARE_TYPE_LINK) {
1517
			$shareWith = $item['share_with'];
1518
		}
1519
1520
		// Pass all the vars we have for now, they may be useful
1521
		$hookParams = [
1522
			'id'            => $item['id'],
1523
			'itemType'      => $item['item_type'],
1524
			'itemSource'    => $item['item_source'],
1525
			'shareType'     => $shareType,
1526
			'shareWith'     => $shareWith,
1527
			'itemParent'    => $item['parent'],
1528
			'uidOwner'      => $item['uid_owner'],
1529
		];
1530
		if($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
1531
			$hookParams['fileSource'] = $item['file_source'];
1532
			$hookParams['fileTarget'] = $item['file_target'];
1533
		}
1534
1535
		\OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams);
1536
		$deletedShares = Helper::delete($item['id'], false, null, $newParent);
1537
		$deletedShares[] = $hookParams;
1538
		$hookParams['deletedShares'] = $deletedShares;
1539
		\OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams);
1540
		if ((int)$item['share_type'] === \OCP\Share::SHARE_TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
1541
			list(, $remote) = Helper::splitUserRemote($item['share_with']);
1542
			self::sendRemoteUnshare($remote, $item['id'], $item['token']);
1543
		}
1544
	}
1545
1546
	/**
1547
	 * Get the backend class for the specified item type
1548
	 * @param string $itemType
1549
	 * @throws \Exception
1550
	 * @return \OCP\Share_Backend
1551
	 */
1552
	public static function getBackend($itemType) {
1553
		$l = \OC::$server->getL10N('lib');
1554
		if (isset(self::$backends[$itemType])) {
1555
			return self::$backends[$itemType];
1556
		} else if (isset(self::$backendTypes[$itemType]['class'])) {
1557
			$class = self::$backendTypes[$itemType]['class'];
1558
			if (class_exists($class)) {
1559
				self::$backends[$itemType] = new $class;
1560 View Code Duplication
				if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1561
					$message = 'Sharing backend %s must implement the interface OCP\Share_Backend';
1562
					$message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', [$class]);
1563
					\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), \OCP\Util::ERROR);
1564
					throw new \Exception($message_t);
1565
				}
1566
				return self::$backends[$itemType];
1567 View Code Duplication
			} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1568
				$message = 'Sharing backend %s not found';
1569
				$message_t = $l->t('Sharing backend %s not found', [$class]);
1570
				\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), \OCP\Util::ERROR);
1571
				throw new \Exception($message_t);
1572
			}
1573
		}
1574
		$message = 'Sharing backend for %s not found';
1575
		$message_t = $l->t('Sharing backend for %s not found', [$itemType]);
1576
		\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemType), \OCP\Util::ERROR);
1577
		throw new \Exception($message_t);
1578
	}
1579
1580
	/**
1581
	 * Check if resharing is allowed
1582
	 * @return boolean true if allowed or false
1583
	 *
1584
	 * Resharing is allowed by default if not configured
1585
	 */
1586
	public static function isResharingAllowed() {
1587
		if (!isset(self::$isResharingAllowed)) {
1588
			if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') {
1589
				self::$isResharingAllowed = true;
1590
			} else {
1591
				self::$isResharingAllowed = false;
1592
			}
1593
		}
1594
		return self::$isResharingAllowed;
1595
	}
1596
1597
	/**
1598
	 * Get a list of collection item types for the specified item type
1599
	 * @param string $itemType
1600
	 * @return array
1601
	 */
1602
	private static function getCollectionItemTypes($itemType) {
1603
		$collectionTypes = [$itemType];
1604
		foreach (self::$backendTypes as $type => $backend) {
1605
			if (in_array($backend['collectionOf'], $collectionTypes)) {
1606
				$collectionTypes[] = $type;
1607
			}
1608
		}
1609
		// TODO Add option for collections to be collection of themselves, only 'folder' does it now...
1610
		if (isset(self::$backendTypes[$itemType]) && (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder')) {
1611
			unset($collectionTypes[0]);
1612
		}
1613
		// Return array if collections were found or the item type is a
1614
		// collection itself - collections can be inside collections
1615
		if (count($collectionTypes) > 0) {
1616
			return $collectionTypes;
1617
		}
1618
		return false;
1619
	}
1620
1621
	/**
1622
	 * Get the owners of items shared with a user.
1623
	 *
1624
	 * @param string $user The user the items are shared with.
1625
	 * @param string $type The type of the items shared with the user.
1626
	 * @param boolean $includeCollections Include collection item types (optional)
1627
	 * @param boolean $includeOwner include owner in the list of users the item is shared with (optional)
1628
	 * @return array
1629
	 */
1630
	public static function getSharedItemsOwners($user, $type, $includeCollections = false, $includeOwner = false) {
1631
		// First, we find out if $type is part of a collection (and if that collection is part of
1632
		// another one and so on).
1633
		$collectionTypes = [];
1634
		if (!$includeCollections || !$collectionTypes = self::getCollectionItemTypes($type)) {
1635
			$collectionTypes[] = $type;
1636
		}
1637
1638
		// Of these collection types, along with our original $type, we make a
1639
		// list of the ones for which a sharing backend has been registered.
1640
		// FIXME: Ideally, we wouldn't need to nest getItemsSharedWith in this loop but just call it
1641
		// with its $includeCollections parameter set to true. Unfortunately, this fails currently.
1642
		$allMaybeSharedItems = [];
1643
		foreach ($collectionTypes as $collectionType) {
0 ignored issues
show
Bug introduced by
The expression $collectionTypes of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1644
			if (isset(self::$backends[$collectionType])) {
1645
				$allMaybeSharedItems[$collectionType] = self::getItemsSharedWithUser(
1646
					$collectionType,
1647
					$user,
1648
					self::FORMAT_NONE
1649
				);
1650
			}
1651
		}
1652
1653
		$owners = [];
1654
		if ($includeOwner) {
1655
			$owners[] = $user;
1656
		}
1657
1658
		// We take a look at all shared items of the given $type (or of the collections it is part of)
1659
		// and find out their owners. Then, we gather the tags for the original $type from all owners,
1660
		// and return them as elements of a list that look like "Tag (owner)".
1661
		foreach ($allMaybeSharedItems as $collectionType => $maybeSharedItems) {
1662
			foreach ($maybeSharedItems as $sharedItem) {
1663
				if (isset($sharedItem['id'])) { //workaround for https://github.com/owncloud/core/issues/2814
1664
					$owners[] = $sharedItem['uid_owner'];
1665
				}
1666
			}
1667
		}
1668
1669
		return $owners;
1670
	}
1671
1672
	/**
1673
	 * Get shared items from the database
1674
	 * @param string $itemType
1675
	 * @param string $item Item source or target (optional)
1676
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique
1677
	 * @param string $shareWith User or group the item is being shared with
1678
	 * @param string $uidOwner User that is the owner of shared items (optional)
1679
	 * @param int $format Format to convert items to with formatItems() (optional)
1680
	 * @param mixed $parameters to pass to formatItems() (optional)
1681
	 * @param int $limit Number of items to return, -1 to return all matches (optional)
1682
	 * @param boolean $includeCollections Include collection item types (optional)
1683
	 * @param boolean $itemShareWithBySource (optional)
1684
	 * @param boolean $checkExpireDate
1685
	 * @return array
1686
	 *
1687
	 * See public functions getItem(s)... for parameter usage
1688
	 *
1689
	 */
1690
	public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null,
1691
									$uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1,
1692
									$includeCollections = false, $itemShareWithBySource = false, $checkExpireDate  = true) {
1693
		if (!self::isEnabled()) {
1694
			return [];
1695
		}
1696
		$backend = self::getBackend($itemType);
1697
		$collectionTypes = false;
1698
		// Get filesystem root to add it to the file target and remove from the
1699
		// file source, match file_source with the file cache
1700
		if ($itemType == 'file' || $itemType == 'folder') {
1701
			if(!is_null($uidOwner)) {
1702
				$root = \OC\Files\Filesystem::getRoot();
1703
			} else {
1704
				$root = '';
1705
			}
1706
			$where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
1707
			if (!isset($item)) {
1708
				$where .= ' AND `file_target` IS NOT NULL ';
1709
			}
1710
			$where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
1711
			$fileDependent = true;
1712
			$queryArgs = [];
1713
		} else {
1714
			$fileDependent = false;
1715
			$root = '';
1716
			$collectionTypes = self::getCollectionItemTypes($itemType);
1717
			if ($includeCollections && !isset($item) && $collectionTypes) {
1718
				// If includeCollections is true, find collections of this item type, e.g. a music album contains songs
1719
				if (!in_array($itemType, $collectionTypes)) {
1720
					$itemTypes = array_merge([$itemType], $collectionTypes);
1721
				} else {
1722
					$itemTypes = $collectionTypes;
1723
				}
1724
				$placeholders = join(',', array_fill(0, count($itemTypes), '?'));
1725
				$where = ' WHERE `item_type` IN ('.$placeholders.'))';
1726
				$queryArgs = $itemTypes;
1727
			} else {
1728
				$where = ' WHERE `item_type` = ?';
1729
				$queryArgs = [$itemType];
1730
			}
1731
		}
1732
		if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
1733
			$where .= ' AND `share_type` != ?';
1734
			$queryArgs[] = self::SHARE_TYPE_LINK;
1735
		}
1736
		if (isset($shareType)) {
1737
			// Include all user and group items
1738
			if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
1739
				$where .= ' AND ((`share_type` in (?, ?) AND `share_with` = ?) ';
1740
				$queryArgs[] = self::SHARE_TYPE_USER;
1741
				$queryArgs[] = self::$shareTypeGroupUserUnique;
1742
				$queryArgs[] = $shareWith;
1743
				$groups = self::getGroupsForUser($shareWith);
1744
				if (!empty($groups)) {
1745
					$placeholders = join(',', array_fill(0, count($groups), '?'));
1746
					$where .= ' OR (`share_type` = ? AND `share_with` IN ('.$placeholders.')) ';
1747
					$queryArgs[] = self::SHARE_TYPE_GROUP;
1748
					$queryArgs = array_merge($queryArgs, $groups);
1749
				}
1750
				$where .= ')';
1751
				// Don't include own group shares
1752
				$where .= ' AND `uid_owner` != ?';
1753
				$queryArgs[] = $shareWith;
1754
			} else {
1755
				$where .= ' AND `share_type` = ?';
1756
				$queryArgs[] = $shareType;
1757
				if (isset($shareWith)) {
1758
					$where .= ' AND `share_with` = ?';
1759
					$queryArgs[] = $shareWith;
1760
				}
1761
			}
1762
		}
1763
		if (isset($uidOwner)) {
1764
			$where .= ' AND `uid_owner` = ?';
1765
			$queryArgs[] = $uidOwner;
1766
			if (!isset($shareType)) {
1767
				// Prevent unique user targets for group shares from being selected
1768
				$where .= ' AND `share_type` != ?';
1769
				$queryArgs[] = self::$shareTypeGroupUserUnique;
1770
			}
1771
			if ($fileDependent) {
1772
				$column = 'file_source';
1773
			} else {
1774
				$column = 'item_source';
1775
			}
1776
		} else {
1777
			if ($fileDependent) {
1778
				$column = 'file_target';
1779
			} else {
1780
				$column = 'item_target';
1781
			}
1782
		}
1783
		if (isset($item)) {
1784
			$collectionTypes = self::getCollectionItemTypes($itemType);
1785
			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
1786
				$where .= ' AND (';
1787
			} else {
1788
				$where .= ' AND';
1789
			}
1790
			// If looking for own shared items, check item_source else check item_target
1791
			if (isset($uidOwner) || $itemShareWithBySource) {
1792
				// If item type is a file, file source needs to be checked in case the item was converted
1793
				if ($fileDependent) {
1794
					$where .= ' `file_source` = ?';
1795
					$column = 'file_source';
1796
				} else {
1797
					$where .= ' `item_source` = ?';
1798
					$column = 'item_source';
1799
				}
1800
			} else {
1801
				if ($fileDependent) {
1802
					$where .= ' `file_target` = ?';
1803
					$item = \OC\Files\Filesystem::normalizePath($item);
1804
				} else {
1805
					$where .= ' `item_target` = ?';
1806
				}
1807
			}
1808
			$queryArgs[] = $item;
1809
			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
1810
				$placeholders = join(',', array_fill(0, count($collectionTypes), '?'));
1811
				$where .= ' OR `item_type` IN ('.$placeholders.'))';
1812
				$queryArgs = array_merge($queryArgs, $collectionTypes);
1813
			}
1814
		}
1815
1816
		if ($shareType == self::$shareTypeUserAndGroups && $limit === 1) {
1817
			// Make sure the unique user target is returned if it exists,
1818
			// unique targets should follow the group share in the database
1819
			// If the limit is not 1, the filtering can be done later
1820
			$where .= ' ORDER BY `*PREFIX*share`.`id` DESC';
1821
		} else {
1822
			$where .= ' ORDER BY `*PREFIX*share`.`id` ASC';
1823
		}
1824
1825
		if ($limit != -1 && !$includeCollections) {
1826
			// The limit must be at least 3, because filtering needs to be done
1827
			if ($limit < 3) {
1828
				$queryLimit = 3;
1829
			} else {
1830
				$queryLimit = $limit;
1831
			}
1832
		} else {
1833
			$queryLimit = null;
1834
		}
1835
		$select = self::createSelectStatement($format, $fileDependent, $uidOwner);
1836
		$root = strlen($root);
1837
		$query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit);
1838
		$result = $query->execute($queryArgs);
1839 View Code Duplication
		if ($result === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1840
			\OCP\Util::writeLog('OCP\Share',
1841
				\OC_DB::getErrorMessage() . ', select=' . $select . ' where=',
1842
				\OCP\Util::ERROR);
1843
		}
1844
		$items = [];
1845
		$targets = [];
1846
		$switchedItems = [];
1847
		$mounts = [];
1848
		while ($row = $result->fetchRow()) {
1849
			self::transformDBResults($row);
1850
			// Filter out duplicate group shares for users with unique targets
1851
			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
1852
				continue;
1853
			}
1854
			if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) {
1855
				$row['share_type'] = self::SHARE_TYPE_GROUP;
1856
				$row['unique_name'] = true; // remember that we use a unique name for this user
1857
				$row['share_with'] = $items[$row['parent']]['share_with'];
1858
				// if the group share was unshared from the user we keep the permission, otherwise
1859
				// we take the permission from the parent because this is always the up-to-date
1860
				// permission for the group share
1861
				if ($row['permissions'] > 0) {
1862
					$row['permissions'] = $items[$row['parent']]['permissions'];
1863
				}
1864
				// Remove the parent group share
1865
				unset($items[$row['parent']]);
1866
				if ($row['permissions'] == 0) {
1867
					continue;
1868
				}
1869
			} else if (!isset($uidOwner)) {
1870
				// Check if the same target already exists
1871
				if (isset($targets[$row['id']])) {
1872
					// Check if the same owner shared with the user twice
1873
					// through a group and user share - this is allowed
1874
					$id = $targets[$row['id']];
1875
					if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
1876
						// Switch to group share type to ensure resharing conditions aren't bypassed
1877
						if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) {
1878
							$items[$id]['share_type'] = self::SHARE_TYPE_GROUP;
1879
							$items[$id]['share_with'] = $row['share_with'];
1880
						}
1881
						// Switch ids if sharing permission is granted on only
1882
						// one share to ensure correct parent is used if resharing
1883
						if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
1884
							&& (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
1885
							$items[$row['id']] = $items[$id];
1886
							$switchedItems[$id] = $row['id'];
1887
							unset($items[$id]);
1888
							$id = $row['id'];
1889
						}
1890
						$items[$id]['permissions'] |= (int)$row['permissions'];
1891
1892
					}
1893
					continue;
1894
				} elseif (!empty($row['parent'])) {
1895
					$targets[$row['parent']] = $row['id'];
1896
				}
1897
			}
1898
			// Remove root from file source paths if retrieving own shared items
1899
			if (isset($uidOwner) && isset($row['path'])) {
1900
				if (isset($row['parent'])) {
1901
					$query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?');
1902
					$parentResult = $query->execute([$row['parent']]);
1903
					if ($result === false) {
1904
						\OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: ' .
1905
							\OC_DB::getErrorMessage() . ', select=' . $select . ' where=' . $where,
1906
							\OCP\Util::ERROR);
1907
					} else {
1908
						$parentRow = $parentResult->fetchRow();
1909
						$tmpPath = $parentRow['file_target'];
1910
						// find the right position where the row path continues from the target path
1911
						$pos = strrpos($row['path'], $parentRow['file_target']);
1912
						$subPath = substr($row['path'], $pos);
1913
						$splitPath = explode('/', $subPath);
1914
						foreach (array_slice($splitPath, 2) as $pathPart) {
1915
							$tmpPath = $tmpPath . '/' . $pathPart;
1916
						}
1917
						$row['path'] = $tmpPath;
1918
					}
1919
				} else {
1920
					if (!isset($mounts[$row['storage']])) {
1921
						$mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']);
1922
						if (is_array($mountPoints) && !empty($mountPoints)) {
1923
							$mounts[$row['storage']] = current($mountPoints);
1924
						}
1925
					}
1926
					if (!empty($mounts[$row['storage']])) {
1927
						$path = $mounts[$row['storage']]->getMountPoint().$row['path'];
1928
						$relPath = substr($path, $root); // path relative to data/user
1929
						$row['path'] = rtrim($relPath, '/');
1930
					}
1931
				}
1932
			}
1933
1934
			if($checkExpireDate) {
1935
				if (self::expireItem($row)) {
1936
					continue;
1937
				}
1938
			}
1939
			// Check if resharing is allowed, if not remove share permission
1940
			if (isset($row['permissions']) && (!self::isResharingAllowed() | \OCP\Util::isSharingDisabledForUser())) {
1941
				$row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE;
1942
			}
1943
			// Add display names to result
1944
			$row['share_with_displayname'] = $row['share_with'];
1945
			if ( isset($row['share_with']) && $row['share_with'] != '' &&
1946
				$row['share_type'] === self::SHARE_TYPE_USER) {
1947
				$row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']);
1948
			} else if(isset($row['share_with']) && $row['share_with'] != '' &&
1949
				$row['share_type'] === self::SHARE_TYPE_REMOTE) {
1950
				$addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD']);
1951
				foreach ($addressBookEntries as $entry) {
1952
					foreach ($entry['CLOUD'] as $cloudID) {
1953
						if ($cloudID === $row['share_with']) {
1954
							$row['share_with_displayname'] = $entry['FN'];
1955
						}
1956
					}
1957
				}
1958
			}
1959
			if ( isset($row['uid_owner']) && $row['uid_owner'] != '') {
1960
				$row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']);
1961
			}
1962
1963
			if ($row['permissions'] > 0) {
1964
				$items[$row['id']] = $row;
1965
			}
1966
1967
		}
1968
1969
		// group items if we are looking for items shared with the current user
1970
		if (isset($shareWith) && $shareWith === \OCP\User::getUser()) {
1971
			$items = self::groupItems($items, $itemType);
1972
		}
1973
1974
		if (!empty($items)) {
1975
			$collectionItems = [];
1976
			foreach ($items as &$row) {
1977
				// Return only the item instead of a 2-dimensional array
1978
				if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) {
1979
					if ($format == self::FORMAT_NONE) {
1980
						return $row;
1981
					} else {
1982
						break;
1983
					}
1984
				}
1985
				// Check if this is a collection of the requested item type
1986
				if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) {
1987
					if (($collectionBackend = self::getBackend($row['item_type']))
1988
						&& $collectionBackend instanceof \OCP\Share_Backend_Collection) {
1989
						// Collections can be inside collections, check if the item is a collection
1990
						if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
1991
							$collectionItems[] = $row;
1992
						} else {
1993
							$collection = [];
1994
							$collection['item_type'] = $row['item_type'];
1995
							if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
1996
								$collection['path'] = basename($row['path']);
1997
							}
1998
							$row['collection'] = $collection;
1999
							// Fetch all of the children sources
2000
							$children = $collectionBackend->getChildren($row[$column]);
0 ignored issues
show
Documentation introduced by
$row[$column] is of type array<string,?>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2001
							foreach ($children as $child) {
2002
								$childItem = $row;
2003
								$childItem['item_type'] = $itemType;
2004
								if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
2005
									$childItem['item_source'] = $child['source'];
2006
									$childItem['item_target'] = $child['target'];
2007
								}
2008
								if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
2009
									if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
2010
										$childItem['file_source'] = $child['source'];
2011
									} else { // TODO is this really needed if we already know that we use the file backend?
2012
										$meta = \OC\Files\Filesystem::getFileInfo($child['file_path']);
2013
										$childItem['file_source'] = $meta['fileid'];
2014
									}
2015
									$childItem['file_target'] =
2016
										\OC\Files\Filesystem::normalizePath($child['file_path']);
2017
								}
2018
								if (isset($item)) {
2019
									if ($childItem[$column] == $item) {
2020
										// Return only the item instead of a 2-dimensional array
2021
										if ($limit == 1) {
2022
											if ($format == self::FORMAT_NONE) {
2023
												return $childItem;
2024
											} else {
2025
												// Unset the items array and break out of both loops
2026
												$items = [];
2027
												$items[] = $childItem;
2028
												break 2;
2029
											}
2030
										} else {
2031
											$collectionItems[] = $childItem;
2032
										}
2033
									}
2034
								} else {
2035
									$collectionItems[] = $childItem;
2036
								}
2037
							}
2038
						}
2039
					}
2040
					// Remove collection item
2041
					$toRemove = $row['id'];
2042
					if (array_key_exists($toRemove, $switchedItems)) {
2043
						$toRemove = $switchedItems[$toRemove];
2044
					}
2045
					unset($items[$toRemove]);
2046
				} elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
2047
					// FIXME: Thats a dirty hack to improve file sharing performance,
2048
					// see github issue #10588 for more details
2049
					// Need to find a solution which works for all back-ends
2050
					$collectionBackend = self::getBackend($row['item_type']);
2051
					$sharedParents = $collectionBackend->getParents($row['item_source']);
2052
					foreach ($sharedParents as $parent) {
2053
						$collectionItems[] = $parent;
2054
					}
2055
				}
2056
			}
2057
			if (!empty($collectionItems)) {
2058
				$collectionItems = array_unique($collectionItems, SORT_REGULAR);
2059
				$items = array_merge($items, $collectionItems);
2060
			}
2061
2062
			// filter out invalid items, these can appear when subshare entries exist
2063
			// for a group in which the requested user isn't a member any more
2064
			$items = array_filter($items, function($item) {
2065
				return $item['share_type'] !== self::$shareTypeGroupUserUnique;
2066
			});
2067
2068
			return self::formatResult($items, $column, $backend, $format, $parameters);
2069
		} elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) {
2070
			// FIXME: Thats a dirty hack to improve file sharing performance,
2071
			// see github issue #10588 for more details
2072
			// Need to find a solution which works for all back-ends
2073
			$collectionItems = [];
2074
			$collectionBackend = self::getBackend('folder');
2075
			$sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner);
2076
			foreach ($sharedParents as $parent) {
2077
				$collectionItems[] = $parent;
2078
			}
2079
			if ($limit === 1) {
2080
				return reset($collectionItems);
2081
			}
2082
			return self::formatResult($collectionItems, $column, $backend, $format, $parameters);
2083
		}
2084
2085
		return [];
2086
	}
2087
2088
	/**
2089
	 * group items with link to the same source
2090
	 *
2091
	 * @param array $items
2092
	 * @param string $itemType
2093
	 * @return array of grouped items
2094
	 */
2095
	protected static function groupItems($items, $itemType) {
2096
2097
		$fileSharing = ($itemType === 'file' || $itemType === 'folder') ? true : false;
2098
2099
		$result = [];
2100
2101
		foreach ($items as $item) {
2102
			$grouped = false;
2103
			foreach ($result as $key => $r) {
2104
				// for file/folder shares we need to compare file_source, otherwise we compare item_source
2105
				// only group shares if they already point to the same target, otherwise the file where shared
2106
				// before grouping of shares was added. In this case we don't group them toi avoid confusions
2107
				if (( $fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
2108
					(!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
2109
					// add the first item to the list of grouped shares
2110
					if (!isset($result[$key]['grouped'])) {
2111
						$result[$key]['grouped'][] = $result[$key];
2112
					}
2113
					$result[$key]['permissions'] = (int) $item['permissions'] | (int) $r['permissions'];
2114
					$result[$key]['grouped'][] = $item;
2115
					$grouped = true;
2116
					break;
2117
				}
2118
			}
2119
2120
			if (!$grouped) {
2121
				$result[] = $item;
2122
			}
2123
2124
		}
2125
2126
		return $result;
2127
	}
2128
2129
	/**
2130
	 * Put shared item into the database
2131
	 * @param string $itemType Item type
2132
	 * @param string $itemSource Item source
2133
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
2134
	 * @param string $shareWith User or group the item is being shared with
2135
	 * @param string $uidOwner User that is the owner of shared item
2136
	 * @param int $permissions CRUDS permissions
2137
	 * @param boolean|array $parentFolder Parent folder target (optional)
2138
	 * @param string $token (optional)
2139
	 * @param string $itemSourceName name of the source item (optional)
2140
	 * @param \DateTime $expirationDate (optional)
2141
	 * @throws \Exception
2142
	 * @return mixed id of the new share or false
2143
	 */
2144
	private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
2145
								$permissions, $parentFolder = null, $token = null, $itemSourceName = null, \DateTime $expirationDate = null) {
2146
2147
		$queriesToExecute = [];
2148
		$suggestedItemTarget = null;
2149
		$groupFileTarget = $fileTarget = $suggestedFileTarget = $filePath = '';
2150
		$groupItemTarget = $itemTarget = $fileSource = $parent = 0;
2151
2152
		$result = self::checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate);
2153
		if(!empty($result)) {
2154
			$parent = $result['parent'];
2155
			$itemSource = $result['itemSource'];
2156
			$fileSource = $result['fileSource'];
2157
			$suggestedItemTarget = $result['suggestedItemTarget'];
2158
			$suggestedFileTarget = $result['suggestedFileTarget'];
2159
			$filePath = $result['filePath'];
2160
		}
2161
2162
		$isGroupShare = false;
2163
		if ($shareType == self::SHARE_TYPE_GROUP) {
2164
			$isGroupShare = true;
2165
			if (isset($shareWith['users'])) {
2166
				$users = $shareWith['users'];
2167
			} else {
2168
				$users = self::usersInGroup($shareWith['group']);
2169
			}
2170
			// remove current user from list
2171
			if (in_array(\OCP\User::getUser(), $users)) {
2172
				unset($users[array_search(\OCP\User::getUser(), $users)]);
2173
			}
2174
			$groupItemTarget = Helper::generateTarget($itemType, $itemSource,
2175
				$shareType, $shareWith['group'], $uidOwner, $suggestedItemTarget);
2176
			$groupFileTarget = Helper::generateTarget($itemType, $itemSource,
2177
				$shareType, $shareWith['group'], $uidOwner, $filePath);
2178
2179
			// add group share to table and remember the id as parent
2180
			$queriesToExecute['groupShare'] = [
2181
				'itemType'			=> $itemType,
2182
				'itemSource'		=> $itemSource,
2183
				'itemTarget'		=> $groupItemTarget,
2184
				'shareType'			=> $shareType,
2185
				'shareWith'			=> $shareWith['group'],
2186
				'uidOwner'			=> $uidOwner,
2187
				'permissions'		=> $permissions,
2188
				'shareTime'			=> time(),
2189
				'fileSource'		=> $fileSource,
2190
				'fileTarget'		=> $groupFileTarget,
2191
				'token'				=> $token,
2192
				'parent'			=> $parent,
2193
				'expiration'		=> $expirationDate,
2194
			];
2195
2196
		} else {
2197
			$users = [$shareWith];
2198
			$itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
2199
				$suggestedItemTarget);
2200
		}
2201
2202
		$run = true;
2203
		$error = '';
2204
		$preHookData = [
2205
			'itemType' => $itemType,
2206
			'itemSource' => $itemSource,
2207
			'shareType' => $shareType,
2208
			'uidOwner' => $uidOwner,
2209
			'permissions' => $permissions,
2210
			'fileSource' => $fileSource,
2211
			'expiration' => $expirationDate,
2212
			'token' => $token,
2213
			'run' => &$run,
2214
			'error' => &$error
2215
		];
2216
2217
		$preHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget;
2218
		$preHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith;
2219
2220
		\OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
2221
2222
		if ($run === false) {
2223
			throw new \Exception($error);
2224
		}
2225
2226
		foreach ($users as $user) {
0 ignored issues
show
Bug introduced by
The expression $users of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
2227
			$sourceId = ($itemType === 'file' || $itemType === 'folder') ? $fileSource : $itemSource;
2228
			$sourceExists = self::getItemSharedWithBySource($itemType, $sourceId, self::FORMAT_NONE, null, true, $user);
2229
2230
			$userShareType = ($isGroupShare) ? self::$shareTypeGroupUserUnique : $shareType;
2231
2232
			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...
2233
				$fileTarget = $sourceExists['file_target'];
2234
				$itemTarget = $sourceExists['item_target'];
2235
2236
				// for group shares we don't need a additional entry if the target is the same
2237
				if($isGroupShare && $groupItemTarget === $itemTarget) {
2238
					continue;
2239
				}
2240
2241
			} elseif(!$sourceExists && !$isGroupShare)  {
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...
2242
2243
				$itemTarget = Helper::generateTarget($itemType, $itemSource, $userShareType, $user,
2244
					$uidOwner, $suggestedItemTarget, $parent);
2245
				if (isset($fileSource)) {
2246
					if ($parentFolder) {
2247
						if ($parentFolder === true) {
2248
							$fileTarget = Helper::generateTarget('file', $filePath, $userShareType, $user,
2249
								$uidOwner, $suggestedFileTarget, $parent);
2250
							if ($fileTarget != $groupFileTarget) {
2251
								$parentFolders[$user]['folder'] = $fileTarget;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$parentFolders was never initialized. Although not strictly required by PHP, it is generally a good practice to add $parentFolders = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
2252
							}
2253
						} else if (isset($parentFolder[$user])) {
2254
							$fileTarget = $parentFolder[$user]['folder'].$itemSource;
2255
							$parent = $parentFolder[$user]['id'];
2256
						}
2257
					} else {
2258
						$fileTarget = Helper::generateTarget('file', $filePath, $userShareType,
2259
							$user, $uidOwner, $suggestedFileTarget, $parent);
2260
					}
2261
				} else {
2262
					$fileTarget = null;
2263
				}
2264
2265
			} else {
2266
2267
				// group share which doesn't exists until now, check if we need a unique target for this user
2268
2269
				$itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $user,
2270
					$uidOwner, $suggestedItemTarget, $parent);
2271
2272
				// do we also need a file target
2273
				if (isset($fileSource)) {
2274
					$fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $user,
2275
						$uidOwner, $suggestedFileTarget, $parent);
2276
				} else {
2277
					$fileTarget = null;
2278
				}
2279
2280
				if (($itemTarget === $groupItemTarget) &&
2281
					(!isset($fileSource) || $fileTarget === $groupFileTarget)) {
2282
					continue;
2283
				}
2284
			}
2285
2286
			$queriesToExecute[] = [
2287
				'itemType'			=> $itemType,
2288
				'itemSource'		=> $itemSource,
2289
				'itemTarget'		=> $itemTarget,
2290
				'shareType'			=> $userShareType,
2291
				'shareWith'			=> $user,
2292
				'uidOwner'			=> $uidOwner,
2293
				'permissions'		=> $permissions,
2294
				'shareTime'			=> time(),
2295
				'fileSource'		=> $fileSource,
2296
				'fileTarget'		=> $fileTarget,
2297
				'token'				=> $token,
2298
				'parent'			=> $parent,
2299
				'expiration'		=> $expirationDate,
2300
			];
2301
2302
		}
2303
2304
		$id = false;
2305
		if ($isGroupShare) {
2306
			$id = self::insertShare($queriesToExecute['groupShare']);
2307
			// Save this id, any extra rows for this group share will need to reference it
2308
			$parent = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
2309
			unset($queriesToExecute['groupShare']);
2310
		}
2311
2312
		foreach ($queriesToExecute as $shareQuery) {
2313
			$shareQuery['parent'] = $parent;
2314
			$id = self::insertShare($shareQuery);
2315
		}
2316
2317
		$postHookData = [
2318
			'itemType' => $itemType,
2319
			'itemSource' => $itemSource,
2320
			'parent' => $parent,
2321
			'shareType' => $shareType,
2322
			'uidOwner' => $uidOwner,
2323
			'permissions' => $permissions,
2324
			'fileSource' => $fileSource,
2325
			'id' => $parent,
2326
			'token' => $token,
2327
			'expirationDate' => $expirationDate,
2328
		];
2329
2330
		$postHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith;
2331
		$postHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget;
2332
		$postHookData['fileTarget'] = ($isGroupShare) ? $groupFileTarget : $fileTarget;
2333
2334
		\OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
2335
2336
2337
		return $id ? $id : false;
2338
	}
2339
2340
	/**
2341
	 * @param string $itemType
2342
	 * @param string $itemSource
2343
	 * @param int $shareType
2344
	 * @param string $shareWith
2345
	 * @param string $uidOwner
2346
	 * @param int $permissions
2347
	 * @param string|null $itemSourceName
2348
	 * @param null|\DateTime $expirationDate
2349
	 */
2350
	private static function checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate) {
2351
		$backend = self::getBackend($itemType);
2352
2353
		$l = \OC::$server->getL10N('lib');
2354
		$result = [];
2355
2356
		$column = ($itemType === 'file' || $itemType === 'folder') ? 'file_source' : 'item_source';
2357
2358
		$checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true);
2359
		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...
2360
			// Check if attempting to share back to owner
2361
			if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) {
2362
				$message = 'Sharing %s failed, because the user %s is the original sharer';
2363
				$message_t = $l->t('Sharing failed, because the user %s is the original sharer', [$shareWith]);
2364
2365
				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
2366
				throw new \Exception($message_t);
2367
			}
2368
		}
2369
2370
		if ($checkReshare && $checkReshare['uid_owner'] !== \OC_User::getUser()) {
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...
2371
			// Check if share permissions is granted
2372
			if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
2373
				if (~(int)$checkReshare['permissions'] & $permissions) {
2374
					$message = 'Sharing %s failed, because the permissions exceed permissions granted to %s';
2375
					$message_t = $l->t('Sharing %s failed, because the permissions exceed permissions granted to %s', [$itemSourceName, $uidOwner]);
2376
2377
					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $uidOwner), \OCP\Util::DEBUG);
2378
					throw new \Exception($message_t);
2379
				} else {
2380
					// TODO Don't check if inside folder
2381
					$result['parent'] = $checkReshare['id'];
2382
2383
					$result['expirationDate'] = $expirationDate;
2384
					// $checkReshare['expiration'] could be null and then is always less than any value
2385
					if(isset($checkReshare['expiration']) && $checkReshare['expiration'] < $expirationDate) {
2386
						$result['expirationDate'] = $checkReshare['expiration'];
2387
					}
2388
2389
					// only suggest the same name as new target if it is a reshare of the
2390
					// same file/folder and not the reshare of a child
2391
					if ($checkReshare[$column] === $itemSource) {
2392
						$result['filePath'] = $checkReshare['file_target'];
2393
						$result['itemSource'] = $checkReshare['item_source'];
2394
						$result['fileSource'] = $checkReshare['file_source'];
2395
						$result['suggestedItemTarget'] = $checkReshare['item_target'];
2396
						$result['suggestedFileTarget'] = $checkReshare['file_target'];
2397
					} else {
2398
						$result['filePath'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $backend->getFilePath($itemSource, $uidOwner) : null;
2399
						$result['suggestedItemTarget'] = null;
2400
						$result['suggestedFileTarget'] = null;
2401
						$result['itemSource'] = $itemSource;
2402
						$result['fileSource'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $itemSource : null;
2403
					}
2404
				}
2405 View Code Duplication
			} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2406
				$message = 'Sharing %s failed, because resharing is not allowed';
2407
				$message_t = $l->t('Sharing %s failed, because resharing is not allowed', [$itemSourceName]);
2408
2409
				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
2410
				throw new \Exception($message_t);
2411
			}
2412
		} else {
2413
			$result['parent'] = null;
2414
			$result['suggestedItemTarget'] = null;
2415
			$result['suggestedFileTarget'] = null;
2416
			$result['itemSource'] = $itemSource;
2417
			$result['expirationDate'] = $expirationDate;
2418 View Code Duplication
			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...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2419
				$message = 'Sharing %s failed, because the sharing backend for '
2420
					.'%s could not find its source';
2421
				$message_t = $l->t('Sharing %s failed, because the sharing backend for %s could not find its source', [$itemSource, $itemType]);
2422
				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource, $itemType), \OCP\Util::DEBUG);
2423
				throw new \Exception($message_t);
2424
			}
2425
			if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
2426
				$result['filePath'] = $backend->getFilePath($itemSource, $uidOwner);
2427
				if ($itemType == 'file' || $itemType == 'folder') {
2428
					$result['fileSource'] = $itemSource;
2429
				} else {
2430
					$meta = \OC\Files\Filesystem::getFileInfo($result['filePath']);
0 ignored issues
show
Security Bug introduced by
It seems like $result['filePath'] can also be of type false; however, OC\Files\Filesystem::getFileInfo() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
2431
					$result['fileSource'] = $meta['fileid'];
2432
				}
2433 View Code Duplication
				if ($result['fileSource'] == -1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2434
					$message = 'Sharing %s failed, because the file could not be found in the file cache';
2435
					$message_t = $l->t('Sharing %s failed, because the file could not be found in the file cache', [$itemSource]);
2436
2437
					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource), \OCP\Util::DEBUG);
2438
					throw new \Exception($message_t);
2439
				}
2440
			} else {
2441
				$result['filePath'] = null;
2442
				$result['fileSource'] = null;
2443
			}
2444
		}
2445
2446
		return $result;
2447
	}
2448
2449
	/**
2450
	 *
2451
	 * @param array $shareData
2452
	 * @return mixed false in case of a failure or the id of the new share
2453
	 */
2454
	private static function insertShare(array $shareData) {
2455
2456
		$query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` ('
2457
			.' `item_type`, `item_source`, `item_target`, `share_type`,'
2458
			.' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,'
2459
			.' `file_target`, `token`, `parent`, `expiration`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)');
2460
		$query->bindValue(1, $shareData['itemType']);
2461
		$query->bindValue(2, $shareData['itemSource']);
2462
		$query->bindValue(3, $shareData['itemTarget']);
2463
		$query->bindValue(4, $shareData['shareType']);
2464
		$query->bindValue(5, $shareData['shareWith']);
2465
		$query->bindValue(6, $shareData['uidOwner']);
2466
		$query->bindValue(7, $shareData['permissions']);
2467
		$query->bindValue(8, $shareData['shareTime']);
2468
		$query->bindValue(9, $shareData['fileSource']);
2469
		$query->bindValue(10, $shareData['fileTarget']);
2470
		$query->bindValue(11, $shareData['token']);
2471
		$query->bindValue(12, $shareData['parent']);
2472
		$query->bindValue(13, $shareData['expiration'], 'datetime');
2473
		$result = $query->execute();
2474
2475
		$id = false;
2476
		if ($result) {
2477
			$id =  \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
2478
		}
2479
2480
		return $id;
2481
2482
	}
2483
2484
	/**
2485
	 * Delete all shares with type SHARE_TYPE_LINK
2486
	 */
2487
	public static function removeAllLinkShares() {
2488
		// Delete any link shares
2489
		$query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ?');
2490
		$result = $query->execute([self::SHARE_TYPE_LINK]);
2491
		while ($item = $result->fetchRow()) {
2492
			Helper::delete($item['id']);
2493
		}
2494
	}
2495
2496
	/**
2497
	 * In case a password protected link is not yet authenticated this function will return false
2498
	 *
2499
	 * @param array $linkItem
2500
	 * @return boolean
2501
	 */
2502
	public static function checkPasswordProtectedShare(array $linkItem) {
2503
		if (!isset($linkItem['share_with'])) {
2504
			return true;
2505
		}
2506
		if (!isset($linkItem['share_type'])) {
2507
			return true;
2508
		}
2509
		if (!isset($linkItem['id'])) {
2510
			return true;
2511
		}
2512
2513
		if ($linkItem['share_type'] != \OCP\Share::SHARE_TYPE_LINK) {
2514
			return true;
2515
		}
2516
2517 View Code Duplication
		if ( \OC::$server->getSession()->exists('public_link_authenticated')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2518
			&& \OC::$server->getSession()->get('public_link_authenticated') === (string)$linkItem['id'] ) {
2519
			return true;
2520
		}
2521
2522
		return false;
2523
	}
2524
2525
	/**
2526
	 * construct select statement
2527
	 * @param int $format
2528
	 * @param boolean $fileDependent ist it a file/folder share or a generla share
2529
	 * @param string $uidOwner
2530
	 * @return string select statement
2531
	 */
2532
	private static function createSelectStatement($format, $fileDependent, $uidOwner = null) {
2533
		$select = '*';
2534
		if ($format == self::FORMAT_STATUSES) {
2535
			if ($fileDependent) {
2536
				$select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, '
2537
					. '`share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`, '
2538
					. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`, '
2539
					. '`uid_initiator`';
2540
			} else {
2541
				$select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`, `*PREFIX*share`.`permissions`';
2542
			}
2543
		} else {
2544
			if (isset($uidOwner)) {
2545
				if ($fileDependent) {
2546
					$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,'
2547
						. ' `share_type`, `share_with`, `file_source`, `file_target`, `path`, `*PREFIX*share`.`permissions`, `stime`,'
2548
						. ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`, '
2549
						. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
2550
				} else {
2551
					$select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `*PREFIX*share`.`permissions`,'
2552
						. ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`';
2553
				}
2554
			} else {
2555
				if ($fileDependent) {
2556
					if ($format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_GET_FOLDER_CONTENTS || $format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_FILE_APP_ROOT) {
2557
						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, '
2558
							. '`share_type`, `share_with`, `file_source`, `path`, `file_target`, `stime`, '
2559
							. '`*PREFIX*share`.`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
2560
							. '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `mail_send`';
2561
					} else {
2562
						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,'
2563
							. '`*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,'
2564
							. '`file_source`, `path`, `file_target`, `*PREFIX*share`.`permissions`,'
2565
						    . '`stime`, `expiration`, `token`, `storage`, `mail_send`,'
2566
							. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
2567
					}
2568
				}
2569
			}
2570
		}
2571
		return $select;
2572
	}
2573
2574
2575
	/**
2576
	 * transform db results
2577
	 * @param array $row result
2578
	 */
2579
	private static function transformDBResults(&$row) {
2580
		if (isset($row['id'])) {
2581
			$row['id'] = (int) $row['id'];
2582
		}
2583
		if (isset($row['share_type'])) {
2584
			$row['share_type'] = (int) $row['share_type'];
2585
		}
2586
		if (isset($row['parent'])) {
2587
			$row['parent'] = (int) $row['parent'];
2588
		}
2589
		if (isset($row['file_parent'])) {
2590
			$row['file_parent'] = (int) $row['file_parent'];
2591
		}
2592
		if (isset($row['file_source'])) {
2593
			$row['file_source'] = (int) $row['file_source'];
2594
		}
2595
		if (isset($row['permissions'])) {
2596
			$row['permissions'] = (int) $row['permissions'];
2597
		}
2598
		if (isset($row['storage'])) {
2599
			$row['storage'] = (int) $row['storage'];
2600
		}
2601
		if (isset($row['stime'])) {
2602
			$row['stime'] = (int) $row['stime'];
2603
		}
2604
		if (isset($row['expiration']) && $row['share_type'] !== self::SHARE_TYPE_LINK) {
2605
			// discard expiration date for non-link shares, which might have been
2606
			// set by ancient bugs
2607
			$row['expiration'] = null;
2608
		}
2609
	}
2610
2611
	/**
2612
	 * format result
2613
	 * @param array $items result
2614
	 * @param string $column is it a file share or a general share ('file_target' or 'item_target')
2615
	 * @param \OCP\Share_Backend $backend sharing backend
2616
	 * @param int $format
2617
	 * @param array $parameters additional format parameters
2618
	 * @return array format result
2619
	 */
2620
	private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) {
2621
		if ($format === self::FORMAT_NONE) {
2622
			return $items;
2623
		} else if ($format === self::FORMAT_STATUSES) {
2624
			$statuses = [];
2625
			foreach ($items as $item) {
2626
				if ($item['share_type'] === self::SHARE_TYPE_LINK) {
2627
					if ($item['uid_initiator'] !== \OC::$server->getUserSession()->getUser()->getUID()) {
2628
						continue;
2629
					}
2630
					$statuses[$item[$column]]['link'] = true;
2631
				} else if (!isset($statuses[$item[$column]])) {
2632
					$statuses[$item[$column]]['link'] = false;
2633
				}
2634
				if (!empty($item['file_target'])) {
2635
					$statuses[$item[$column]]['path'] = $item['path'];
2636
				}
2637
			}
2638
			return $statuses;
2639
		} else {
2640
			return $backend->formatItems($items, $format, $parameters);
2641
		}
2642
	}
2643
2644
	/**
2645
	 * remove protocol from URL
2646
	 *
2647
	 * @param string $url
2648
	 * @return string
2649
	 */
2650 View Code Duplication
	public static function removeProtocolFromUrl($url) {
2651
		if (strpos($url, 'https://') === 0) {
2652
			return substr($url, strlen('https://'));
2653
		} else if (strpos($url, 'http://') === 0) {
2654
			return substr($url, strlen('http://'));
2655
		}
2656
2657
		return $url;
2658
	}
2659
2660
	/**
2661
	 * try http post first with https and then with http as a fallback
2662
	 *
2663
	 * @param string $remoteDomain
2664
	 * @param string $urlSuffix
2665
	 * @param array $fields post parameters
2666
	 * @return array
2667
	 */
2668
	private static function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) {
2669
		$allowHttpFallback = \OC::$server->getConfig()->getSystemValue('sharing.federation.allowHttpFallback',  false) === true;
2670
		// Always try https first
2671
		$protocol = 'https://';
2672
		$discoveryManager = new DiscoveryManager(
2673
			\OC::$server->getMemCacheFactory(),
2674
			\OC::$server->getHTTPClientService()
2675
		);
2676
2677
		$endpoint = $discoveryManager->getShareEndpoint($protocol . $remoteDomain);
2678
		// Try HTTPS
2679
		$result = \OC::$server->getHTTPHelper()->post(
2680
			$protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT,
2681
			$fields);
2682
2683
		if ($result['success'] === true) {
2684
			// Return if https worked
2685
			return $result;
2686
		} elseif ($result['success'] === false && $allowHttpFallback) {
2687
			// If https failed and we can try http - try that
2688
			$protocol = 'http://';
2689
			$result = \OC::$server->getHTTPHelper()->post(
2690
			$protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT,
2691
			$fields);
2692
			return $result;
2693
		} else {
2694
			// Else we just return the failure
2695
			return $result;
2696
		}
2697
2698
	}
2699
2700
	/**
2701
	 * send server-to-server share to remote server
2702
	 *
2703
	 * @param string $token
2704
	 * @param string $shareWith
2705
	 * @param string $name
2706
	 * @param int $remote_id
2707
	 * @param string $owner
2708
	 * @return bool
2709
	 */
2710
	private static function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner) {
2711
2712
		list($user, $remote) = Helper::splitUserRemote($shareWith);
2713
2714
		if ($user && $remote) {
2715
			$url = $remote;
2716
2717
			$local = \OC::$server->getURLGenerator()->getAbsoluteURL('/');
2718
2719
			$fields = [
2720
				'shareWith' => $user,
2721
				'token' => $token,
2722
				'name' => $name,
2723
				'remoteId' => $remote_id,
2724
				'owner' => $owner,
2725
				'remote' => $local,
2726
			];
2727
2728
			$url = self::removeProtocolFromUrl($url);
2729
			$result = self::tryHttpPostToShareEndpoint($url, '', $fields);
2730
			$status = json_decode($result['result'], true);
2731
2732
			if ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200)) {
2733
				\OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]);
2734
				return true;
2735
			}
2736
2737
		}
2738
2739
		return false;
2740
	}
2741
2742
	/**
2743
	 * send server-to-server unshare to remote server
2744
	 *
2745
	 * @param string $remote url
2746
	 * @param int $id share id
2747
	 * @param string $token
2748
	 * @return bool
2749
	 */
2750
	private static function sendRemoteUnshare($remote, $id, $token) {
2751
		$url = rtrim($remote, '/');
2752
		$fields = ['token' => $token, 'format' => 'json'];
2753
		$url = self::removeProtocolFromUrl($url);
2754
		$result = self::tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields);
2755
		$status = json_decode($result['result'], true);
2756
2757
		return ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200));
2758
	}
2759
2760
	/**
2761
	 * check if user can only share with group members
2762
	 * @return bool
2763
	 */
2764
	public static function shareWithGroupMembersOnly() {
2765
		$value = \OC::$server->getAppConfig()->getValue('core', 'shareapi_only_share_with_group_members', 'no');
2766
		return ($value === 'yes') ? true : false;
2767
	}
2768
2769
	/**
2770
	 * check if user can only share with groups he's member of
2771
	 * @return bool
2772
	 */
2773
	public static function shareWithMembershipGroupOnly() {
2774
		$value = \OC::$server->getAppConfig()->getValue('core', 'shareapi_only_share_with_membership_groups', 'no');
2775
		return ($value === 'yes') ? true : false;
2776
	}
2777
2778
	/**
2779
	 * @return bool
2780
	 */
2781
	public static function isDefaultExpireDateEnabled() {
2782
		$defaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
2783
		return ($defaultExpireDateEnabled === "yes") ? true : false;
2784
	}
2785
2786
	/**
2787
	 * @return bool
2788
	 */
2789
	public static function enforceDefaultExpireDate() {
2790
		$enforceDefaultExpireDate = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
2791
		return ($enforceDefaultExpireDate === "yes") ? true : false;
2792
	}
2793
2794
	/**
2795
	 * @return int
2796
	 */
2797
	public static function getExpireInterval() {
2798
		return (int)\OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
2799
	}
2800
2801
	/**
2802
	 * Checks whether the given path is reachable for the given owner
2803
	 *
2804
	 * @param string $path path relative to files
2805
	 * @param string $ownerStorageId storage id of the owner
2806
	 *
2807
	 * @return boolean true if file is reachable, false otherwise
2808
	 */
2809
	private static function isFileReachable($path, $ownerStorageId) {
2810
		// if outside the home storage, file is always considered reachable
2811
		if (!(substr($ownerStorageId, 0, 6) === 'home::' ||
2812
			substr($ownerStorageId, 0, 13) === 'object::user:'
2813
		)) {
2814
			return true;
2815
		}
2816
2817
		// if inside the home storage, the file has to be under "/files/"
2818
		$path = ltrim($path, '/');
2819
		if (substr($path, 0, 6) === 'files/') {
2820
			return true;
2821
		}
2822
2823
		return false;
2824
	}
2825
2826
	/**
2827
	 * @param IConfig $config
2828
	 * @return bool
2829
	 */
2830
	public static function enforcePassword(IConfig $config) {
2831
		$enforcePassword = $config->getAppValue('core', 'shareapi_enforce_links_password', 'no');
2832
		return ($enforcePassword === "yes") ? true : false;
2833
	}
2834
2835
	/**
2836
	 * Get all share entries, including non-unique group items
2837
	 *
2838
	 * @param string $owner
2839
	 * @return array
2840
	 */
2841
	public static function getAllSharesForOwner($owner) {
2842
		$query = 'SELECT * FROM `*PREFIX*share` WHERE `uid_owner` = ?';
2843
		$result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$owner]);
2844
		return $result->fetchAll();
2845
	}
2846
2847
	/**
2848
	 * Get all share entries, including non-unique group items for a file
2849
	 *
2850
	 * @param int $id
2851
	 * @return array
2852
	 */
2853
	public static function getAllSharesForFileId($id) {
2854
		$query = 'SELECT * FROM `*PREFIX*share` WHERE `file_source` = ?';
2855
		$result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$id]);
2856
		return $result->fetchAll();
2857
	}
2858
2859
	/**
2860
	 * @param string $password
2861
	 * @throws \Exception
2862
	 */
2863 View Code Duplication
	private static function verifyPassword($password) {
2864
2865
		$accepted = true;
2866
		$message = '';
2867
		\OCP\Util::emitHook('\OC\Share', 'verifyPassword', [
2868
			'password' => $password,
2869
			'accepted' => &$accepted,
2870
			'message' => &$message
2871
		]);
2872
2873
		if (!$accepted) {
2874
			throw new \Exception($message);
2875
		}
2876
2877
		\OC::$server->getEventDispatcher()->dispatch(
2878
			'OCP\Share::validatePassword',
2879
			new GenericEvent(null, ['password' => $password])
2880
		);
2881
	}
2882
2883
	/**
2884
	 * @param $user
2885
	 * @return Group[]
2886
	 */
2887
	private static function getGroupsForUser($user) {
2888
		$groups = \OC::$server->getGroupManager()->getUserIdGroups($user, 'sharing');
2889
		return array_values(array_map(function(Group $g) {
2890
			return $g->getGID();
2891
		}, $groups));
2892
	}
2893
2894
	/**
2895
	 * @param $group
2896
	 * @return mixed
2897
	 */
2898
	private static function usersInGroup($group) {
2899
		$g = \OC::$server->getGroupManager()->get($group);
2900
		if (is_null($g)) {
2901
			return [];
2902
		}
2903
		return array_values(array_map(function(IUser $u){
2904
			return $u->getUID();
2905
		}, $g->getUsers()));
2906
	}
2907
}
2908