Completed
Push — stable8 ( 42720e...c45eda )
by
unknown
35s
created

Share::getItems()   F

Complexity

Conditions 113
Paths > 20000

Size

Total Lines 381
Code Lines 260

Duplication

Lines 5
Ratio 1.31 %

Importance

Changes 0
Metric Value
cc 113
eloc 260
nc 4294967295
nop 11
dl 5
loc 381
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * ownCloud
4
 *
5
 * @author Bjoern Schiessle, Michael Gapczynski
6
 * @copyright 2012 Michael Gapczynski <[email protected]>
7
 *            2014 Bjoern Schiessle <[email protected]>
8
 *
9
 * This library is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
11
 * License as published by the Free Software Foundation; either
12
 * version 3 of the License, or any later version.
13
 *
14
 * This library 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
20
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
namespace OC\Share;
24
25
use OCP\IUserSession;
26
use OC\DB\Connection;
27
use OCP\IConfig;
28
29
/**
30
 * This class provides the ability for apps to share their content between users.
31
 * Apps must create a backend class that implements OCP\Share_Backend and register it with this class.
32
 *
33
 * It provides the following hooks:
34
 *  - post_shared
35
 */
36
class Share extends \OC\Share\Constants {
37
38
	/** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask
39
	 * Construct permissions for share() and setPermissions with Or (|) e.g.
40
	 * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE
41
	 *
42
	 * Check if permission is granted with And (&) e.g. Check if delete is
43
	 * granted: if ($permissions & PERMISSION_DELETE)
44
	 *
45
	 * Remove permissions with And (&) and Not (~) e.g. Remove the update
46
	 * permission: $permissions &= ~PERMISSION_UPDATE
47
	 *
48
	 * Apps are required to handle permissions on their own, this class only
49
	 * stores and manages the permissions of shares
50
	 * @see lib/public/constants.php
51
	 */
52
53
	/**
54
	 * Register a sharing backend class that implements OCP\Share_Backend for an item type
55
	 * @param string $itemType Item type
56
	 * @param string $class Backend class
57
	 * @param string $collectionOf (optional) Depends on item type
58
	 * @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files
59
	 * @return boolean true if backend is registered or false if error
60
	 */
61
	public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) {
62
		if (self::isEnabled()) {
63
			if (!isset(self::$backendTypes[$itemType])) {
64
				self::$backendTypes[$itemType] = array(
65
					'class' => $class,
66
					'collectionOf' => $collectionOf,
67
					'supportedFileExtensions' => $supportedFileExtensions
68
				);
69
				if(count(self::$backendTypes) === 1) {
70
					\OC_Util::addScript('core', 'share');
71
					\OC_Util::addStyle('core', 'share');
72
				}
73
				return true;
74
			}
75
			\OC_Log::write('OCP\Share',
76
				'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class']
77
				.' is already registered for '.$itemType,
78
				\OC_Log::WARN);
79
		}
80
		return false;
81
	}
82
83
	/**
84
	 * Check if the Share API is enabled
85
	 * @return boolean true if enabled or false
86
	 *
87
	 * The Share API is enabled by default if not configured
88
	 */
89
	public static function isEnabled() {
90
		if (\OC_Appconfig::getValue('core', 'shareapi_enabled', 'yes') == 'yes') {
91
			return true;
92
		}
93
		return false;
94
	}
95
96
	/**
97
	 * Find which users can access a shared item
98
	 * @param string $path to the file
99
	 * @param string $ownerUser owner of the file
100
	 * @param boolean $includeOwner include owner to the list of users with access to the file
101
	 * @param boolean $returnUserPaths Return an array with the user => path map
102
	 * @return array
103
	 * @note $path needs to be relative to user data dir, e.g. 'file.txt'
104
	 *       not '/admin/data/file.txt'
105
	 */
106
	public static function getUsersSharingFile($path, $ownerUser, $includeOwner = false, $returnUserPaths = false) {
107
108
		$shares = $sharePaths = $fileTargets = array();
109
		$publicShare = false;
110
		$remoteShare = false;
111
		$source = -1;
112
		$cache = false;
113
114
		$view = new \OC\Files\View('/' . $ownerUser . '/files');
115
		if ($view->file_exists($path)) {
116
			$meta = $view->getFileInfo($path);
117
			$path = substr($meta->getPath(), strlen('/' . $ownerUser . '/files'));
118
		} else {
119
			// if the file doesn't exists yet we start with the parent folder
120
			$meta = $view->getFileInfo(dirname($path));
121
		}
122
123
		if($meta !== false) {
124
			$source = $meta['fileid'];
125
			$cache = new \OC\Files\Cache\Cache($meta['storage']);
126
		}
127
128
		while ($source !== -1) {
129
			// Fetch all shares with another user
130
			$query = \OC_DB::prepare(
131
				'SELECT `share_with`, `file_source`, `file_target`
132
				FROM
133
				`*PREFIX*share`
134
				WHERE
135
				`item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
136
			);
137
138
			$result = $query->execute(array($source, self::SHARE_TYPE_USER));
139
140
			if (\OCP\DB::isError($result)) {
141
				\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
142
			} else {
143
				while ($row = $result->fetchRow()) {
144
					$shares[] = $row['share_with'];
145
					if ($returnUserPaths) {
146
						$fileTargets[(int) $row['file_source']][$row['share_with']] = $row;
147
					}
148
				}
149
			}
150
151
			// We also need to take group shares into account
152
			$query = \OC_DB::prepare(
153
				'SELECT `share_with`, `file_source`, `file_target`
154
				FROM
155
				`*PREFIX*share`
156
				WHERE
157
				`item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
158
			);
159
160
			$result = $query->execute(array($source, self::SHARE_TYPE_GROUP));
161
162
			if (\OCP\DB::isError($result)) {
163
				\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
164
			} else {
165
				while ($row = $result->fetchRow()) {
166
					$usersInGroup = \OC_Group::usersInGroup($row['share_with']);
167
					$shares = array_merge($shares, $usersInGroup);
168
					if ($returnUserPaths) {
169
						foreach ($usersInGroup as $user) {
170
							$fileTargets[(int) $row['file_source']][$user] = $row;
171
						}
172
					}
173
				}
174
			}
175
176
			//check for public link shares
177 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...
178
				$query = \OC_DB::prepare('
179
					SELECT `share_with`
180
					FROM `*PREFIX*share`
181
					WHERE `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')', 1
182
				);
183
184
				$result = $query->execute(array($source, self::SHARE_TYPE_LINK));
185
186
				if (\OCP\DB::isError($result)) {
187
					\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
188
				} else {
189
					if ($result->fetchRow()) {
190
						$publicShare = true;
191
					}
192
				}
193
			}
194
195
			//check for remote share
196 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...
197
				$query = \OC_DB::prepare('
198
					SELECT `share_with`
199
					FROM `*PREFIX*share`
200
					WHERE `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')', 1
201
				);
202
203
				$result = $query->execute(array($source, self::SHARE_TYPE_REMOTE));
204
205
				if (\OCP\DB::isError($result)) {
206
					\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
207
				} else {
208
					if ($result->fetchRow()) {
209
						$remoteShare = true;
210
					}
211
				}
212
			}
213
214
			// let's get the parent for the next round
215
			$meta = $cache->get((int)$source);
216
			if($meta !== false) {
217
				$source = (int)$meta['parent'];
218
			} else {
219
				$source = -1;
220
			}
221
		}
222
223
		// Include owner in list of users, if requested
224
		if ($includeOwner) {
225
			$shares[] = $ownerUser;
226
			if ($returnUserPaths) {
227
				$sharePaths[$ownerUser] = $path;
228
			}
229
		}
230
231
		if ($returnUserPaths) {
232
			$fileTargetIDs = array_keys($fileTargets);
233
			$fileTargetIDs = array_unique($fileTargetIDs);
234
235
			if (!empty($fileTargetIDs)) {
236
				$query = \OC_DB::prepare(
237
					'SELECT `fileid`, `path`
238
					FROM `*PREFIX*filecache`
239
					WHERE `fileid` IN (' . implode(',', $fileTargetIDs) . ')'
240
				);
241
				$result = $query->execute();
242
243
				if (\OCP\DB::isError($result)) {
244
					\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
245
				} else {
246
					while ($row = $result->fetchRow()) {
247
						foreach ($fileTargets[$row['fileid']] as $uid => $shareData) {
248
							$sharedPath = $shareData['file_target'];
249
							$sharedPath .= substr($path, strlen($row['path']) -5);
250
							$sharePaths[$uid] = $sharedPath;
251
						}
252
					}
253
				}
254
			}
255
256
			return $sharePaths;
257
		}
258
259
		return array('users' => array_unique($shares), 'public' => $publicShare, 'remote' => $remoteShare);
260
	}
261
262
	/**
263
	 * Get the items of item type shared with the current user
264
	 * @param string $itemType
265
	 * @param int $format (optional) Format type must be defined by the backend
266
	 * @param mixed $parameters (optional)
267
	 * @param int $limit Number of items to return (optional) Returns all by default
268
	 * @param boolean $includeCollections (optional)
269
	 * @return mixed Return depends on format
270
	 */
271
	public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE,
272
											  $parameters = null, $limit = -1, $includeCollections = false) {
273
		return self::getItems($itemType, null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
274
			$parameters, $limit, $includeCollections);
275
	}
276
277
	/**
278
	 * Get the items of item type shared with a user
279
	 * @param string $itemType
280
	 * @param string $user id for which user we want the shares
281
	 * @param int $format (optional) Format type must be defined by the backend
282
	 * @param mixed $parameters (optional)
283
	 * @param int $limit Number of items to return (optional) Returns all by default
284
	 * @param boolean $includeCollections (optional)
285
	 * @return mixed Return depends on format
286
	 */
287
	public static function getItemsSharedWithUser($itemType, $user, $format = self::FORMAT_NONE,
288
												  $parameters = null, $limit = -1, $includeCollections = false) {
289
		return self::getItems($itemType, null, self::$shareTypeUserAndGroups, $user, null, $format,
290
			$parameters, $limit, $includeCollections);
291
	}
292
293
	/**
294
	 * Get the item of item type shared with the current user
295
	 * @param string $itemType
296
	 * @param string $itemTarget
297
	 * @param int $format (optional) Format type must be defined by the backend
298
	 * @param mixed $parameters (optional)
299
	 * @param boolean $includeCollections (optional)
300
	 * @return mixed Return depends on format
301
	 */
302
	public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE,
303
											 $parameters = null, $includeCollections = false) {
304
		return self::getItems($itemType, $itemTarget, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
305
			$parameters, 1, $includeCollections);
306
	}
307
308
	/**
309
	 * Get the item of item type shared with a given user by source
310
	 * @param string $itemType
311
	 * @param string $itemSource
312
	 * @param string $user User to whom the item was shared
313
	 * @param string $owner Owner of the share
314
	 * @param int $shareType only look for a specific share type
315
	 * @return array Return list of items with file_target, permissions and expiration
316
	 */
317
	public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null, $shareType = null) {
318
		$shares = array();
319
		$fileDependent = false;
320
321
		$where = 'WHERE';
322
		$fileDependentWhere = '';
323
		if ($itemType === 'file' || $itemType === 'folder') {
324
			$fileDependent = true;
325
			$column = 'file_source';
326
			$fileDependentWhere = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
327
			$fileDependentWhere .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
328
		} else {
329
			$column = 'item_source';
330
		}
331
332
		$select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent);
333
334
		$where .= ' `' . $column . '` = ? AND `item_type` = ? ';
335
		$arguments = array($itemSource, $itemType);
336
		// for link shares $user === null
337
		if ($user !== null) {
338
			$where .= ' AND `share_with` = ? ';
339
			$arguments[] = $user;
340
		}
341
342
		if ($shareType !== null) {
343
			$where .= ' AND `share_type` = ? ';
344
			$arguments[] = $shareType;
345
		}
346
347
		if ($owner !== null) {
348
			$where .= ' AND `uid_owner` = ? ';
349
			$arguments[] = $owner;
350
		}
351
352
		$query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` '. $fileDependentWhere . $where);
353
354
		$result = \OC_DB::executeAudited($query, $arguments);
355
356
		while ($row = $result->fetchRow()) {
357
			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
358
				continue;
359
			}
360
			$shares[] = $row;
361
		}
362
363
		//if didn't found a result than let's look for a group share.
364
		if(empty($shares) && $user !== null) {
365
			$groups = \OC_Group::getUserGroups($user);
366
367
			if (!empty($groups)) {
368
				$where = $fileDependentWhere . ' WHERE `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)';
369
				$arguments = array($itemSource, $itemType, $groups);
370
				$types = array(null, null, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);
371
372
				if ($owner !== null) {
373
					$where .= ' AND `uid_owner` = ?';
374
					$arguments[] = $owner;
375
					$types[] = null;
376
				}
377
378
				// TODO: inject connection, hopefully one day in the future when this
379
				// class isn't static anymore...
380
				$conn = \OC_DB::getConnection();
381
				$result = $conn->executeQuery(
382
					'SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where,
383
					$arguments,
0 ignored issues
show
Documentation introduced by
$arguments is of type array<integer,string|array>, 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...
384
					$types
385
				);
386
387
				while ($row = $result->fetch()) {
388
					$shares[] = $row;
389
				}
390
			}
391
		}
392
393
		return $shares;
394
395
	}
396
397
	/**
398
	 * Get the item of item type shared with the current user by source
399
	 * @param string $itemType
400
	 * @param string $itemSource
401
	 * @param int $format (optional) Format type must be defined by the backend
402
	 * @param mixed $parameters
403
	 * @param boolean $includeCollections
404
	 * @param string $shareWith (optional) define against which user should be checked, default: current user
405
	 * @return array
406
	 */
407
	public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE,
408
													 $parameters = null, $includeCollections = false, $shareWith = null) {
409
		$shareWith = ($shareWith === null) ? \OC_User::getUser() : $shareWith;
410
		return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, $shareWith, null, $format,
411
			$parameters, 1, $includeCollections, true);
412
	}
413
414
	/**
415
	 * Get the item of item type shared by a link
416
	 * @param string $itemType
417
	 * @param string $itemSource
418
	 * @param string $uidOwner Owner of link
419
	 * @return array
420
	 */
421
	public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) {
422
		return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE,
423
			null, 1);
424
	}
425
426
	/**
427
	 * Based on the given token the share information will be returned - password protected shares will be verified
428
	 * @param string $token
429
	 * @return array|boolean false will be returned in case the token is unknown or unauthorized
430
	 */
431
	public static function getShareByToken($token, $checkPasswordProtection = true) {
432
		$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `token` = ?', 1);
433
		$result = $query->execute(array($token));
434 View Code Duplication
		if (\OC_DB::isError($result)) {
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...
435
			\OC_Log::write('OCP\Share', \OC_DB::getErrorMessage($result) . ', token=' . $token, \OC_Log::ERROR);
436
		}
437
		$row = $result->fetchRow();
438
		if ($row === false) {
439
			return false;
440
		}
441
		if (is_array($row) and self::expireItem($row)) {
442
			return false;
443
		}
444
445
		// password protected shares need to be authenticated
446
		if ($checkPasswordProtection && !\OCP\Share::checkPasswordProtectedShare($row)) {
447
			return false;
448
		}
449
450
		return $row;
451
	}
452
453
	/**
454
	 * resolves reshares down to the last real share
455
	 * @param array $linkItem
456
	 * @return array file owner
457
	 */
458
	public static function resolveReShare($linkItem)
459
	{
460
		if (isset($linkItem['parent'])) {
461
			$parent = $linkItem['parent'];
462 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...
463
				$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `id` = ?', 1);
464
				$item = $query->execute(array($parent))->fetchRow();
465
				if (isset($item['parent'])) {
466
					$parent = $item['parent'];
467
				} else {
468
					return $item;
469
				}
470
			}
471
		}
472
		return $linkItem;
473
	}
474
475
476
	/**
477
	 * Get the shared items of item type owned by the current user
478
	 * @param string $itemType
479
	 * @param int $format (optional) Format type must be defined by the backend
480
	 * @param mixed $parameters
481
	 * @param int $limit Number of items to return (optional) Returns all by default
482
	 * @param boolean $includeCollections
483
	 * @return mixed Return depends on format
484
	 */
485
	public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null,
486
										  $limit = -1, $includeCollections = false) {
487
		return self::getItems($itemType, null, null, null, \OC_User::getUser(), $format,
488
			$parameters, $limit, $includeCollections);
489
	}
490
491
	/**
492
	 * Get the shared item of item type owned by the current user
493
	 * @param string $itemType
494
	 * @param string $itemSource
495
	 * @param int $format (optional) Format type must be defined by the backend
496
	 * @param mixed $parameters
497
	 * @param boolean $includeCollections
498
	 * @return mixed Return depends on format
499
	 */
500
	public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE,
501
										 $parameters = null, $includeCollections = false) {
502
		return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format,
503
			$parameters, -1, $includeCollections);
504
	}
505
506
	/**
507
	 * Get all users an item is shared with
508
	 * @param string $itemType
509
	 * @param string $itemSource
510
	 * @param string $uidOwner
511
	 * @param boolean $includeCollections
512
	 * @param boolean $checkExpireDate
513
	 * @return array Return array of users
514
	 */
515
	public static function getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections = false, $checkExpireDate = true) {
516
517
		$users = array();
518
		$items = self::getItems($itemType, $itemSource, null, null, $uidOwner, self::FORMAT_NONE, null, -1, $includeCollections, false, $checkExpireDate);
519
		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...
520
			foreach ($items as $item) {
521
				if ((int)$item['share_type'] === self::SHARE_TYPE_USER) {
522
					$users[] = $item['share_with'];
523
				} else if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) {
524
					$users = array_merge($users, \OC_Group::usersInGroup($item['share_with']));
525
				}
526
			}
527
		}
528
		return $users;
529
	}
530
531
	/**
532
	 * Share an item with a user, group, or via private link
533
	 * @param string $itemType
534
	 * @param string $itemSource
535
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
536
	 * @param string $shareWith User or group the item is being shared with
537
	 * @param int $permissions CRUDS
538
	 * @param string $itemSourceName
539
	 * @param \DateTime $expirationDate
540
	 * @return boolean|string Returns true on success or false on failure, Returns token on success for links
541
	 * @throws \Exception
542
	 */
543
	public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null) {
544
545
		$backend = self::getBackend($itemType);
546
		$l = \OC::$server->getL10N('lib');
547
548 View Code Duplication
		if ($backend->isShareTypeAllowed($shareType) === 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...
549
			$message = 'Sharing %s failed, because the backend does not allow shares from type %i';
550
			$message_t = $l->t('Sharing %s failed, because the backend does not allow shares from type %i', array($itemSourceName, $shareType));
551
			\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareType), \OC_Log::ERROR);
552
			throw new \Exception($message_t);
553
		}
554
555
		$uidOwner = \OC_User::getUser();
556
		$shareWithinGroupOnly = self::shareWithGroupMembersOnly();
557
558
		if (is_null($itemSourceName)) {
559
			$itemSourceName = $itemSource;
560
		}
561
562
		// check if file can be shared
563
		if ($itemType === 'file' or $itemType === 'folder') {
564
			$path = \OC\Files\Filesystem::getPath($itemSource);
565
			// verify that the file exists before we try to share it
566 View Code Duplication
			if (!$path) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $path of type string|null is loosely compared to false; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // 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...
567
				$message = 'Sharing %s failed, because the file does not exist';
568
				$message_t = $l->t('Sharing %s failed, because the file does not exist', array($itemSourceName));
569
				\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName), \OC_Log::ERROR);
570
				throw new \Exception($message_t);
571
			}
572
			// verify that the user has share permission
573
			if (!\OC\Files\Filesystem::isSharable($path)) {
574
				$message = 'You are not allowed to share %s';
575
				$message_t = $l->t('You are not allowed to share %s', array($itemSourceName));
576
				\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName), \OC_Log::ERROR);
577
				throw new \Exception($message_t);
578
			}
579
		}
580
581
		//verify that we don't share a folder which already contains a share mount point
582
		if ($itemType === 'folder') {
583
			$path = '/' . $uidOwner . '/files' . \OC\Files\Filesystem::getPath($itemSource) . '/';
584
			$mountManager = \OC\Files\Filesystem::getMountManager();
585
			$mounts = $mountManager->findIn($path);
586
			foreach ($mounts as $mount) {
587
				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
588
					$message = 'Sharing "' . $itemSourceName . '" failed, because it contains files shared with you!';
589
					\OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
590
					throw new \Exception($message);
591
				}
592
593
			}
594
		}
595
596
		// single file shares should never have delete permissions
597
		if ($itemType === 'file') {
598
			$permissions = (int)$permissions & ~\OCP\Constants::PERMISSION_DELETE;
599
		}
600
601
		// Verify share type and sharing conditions are met
602
		if ($shareType === self::SHARE_TYPE_USER) {
603 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...
604
				$message = 'Sharing %s failed, because the user %s is the item owner';
605
				$message_t = $l->t('Sharing %s failed, because the user %s is the item owner', array($itemSourceName, $shareWith));
606
				\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR);
607
				throw new \Exception($message_t);
608
			}
609 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...
610
				$message = 'Sharing %s failed, because the user %s does not exist';
611
				$message_t = $l->t('Sharing %s failed, because the user %s does not exist', array($itemSourceName, $shareWith));
612
				\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR);
613
				throw new \Exception($message_t);
614
			}
615
			if ($shareWithinGroupOnly) {
616
				$inGroup = array_intersect(\OC_Group::getUserGroups($uidOwner), \OC_Group::getUserGroups($shareWith));
617 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...
618
					$message = 'Sharing %s failed, because the user '
619
						.'%s is not a member of any groups that %s is a member of';
620
					$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($itemSourceName, $shareWith, $uidOwner));
621
					\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith, $uidOwner), \OC_Log::ERROR);
622
					throw new \Exception($message_t);
623
				}
624
			}
625
			// Check if the item source is already shared with the user, either from the same owner or a different user
626 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...
627
				$shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
628
				// Only allow the same share to occur again if it is the same
629
				// owner and is not a user share, this use case is for increasing
630
				// permissions for a specific user
631
				if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
632
					$message = 'Sharing %s failed, because this item is already shared with %s';
633
					$message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
634
					\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR);
635
					throw new \Exception($message_t);
636
				}
637
			}
638
		} else if ($shareType === self::SHARE_TYPE_GROUP) {
639 View Code Duplication
			if (!\OC_Group::groupExists($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...
640
				$message = 'Sharing %s failed, because the group %s does not exist';
641
				$message_t = $l->t('Sharing %s failed, because the group %s does not exist', array($itemSourceName, $shareWith));
642
				\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR);
643
				throw new \Exception($message_t);
644
			}
645
			if ($shareWithinGroupOnly && !\OC_Group::inGroup($uidOwner, $shareWith)) {
646
				$message = 'Sharing %s failed, because '
647
					.'%s is not a member of the group %s';
648
				$message_t = $l->t('Sharing %s failed, because %s is not a member of the group %s', array($itemSourceName, $uidOwner, $shareWith));
649
				\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $uidOwner, $shareWith), \OC_Log::ERROR);
650
				throw new \Exception($message_t);
651
			}
652
			// Check if the item source is already shared with the group, either from the same owner or a different user
653
			// The check for each user in the group is done inside the put() function
654 View Code Duplication
			if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $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...
655
				null, self::FORMAT_NONE, null, 1, true, true)) {
656
				// Only allow the same share to occur again if it is the same
657
				// owner and is not a group share, this use case is for increasing
658
				// permissions for a specific user
659
				if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
660
					$message = 'Sharing %s failed, because this item is already shared with %s';
661
					$message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
662
					\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR);
663
					throw new \Exception($message_t);
664
				}
665
			}
666
			// Convert share with into an array with the keys group and users
667
			$group = $shareWith;
668
			$shareWith = array();
669
			$shareWith['group'] = $group;
670
			$shareWith['users'] = array_diff(\OC_Group::usersInGroup($group), array($uidOwner));
671
		} else if ($shareType === self::SHARE_TYPE_LINK) {
672
			$updateExistingShare = false;
673
			if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') == 'yes') {
674
675
				// when updating a link share
676
				// FIXME Don't delete link if we update it
677
				if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null,
678
					$uidOwner, self::FORMAT_NONE, null, 1)) {
679
					// remember old token
680
					$oldToken = $checkExists['token'];
681
					$oldPermissions = $checkExists['permissions'];
682
					//delete the old share
683
					Helper::delete($checkExists['id']);
684
					$updateExistingShare = true;
685
				}
686
687
				// Generate hash of password - same method as user passwords
688
				if (!empty($shareWith)) {
689
					$shareWith = \OC::$server->getHasher()->hash($shareWith);
690
				} else {
691
					// reuse the already set password, but only if we change permissions
692
					// otherwise the user disabled the password protection
693
					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...
694
						$shareWith = $checkExists['share_with'];
695
					}
696
				}
697
698
				if (\OCP\Util::isPublicLinkPasswordRequired() && empty($shareWith)) {
699
					$message = 'You need to provide a password to create a public link, only protected links are allowed';
700
					$message_t = $l->t('You need to provide a password to create a public link, only protected links are allowed');
701
					\OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
702
					throw new \Exception($message_t);
703
				}
704
705
				if ($updateExistingShare === false &&
706
					self::isDefaultExpireDateEnabled() &&
707
					empty($expirationDate)) {
708
					$expirationDate = Helper::calcExpireDate();
709
				}
710
711
				// Generate token
712
				if (isset($oldToken)) {
713
					$token = $oldToken;
714
				} else {
715
					$token = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(self::TOKEN_LENGTH,
716
						\OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_UPPER.
717
						\OCP\Security\ISecureRandom::CHAR_DIGITS
718
					);
719
				}
720
				$result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions,
721
					null, $token, $itemSourceName, $expirationDate);
722
				if ($result) {
723
					return $token;
724
				} else {
725
					return false;
726
				}
727
			}
728
			$message = 'Sharing %s failed, because sharing with links is not allowed';
729
			$message_t = $l->t('Sharing %s failed, because sharing with links is not allowed', array($itemSourceName));
730
			\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName), \OC_Log::ERROR);
731
			throw new \Exception($message_t);
732
		} else if ($shareType === self::SHARE_TYPE_REMOTE) {
733
734
			$shareWith = Helper::fixRemoteURLInShareWith($shareWith);
735
736
			// don't allow federated shares if source and target server are the same
737
			list($user, $remote) = Helper::splitUserRemote($shareWith);
738
			$currentServer = self::removeProtocolFromUrl(\OC::$server->getURLGenerator()->getAbsoluteURL('/'));
739
			$currentUser = \OC::$server->getUserSession()->getUser()->getUID();
740
			if (Helper::isSameUserOnSameServer($user, $remote, $currentUser, $currentServer)) {
741
				$message = 'Not allowed to create a federated share with the same user.';
742
				$message_t = $l->t('Not allowed to create a federated share with the same user');
743
				\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
744
				throw new \Exception($message_t);
745
			}
746
747
			$token = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(self::TOKEN_LENGTH, \OCP\Security\ISecureRandom::CHAR_LOWER . \OCP\Security\ISecureRandom::CHAR_UPPER .
748
				\OCP\Security\ISecureRandom::CHAR_DIGITS);
749
750
			$shareId = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, $token, $itemSourceName);
751
752
			$send = false;
753
			if ($shareId) {
754
				$send = self::sendRemoteShare($token, $shareWith, $itemSourceName, $shareId, $uidOwner);
755
			}
756
757
			if ($send === false) {
758
				$currentUser = \OC::$server->getUserSession()->getUser()->getUID();
759
				self::unshare($itemType, $itemSource, $shareType, $shareWith, $currentUser);
760
				$message_t = $l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.', array($itemSourceName, $shareWith));
761
				throw new \Exception($message_t);
762
			}
763
764
			return $send;
765 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...
766
			// Future share types need to include their own conditions
767
			$message = 'Share type %s is not valid for %s';
768
			$message_t = $l->t('Share type %s is not valid for %s', array($shareType, $itemSource));
769
			\OC_Log::write('OCP\Share', sprintf($message, $shareType, $itemSource), \OC_Log::ERROR);
770
			throw new \Exception($message_t);
771
		}
772
773
		// Put the item into the database
774
		$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 668 can also be of type array<string,string|array,{"users":"array"}>; 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...
775
776
		return $result ? true : false;
777
	}
778
779
	/**
780
	 * Unshare an item from a user, group, or delete a private link
781
	 * @param string $itemType
782
	 * @param string $itemSource
783
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
784
	 * @param string $shareWith User or group the item is being shared with
785
	 * @param string $owner owner of the share, if null the current user is used
786
	 * @return boolean true on success or false on failure
787
	 */
788
	public static function unshare($itemType, $itemSource, $shareType, $shareWith, $owner = null) {
789
790
		// check if it is a valid itemType
791
		self::getBackend($itemType);
792
793
		$items = self::getItemSharedWithUser($itemType, $itemSource, $shareWith, $owner, $shareType);
794
795
		$toDelete = array();
796
		$newParent = null;
797
		$currentUser = $owner ? $owner : \OC_User::getUser();
798
		foreach ($items as $item) {
799
			// delete the item with the expected share_type and owner
800
			if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) {
801
				$toDelete = $item;
802
				// if there is more then one result we don't have to delete the children
803
				// but update their parent. For group shares the new parent should always be
804
				// the original group share and not the db entry with the unique name
805
			} else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
806
				$newParent = $item['parent'];
807
			} else {
808
				$newParent = $item['id'];
809
			}
810
		}
811
812
		if (!empty($toDelete)) {
813
			self::unshareItem($toDelete, $newParent);
814
			return true;
815
		}
816
		return false;
817
	}
818
819
	/**
820
	 * Unshare an item from all users, groups, and remove all links
821
	 * @param string $itemType
822
	 * @param string $itemSource
823
	 * @return boolean true on success or false on failure
824
	 */
825
	public static function unshareAll($itemType, $itemSource) {
826
		// Get all of the owners of shares of this item.
827
		$query = \OC_DB::prepare( 'SELECT `uid_owner` from `*PREFIX*share` WHERE `item_type`=? AND `item_source`=?' );
828
		$result = $query->execute(array($itemType, $itemSource));
829
		$shares = array();
830
		// Add each owner's shares to the array of all shares for this item.
831
		while ($row = $result->fetchRow()) {
832
			$shares = array_merge($shares, self::getItems($itemType, $itemSource, null, null, $row['uid_owner']));
833
		}
834
		if (!empty($shares)) {
835
			// Pass all the vars we have for now, they may be useful
836
			$hookParams = array(
837
				'itemType' => $itemType,
838
				'itemSource' => $itemSource,
839
				'shares' => $shares,
840
			);
841
			\OC_Hook::emit('OCP\Share', 'pre_unshareAll', $hookParams);
842
			foreach ($shares as $share) {
843
				self::unshareItem($share);
844
			}
845
			\OC_Hook::emit('OCP\Share', 'post_unshareAll', $hookParams);
846
			return true;
847
		}
848
		return false;
849
	}
850
851
	/**
852
	 * Unshare an item shared with the current user
853
	 * @param string $itemType
854
	 * @param string $itemOrigin Item target or source
855
	 * @param boolean $originIsSource true if $itemOrigin is the source, false if $itemOrigin is the target (optional)
856
	 * @return boolean true on success or false on failure
857
	 *
858
	 * Unsharing from self is not allowed for items inside collections
859
	 */
860
	public static function unshareFromSelf($itemType, $itemOrigin, $originIsSource = false) {
861
		$originType = ($originIsSource) ? 'source' : 'target';
862
		$uid = \OCP\User::getUser();
863
864
		if ($itemType === 'file' || $itemType === 'folder') {
865
			$statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `file_' . $originType . '` = ?';
866
		} else {
867
			$statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `item_' . $originType . '` = ?';
868
		}
869
870
		$query = \OCP\DB::prepare($statement);
871
		$result = $query->execute(array($itemType, $itemOrigin));
872
873
		$shares = $result->fetchAll();
874
875
		$listOfUnsharedItems = array();
876
877
		$itemUnshared = false;
878
		foreach ($shares as $share) {
879
			if ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_USER &&
880
				$share['share_with'] === $uid) {
881
				$deletedShares = Helper::delete($share['id']);
882
				$shareTmp = array(
883
					'id' => $share['id'],
884
					'shareWith' => $share['share_with'],
885
					'itemTarget' => $share['item_target'],
886
					'itemType' => $share['item_type'],
887
					'shareType' => (int)$share['share_type'],
888
				);
889
				if (isset($share['file_target'])) {
890
					$shareTmp['fileTarget'] = $share['file_target'];
891
				}
892
				$listOfUnsharedItems = array_merge($listOfUnsharedItems, $deletedShares, array($shareTmp));
893
				$itemUnshared = true;
894
				break;
895
			} elseif ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
896
				if (\OC_Group::inGroup($uid, $share['share_with'])) {
897
					$groupShare = $share;
898
				}
899
			} elseif ((int)$share['share_type'] === self::$shareTypeGroupUserUnique &&
900
				$share['share_with'] === $uid) {
901
				$uniqueGroupShare = $share;
902
			}
903
		}
904
905
		if (!$itemUnshared && isset($groupShare) && !isset($uniqueGroupShare)) {
906
			$query = \OC_DB::prepare('INSERT INTO `*PREFIX*share`'
907
				.' (`item_type`, `item_source`, `item_target`, `parent`, `share_type`,'
908
				.' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`, `file_target`)'
909
				.' VALUES (?,?,?,?,?,?,?,?,?,?,?)');
910
			$query->execute(array($groupShare['item_type'], $groupShare['item_source'], $groupShare['item_target'],
911
				$groupShare['id'], self::$shareTypeGroupUserUnique,
912
				\OC_User::getUser(), $groupShare['uid_owner'], 0, $groupShare['stime'], $groupShare['file_source'],
913
				$groupShare['file_target']));
914
			$shareTmp = array(
915
				'id' => $groupShare['id'],
916
				'shareWith' => $groupShare['share_with'],
917
				'itemTarget' => $groupShare['item_target'],
918
				'itemType' => $groupShare['item_type'],
919
				'shareType' => (int)$groupShare['share_type'],
920
			);
921
			if (isset($groupShare['file_target'])) {
922
				$shareTmp['fileTarget'] = $groupShare['file_target'];
923
			}
924
			$listOfUnsharedItems = array_merge($listOfUnsharedItems, array($groupShare));
925
			$itemUnshared = true;
926
		} elseif (!$itemUnshared && isset($uniqueGroupShare)) {
927
			$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?');
928
			$query->execute(array(0, $uniqueGroupShare['id']));
929
			$shareTmp = array(
930
				'id' => $uniqueGroupShare['id'],
931
				'shareWith' => $uniqueGroupShare['share_with'],
932
				'itemTarget' => $uniqueGroupShare['item_target'],
933
				'itemType' => $uniqueGroupShare['item_type'],
934
				'shareType' => (int)$uniqueGroupShare['share_type'],
935
			);
936
			if (isset($uniqueGroupShare['file_target'])) {
937
				$shareTmp['fileTarget'] = $uniqueGroupShare['file_target'];
938
			}
939
			$listOfUnsharedItems = array_merge($listOfUnsharedItems, array($uniqueGroupShare));
940
			$itemUnshared = true;
941
		}
942
943
		if ($itemUnshared) {
944
			\OC_Hook::emit('OCP\Share', 'post_unshareFromSelf',
945
				array('unsharedItems' => $listOfUnsharedItems, 'itemType' => $itemType));
946
		}
947
948
		return $itemUnshared;
949
	}
950
951
	/**
952
	 * sent status if users got informed by mail about share
953
	 * @param string $itemType
954
	 * @param string $itemSource
955
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
956
	 * @param string $recipient with whom was the file shared
957
	 * @param boolean $status
958
	 */
959
	public static function setSendMailStatus($itemType, $itemSource, $shareType, $recipient, $status) {
960
		$status = $status ? 1 : 0;
961
962
		$query = \OC_DB::prepare(
963
			'UPDATE `*PREFIX*share`
964
					SET `mail_send` = ?
965
					WHERE `item_type` = ? AND `item_source` = ? AND `share_type` = ? AND `share_with` = ?');
966
967
		$result = $query->execute(array($status, $itemType, $itemSource, $shareType, $recipient));
968
969
		if($result === false) {
970
			\OC_Log::write('OCP\Share', 'Couldn\'t set send mail status', \OC_Log::ERROR);
971
		}
972
	}
973
974
	/**
975
	 * Set the permissions of an item for a specific user or group
976
	 * @param string $itemType
977
	 * @param string $itemSource
978
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
979
	 * @param string $shareWith User or group the item is being shared with
980
	 * @param int $permissions CRUDS permissions
981
	 * @return boolean true on success or false on failure
982
	 */
983
	public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) {
984
		$l = \OC::$server->getL10N('lib');
985
		if ($item = self::getItems($itemType, $itemSource, $shareType, $shareWith,
986
			\OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) {
987
			// Check if this item is a reshare and verify that the permissions
988
			// granted don't exceed the parent shared item
989
			if (isset($item['parent'])) {
990
				$query = \OC_DB::prepare('SELECT `permissions` FROM `*PREFIX*share` WHERE `id` = ?', 1);
991
				$result = $query->execute(array($item['parent']))->fetchRow();
992
				if (~(int)$result['permissions'] & $permissions) {
993
					$message = 'Setting permissions for %s failed,'
994
						.' because the permissions exceed permissions granted to %s';
995
					$message_t = $l->t('Setting permissions for %s failed, because the permissions exceed permissions granted to %s', array($itemSource, \OC_User::getUser()));
996
					\OC_Log::write('OCP\Share', sprintf($message, $itemSource, \OC_User::getUser()), \OC_Log::ERROR);
997
					throw new \Exception($message_t);
998
				}
999
			}
1000
			$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?');
1001
			$query->execute(array($permissions, $item['id']));
1002
			if ($itemType === 'file' || $itemType === 'folder') {
1003
				\OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
1004
					'itemType' => $itemType,
1005
					'itemSource' => $itemSource,
1006
					'shareType' => $shareType,
1007
					'shareWith' => $shareWith,
1008
					'uidOwner' => \OC_User::getUser(),
1009
					'permissions' => $permissions,
1010
					'path' => $item['path'],
1011
				));
1012
			}
1013
			// Check if permissions were removed
1014
			if ($item['permissions'] & ~$permissions) {
1015
				// If share permission is removed all reshares must be deleted
1016
				if (($item['permissions'] & \OCP\Constants::PERMISSION_SHARE) && (~$permissions & \OCP\Constants::PERMISSION_SHARE)) {
1017
					// delete all shares, keep parent and group children
1018
					Helper::delete($item['id'], true, null, null, true);
1019
				} else {
1020
					$ids = array();
1021
					$parents = array($item['id']);
1022
					while (!empty($parents)) {
1023
						$parents = "'".implode("','", $parents)."'";
1024
						$query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`'
1025
							.' WHERE `parent` IN ('.$parents.')');
1026
						$result = $query->execute();
1027
						// Reset parents array, only go through loop again if
1028
						// items are found that need permissions removed
1029
						$parents = array();
1030
						while ($item = $result->fetchRow()) {
1031
							// Check if permissions need to be removed
1032
							if ($item['permissions'] & ~$permissions) {
1033
								// Add to list of items that need permissions removed
1034
								$ids[] = $item['id'];
1035
								$parents[] = $item['id'];
1036
							}
1037
						}
1038
					}
1039
					// Remove the permissions for all reshares of this item
1040
					if (!empty($ids)) {
1041
						$ids = "'".implode("','", $ids)."'";
1042
						// TODO this should be done with Doctrine platform objects
1043
						if (\OC_Config::getValue( "dbtype") === 'oci') {
1044
							$andOp = 'BITAND(`permissions`, ?)';
1045
						} else {
1046
							$andOp = '`permissions` & ?';
1047
						}
1048
						$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = '.$andOp
1049
							.' WHERE `id` IN ('.$ids.')');
1050
						$query->execute(array($permissions));
1051
					}
1052
				}
1053
			}
1054
			return true;
1055
		}
1056
		$message = 'Setting permissions for %s failed, because the item was not found';
1057
		$message_t = $l->t('Setting permissions for %s failed, because the item was not found', array($itemSource));
1058
1059
		\OC_Log::write('OCP\Share', sprintf($message, $itemSource), \OC_Log::ERROR);
1060
		throw new \Exception($message_t);
1061
	}
1062
1063
	/**
1064
	 * validate expiration date if it meets all constraints
1065
	 *
1066
	 * @param string $expireDate well formate date string, e.g. "DD-MM-YYYY"
1067
	 * @param string $shareTime timestamp when the file was shared
1068
	 * @param string $itemType
1069
	 * @param string $itemSource
1070
	 * @return DateTime validated date
1071
	 * @throws \Exception
1072
	 */
1073
	private static function validateExpireDate($expireDate, $shareTime, $itemType, $itemSource) {
1074
		$l = \OC::$server->getL10N('lib');
1075
		$date = new \DateTime($expireDate);
1076
		$today = new \DateTime('now');
1077
1078
		// if the user doesn't provide a share time we need to get it from the database
1079
		// fall-back mode to keep API stable, because the $shareTime parameter was added later
1080
		$defaultExpireDateEnforced = \OCP\Util::isDefaultExpireDateEnforced();
1081
		if ($defaultExpireDateEnforced && $shareTime === null) {
1082
			$items = self::getItemShared($itemType, $itemSource);
1083
			$firstItem = reset($items);
1084
			$shareTime = (int)$firstItem['stime'];
1085
		}
1086
1087
		if ($defaultExpireDateEnforced) {
1088
			// initialize max date with share time
1089
			$maxDate = new \DateTime();
1090
			$maxDate->setTimestamp($shareTime);
1091
			$maxDays = \OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
1092
			$maxDate->add(new \DateInterval('P' . $maxDays . 'D'));
1093
			if ($date > $maxDate) {
1094
				$warning = 'Cannot set expiration date. Shares cannot expire later than ' . $maxDays . ' after they have been shared';
1095
				$warning_t = $l->t('Cannot set expiration date. Shares cannot expire later than %s after they have been shared', array($maxDays));
1096
				\OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN);
1097
				throw new \Exception($warning_t);
1098
			}
1099
		}
1100
1101 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...
1102
			$message = 'Cannot set expiration date. Expiration date is in the past';
1103
			$message_t = $l->t('Cannot set expiration date. Expiration date is in the past');
1104
			\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::WARN);
1105
			throw new \Exception($message_t);
1106
		}
1107
1108
		return $date;
1109
	}
1110
1111
	/**
1112
	 * Set expiration date for a share
1113
	 * @param string $itemType
1114
	 * @param string $itemSource
1115
	 * @param string $date expiration date
1116
	 * @param int $shareTime timestamp from when the file was shared
1117
	 * @throws \Exception
1118
	 * @return boolean
1119
	 */
1120
	public static function setExpirationDate($itemType, $itemSource, $date, $shareTime = null) {
1121
		$user = \OC_User::getUser();
1122
		$l = \OC::$server->getL10N('lib');
1123
1124
		if ($date == '') {
1125
			if (\OCP\Util::isDefaultExpireDateEnforced()) {
1126
				$warning = 'Cannot clear expiration date. Shares are required to have an expiration date.';
1127
				$warning_t = $l->t('Cannot clear expiration date. Shares are required to have an expiration date.');
1128
				\OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN);
1129
				throw new \Exception($warning_t);
1130
			} else {
1131
				$date = null;
1132
			}
1133
		} else {
1134
			$date = self::validateExpireDate($date, $shareTime, $itemType, $itemSource);
1135
		}
1136
		$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `item_type` = ? AND `item_source` = ?  AND `uid_owner` = ? AND `share_type` = ?');
1137
		$query->bindValue(1, $date, 'datetime');
1138
		$query->bindValue(2, $itemType);
1139
		$query->bindValue(3, $itemSource);
1140
		$query->bindValue(4, $user);
1141
		$query->bindValue(5, \OCP\Share::SHARE_TYPE_LINK);
1142
1143
		$query->execute();
1144
1145
		\OC_Hook::emit('OCP\Share', 'post_set_expiration_date', array(
1146
			'itemType' => $itemType,
1147
			'itemSource' => $itemSource,
1148
			'date' => $date,
1149
			'uidOwner' => $user
1150
		));
1151
1152
		return true;
1153
	}
1154
1155
	/**
1156
	 * Retrieve the owner of a connection
1157
	 *
1158
	 * @param Connection $connection
1159
	 * @param int $shareId
1160
	 * @throws \Exception
1161
	 * @return string uid of share owner
1162
	 */
1163
	private static function getShareOwner(Connection $connection, $shareId) {
1164
		$qb = $connection->createQueryBuilder();
1165
1166
		$qb->select('`uid_owner`')
1167
			->from('`*PREFIX*share`')
1168
			->where('`id` = :shareId')
1169
			->setParameter(':shareId', $shareId);
1170
		$result = $qb->execute();
1171
		$result = $result->fetch();
1172
1173
		if (empty($result)) {
1174
			throw new \Exception('Share not found');
1175
		}
1176
1177
		return $result['uid_owner'];
1178
	}
1179
1180
	/**
1181
	 * Set expiration date for a share
1182
	 *
1183
	 * @param IUserSession $userSession
1184
	 * @param Connection $connection
1185
	 * @param IConfig $config
1186
	 * @param int $shareId
1187
	 * @param string $password
1188
	 * @throws \Exception
1189
	 * @return boolean
1190
	 */
1191
	public static function setPassword(IUserSession $userSession, 
1192
	                                   Connection $connection,
1193
	                                   IConfig $config,
1194
	                                   $shareId, $password) {
1195
		$user = $userSession->getUser();
1196
		if (is_null($user)) {
1197
			throw new \Exception("User not logged in");
1198
		}
1199
1200
		$uid = self::getShareOwner($connection, $shareId);
1201
1202
		if ($uid !== $user->getUID()) {
1203
			throw new \Exception('Cannot update share of a different user');
1204
		}
1205
1206
		if ($password === '') {
1207
			$password = null;
1208
		}
1209
1210
		//If passwords are enforced the password can't be null
1211
		if (self::enforcePassword($config) && is_null($password)) {
1212
			throw new \Exception('Cannot remove password');
1213
		}
1214
1215
		$qb = $connection->createQueryBuilder();
1216
		$qb->update('`*PREFIX*share`')
1217
			->set('`share_with`', ':pass')
1218
			->where('`id` = :shareId')
1219
			->setParameter(':pass', is_null($password) ? null : \OC::$server->getHasher()->hash($password))
1220
			->setParameter(':shareId', $shareId);
1221
1222
		$qb->execute();
1223
1224
		return true;
1225
	}
1226
1227
	/**
1228
	 * Checks whether a share has expired, calls unshareItem() if yes.
1229
	 * @param array $item Share data (usually database row)
1230
	 * @return boolean True if item was expired, false otherwise.
1231
	 */
1232
	protected static function expireItem(array $item) {
1233
1234
		$result = false;
1235
1236
		// only use default expiration date for link shares
1237
		if ((int) $item['share_type'] === self::SHARE_TYPE_LINK) {
1238
1239
			// calculate expiration date
1240
			if (!empty($item['expiration'])) {
1241
				$userDefinedExpire = new \DateTime($item['expiration']);
1242
				$expires = $userDefinedExpire->getTimestamp();
1243
			} else {
1244
				$expires = null;
1245
			}
1246
1247
1248
			// get default expiration settings
1249
			$defaultSettings = Helper::getDefaultExpireSetting();
1250
			$expires = Helper::calculateExpireDate($defaultSettings, $item['stime'], $expires);
1251
1252
1253
			if (is_int($expires)) {
1254
				$now = time();
1255
				if ($now > $expires) {
1256
					self::unshareItem($item);
1257
					$result = true;
1258
				}
1259
			}
1260
		}
1261
		return $result;
1262
	}
1263
1264
	/**
1265
	 * Unshares a share given a share data array
1266
	 * @param array $item Share data (usually database row)
1267
	 * @param int new parent ID
1268
	 * @return null
1269
	 */
1270
	protected static function unshareItem(array $item, $newParent = null) {
1271
		// Pass all the vars we have for now, they may be useful
1272
		$hookParams = array(
1273
			'id'            => $item['id'],
1274
			'itemType'      => $item['item_type'],
1275
			'itemSource'    => $item['item_source'],
1276
			'shareType'     => (int)$item['share_type'],
1277
			'shareWith'     => $item['share_with'],
1278
			'itemParent'    => $item['parent'],
1279
			'uidOwner'      => $item['uid_owner'],
1280
		);
1281
		if($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
1282
			$hookParams['fileSource'] = $item['file_source'];
1283
			$hookParams['fileTarget'] = $item['file_target'];
1284
		}
1285
1286
		\OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams);
1287
		$deletedShares = Helper::delete($item['id'], false, null, $newParent);
1288
		$deletedShares[] = $hookParams;
1289
		$hookParams['deletedShares'] = $deletedShares;
1290
		\OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams);
1291
		if ((int)$item['share_type'] === \OCP\Share::SHARE_TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
1292
			$urlParts = explode('@', $item['share_with'], 2);
1293
			self::sendRemoteUnshare($urlParts[1], $item['id'], $item['token']);
1294
		}
1295
	}
1296
1297
	/**
1298
	 * Get the backend class for the specified item type
1299
	 * @param string $itemType
1300
	 * @throws \Exception
1301
	 * @return \OCP\Share_Backend
1302
	 */
1303
	public static function getBackend($itemType) {
1304
		$l = \OC::$server->getL10N('lib');
1305
		if (isset(self::$backends[$itemType])) {
1306
			return self::$backends[$itemType];
1307
		} else if (isset(self::$backendTypes[$itemType]['class'])) {
1308
			$class = self::$backendTypes[$itemType]['class'];
1309
			if (class_exists($class)) {
1310
				self::$backends[$itemType] = new $class;
1311
				if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) {
1312
					$message = 'Sharing backend %s must implement the interface OCP\Share_Backend';
1313
					$message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', array($class));
1314
					\OC_Log::write('OCP\Share', sprintf($message, $class), \OC_Log::ERROR);
1315
					throw new \Exception($message_t);
1316
				}
1317
				return self::$backends[$itemType];
1318 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...
1319
				$message = 'Sharing backend %s not found';
1320
				$message_t = $l->t('Sharing backend %s not found', array($class));
1321
				\OC_Log::write('OCP\Share', sprintf($message, $class), \OC_Log::ERROR);
1322
				throw new \Exception($message_t);
1323
			}
1324
		}
1325
		$message = 'Sharing backend for %s not found';
1326
		$message_t = $l->t('Sharing backend for %s not found', array($itemType));
1327
		\OC_Log::write('OCP\Share', sprintf($message, $itemType), \OC_Log::ERROR);
1328
		throw new \Exception($message_t);
1329
	}
1330
1331
	/**
1332
	 * Check if resharing is allowed
1333
	 * @return boolean true if allowed or false
1334
	 *
1335
	 * Resharing is allowed by default if not configured
1336
	 */
1337
	public static function isResharingAllowed() {
1338
		if (!isset(self::$isResharingAllowed)) {
1339
			if (\OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') {
1340
				self::$isResharingAllowed = true;
1341
			} else {
1342
				self::$isResharingAllowed = false;
1343
			}
1344
		}
1345
		return self::$isResharingAllowed;
1346
	}
1347
1348
	/**
1349
	 * Get a list of collection item types for the specified item type
1350
	 * @param string $itemType
1351
	 * @return array
1352
	 */
1353
	private static function getCollectionItemTypes($itemType) {
1354
		$collectionTypes = array($itemType);
1355
		foreach (self::$backendTypes as $type => $backend) {
1356
			if (in_array($backend['collectionOf'], $collectionTypes)) {
1357
				$collectionTypes[] = $type;
1358
			}
1359
		}
1360
		// TODO Add option for collections to be collection of themselves, only 'folder' does it now...
1361
		if (isset(self::$backendTypes[$itemType]) && (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder')) {
1362
			unset($collectionTypes[0]);
1363
		}
1364
		// Return array if collections were found or the item type is a
1365
		// collection itself - collections can be inside collections
1366
		if (count($collectionTypes) > 0) {
1367
			return $collectionTypes;
1368
		}
1369
		return false;
1370
	}
1371
1372
	/**
1373
	 * Get the owners of items shared with a user.
1374
	 *
1375
	 * @param string $user The user the items are shared with.
1376
	 * @param string $type The type of the items shared with the user.
1377
	 * @param boolean $includeCollections Include collection item types (optional)
1378
	 * @param boolean $includeOwner include owner in the list of users the item is shared with (optional)
1379
	 * @return array
1380
	 */
1381
	public static function getSharedItemsOwners($user, $type, $includeCollections = false, $includeOwner = false) {
1382
		// First, we find out if $type is part of a collection (and if that collection is part of
1383
		// another one and so on).
1384
		$collectionTypes = array();
1385
		if (!$includeCollections || !$collectionTypes = self::getCollectionItemTypes($type)) {
1386
			$collectionTypes[] = $type;
1387
		}
1388
1389
		// Of these collection types, along with our original $type, we make a
1390
		// list of the ones for which a sharing backend has been registered.
1391
		// FIXME: Ideally, we wouldn't need to nest getItemsSharedWith in this loop but just call it
1392
		// with its $includeCollections parameter set to true. Unfortunately, this fails currently.
1393
		$allMaybeSharedItems = array();
1394
		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...
1395
			if (isset(self::$backends[$collectionType])) {
1396
				$allMaybeSharedItems[$collectionType] = self::getItemsSharedWithUser(
1397
					$collectionType,
1398
					$user,
1399
					self::FORMAT_NONE
1400
				);
1401
			}
1402
		}
1403
1404
		$owners = array();
1405
		if ($includeOwner) {
1406
			$owners[] = $user;
1407
		}
1408
1409
		// We take a look at all shared items of the given $type (or of the collections it is part of)
1410
		// and find out their owners. Then, we gather the tags for the original $type from all owners,
1411
		// and return them as elements of a list that look like "Tag (owner)".
1412
		foreach ($allMaybeSharedItems as $collectionType => $maybeSharedItems) {
1413
			foreach ($maybeSharedItems as $sharedItem) {
1414
				if (isset($sharedItem['id'])) { //workaround for https://github.com/owncloud/core/issues/2814
1415
					$owners[] = $sharedItem['uid_owner'];
1416
				}
1417
			}
1418
		}
1419
1420
		return $owners;
1421
	}
1422
1423
	/**
1424
	 * Get shared items from the database
1425
	 * @param string $itemType
1426
	 * @param string $item Item source or target (optional)
1427
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique
1428
	 * @param string $shareWith User or group the item is being shared with
1429
	 * @param string $uidOwner User that is the owner of shared items (optional)
1430
	 * @param int $format Format to convert items to with formatItems() (optional)
1431
	 * @param mixed $parameters to pass to formatItems() (optional)
1432
	 * @param int $limit Number of items to return, -1 to return all matches (optional)
1433
	 * @param boolean $includeCollections Include collection item types (optional)
1434
	 * @param boolean $itemShareWithBySource (optional)
1435
	 * @param boolean $checkExpireDate
1436
	 * @return array
1437
	 *
1438
	 * See public functions getItem(s)... for parameter usage
1439
	 *
1440
	 */
1441
	public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null,
1442
									$uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1,
1443
									$includeCollections = false, $itemShareWithBySource = false, $checkExpireDate  = true) {
1444
		if (!self::isEnabled()) {
1445
			return array();
1446
		}
1447
		$backend = self::getBackend($itemType);
1448
		$collectionTypes = false;
1449
		// Get filesystem root to add it to the file target and remove from the
1450
		// file source, match file_source with the file cache
1451
		if ($itemType == 'file' || $itemType == 'folder') {
1452
			if(!is_null($uidOwner)) {
1453
				$root = \OC\Files\Filesystem::getRoot();
1454
			} else {
1455
				$root = '';
1456
			}
1457
			$where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
1458
			if (!isset($item)) {
1459
				$where .= ' AND `file_target` IS NOT NULL ';
1460
			}
1461
			$where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
1462
			$fileDependent = true;
1463
			$queryArgs = array();
1464
		} else {
1465
			$fileDependent = false;
1466
			$root = '';
1467
			$collectionTypes = self::getCollectionItemTypes($itemType);
1468
			if ($includeCollections && !isset($item) && $collectionTypes) {
1469
				// If includeCollections is true, find collections of this item type, e.g. a music album contains songs
1470
				if (!in_array($itemType, $collectionTypes)) {
1471
					$itemTypes = array_merge(array($itemType), $collectionTypes);
1472
				} else {
1473
					$itemTypes = $collectionTypes;
1474
				}
1475
				$placeholders = join(',', array_fill(0, count($itemTypes), '?'));
1476
				$where = ' WHERE `item_type` IN ('.$placeholders.'))';
1477
				$queryArgs = $itemTypes;
1478
			} else {
1479
				$where = ' WHERE `item_type` = ?';
1480
				$queryArgs = array($itemType);
1481
			}
1482
		}
1483
		if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
1484
			$where .= ' AND `share_type` != ?';
1485
			$queryArgs[] = self::SHARE_TYPE_LINK;
1486
		}
1487
		if (isset($shareType)) {
1488
			// Include all user and group items
1489
			if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
1490
				$where .= ' AND ((`share_type` in (?, ?) AND `share_with` = ?) ';
1491
				$queryArgs[] = self::SHARE_TYPE_USER;
1492
				$queryArgs[] = self::$shareTypeGroupUserUnique;
1493
				$queryArgs[] = $shareWith;
1494
				$groups = \OC_Group::getUserGroups($shareWith);
1495
				if (!empty($groups)) {
1496
					$placeholders = join(',', array_fill(0, count($groups), '?'));
1497
					$where .= ' OR (`share_type` = ? AND `share_with` IN ('.$placeholders.')) ';
1498
					$queryArgs[] = self::SHARE_TYPE_GROUP;
1499
					$queryArgs = array_merge($queryArgs, $groups);
1500
				}
1501
				$where .= ')';
1502
				// Don't include own group shares
1503
				$where .= ' AND `uid_owner` != ?';
1504
				$queryArgs[] = $shareWith;
1505
			} else {
1506
				$where .= ' AND `share_type` = ?';
1507
				$queryArgs[] = $shareType;
1508
				if (isset($shareWith)) {
1509
					$where .= ' AND `share_with` = ?';
1510
					$queryArgs[] = $shareWith;
1511
				}
1512
			}
1513
		}
1514
		if (isset($uidOwner)) {
1515
			$where .= ' AND `uid_owner` = ?';
1516
			$queryArgs[] = $uidOwner;
1517
			if (!isset($shareType)) {
1518
				// Prevent unique user targets for group shares from being selected
1519
				$where .= ' AND `share_type` != ?';
1520
				$queryArgs[] = self::$shareTypeGroupUserUnique;
1521
			}
1522
			if ($fileDependent) {
1523
				$column = 'file_source';
1524
			} else {
1525
				$column = 'item_source';
1526
			}
1527
		} else {
1528
			if ($fileDependent) {
1529
				$column = 'file_target';
1530
			} else {
1531
				$column = 'item_target';
1532
			}
1533
		}
1534
		if (isset($item)) {
1535
			$collectionTypes = self::getCollectionItemTypes($itemType);
1536
			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
1537
				$where .= ' AND (';
1538
			} else {
1539
				$where .= ' AND';
1540
			}
1541
			// If looking for own shared items, check item_source else check item_target
1542
			if (isset($uidOwner) || $itemShareWithBySource) {
1543
				// If item type is a file, file source needs to be checked in case the item was converted
1544
				if ($fileDependent) {
1545
					$where .= ' `file_source` = ?';
1546
					$column = 'file_source';
1547
				} else {
1548
					$where .= ' `item_source` = ?';
1549
					$column = 'item_source';
1550
				}
1551
			} else {
1552
				if ($fileDependent) {
1553
					$where .= ' `file_target` = ?';
1554
					$item = \OC\Files\Filesystem::normalizePath($item);
1555
				} else {
1556
					$where .= ' `item_target` = ?';
1557
				}
1558
			}
1559
			$queryArgs[] = $item;
1560
			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
1561
				$placeholders = join(',', array_fill(0, count($collectionTypes), '?'));
1562
				$where .= ' OR `item_type` IN ('.$placeholders.'))';
1563
				$queryArgs = array_merge($queryArgs, $collectionTypes);
1564
			}
1565
		}
1566
1567
		if ($shareType == self::$shareTypeUserAndGroups && $limit === 1) {
1568
			// Make sure the unique user target is returned if it exists,
1569
			// unique targets should follow the group share in the database
1570
			// If the limit is not 1, the filtering can be done later
1571
			$where .= ' ORDER BY `*PREFIX*share`.`id` DESC';
1572
		} else {
1573
			$where .= ' ORDER BY `*PREFIX*share`.`id` ASC';
1574
		}
1575
1576
		if ($limit != -1 && !$includeCollections) {
1577
			// The limit must be at least 3, because filtering needs to be done
1578
			if ($limit < 3) {
1579
				$queryLimit = 3;
1580
			} else {
1581
				$queryLimit = $limit;
1582
			}
1583
		} else {
1584
			$queryLimit = null;
1585
		}
1586
		$select = self::createSelectStatement($format, $fileDependent, $uidOwner);
1587
		$root = strlen($root);
1588
		$query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit);
1589
		$result = $query->execute($queryArgs);
1590 View Code Duplication
		if (\OC_DB::isError($result)) {
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...
1591
			\OC_Log::write('OCP\Share',
1592
				\OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=',
1593
				\OC_Log::ERROR);
1594
		}
1595
		$items = array();
1596
		$targets = array();
1597
		$switchedItems = array();
1598
		$mounts = array();
1599
		while ($row = $result->fetchRow()) {
1600
			self::transformDBResults($row);
1601
			// Filter out duplicate group shares for users with unique targets
1602
			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
1603
				continue;
1604
			}
1605
			if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) {
1606
				$row['share_type'] = self::SHARE_TYPE_GROUP;
1607
				$row['unique_name'] = true; // remember that we use a unique name for this user
1608
				$row['share_with'] = $items[$row['parent']]['share_with'];
1609
				// if the group share was unshared from the user we keep the permission, otherwise
1610
				// we take the permission from the parent because this is always the up-to-date
1611
				// permission for the group share
1612
				if ($row['permissions'] > 0) {
1613
					$row['permissions'] = $items[$row['parent']]['permissions'];
1614
				}
1615
				// Remove the parent group share
1616
				unset($items[$row['parent']]);
1617
				if ($row['permissions'] == 0) {
1618
					continue;
1619
				}
1620
			} else if (!isset($uidOwner)) {
1621
				// Check if the same target already exists
1622
				if (isset($targets[$row['id']])) {
1623
					// Check if the same owner shared with the user twice
1624
					// through a group and user share - this is allowed
1625
					$id = $targets[$row['id']];
1626
					if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
1627
						// Switch to group share type to ensure resharing conditions aren't bypassed
1628
						if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) {
1629
							$items[$id]['share_type'] = self::SHARE_TYPE_GROUP;
1630
							$items[$id]['share_with'] = $row['share_with'];
1631
						}
1632
						// Switch ids if sharing permission is granted on only
1633
						// one share to ensure correct parent is used if resharing
1634
						if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
1635
							&& (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
1636
							$items[$row['id']] = $items[$id];
1637
							$switchedItems[$id] = $row['id'];
1638
							unset($items[$id]);
1639
							$id = $row['id'];
1640
						}
1641
						$items[$id]['permissions'] |= (int)$row['permissions'];
1642
1643
					}
1644
					continue;
1645
				} elseif (!empty($row['parent'])) {
1646
					$targets[$row['parent']] = $row['id'];
1647
				}
1648
			}
1649
			// Remove root from file source paths if retrieving own shared items
1650
			if (isset($uidOwner) && isset($row['path'])) {
1651
				if (isset($row['parent'])) {
1652
					$query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?');
1653
					$parentResult = $query->execute(array($row['parent']));
1654
					if (\OC_DB::isError($result)) {
1655
						\OC_Log::write('OCP\Share', 'Can\'t select parent: ' .
1656
							\OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where,
1657
							\OC_Log::ERROR);
1658
					} else {
1659
						$parentRow = $parentResult->fetchRow();
1660
						$tmpPath = $parentRow['file_target'];
1661
						// find the right position where the row path continues from the target path
1662
						$pos = strrpos($row['path'], $parentRow['file_target']);
1663
						$subPath = substr($row['path'], $pos);
1664
						$splitPath = explode('/', $subPath);
1665
						foreach (array_slice($splitPath, 2) as $pathPart) {
1666
							$tmpPath = $tmpPath . '/' . $pathPart;
1667
						}
1668
						$row['path'] = $tmpPath;
1669
					}
1670
				} else {
1671
					if (!isset($mounts[$row['storage']])) {
1672
						$mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']);
1673
						if (is_array($mountPoints) && !empty($mountPoints)) {
1674
							$mounts[$row['storage']] = current($mountPoints);
1675
						}
1676
					}
1677
					if (!empty($mounts[$row['storage']])) {
1678
						$path = $mounts[$row['storage']]->getMountPoint().$row['path'];
1679
						$relPath = substr($path, $root); // path relative to data/user
1680
						$row['path'] = rtrim($relPath, '/');
1681
					}
1682
				}
1683
			}
1684
1685
			if($checkExpireDate) {
1686
				if (self::expireItem($row)) {
1687
					continue;
1688
				}
1689
			}
1690
			// Check if resharing is allowed, if not remove share permission
1691
			if (isset($row['permissions']) && (!self::isResharingAllowed() | \OC_Util::isSharingDisabledForUser())) {
1692
				$row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE;
1693
			}
1694
			// Add display names to result
1695
			if ( isset($row['share_with']) && $row['share_with'] != '' &&
1696
				isset($row['share_with']) && $row['share_type'] === self::SHARE_TYPE_USER) {
1697
				$row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']);
1698
			} else {
1699
				$row['share_with_displayname'] = $row['share_with'];
1700
			}
1701
			if ( isset($row['uid_owner']) && $row['uid_owner'] != '') {
1702
				$row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']);
1703
			}
1704
1705
			if ($row['permissions'] > 0) {
1706
				$items[$row['id']] = $row;
1707
			}
1708
1709
		}
1710
1711
		// group items if we are looking for items shared with the current user
1712
		if (isset($shareWith) && $shareWith === \OCP\User::getUser()) {
1713
			$items = self::groupItems($items, $itemType);
1714
		}
1715
1716
		if (!empty($items)) {
1717
			$collectionItems = array();
1718
			foreach ($items as &$row) {
1719
				// Return only the item instead of a 2-dimensional array
1720
				if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) {
1721
					if ($format == self::FORMAT_NONE) {
1722
						return $row;
1723
					} else {
1724
						break;
1725
					}
1726
				}
1727
				// Check if this is a collection of the requested item type
1728
				if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) {
1729
					if (($collectionBackend = self::getBackend($row['item_type']))
1730
						&& $collectionBackend instanceof \OCP\Share_Backend_Collection) {
1731
						// Collections can be inside collections, check if the item is a collection
1732
						if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
1733
							$collectionItems[] = $row;
1734
						} else {
1735
							$collection = array();
1736
							$collection['item_type'] = $row['item_type'];
1737
							if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
1738
								$collection['path'] = basename($row['path']);
1739
							}
1740
							$row['collection'] = $collection;
1741
							// Fetch all of the children sources
1742
							$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...
1743
							foreach ($children as $child) {
1744
								$childItem = $row;
1745
								$childItem['item_type'] = $itemType;
1746
								if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
1747
									$childItem['item_source'] = $child['source'];
1748
									$childItem['item_target'] = $child['target'];
1749
								}
1750
								if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
1751
									if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
1752
										$childItem['file_source'] = $child['source'];
1753
									} else { // TODO is this really needed if we already know that we use the file backend?
1754
										$meta = \OC\Files\Filesystem::getFileInfo($child['file_path']);
1755
										$childItem['file_source'] = $meta['fileid'];
1756
									}
1757
									$childItem['file_target'] =
1758
										\OC\Files\Filesystem::normalizePath($child['file_path']);
1759
								}
1760
								if (isset($item)) {
1761
									if ($childItem[$column] == $item) {
1762
										// Return only the item instead of a 2-dimensional array
1763
										if ($limit == 1) {
1764
											if ($format == self::FORMAT_NONE) {
1765
												return $childItem;
1766
											} else {
1767
												// Unset the items array and break out of both loops
1768
												$items = array();
1769
												$items[] = $childItem;
1770
												break 2;
1771
											}
1772
										} else {
1773
											$collectionItems[] = $childItem;
1774
										}
1775
									}
1776
								} else {
1777
									$collectionItems[] = $childItem;
1778
								}
1779
							}
1780
						}
1781
					}
1782
					// Remove collection item
1783
					$toRemove = $row['id'];
1784
					if (array_key_exists($toRemove, $switchedItems)) {
1785
						$toRemove = $switchedItems[$toRemove];
1786
					}
1787
					unset($items[$toRemove]);
1788
				} elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
1789
					// FIXME: Thats a dirty hack to improve file sharing performance,
1790
					// see github issue #10588 for more details
1791
					// Need to find a solution which works for all back-ends
1792
					$collectionBackend = self::getBackend($row['item_type']);
1793
					$sharedParents = $collectionBackend->getParents($row['item_source']);
1794
					foreach ($sharedParents as $parent) {
1795
						$collectionItems[] = $parent;
1796
					}
1797
				}
1798
			}
1799
			if (!empty($collectionItems)) {
1800
				$items = array_merge($items, $collectionItems);
1801
			}
1802
1803
			return self::formatResult($items, $column, $backend, $format, $parameters);
0 ignored issues
show
Bug Compatibility introduced by
The expression self::formatResult($item... $format, $parameters); of type array|OCP\TODO adds the type OCP\TODO to the return on line 1803 which is incompatible with the return type documented by OC\Share\Share::getItems of type array.
Loading history...
1804
		} elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) {
1805
			// FIXME: Thats a dirty hack to improve file sharing performance,
1806
			// see github issue #10588 for more details
1807
			// Need to find a solution which works for all back-ends
1808
			$collectionItems = array();
1809
			$collectionBackend = self::getBackend('folder');
1810
			$sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner);
1811
			foreach ($sharedParents as $parent) {
1812
				$collectionItems[] = $parent;
1813
			}
1814
			if ($limit === 1) {
1815
				return reset($collectionItems);
1816
			}
1817
			return self::formatResult($collectionItems, $column, $backend, $format, $parameters);
0 ignored issues
show
Bug Compatibility introduced by
The expression self::formatResult($coll... $format, $parameters); of type array|OCP\TODO adds the type OCP\TODO to the return on line 1817 which is incompatible with the return type documented by OC\Share\Share::getItems of type array.
Loading history...
1818
		}
1819
1820
		return array();
1821
	}
1822
1823
	/**
1824
	 * group items with link to the same source
1825
	 *
1826
	 * @param array $items
1827
	 * @param string $itemType
1828
	 * @return array of grouped items
1829
	 */
1830
	protected static function groupItems($items, $itemType) {
1831
1832
		$fileSharing = ($itemType === 'file' || $itemType === 'folder') ? true : false;
1833
1834
		$result = array();
1835
1836
		foreach ($items as $item) {
1837
			$grouped = false;
1838
			foreach ($result as $key => $r) {
1839
				// for file/folder shares we need to compare file_source, otherwise we compare item_source
1840
				// only group shares if they already point to the same target, otherwise the file where shared
1841
				// before grouping of shares was added. In this case we don't group them toi avoid confusions
1842
				if (( $fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
1843
					(!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
1844
					// add the first item to the list of grouped shares
1845
					if (!isset($result[$key]['grouped'])) {
1846
						$result[$key]['grouped'][] = $result[$key];
1847
					}
1848
					$result[$key]['permissions'] = (int) $item['permissions'] | (int) $r['permissions'];
1849
					$result[$key]['grouped'][] = $item;
1850
					$grouped = true;
1851
					break;
1852
				}
1853
			}
1854
1855
			if (!$grouped) {
1856
				$result[] = $item;
1857
			}
1858
1859
		}
1860
1861
		return $result;
1862
	}
1863
1864
	/**
1865
	 * Put shared item into the database
1866
	 * @param string $itemType Item type
1867
	 * @param string $itemSource Item source
1868
	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1869
	 * @param string $shareWith User or group the item is being shared with
1870
	 * @param string $uidOwner User that is the owner of shared item
1871
	 * @param int $permissions CRUDS permissions
1872
	 * @param boolean|array $parentFolder Parent folder target (optional)
1873
	 * @param string $token (optional)
1874
	 * @param string $itemSourceName name of the source item (optional)
1875
	 * @param \DateTime $expirationDate (optional)
1876
	 * @throws \Exception
1877
	 * @return mixed id of the new share or false
1878
	 */
1879
	private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
1880
								$permissions, $parentFolder = null, $token = null, $itemSourceName = null, \DateTime $expirationDate = null) {
1881
1882
		$queriesToExecute = array();
1883
		$suggestedItemTarget = null;
1884
1885
		$result = self::checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate);
1886
		if(!empty($result)) {
1887
			$parent = $result['parent'];
1888
			$itemSource = $result['itemSource'];
1889
			$fileSource = $result['fileSource'];
1890
			$suggestedItemTarget = $result['suggestedItemTarget'];
1891
			$suggestedFileTarget = $result['suggestedFileTarget'];
1892
			$filePath = $result['filePath'];
1893
			$expirationDate = $result['expirationDate'];
1894
		}
1895
1896
		$isGroupShare = false;
1897
		if ($shareType == self::SHARE_TYPE_GROUP) {
1898
			$isGroupShare = true;
1899
			$users = \OC_Group::usersInGroup($shareWith['group']);
1900
			// remove current user from list
1901
			if (in_array(\OCP\User::getUser(), $users)) {
1902
				unset($users[array_search(\OCP\User::getUser(), $users)]);
1903
			}
1904
			$groupItemTarget = Helper::generateTarget($itemType, $itemSource,
1905
				$shareType, $shareWith['group'], $uidOwner, $suggestedItemTarget);
1906
			$groupFileTarget = Helper::generateTarget($itemType, $itemSource,
1907
				$shareType, $shareWith['group'], $uidOwner, $filePath);
0 ignored issues
show
Bug introduced by
The variable $filePath 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...
1908
1909
			// add group share to table and remember the id as parent
1910
			$queriesToExecute['groupShare'] = array(
1911
				'itemType'			=> $itemType,
1912
				'itemSource'		=> $itemSource,
1913
				'itemTarget'		=> $groupItemTarget,
1914
				'shareType'			=> $shareType,
1915
				'shareWith'			=> $shareWith['group'],
1916
				'uidOwner'			=> $uidOwner,
1917
				'permissions'		=> $permissions,
1918
				'shareTime'			=> time(),
1919
				'fileSource'		=> $fileSource,
0 ignored issues
show
Bug introduced by
The variable $fileSource 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...
1920
				'fileTarget'		=> $groupFileTarget,
1921
				'token'				=> $token,
1922
				'parent'			=> $parent,
0 ignored issues
show
Bug introduced by
The variable $parent 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...
1923
				'expiration'		=> $expirationDate,
1924
			);
1925
1926
		} else {
1927
			$users = array($shareWith);
1928
			$itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
1929
				$suggestedItemTarget);
1930
		}
1931
1932
		$run = true;
1933
		$error = '';
1934
		$preHookData = array(
1935
			'itemType' => $itemType,
1936
			'itemSource' => $itemSource,
1937
			'shareType' => $shareType,
1938
			'uidOwner' => $uidOwner,
1939
			'permissions' => $permissions,
1940
			'fileSource' => $fileSource,
1941
			'expiration' => $expirationDate,
1942
			'token' => $token,
1943
			'run' => &$run,
1944
			'error' => &$error
1945
		);
1946
1947
		$preHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget;
0 ignored issues
show
Bug introduced by
The variable $groupItemTarget 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 introduced by
The variable $itemTarget 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...
1948
		$preHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith;
1949
1950
		\OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
1951
1952
		if ($run === false) {
1953
			throw new \Exception($error);
1954
		}
1955
1956
		foreach ($users as $user) {
1957
			$sourceId = ($itemType === 'file' || $itemType === 'folder') ? $fileSource : $itemSource;
1958
			$sourceExists = self::getItemSharedWithBySource($itemType, $sourceId, self::FORMAT_NONE, null, true, $user);
1959
1960
			$userShareType = ($isGroupShare) ? self::$shareTypeGroupUserUnique : $shareType;
1961
1962
			if ($sourceExists) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sourceExists of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
1963
				$fileTarget = $sourceExists['file_target'];
1964
				$itemTarget = $sourceExists['item_target'];
1965
1966
				// for group shares we don't need a additional entry if the target is the same
1967
				if($isGroupShare && $groupItemTarget === $itemTarget) {
1968
					continue;
1969
				}
1970
1971
			} 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...
1972
1973
				$itemTarget = Helper::generateTarget($itemType, $itemSource, $userShareType, $user,
1974
					$uidOwner, $suggestedItemTarget, $parent);
1975
				if (isset($fileSource)) {
1976
					if ($parentFolder) {
1977
						if ($parentFolder === true) {
1978
							$fileTarget = Helper::generateTarget('file', $filePath, $userShareType, $user,
1979
								$uidOwner, $suggestedFileTarget, $parent);
0 ignored issues
show
Bug introduced by
The variable $suggestedFileTarget 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...
1980
							if ($fileTarget != $groupFileTarget) {
0 ignored issues
show
Bug introduced by
The variable $groupFileTarget 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...
1981
								$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...
1982
							}
1983
						} else if (isset($parentFolder[$user])) {
1984
							$fileTarget = $parentFolder[$user]['folder'].$itemSource;
1985
							$parent = $parentFolder[$user]['id'];
1986
						}
1987
					} else {
1988
						$fileTarget = Helper::generateTarget('file', $filePath, $userShareType,
1989
							$user, $uidOwner, $suggestedFileTarget, $parent);
1990
					}
1991
				} else {
1992
					$fileTarget = null;
1993
				}
1994
1995
			} else {
1996
1997
				// group share which doesn't exists until now, check if we need a unique target for this user
1998
1999
				$itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $user,
2000
					$uidOwner, $suggestedItemTarget, $parent);
2001
2002
				// do we also need a file target
2003
				if (isset($fileSource)) {
2004
					$fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $user,
2005
						$uidOwner, $suggestedFileTarget, $parent);
2006
				} else {
2007
					$fileTarget = null;
2008
				}
2009
2010
				if ($itemTarget === $groupItemTarget && (isset($fileSource) && $fileTarget === $groupItemTarget)) {
2011
					continue;
2012
				}
2013
			}
2014
2015
			$queriesToExecute[] = array(
2016
				'itemType'			=> $itemType,
2017
				'itemSource'		=> $itemSource,
2018
				'itemTarget'		=> $itemTarget,
2019
				'shareType'			=> $userShareType,
2020
				'shareWith'			=> $user,
2021
				'uidOwner'			=> $uidOwner,
2022
				'permissions'		=> $permissions,
2023
				'shareTime'			=> time(),
2024
				'fileSource'		=> $fileSource,
2025
				'fileTarget'		=> $fileTarget,
0 ignored issues
show
Bug introduced by
The variable $fileTarget 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...
2026
				'token'				=> $token,
2027
				'parent'			=> $parent,
2028
				'expiration'		=> $expirationDate,
2029
			);
2030
2031
		}
2032
2033
		$id = false;
2034
		if ($isGroupShare) {
2035
			$id = self::insertShare($queriesToExecute['groupShare']);
2036
			// Save this id, any extra rows for this group share will need to reference it
2037
			$parent = \OC_DB::insertid('*PREFIX*share');
2038
			unset($queriesToExecute['groupShare']);
2039
		}
2040
2041
		foreach ($queriesToExecute as $shareQuery) {
2042
			$shareQuery['parent'] = $parent;
2043
			$id = self::insertShare($shareQuery);
2044
		}
2045
2046
		$postHookData = array(
2047
			'itemType' => $itemType,
2048
			'itemSource' => $itemSource,
2049
			'parent' => $parent,
2050
			'shareType' => $shareType,
2051
			'uidOwner' => $uidOwner,
2052
			'permissions' => $permissions,
2053
			'fileSource' => $fileSource,
2054
			'id' => $parent,
2055
			'token' => $token,
2056
			'expirationDate' => $expirationDate,
2057
		);
2058
2059
		$postHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith;
2060
		$postHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget;
2061
		$postHookData['fileTarget'] = ($isGroupShare) ? $groupFileTarget : $fileTarget;
2062
2063
		\OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
2064
2065
2066
		return $id ? $id : false;
2067
	}
2068
2069
	private static function checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate) {
2070
		$backend = self::getBackend($itemType);
2071
2072
		$l = \OC::$server->getL10N('lib');
2073
		$result = array();
2074
2075
		$column = ($itemType === 'file' || $itemType === 'folder') ? 'file_source' : 'item_source';
2076
2077
		$checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true);
2078
		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...
2079
			// Check if attempting to share back to owner
2080
			if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) {
2081
				$message = 'Sharing %s failed, because the user %s is the original sharer';
2082
				$message_t = $l->t('Sharing %s failed, because the user %s is the original sharer', array($itemSourceName, $shareWith));
2083
2084
				\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR);
2085
				throw new \Exception($message_t);
2086
			}
2087
		}
2088
2089
		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...
2090
			// Check if share permissions is granted
2091
			if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
2092
				if (~(int)$checkReshare['permissions'] & $permissions) {
2093
					$message = 'Sharing %s failed, because the permissions exceed permissions granted to %s';
2094
					$message_t = $l->t('Sharing %s failed, because the permissions exceed permissions granted to %s', array($itemSourceName, $uidOwner));
2095
2096
					\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $uidOwner), \OC_Log::ERROR);
2097
					throw new \Exception($message_t);
2098
				} else {
2099
					// TODO Don't check if inside folder
2100
					$result['parent'] = $checkReshare['id'];
2101
					$result['expirationDate'] = min($expirationDate, $checkReshare['expiration']);
2102
					// only suggest the same name as new target if it is a reshare of the
2103
					// same file/folder and not the reshare of a child
2104
					if ($checkReshare[$column] === $itemSource) {
2105
						$result['filePath'] = $checkReshare['file_target'];
2106
						$result['itemSource'] = $checkReshare['item_source'];
2107
						$result['fileSource'] = $checkReshare['file_source'];
2108
						$result['suggestedItemTarget'] = $checkReshare['item_target'];
2109
						$result['suggestedFileTarget'] = $checkReshare['file_target'];
2110
					} else {
2111
						$result['filePath'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $backend->getFilePath($itemSource, $uidOwner) : null;
2112
						$result['suggestedItemTarget'] = null;
2113
						$result['suggestedFileTarget'] = null;
2114
						$result['itemSource'] = $itemSource;
2115
						$result['fileSource'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $itemSource : null;
2116
					}
2117
				}
2118 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...
2119
				$message = 'Sharing %s failed, because resharing is not allowed';
2120
				$message_t = $l->t('Sharing %s failed, because resharing is not allowed', array($itemSourceName));
2121
2122
				\OC_Log::write('OCP\Share', sprintf($message, $itemSourceName), \OC_Log::ERROR);
2123
				throw new \Exception($message_t);
2124
			}
2125
		} else {
2126
			$result['parent'] = null;
2127
			$result['suggestedItemTarget'] = null;
2128
			$result['suggestedFileTarget'] = null;
2129
			$result['itemSource'] = $itemSource;
2130
			$result['expirationDate'] = $expirationDate;
2131 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...
2132
				$message = 'Sharing %s failed, because the sharing backend for '
2133
					.'%s could not find its source';
2134
				$message_t = $l->t('Sharing %s failed, because the sharing backend for %s could not find its source', array($itemSource, $itemType));
2135
				\OC_Log::write('OCP\Share', sprintf($message, $itemSource, $itemType), \OC_Log::ERROR);
2136
				throw new \Exception($message_t);
2137
			}
2138
			if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
2139
				$result['filePath'] = $backend->getFilePath($itemSource, $uidOwner);
2140
				if ($itemType == 'file' || $itemType == 'folder') {
2141
					$result['fileSource'] = $itemSource;
2142
				} else {
2143
					$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...
2144
					$result['fileSource'] = $meta['fileid'];
2145
				}
2146 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...
2147
					$message = 'Sharing %s failed, because the file could not be found in the file cache';
2148
					$message_t = $l->t('Sharing %s failed, because the file could not be found in the file cache', array($itemSource));
2149
2150
					\OC_Log::write('OCP\Share', sprintf($message, $itemSource), \OC_Log::ERROR);
2151
					throw new \Exception($message_t);
2152
				}
2153
			} else {
2154
				$result['filePath'] = null;
2155
				$result['fileSource'] = null;
2156
			}
2157
		}
2158
2159
		return $result;
2160
	}
2161
2162
	/**
2163
	 *
2164
	 * @param array $shareData
2165
	 * @return mixed false in case of a failure or the id of the new share
2166
	 */
2167
	private static function insertShare(array $shareData)
2168
	{
2169
		$query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` ('
2170
			.' `item_type`, `item_source`, `item_target`, `share_type`,'
2171
			.' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,'
2172
			.' `file_target`, `token`, `parent`, `expiration`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)');
2173
		$query->bindValue(1, $shareData['itemType']);
2174
		$query->bindValue(2, $shareData['itemSource']);
2175
		$query->bindValue(3, $shareData['itemTarget']);
2176
		$query->bindValue(4, $shareData['shareType']);
2177
		$query->bindValue(5, $shareData['shareWith']);
2178
		$query->bindValue(6, $shareData['uidOwner']);
2179
		$query->bindValue(7, $shareData['permissions']);
2180
		$query->bindValue(8, $shareData['shareTime']);
2181
		$query->bindValue(9, $shareData['fileSource']);
2182
		$query->bindValue(10, $shareData['fileTarget']);
2183
		$query->bindValue(11, $shareData['token']);
2184
		$query->bindValue(12, $shareData['parent']);
2185
		$query->bindValue(13, $shareData['expiration'], 'datetime');
2186
		$result = $query->execute();
2187
2188
		$id = false;
2189
		if ($result) {
2190
			$id =  \OC::$server->getDatabaseConnection()->lastInsertId();
2191
			// Fallback, if lastInterId() doesn't work we need to perform a select
2192
			// to get the ID (seems to happen sometimes on Oracle)
2193
			if (!$id) {
2194
				$getId = \OC_DB::prepare('
2195
					SELECT `id`
2196
					FROM`*PREFIX*share`
2197
					WHERE `uid_owner` = ? AND `item_target` = ? AND `item_source` = ? AND `stime` = ?
2198
					');
2199
				$r = $getId->execute(array($shareData['uidOwner'], $shareData['itemTarget'], $shareData['itemSource'], $shareData['shareTime']));
2200
				if ($r) {
2201
					$row = $r->fetchRow();
2202
					$id = $row['id'];
2203
				}
2204
			}
2205
2206
		}
2207
2208
		return $id;
2209
2210
	}
2211
	/**
2212
	 * Delete all shares with type SHARE_TYPE_LINK
2213
	 */
2214
	public static function removeAllLinkShares() {
2215
		// Delete any link shares
2216
		$query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ?');
2217
		$result = $query->execute(array(self::SHARE_TYPE_LINK));
2218
		while ($item = $result->fetchRow()) {
2219
			Helper::delete($item['id']);
2220
		}
2221
	}
2222
2223
	/**
2224
	 * In case a password protected link is not yet authenticated this function will return false
2225
	 *
2226
	 * @param array $linkItem
2227
	 * @return boolean
2228
	 */
2229
	public static function checkPasswordProtectedShare(array $linkItem) {
2230
		if (!isset($linkItem['share_with'])) {
2231
			return true;
2232
		}
2233
		if (!isset($linkItem['share_type'])) {
2234
			return true;
2235
		}
2236
		if (!isset($linkItem['id'])) {
2237
			return true;
2238
		}
2239
2240
		if ($linkItem['share_type'] != \OCP\Share::SHARE_TYPE_LINK) {
2241
			return true;
2242
		}
2243
2244 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...
2245
			&& \OC::$server->getSession()->get('public_link_authenticated') === $linkItem['id'] ) {
2246
			return true;
2247
		}
2248
2249
		return false;
2250
	}
2251
2252
	/**
2253
	 * construct select statement
2254
	 * @param int $format
2255
	 * @param boolean $fileDependent ist it a file/folder share or a generla share
2256
	 * @param string $uidOwner
2257
	 * @return string select statement
2258
	 */
2259
	private static function createSelectStatement($format, $fileDependent, $uidOwner = null) {
2260
		$select = '*';
2261
		if ($format == self::FORMAT_STATUSES) {
2262
			if ($fileDependent) {
2263
				$select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, '
2264
					. '`share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`, '
2265
					. '`*PREFIX*storages`.`id` AS `storage_id`';
2266
			} else {
2267
				$select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`, `*PREFIX*share`.`permissions`';
2268
			}
2269
		} else {
2270
			if (isset($uidOwner)) {
2271
				if ($fileDependent) {
2272
					$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,'
2273
						. ' `share_type`, `share_with`, `file_source`, `file_target`, `path`, `*PREFIX*share`.`permissions`, `stime`,'
2274
						. ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`, '
2275
						. '`*PREFIX*storages`.`id` AS `storage_id`';
2276
				} else {
2277
					$select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `*PREFIX*share`.`permissions`,'
2278
						. ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`';
2279
				}
2280
			} else {
2281
				if ($fileDependent) {
2282
					if ($format == \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS || $format == \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT) {
2283
						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, '
2284
							. '`share_type`, `share_with`, `file_source`, `path`, `file_target`, `stime`, '
2285
							. '`*PREFIX*share`.`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
2286
							. '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `unencrypted_size`, `encrypted`, `etag`, `mail_send`';
2287
					} else {
2288
						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,'
2289
							. '`*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,'
2290
							. '`file_source`, `path`, `file_target`, `*PREFIX*share`.`permissions`,'
2291
						    . '`stime`, `expiration`, `token`, `storage`, `mail_send`,'
2292
							. '`*PREFIX*storages`.`id` AS `storage_id`';
2293
					}
2294
				}
2295
			}
2296
		}
2297
		return $select;
2298
	}
2299
2300
2301
	/**
2302
	 * transform db results
2303
	 * @param array $row result
2304
	 */
2305
	private static function transformDBResults(&$row) {
2306
		if (isset($row['id'])) {
2307
			$row['id'] = (int) $row['id'];
2308
		}
2309
		if (isset($row['share_type'])) {
2310
			$row['share_type'] = (int) $row['share_type'];
2311
		}
2312
		if (isset($row['parent'])) {
2313
			$row['parent'] = (int) $row['parent'];
2314
		}
2315
		if (isset($row['file_parent'])) {
2316
			$row['file_parent'] = (int) $row['file_parent'];
2317
		}
2318
		if (isset($row['file_source'])) {
2319
			$row['file_source'] = (int) $row['file_source'];
2320
		}
2321
		if (isset($row['permissions'])) {
2322
			$row['permissions'] = (int) $row['permissions'];
2323
		}
2324
		if (isset($row['storage'])) {
2325
			$row['storage'] = (int) $row['storage'];
2326
		}
2327
		if (isset($row['stime'])) {
2328
			$row['stime'] = (int) $row['stime'];
2329
		}
2330
	}
2331
2332
	/**
2333
	 * format result
2334
	 * @param array $items result
2335
	 * @param string $column is it a file share or a general share ('file_target' or 'item_target')
2336
	 * @param \OCP\Share_Backend $backend sharing backend
2337
	 * @param int $format
2338
	 * @param array $parameters additional format parameters
2339
	 * @return array format result
2340
	 */
2341
	private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) {
2342
		if ($format === self::FORMAT_NONE) {
2343
			return $items;
2344
		} else if ($format === self::FORMAT_STATUSES) {
2345
			$statuses = array();
2346
			foreach ($items as $item) {
2347
				if ($item['share_type'] === self::SHARE_TYPE_LINK) {
2348
					$statuses[$item[$column]]['link'] = true;
2349
				} else if (!isset($statuses[$item[$column]])) {
2350
					$statuses[$item[$column]]['link'] = false;
2351
				}
2352
				if (!empty($item['file_target'])) {
2353
					$statuses[$item[$column]]['path'] = $item['path'];
2354
				}
2355
			}
2356
			return $statuses;
2357
		} else {
2358
			return $backend->formatItems($items, $format, $parameters);
2359
		}
2360
	}
2361
2362
	/**
2363
	 * remove protocol from URL
2364
	 *
2365
	 * @param string $url
2366
	 * @return string
2367
	 */
2368
	public static function removeProtocolFromUrl($url) {
2369
		if (strpos($url, 'https://') === 0) {
2370
			return substr($url, strlen('https://'));
2371
		} else if (strpos($url, 'http://') === 0) {
2372
			return substr($url, strlen('http://'));
2373
		}
2374
2375
		return $url;
2376
	}
2377
2378
	/**
2379
	 * try http post first with https and then with http as a fallback
2380
	 *
2381
	 * @param string $url
2382
	 * @param array $fields post parameters
2383
	 * @return array
2384
	 */
2385
	private static function tryHttpPost($url, $fields) {
2386
		$protocol = 'https://';
2387
		$result = [
2388
			'success' => false,
2389
			'result' => '',
2390
		];
2391
		$try = 0;
2392
		while ($result['success'] === false && $try < 2) {
2393
			$result = \OC::$server->getHTTPHelper()->post($protocol . $url, $fields);
2394
			$try++;
2395
			$protocol = 'http://';
2396
		}
2397
2398
		return $result;
2399
	}
2400
2401
	/**
2402
	 * send server-to-server share to remote server
2403
	 *
2404
	 * @param string $token
2405
	 * @param string $shareWith
2406
	 * @param string $name
2407
	 * @param int $remote_id
2408
	 * @param string $owner
2409
	 * @return bool
2410
	 */
2411
	private static function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner) {
2412
2413
		list($user, $remote) = explode('@', $shareWith, 2);
2414
2415
		if ($user && $remote) {
2416
			$url = rtrim($remote, '/') . self::BASE_PATH_TO_SHARE_API . '?format=' . self::RESPONSE_FORMAT;
2417
2418
			$local = \OC::$server->getURLGenerator()->getAbsoluteURL('/');
2419
2420
			$fields = array(
2421
				'shareWith' => $user,
2422
				'token' => $token,
2423
				'name' => $name,
2424
				'remoteId' => $remote_id,
2425
				'owner' => $owner,
2426
				'remote' => $local,
2427
			);
2428
2429
			$url = self::removeProtocolFromUrl($url);
2430
			$result = self::tryHttpPost($url, $fields);
2431
			$status = json_decode($result['result'], true);
2432
2433
			return ($result['success'] && $status['ocs']['meta']['statuscode'] === 100);
2434
2435
		}
2436
2437
		return false;
2438
	}
2439
2440
	/**
2441
	 * send server-to-server unshare to remote server
2442
	 *
2443
	 * @param string remote url
2444
	 * @param int $id share id
2445
	 * @param string $token
2446
	 * @return bool
2447
	 */
2448
	private static function sendRemoteUnshare($remote, $id, $token) {
2449
		$url = rtrim($remote, '/') . self::BASE_PATH_TO_SHARE_API . '/' . $id . '/unshare?format=' . self::RESPONSE_FORMAT;
2450
		$fields = array('token' => $token, 'format' => 'json');
2451
		$url = self::removeProtocolFromUrl($url);
2452
		$result = self::tryHttpPost($url, $fields);
2453
		$status = json_decode($result['result'], true);
2454
2455
		return ($result['success'] && $status['ocs']['meta']['statuscode'] === 100);
2456
	}
2457
2458
	/**
2459
	 * check if user can only share with group members
2460
	 * @return bool
2461
	 */
2462
	public static function shareWithGroupMembersOnly() {
2463
		$value = \OC_Appconfig::getValue('core', 'shareapi_only_share_with_group_members', 'no');
2464
		return ($value === 'yes') ? true : false;
2465
	}
2466
2467
	public static function isDefaultExpireDateEnabled() {
2468
		$defaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
2469
		return ($defaultExpireDateEnabled === "yes") ? true : false;
2470
	}
2471
2472
	public static function enforceDefaultExpireDate() {
2473
		$enforceDefaultExpireDate = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
2474
		return ($enforceDefaultExpireDate === "yes") ? true : false;
2475
	}
2476
2477
	public static function getExpireInterval() {
2478
		return (int)\OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
2479
	}
2480
2481
	/**
2482
	 * Checks whether the given path is reachable for the given owner
2483
	 *
2484
	 * @param string $path path relative to files
2485
	 * @param string $ownerStorageId storage id of the owner
2486
	 *
2487
	 * @return boolean true if file is reachable, false otherwise
2488
	 */
2489
	private static function isFileReachable($path, $ownerStorageId) {
2490
		// if outside the home storage, file is always considered reachable
2491
		if (!(substr($ownerStorageId, 0, 6) === 'home::')) {
2492
			return true;
2493
		}
2494
2495
		// if inside the home storage, the file has to be under "/files/"
2496
		$path = ltrim($path, '/');
2497
		if (substr($path, 0, 6) === 'files/') {
2498
			return true;
2499
		}
2500
2501
		return false;
2502
	}
2503
2504
	/**
2505
	 * @param IConfig $config
2506
	 * @return bool 
2507
	 */
2508
	public static function enforcePassword(IConfig $config) {
2509
		$enforcePassword = $config->getAppValue('core', 'shareapi_enforce_links_password', 'no');
2510
		return ($enforcePassword === "yes") ? true : false;
2511
	}
2512
}
2513