Failed Conditions
Pull Request — master (#55)
by Sander
02:10
created

lib/Fixtures/ShareFix.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Nextcloud - NextNote
4
 *
5
 * @copyright Copyright (c) 2015, Ben Curtis <[email protected]>
6
 * @copyright Copyright (c) 2017, Sander Brand ([email protected])
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 * This program is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License as
11
 * published by the Free Software Foundation, either version 3 of the
12
 * License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
namespace OCA\NextNote\Fixtures;
25
26
use OC\Share\Helper;
27
use OC\Share\Share;
28
use OCA\NextNote\Utility\Utils;
29
30
class ShareFix extends Share {
31
32
	private static function log($level, $message, $context) {
33
		\OC::$server->getLogger()->log($level, $message, $context);
34
	}
35
36
	/**
37
	 * Set the permissions of an item for a specific user or group
38
	 *
39
	 * @param string $itemType
40
	 * @param string $itemSource
41
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
42
	 * @param string $shareWith User or group the item is being shared with
43
	 * @param int $permissions CRUDS permissions
44
	 * @return boolean true on success or false on failure
45
	 * @throws \Exception when trying to grant more permissions then the user has himself
46
	 */
47
	public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) {
48
		$l = \OC::$server->getL10N('lib');
49
		$connection = \OC::$server->getDatabaseConnection();
50
51
		$intArrayToLiteralArray = function ($intArray, $eb) {
52
			return array_map(function ($int) use ($eb) {
53
				return $eb->literal((int)$int, 'integer');
54
			}, $intArray);
55
		};
56
		$sanitizeItem = function ($item) {
57
			$item['id'] = (int)$item['id'];
58
			$item['premissions'] = (int)$item['permissions'];
59
			return $item;
60
		};
61
62
		if ($rootItem = self::getItems($itemType, $itemSource, $shareType, $shareWith,
63
			\OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) {
64
			// Check if this item is a reshare and verify that the permissions
65
			// granted don't exceed the parent shared item
66
			if (isset($rootItem['parent'])) {
67
				$qb = $connection->getQueryBuilder();
68
				$qb->select('permissions')
69
					->from('share')
70
					->where($qb->expr()->eq('id', $qb->createParameter('id')))
71
					->setParameter(':id', $rootItem['parent']);
72
				$dbresult = $qb->execute();
73
74
				$result = $dbresult->fetch();
75
				$dbresult->closeCursor();
76
				if (~(int)$result['permissions'] & $permissions) {
77
					$message = 'Setting permissions for %s failed,'
78
						. ' because the permissions exceed permissions granted to %s';
79
					$message_t = $l->t('Setting permissions for %s failed, because the permissions exceed permissions granted to %s', array($itemSource, \OC_User::getUser()));
80
					self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSource, \OC_User::getUser()), \OCP\Util::DEBUG);
81
					throw new \Exception($message_t);
82
				}
83
			}
84
			$qb = $connection->getQueryBuilder();
85
			$qb->update('share')
86
				->set('permissions', $qb->createParameter('permissions'))
87
				->where($qb->expr()->eq('id', $qb->createParameter('id')))
88
				->setParameter(':id', $rootItem['id'])
89
				->setParameter(':permissions', $permissions);
90
			$qb->execute();
91
			if ($itemType === 'file' || $itemType === 'folder') {
92
				\OC_Hook::emit('NextNote\Fixtures\ShareFix', 'post_update_permissions', array(
93
					'itemType' => $itemType,
94
					'itemSource' => $itemSource,
95
					'shareType' => $shareType,
96
					'shareWith' => $shareWith,
97
					'uidOwner' => \OC_User::getUser(),
98
					'permissions' => $permissions,
99
					'path' => $rootItem['path'],
100
					'share' => $rootItem
101
				));
102
			}
103
104
			// Share id's to update with the new permissions
105
			$ids = [];
106
			$items = [];
107
108
			// Check if permissions were removed
109
			if ((int)$rootItem['permissions'] & ~$permissions) {
110
				// If share permission is removed all reshares must be deleted
111
				if (($rootItem['permissions'] & \OCP\Constants::PERMISSION_SHARE) && (~$permissions & \OCP\Constants::PERMISSION_SHARE)) {
112
					// delete all shares, keep parent and group children
113
					Helper::delete($rootItem['id'], true, null, null, true);
114
				}
115
116
				// Remove permission from all children
117
				$parents = [$rootItem['id']];
118
				while (!empty($parents)) {
119
					$parents = $intArrayToLiteralArray($parents, $qb->expr());
120
					$qb = $connection->getQueryBuilder();
121
					$qb->select('id', 'permissions', 'item_type')
122
						->from('share')
123
						->where($qb->expr()->in('parent', $parents));
124
					$result = $qb->execute();
125
					// Reset parents array, only go through loop again if
126
					// items are found that need permissions removed
127
					$parents = [];
128
					while ($item = $result->fetch()) {
129
						$item = $sanitizeItem($item);
130
131
						$items[] = $item;
132
						// Check if permissions need to be removed
133
						if ($item['permissions'] & ~$permissions) {
134
							// Add to list of items that need permissions removed
135
							$ids[] = $item['id'];
136
							$parents[] = $item['id'];
137
						}
138
					}
139
					$result->closeCursor();
140
				}
141
142
				// Remove the permissions for all reshares of this item
143
				if (!empty($ids)) {
144
					$ids = "'" . implode("','", $ids) . "'";
145
					// TODO this should be done with Doctrine platform objects
146
					if (\OC::$server->getConfig()->getSystemValue("dbtype") === 'oci') {
147
						$andOp = 'BITAND(`permissions`, ?)';
148
					} else {
149
						$andOp = '`permissions` & ?';
150
					}
151
					$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ' . $andOp
152
						. ' WHERE `id` IN (' . $ids . ')');
153
					$query->execute(array($permissions));
154
				}
155
156
			}
157
158
			/*
159
			 * Permissions were added
160
			 * Update all USERGROUP shares. (So group shares where the user moved their mountpoint).
161
			 */
162
			if ($permissions & ~(int)$rootItem['permissions']) {
163
				$qb = $connection->getQueryBuilder();
164
				$qb->select('id', 'permissions', 'item_type')
165
					->from('share')
166
					->where($qb->expr()->eq('parent', $qb->createParameter('parent')))
167
					->andWhere($qb->expr()->eq('share_type', $qb->createParameter('share_type')))
168
					->andWhere($qb->expr()->neq('permissions', $qb->createParameter('shareDeleted')))
169
					->setParameter(':parent', (int)$rootItem['id'])
170
					->setParameter(':share_type', 2)
171
					->setParameter(':shareDeleted', 0);
172
				$result = $qb->execute();
173
174
				$ids = [];
175
				while ($item = $result->fetch()) {
176
					$item = $sanitizeItem($item);
177
					$items[] = $item;
178
					$ids[] = $item['id'];
179
				}
180
				$result->closeCursor();
181
182
				// Add permssions for all USERGROUP shares of this item
183
				if (!empty($ids)) {
184
					$ids = $intArrayToLiteralArray($ids, $qb->expr());
185
186
					$qb = $connection->getQueryBuilder();
187
					$qb->update('share')
188
						->set('permissions', $qb->createParameter('permissions'))
189
						->where($qb->expr()->in('id', $ids))
190
						->setParameter(':permissions', $permissions);
191
					$qb->execute();
192
				}
193
			}
194
195
			foreach ($items as $item) {
196
				\OC_Hook::emit('OCP\Share', 'post_update_permissions', ['share' => $item]);
197
			}
198
199
			return true;
200
		}
201
		$message = 'Setting permissions for %s failed, because the item was not found';
202
		$message_t = $l->t('Setting permissions for %s failed, because the item was not found', array($itemSource));
203
204
		self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSource), \OCP\Util::DEBUG);
205
		throw new \Exception($message_t);
206
	}
207
208
	/**
209
	 * Get the item of item type shared with the current user
210
	 *
211
	 * @param string $itemType
212
	 * @param string $itemTarget
213
	 * @param int $format (optional) Format type must be defined by the backend
214
	 * @param mixed $parameters (optional)
215
	 * @param boolean $includeCollections (optional)
216
	 * @return mixed Return depends on format
217
	 */
218
	public static function getItemSharedWith($itemType, $itemTarget, $format = parent::FORMAT_NONE,
219
											 $parameters = null, $includeCollections = false) {
220
221
		return self::getItems($itemType, $itemTarget, parent::$shareTypeUserAndGroups, \OC_User::getUser(), null, self::FORMAT_NONE,
222
			$parameters, 1, $includeCollections);
223
	}
224
225
226
	/**
227
	 * Get all users an item is shared with
228
	 *
229
	 * @param string $itemType
230
	 * @param string $itemSource
231
	 * @return array Return array of users
232
	 */
233
	public static function getUsersItemShared($itemType, $itemSource) {
234
235
		$users = array();
236
		$queryArgs = [
237
			$itemType,
238
			$itemSource
239
		];
240
		$where = '`item_type` = ?';
241
		$where .= ' AND `item_source`= ?';
242
		$q = 'SELECT * FROM `*PREFIX*share` WHERE ' . $where;
243
		$query = \OC_DB::prepare($q);
244
245
		$result = $query->execute($queryArgs);
246
		while ($row = $result->fetchRow()) {
247
			if ($row['share_type'] == self::SHARE_TYPE_USER) {
248
				$u = Utils::getUserInfo($row['share_with']);
249
				$users[] = $u['display_name'];
250
			}
251
			if ($row['share_type'] == self::SHARE_TYPE_GROUP) {
252
				$users[] = $row['share_with'];
253
			}
254
		}
255
256
		return $users;
257
	}
258
259
260
	/**
261
	 * Share an item with a user, group, or via private link
262
	 *
263
	 * @param string $itemType
264
	 * @param string $itemSource
265
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
266
	 * @param string $shareWith User or group the item is being shared with
267
	 * @param int $permissions CRUDS
268
	 * @param string $itemSourceName
269
	 * @param \DateTime|null $expirationDate
270
	 * @param bool|null $passwordChanged
271
	 * @return boolean|string Returns true on success or false on failure, Returns token on success for links
272
	 * @throws \OC\HintException when the share type is remote and the shareWith is invalid
273
	 * @throws \Exception
274
	 * @since 5.0.0 - parameter $itemSourceName was added in 6.0.0, parameter $expirationDate was added in 7.0.0, parameter $passwordChanged added in 9.0.0
275
	 */
276
	public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null, $passwordChanged = null) {
277
278
		$backend = self::getBackend($itemType);
279
		$l = \OC::$server->getL10N('lib');
280
281 View Code Duplication
		if ($backend->isShareTypeAllowed($shareType) === false) {
282
			$message = 'Sharing %s failed, because the backend does not allow shares from type %i';
283
			$message_t = $l->t('Sharing %s failed, because the backend does not allow shares from type %i', array($itemSourceName, $shareType));
284
			self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSourceName, $shareType), \OCP\Util::DEBUG);
285
			throw new \Exception($message_t);
286
		}
287
288
		$uidOwner = \OC_User::getUser();
289
		$shareWithinGroupOnly = self::shareWithGroupMembersOnly();
290
291
		if (is_null($itemSourceName)) {
292
			$itemSourceName = $itemSource;
293
		}
294
		$itemName = $itemSourceName;
295
296
		//Validate expirationDate
297
		if ($expirationDate !== null) {
298
			try {
299
				/*
300
				 * Reuse the validateExpireDate.
301
				 * We have to pass time() since the second arg is the time
302
				 * the file was shared, since it is not shared yet we just use
303
				 * the current time.
304
				 */
305
				$expirationDate = self::validateExpireDate($expirationDate->format('Y-m-d'), time(), $itemType, $itemSource);
306
			} catch (\Exception $e) {
307
				throw new \OC\HintException($e->getMessage(), $e->getMessage(), 404);
308
			}
309
		}
310
311
		// Verify share type and sharing conditions are met
312
		if ($shareType === self::SHARE_TYPE_USER) {
313
			if ($shareWith == $uidOwner) {
314
				$message = 'Sharing %s failed, because you can not share with yourself';
315
				$message_t = $l->t('Sharing %s failed, because you can not share with yourself', [$itemName]);
316
				self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
317
				throw new \Exception($message_t);
318
			}
319 View Code Duplication
			if (!\OC::$server->getUserManager()->userExists($shareWith)) {
320
				$message = 'Sharing %s failed, because the user %s does not exist';
321
				$message_t = $l->t('Sharing %s failed, because the user %s does not exist', array($itemSourceName, $shareWith));
322
				self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
323
				throw new \Exception($message_t);
324
			}
325
			if ($shareWithinGroupOnly) {
326
				$userManager = \OC::$server->getUserManager();
327
				$groupManager = \OC::$server->getGroupManager();
328
				$userOwner = $userManager->get($uidOwner);
329
				$userShareWith = $userManager->get($shareWith);
330
				$groupsOwner = [];
331
				$groupsShareWith = [];
332
				if ($userOwner) {
333
					$groupsOwner = $groupManager->getUserGroupIds($userOwner);
334
				}
335
				if ($userShareWith) {
336
					$groupsShareWith = $groupManager->getUserGroupIds($userShareWith);
337
				}
338
				$inGroup = array_intersect($groupsOwner, $groupsShareWith);
339 View Code Duplication
				if (empty($inGroup)) {
340
					$message = 'Sharing %s failed, because the user '
341
						. '%s is not a member of any groups that %s is a member of';
342
					$message_t = $l->t('Sharing %s failed, because the user %s is not a member of any groups that %s is a member of', array($itemName, $shareWith, $uidOwner));
343
					self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemName, $shareWith, $uidOwner), \OCP\Util::DEBUG);
344
					throw new \Exception($message_t);
345
				}
346
			}
347
			// Check if the item source is already shared with the user, either from the same owner or a different user
348 View Code Duplication
			if ($checkExists = self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups,
349
				$shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
350
				// Only allow the same share to occur again if it is the same
351
				// owner and is not a user share, this use case is for increasing
352
				// permissions for a specific user
353
				if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
354
					$message = 'Sharing %s failed, because this item is already shared with %s';
355
					$message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
356
					self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
357
					throw new \Exception($message_t);
358
				}
359
			}
360 View Code Duplication
			if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_USER,
361
				$shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
362
				// Only allow the same share to occur again if it is the same
363
				// owner and is not a user share, this use case is for increasing
364
				// permissions for a specific user
365
				if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
366
					$message = 'Sharing %s failed, because this item is already shared with user %s';
367
					$message_t = $l->t('Sharing %s failed, because this item is already shared with user %s', array($itemSourceName, $shareWith));
368
					self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::ERROR);
369
					throw new \Exception($message_t);
370
				}
371
			}
372
		} else if ($shareType === self::SHARE_TYPE_GROUP) {
373 View Code Duplication
			if (!\OC::$server->getGroupManager()->groupExists($shareWith)) {
374
				$message = 'Sharing %s failed, because the group %s does not exist';
375
				$message_t = $l->t('Sharing %s failed, because the group %s does not exist', array($itemSourceName, $shareWith));
376
				self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
377
				throw new \Exception($message_t);
378
			}
379
			if ($shareWithinGroupOnly) {
380
				$group = \OC::$server->getGroupManager()->get($shareWith);
381
				$user = \OC::$server->getUserManager()->get($uidOwner);
382
				if (!$group || !$user || !$group->inGroup($user)) {
383
					$message = 'Sharing %s failed, because '
384
						. '%s is not a member of the group %s';
385
					$message_t = $l->t('Sharing %s failed, because %s is not a member of the group %s', array($itemSourceName, $uidOwner, $shareWith));
386
					self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSourceName, $uidOwner, $shareWith), \OCP\Util::DEBUG);
387
					throw new \Exception($message_t);
388
				}
389
			}
390
			// Check if the item source is already shared with the group, either from the same owner or a different user
391
			// The check for each user in the group is done inside the put() function
392
			if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $shareWith,
393
				null, self::FORMAT_NONE, null, 1, true, true)) {
394
395
				if ($checkExists['share_with'] === $shareWith && $checkExists['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
396
					$message = 'Sharing %s failed, because this item is already shared with %s';
397
					$message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
398
					self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
399
					throw new \Exception($message_t);
400
				}
401
			}
402
			// Convert share with into an array with the keys group and users
403
			$group = $shareWith;
404
			$shareWith = array();
405
			$shareWith['group'] = $group;
406
407
408
			$groupObject = \OC::$server->getGroupManager()->get($group);
409
			$userIds = [];
410
			if ($groupObject) {
411
				$users = $groupObject->searchUsers('');
412
				foreach ($users as $user) {
413
					$userIds[] = $user->getUID();
414
				}
415
			}
416
417
418
			$shareWith['users'] = array_diff($userIds, array($uidOwner));
419
		} else {
420
			// Future share types need to include their own conditions
421
			$message = 'Share type %s is not valid for %s';
422
			$message_t = $l->t('Share type %s is not valid for %s', array($shareType, $itemSource));
423
			self::log('NextNote\Fixtures\ShareFix', sprintf($message, $shareType, $itemSource), \OCP\Util::DEBUG);
424
			throw new \Exception($message_t);
425
		}
426
427
		// Put the item into the database
428
		$result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, null, $itemSourceName, $expirationDate);
429
430
		return $result ? true : false;
431
	}
432
433
	/**
434
	 * Put shared item into the database
435
	 *
436
	 * @param string $itemType Item type
437
	 * @param string $itemSource Item source
438
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
439
	 * @param string $shareWith User or group the item is being shared with
440
	 * @param string $uidOwner User that is the owner of shared item
441
	 * @param int $permissions CRUDS permissions
442
	 * @param boolean|array $parentFolder Parent folder target (optional)
443
	 * @param string $token (optional)
444
	 * @param string $itemSourceName name of the source item (optional)
445
	 * @param \DateTime $expirationDate (optional)
446
	 * @throws \Exception
447
	 * @return mixed id of the new share or false
448
	 */
449
	private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
450
								$permissions, $parentFolder = null, $token = null, $itemSourceName = null, \DateTime $expirationDate = null) {
451
452
		$queriesToExecute = array();
453
		$suggestedItemTarget = null;
454
		$groupFileTarget = $fileTarget = $suggestedFileTarget = $filePath = '';
455
		$groupItemTarget = $itemTarget = $fileSource = $parent = 0;
456
457
		$result = self::checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate);
458
		if (!empty($result)) {
459
			$parent = $result['parent'];
460
			$itemSource = $result['itemSource'];
461
			$fileSource = $result['fileSource'];
462
			$suggestedItemTarget = $result['suggestedItemTarget'];
463
			$suggestedFileTarget = $result['suggestedFileTarget'];
464
			$filePath = $result['filePath'];
465
		}
466
467
		$isGroupShare = false;
468
		if ($shareType == self::SHARE_TYPE_GROUP) {
469
			$isGroupShare = true;
470
			if (isset($shareWith['users'])) {
471
				$users = $shareWith['users'];
472
			} else {
473
				$group = \OC::$server->getGroupManager()->get($shareWith['group']);
474
				if ($group) {
475
					$users = $group->searchUsers('');
476
					$userIds = [];
477
					foreach ($users as $user) {
478
						$userIds[] = $user->getUID();
479
					}
480
					$users = $userIds;
481
				} else {
482
					$users = [];
483
				}
484
			}
485
			// remove current user from list
486
			if (in_array(\OC::$server->getUserSession()->getUser()->getUID(), $users)) {
487
				unset($users[array_search(\OC::$server->getUserSession()->getUser()->getUID(), $users)]);
488
			}
489
			$groupItemTarget = Helper::generateTarget($itemType, $itemSource,
490
				$shareType, $shareWith['group'], $uidOwner, $suggestedItemTarget);
491
			$groupFileTarget = Helper::generateTarget($itemType, $itemSource,
492
				$shareType, $shareWith['group'], $uidOwner, $filePath);
493
494
			// add group share to table and remember the id as parent
495
			$queriesToExecute['groupShare'] = array(
496
				'itemType' => $itemType,
497
				'itemSource' => $itemSource,
498
				'itemTarget' => $groupItemTarget,
499
				'shareType' => $shareType,
500
				'shareWith' => $shareWith['group'],
501
				'uidOwner' => $uidOwner,
502
				'permissions' => $permissions,
503
				'shareTime' => time(),
504
				'fileSource' => $fileSource,
505
				'fileTarget' => $groupFileTarget,
506
				'token' => $token,
507
				'parent' => $parent,
508
				'expiration' => $expirationDate,
509
			);
510
511
		} else {
512
			$users = array($shareWith);
513
			$itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
514
				$suggestedItemTarget);
515
		}
516
517
		$run = true;
518
		$error = '';
519
		$preHookData = array(
520
			'itemType' => $itemType,
521
			'itemSource' => $itemSource,
522
			'shareType' => $shareType,
523
			'uidOwner' => $uidOwner,
524
			'permissions' => $permissions,
525
			'fileSource' => $fileSource,
526
			'expiration' => $expirationDate,
527
			'token' => $token,
528
			'run' => &$run,
529
			'error' => &$error
530
		);
531
532
		$preHookData['itemTarget'] = $isGroupShare ? $groupItemTarget : $itemTarget;
533
		$preHookData['shareWith'] = $isGroupShare ? $shareWith['group'] : $shareWith;
534
535
		\OC_Hook::emit(\OCP\Share::class, 'pre_shared', $preHookData);
536
537
		if ($run === false) {
538
			throw new \Exception($error);
539
		}
540
541
		foreach ($users as $user) {
542
			$sourceId = ($itemType === 'file' || $itemType === 'folder') ? $fileSource : $itemSource;
543
			$sourceExists = self::getItemSharedWithBySource($itemType, $sourceId, self::FORMAT_NONE, null, true, $user);
544
545
			$userShareType = $isGroupShare ? self::$shareTypeGroupUserUnique : $shareType;
546
547
			if ($sourceExists && $sourceExists['item_source'] === $itemSource) {
548
				$fileTarget = $sourceExists['file_target'];
549
				$itemTarget = $sourceExists['item_target'];
550
551
				// for group shares we don't need a additional entry if the target is the same
552
				if ($isGroupShare && $groupItemTarget === $itemTarget) {
553
					continue;
554
				}
555
556
			} elseif (!$sourceExists && !$isGroupShare) {
557
558
				$itemTarget = Helper::generateTarget($itemType, $itemSource, $userShareType, $user,
559
					$uidOwner, $suggestedItemTarget, $parent);
560
				if (isset($fileSource)) {
561
					if ($parentFolder) {
562
						if ($parentFolder === true) {
563
							$fileTarget = Helper::generateTarget('file', $filePath, $userShareType, $user,
564
								$uidOwner, $suggestedFileTarget, $parent);
565
							if ($fileTarget != $groupFileTarget) {
566
								$parentFolders[$user]['folder'] = $fileTarget;
567
							}
568
						} else if (isset($parentFolder[$user])) {
569
							$fileTarget = $parentFolder[$user]['folder'] . $itemSource;
570
							$parent = $parentFolder[$user]['id'];
571
						}
572
					} else {
573
						$fileTarget = Helper::generateTarget('file', $filePath, $userShareType,
574
							$user, $uidOwner, $suggestedFileTarget, $parent);
575
					}
576
				} else {
577
					$fileTarget = null;
578
				}
579
580
			} else {
581
582
				// group share which doesn't exists until now, check if we need a unique target for this user
583
584
				$itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $user,
585
					$uidOwner, $suggestedItemTarget, $parent);
586
587
				// do we also need a file target
588
				if (isset($fileSource)) {
589
					$fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $user,
590
						$uidOwner, $suggestedFileTarget, $parent);
591
				} else {
592
					$fileTarget = null;
593
				}
594
595
				if (($itemTarget === $groupItemTarget) &&
596
					(!isset($fileSource) || $fileTarget === $groupFileTarget)) {
597
					continue;
598
				}
599
			}
600
601
			$queriesToExecute[] = array(
602
				'itemType' => $itemType,
603
				'itemSource' => $itemSource,
604
				'itemTarget' => $itemTarget,
605
				'shareType' => $userShareType,
606
				'shareWith' => $user,
607
				'uidOwner' => $uidOwner,
608
				'permissions' => $permissions,
609
				'shareTime' => time(),
610
				'fileSource' => $fileSource,
611
				'fileTarget' => $fileTarget,
612
				'token' => $token,
613
				'parent' => $parent,
614
				'expiration' => $expirationDate,
615
			);
616
617
		}
618
619
		$id = false;
620
		if ($isGroupShare) {
621
			$id = self::insertShare($queriesToExecute['groupShare']);
622
			// Save this id, any extra rows for this group share will need to reference it
623
			$parent = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
624
			unset($queriesToExecute['groupShare']);
625
		}
626
627
		foreach ($queriesToExecute as $shareQuery) {
628
			$shareQuery['parent'] = $parent;
629
			$id = self::insertShare($shareQuery);
630
		}
631
632
		$postHookData = array(
633
			'itemType' => $itemType,
634
			'itemSource' => $itemSource,
635
			'parent' => $parent,
636
			'shareType' => $shareType,
637
			'uidOwner' => $uidOwner,
638
			'permissions' => $permissions,
639
			'fileSource' => $fileSource,
640
			'id' => $parent,
641
			'token' => $token,
642
			'expirationDate' => $expirationDate,
643
		);
644
645
		$postHookData['shareWith'] = $isGroupShare ? $shareWith['group'] : $shareWith;
646
		$postHookData['itemTarget'] = $isGroupShare ? $groupItemTarget : $itemTarget;
647
		$postHookData['fileTarget'] = $isGroupShare ? $groupFileTarget : $fileTarget;
648
649
		\OC_Hook::emit(\OCP\Share::class, 'post_shared', $postHookData);
650
651
652
		return $id ? $id : false;
653
	}
654
655
	/**
656
	 * @param string $itemType
657
	 * @param string $itemSource
658
	 * @param int $shareType
659
	 * @param string $shareWith
660
	 * @param string $uidOwner
661
	 * @param int $permissions
662
	 * @param string|null $itemSourceName
663
	 * @param null|\DateTime $expirationDate
664
	 */
665
	private static function checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate) {
666
		$backend = self::getBackend($itemType);
667
668
		$l = \OC::$server->getL10N('lib');
669
		$result = array();
670
671
		$column = ($itemType === 'file' || $itemType === 'folder') ? 'file_source' : 'item_source';
672
673
		$checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true);
674
		if ($checkReshare) {
675
			// Check if attempting to share back to owner
676
			if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) {
677
				$message = 'Sharing %s failed, because the user %s is the original sharer';
678
				$message_t = $l->t('Sharing failed, because the user %s is the original sharer', [$shareWith]);
679
680
				self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
681
				throw new \Exception($message_t);
682
			}
683
		}
684
685
		if ($checkReshare && $checkReshare['uid_owner'] !== \OC_User::getUser()) {
686
			// Check if share permissions is granted
687
			if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
688
				if (~(int)$checkReshare['permissions'] & $permissions) {
689
					$message = 'Sharing %s failed, because the permissions exceed permissions granted to %s';
690
					$message_t = $l->t('Sharing %s failed, because the permissions exceed permissions granted to %s', array($itemSourceName, $uidOwner));
691
692
					self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSourceName, $uidOwner), \OCP\Util::DEBUG);
693
					throw new \Exception($message_t);
694
				} else {
695
					// TODO Don't check if inside folder
696
					$result['parent'] = $checkReshare['id'];
697
698
					$result['expirationDate'] = $expirationDate;
699
					// $checkReshare['expiration'] could be null and then is always less than any value
700
					if (isset($checkReshare['expiration']) && $checkReshare['expiration'] < $expirationDate) {
701
						$result['expirationDate'] = $checkReshare['expiration'];
702
					}
703
704
					// only suggest the same name as new target if it is a reshare of the
705
					// same file/folder and not the reshare of a child
706
					if ($checkReshare[$column] === $itemSource) {
707
						$result['filePath'] = $checkReshare['file_target'];
708
						$result['itemSource'] = $checkReshare['item_source'];
709
						$result['fileSource'] = $checkReshare['file_source'];
710
						$result['suggestedItemTarget'] = $checkReshare['item_target'];
711
						$result['suggestedFileTarget'] = $checkReshare['file_target'];
712
					} else {
713
						$result['filePath'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $backend->getFilePath($itemSource, $uidOwner) : null;
0 ignored issues
show
The class OCP\Share_Backend_File_Dependent does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
714
						$result['suggestedItemTarget'] = null;
715
						$result['suggestedFileTarget'] = null;
716
						$result['itemSource'] = $itemSource;
717
						$result['fileSource'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $itemSource : null;
0 ignored issues
show
The class OCP\Share_Backend_File_Dependent does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
718
					}
719
				}
720 View Code Duplication
			} else {
721
				$message = 'Sharing %s failed, because resharing is not allowed';
722
				$message_t = $l->t('Sharing %s failed, because resharing is not allowed', array($itemSourceName));
723
724
				self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
725
				throw new \Exception($message_t);
726
			}
727
		} else {
728
			$result['parent'] = null;
729
			$result['suggestedItemTarget'] = null;
730
			$result['suggestedFileTarget'] = null;
731
			$result['itemSource'] = $itemSource;
732
			$result['expirationDate'] = $expirationDate;
733 View Code Duplication
			if (!$backend->isValidSource($itemSource, $uidOwner)) {
734
				$message = 'Sharing %s failed, because the sharing backend for '
735
					. '%s could not find its source';
736
				$message_t = $l->t('Sharing %s failed, because the sharing backend for %s could not find its source', array($itemSource, $itemType));
737
				self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSource, $itemType), \OCP\Util::DEBUG);
738
				throw new \Exception($message_t);
739
			}
740
			if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
0 ignored issues
show
The class OCP\Share_Backend_File_Dependent does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
741
				$result['filePath'] = $backend->getFilePath($itemSource, $uidOwner);
742
				if ($itemType == 'file' || $itemType == 'folder') {
743
					$result['fileSource'] = $itemSource;
744
				} else {
745
					$meta = \OC\Files\Filesystem::getFileInfo($result['filePath']);
746
					$result['fileSource'] = $meta['fileid'];
747
				}
748
				if ($result['fileSource'] == -1) {
749
					$message = 'Sharing %s failed, because the file could not be found in the file cache';
750
					$message_t = $l->t('Sharing %s failed, because the file could not be found in the file cache', array($itemSource));
751
752
					self::log('NextNote\Fixtures\ShareFix', sprintf($message, $itemSource), \OCP\Util::DEBUG);
753
					throw new \Exception($message_t);
754
				}
755
			} else {
756
				$result['filePath'] = null;
757
				$result['fileSource'] = null;
758
			}
759
		}
760
761
		return $result;
762
	}
763
764
	/**
765
	 *
766
	 * @param array $shareData
767
	 * @return mixed false in case of a failure or the id of the new share
768
	 */
769
	private static function insertShare(array $shareData) {
770
771
		$query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` ('
772
			. ' `item_type`, `item_source`, `item_target`, `share_type`,'
773
			. ' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,'
774
			. ' `file_target`, `token`, `parent`, `expiration`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)');
775
		$query->bindValue(1, $shareData['itemType']);
776
		$query->bindValue(2, $shareData['itemSource']);
777
		$query->bindValue(3, $shareData['itemTarget']);
778
		$query->bindValue(4, $shareData['shareType']);
779
		$query->bindValue(5, $shareData['shareWith']);
780
		$query->bindValue(6, $shareData['uidOwner']);
781
		$query->bindValue(7, $shareData['permissions']);
782
		$query->bindValue(8, $shareData['shareTime']);
783
		$query->bindValue(9, $shareData['fileSource']);
784
		$query->bindValue(10, $shareData['fileTarget']);
785
		$query->bindValue(11, $shareData['token']);
786
		$query->bindValue(12, $shareData['parent']);
787
		$query->bindValue(13, $shareData['expiration'], 'datetime');
788
		$result = $query->execute();
789
790
		$id = false;
791
		if ($result) {
792
			$id = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
793
		}
794
795
		return $id;
796
797
	}
798
799
	/**
800
	 * validate expiration date if it meets all constraints
801
	 *
802
	 * @param string $expireDate well formatted date string, e.g. "DD-MM-YYYY"
803
	 * @param string $shareTime timestamp when the file was shared
804
	 * @param string $itemType
805
	 * @param string $itemSource
806
	 * @return \DateTime validated date
807
	 * @throws \Exception when the expire date is in the past or further in the future then the enforced date
808
	 */
809
	private static function validateExpireDate($expireDate, $shareTime, $itemType, $itemSource) {
810
		$l = \OC::$server->getL10N('lib');
811
		$date = new \DateTime($expireDate);
812
		$today = new \DateTime('now');
813
814
		// if the user doesn't provide a share time we need to get it from the database
815
		// fall-back mode to keep API stable, because the $shareTime parameter was added later
816
		$defaultExpireDateEnforced = \OCP\Util::isDefaultExpireDateEnforced();
817
		if ($defaultExpireDateEnforced && $shareTime === null) {
818
			$items = self::getItemShared($itemType, $itemSource);
819
			$firstItem = reset($items);
820
			$shareTime = (int)$firstItem['stime'];
821
		}
822
823
		if ($defaultExpireDateEnforced) {
824
			// initialize max date with share time
825
			$maxDate = new \DateTime();
826
			$maxDate->setTimestamp($shareTime);
827
			$maxDays = \OC::$server->getConfig()->getAppValue('core', 'shareapi_expire_after_n_days', '7');
828
			$maxDate->add(new \DateInterval('P' . $maxDays . 'D'));
829
			if ($date > $maxDate) {
830
				$warning = 'Cannot set expiration date. Shares cannot expire later than ' . $maxDays . ' after they have been shared';
831
				$warning_t = $l->t('Cannot set expiration date. Shares cannot expire later than %s after they have been shared', array($maxDays));
832
				self::log('NextNote\Fixtures\ShareFix', $warning, \OCP\Util::WARN);
833
				throw new \Exception($warning_t);
834
			}
835
		}
836
837 View Code Duplication
		if ($date < $today) {
838
			$message = 'Cannot set expiration date. Expiration date is in the past';
839
			$message_t = $l->t('Cannot set expiration date. Expiration date is in the past');
840
			self::log('NextNote\Fixtures\ShareFix', $message, \OCP\Util::WARN);
841
			throw new \Exception($message_t);
842
		}
843
844
		return $date;
845
	}
846
847
	public static function getPermissions($itemType, $itemSource, $uid) {
848
		$uid = \OC::$server->getUserSession()->getUser();
849
		$gm = \OC::$server->getGroupManager();
850
851
		$users = array();
0 ignored issues
show
$users is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
852
		$queryArgs = [
853
			$itemType,
854
			$itemSource,
855
			$uid->getUID()
856
		];
857
		$where = '`item_type` = ?';
858
		$where .= ' AND `item_source`= ?';
859
		$where .= ' AND (`share_with` = ? AND `share_type`= 0 ';
860
861
		$user_groups = $gm->getUserGroupIds($uid);
862
863
		foreach ($user_groups as $group) {
864
			$where .= ' OR (`share_with` = ? AND `share_type`=1)';
865
			$queryArgs[] = $group;
866
		}
867
		$where .= ')';
868
869
		$q = 'SELECT * FROM `*PREFIX*share` WHERE ' . $where;
870
871
872
		$query = \OC_DB::prepare($q);
873
874
		$result = $query->execute($queryArgs);
875
		$permissions = 0;
876
		while ($row = $result->fetchRow()) {
877
			if ($row['permissions'] > $permissions) {
878
				$permissions = $row['permissions'];
879
			}
880
		}
881
882
883
		return $permissions;
884
	}
885
886
}