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

lib/Fixtures/ShareFix.php (1 issue)

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;
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...
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;
714
						$result['suggestedItemTarget'] = null;
715
						$result['suggestedFileTarget'] = null;
716
						$result['itemSource'] = $itemSource;
717
						$result['fileSource'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $itemSource : null;
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) {
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();
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
}