Total Complexity | 189 |
Total Lines | 933 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like Share often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Share, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
52 | class Share extends Constants { |
||
53 | /** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask |
||
54 | * Construct permissions for share() and setPermissions with Or (|) e.g. |
||
55 | * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE |
||
56 | * |
||
57 | * Check if permission is granted with And (&) e.g. Check if delete is |
||
58 | * granted: if ($permissions & PERMISSION_DELETE) |
||
59 | * |
||
60 | * Remove permissions with And (&) and Not (~) e.g. Remove the update |
||
61 | * permission: $permissions &= ~PERMISSION_UPDATE |
||
62 | * |
||
63 | * Apps are required to handle permissions on their own, this class only |
||
64 | * stores and manages the permissions of shares |
||
65 | * |
||
66 | * @see lib/public/Constants.php |
||
67 | */ |
||
68 | |||
69 | /** |
||
70 | * Register a sharing backend class that implements OCP\Share_Backend for an item type |
||
71 | * |
||
72 | * @param string $itemType Item type |
||
73 | * @param string $class Backend class |
||
74 | * @param string $collectionOf (optional) Depends on item type |
||
75 | * @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files |
||
76 | * @return boolean true if backend is registered or false if error |
||
77 | */ |
||
78 | public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) { |
||
79 | if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_enabled', 'yes') == 'yes') { |
||
80 | if (!isset(self::$backendTypes[$itemType])) { |
||
81 | self::$backendTypes[$itemType] = [ |
||
82 | 'class' => $class, |
||
83 | 'collectionOf' => $collectionOf, |
||
84 | 'supportedFileExtensions' => $supportedFileExtensions |
||
85 | ]; |
||
86 | return true; |
||
87 | } |
||
88 | \OC::$server->get(LoggerInterface::class)->warning( |
||
89 | 'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class'] |
||
90 | .' is already registered for '.$itemType, |
||
91 | ['app' => 'files_sharing']); |
||
92 | } |
||
93 | return false; |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * Get the items of item type shared with the current user |
||
98 | * |
||
99 | * @param string $itemType |
||
100 | * @param int $format (optional) Format type must be defined by the backend |
||
101 | * @param mixed $parameters (optional) |
||
102 | * @param int $limit Number of items to return (optional) Returns all by default |
||
103 | * @param boolean $includeCollections (optional) |
||
104 | * @return mixed Return depends on format |
||
105 | * @deprecated TESTS ONLY - this methods is only used by tests |
||
106 | * called like this: |
||
107 | * \OC\Share\Share::getItemsSharedWith('folder'); (apps/files_sharing/tests/UpdaterTest.php) |
||
108 | */ |
||
109 | public static function getItemsSharedWith() { |
||
110 | return self::getItems('folder', null, self::$shareTypeUserAndGroups, \OC_User::getUser()); |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * Get the items of item type shared with a user |
||
115 | * |
||
116 | * @param string $itemType |
||
117 | * @param string $user id for which user we want the shares |
||
118 | * @param int $format (optional) Format type must be defined by the backend |
||
119 | * @param mixed $parameters (optional) |
||
120 | * @param int $limit Number of items to return (optional) Returns all by default |
||
121 | * @param boolean $includeCollections (optional) |
||
122 | * @return mixed Return depends on format |
||
123 | * @deprecated TESTS ONLY - this methods is only used by tests |
||
124 | * called like this: |
||
125 | * \OC\Share\Share::getItemsSharedWithUser('test', $shareWith); (tests/lib/Share/Backend.php) |
||
126 | */ |
||
127 | public static function getItemsSharedWithUser($itemType, $user) { |
||
|
|||
128 | return self::getItems('test', null, self::$shareTypeUserAndGroups, $user); |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * Get the item of item type shared with a given user by source |
||
133 | * |
||
134 | * @param string $itemType |
||
135 | * @param string $itemSource |
||
136 | * @param ?string $user User to whom the item was shared |
||
137 | * @param ?string $owner Owner of the share |
||
138 | * @param ?int $shareType only look for a specific share type |
||
139 | * @return array Return list of items with file_target, permissions and expiration |
||
140 | * @throws Exception |
||
141 | */ |
||
142 | public static function getItemSharedWithUser(string $itemType, string $itemSource, ?string $user = null, ?string $owner = null, ?int $shareType = null) { |
||
143 | $shares = []; |
||
144 | $fileDependent = $itemType === 'file' || $itemType === 'folder'; |
||
145 | $qb = self::getSelectStatement(self::FORMAT_NONE, $fileDependent); |
||
146 | $qb->from('share', 's'); |
||
147 | if ($fileDependent) { |
||
148 | $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('file_source', 'f.fileid')); |
||
149 | $qb->innerJoin('s', 'storages', 'st', $qb->expr()->eq('numeric_id', 'f.storage')); |
||
150 | $column = 'file_source'; |
||
151 | } else { |
||
152 | $column = 'item_source'; |
||
153 | } |
||
154 | |||
155 | $qb->where($qb->expr()->eq($column, $qb->createNamedParameter($itemSource))) |
||
156 | ->andWhere($qb->expr()->eq('item_type', $qb->createNamedParameter($itemType))); |
||
157 | |||
158 | // for link shares $user === null |
||
159 | if ($user !== null) { |
||
160 | $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($user))); |
||
161 | } |
||
162 | |||
163 | if ($shareType !== null) { |
||
164 | $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType, IQueryBuilder::PARAM_INT))); |
||
165 | } |
||
166 | |||
167 | if ($owner !== null) { |
||
168 | $qb->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($owner))); |
||
169 | } |
||
170 | |||
171 | $result = $qb->executeQuery(); |
||
172 | while ($row = $result->fetch()) { |
||
173 | if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) { |
||
174 | continue; |
||
175 | } |
||
176 | if ($fileDependent && (int)$row['file_parent'] === -1) { |
||
177 | // if it is a mount point we need to get the path from the mount manager |
||
178 | $mountManager = \OC\Files\Filesystem::getMountManager(); |
||
179 | $mountPoint = $mountManager->findByStorageId($row['storage_id']); |
||
180 | if (!empty($mountPoint)) { |
||
181 | $path = $mountPoint[0]->getMountPoint(); |
||
182 | $path = trim($path, '/'); |
||
183 | $path = substr($path, strlen($owner) + 1); //normalize path to 'files/foo.txt` |
||
184 | $row['path'] = $path; |
||
185 | } else { |
||
186 | \OC::$server->get(LoggerInterface::class)->warning( |
||
187 | 'Could not resolve mount point for ' . $row['storage_id'], |
||
188 | ['app' => 'OCP\Share'] |
||
189 | ); |
||
190 | } |
||
191 | } |
||
192 | $shares[] = $row; |
||
193 | } |
||
194 | $result->closeCursor(); |
||
195 | |||
196 | // if we didn't found a result then let's look for a group share. |
||
197 | if (empty($shares) && $user !== null) { |
||
198 | $userObject = \OC::$server->getUserManager()->get($user); |
||
199 | $groups = []; |
||
200 | if ($userObject) { |
||
201 | $groups = \OC::$server->getGroupManager()->getUserGroupIds($userObject); |
||
202 | } |
||
203 | |||
204 | if (!empty($groups)) { |
||
205 | $qb = self::getSelectStatement(self::FORMAT_NONE, $fileDependent); |
||
206 | $qb->from('share', 's'); |
||
207 | |||
208 | if ($fileDependent) { |
||
209 | $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('file_source', 'f.fileid')) |
||
210 | ->innerJoin('s', 'storages', 'st', $qb->expr()->eq('numeric_id', 'f.storage')); |
||
211 | } |
||
212 | |||
213 | $qb->where($qb->expr()->eq($column, $qb->createNamedParameter($itemSource))) |
||
214 | ->andWhere($qb->expr()->eq('item_type', $qb->createNamedParameter($itemType, IQueryBuilder::PARAM_STR))) |
||
215 | ->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY))); |
||
216 | |||
217 | if ($owner !== null) { |
||
218 | $qb->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($owner))); |
||
219 | } |
||
220 | $result = $qb->executeQuery(); |
||
221 | |||
222 | while ($row = $result->fetch()) { |
||
223 | $shares[] = $row; |
||
224 | } |
||
225 | $result->closeCursor(); |
||
226 | } |
||
227 | } |
||
228 | |||
229 | return $shares; |
||
230 | } |
||
231 | |||
232 | /** |
||
233 | * Get the shared item of item type owned by the current user |
||
234 | * |
||
235 | * @param string $itemType |
||
236 | * @param string $itemSource |
||
237 | * @param int $format (optional) Format type must be defined by the backend |
||
238 | * @param mixed $parameters |
||
239 | * @param boolean $includeCollections |
||
240 | * @return mixed Return depends on format |
||
241 | * |
||
242 | * Refactoring notes: |
||
243 | * * defacto $parameters and $format is always the default and therefore is removed in the subsequent call |
||
244 | */ |
||
245 | public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE, |
||
246 | $parameters = null, $includeCollections = false) { |
||
247 | return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), self::FORMAT_NONE, |
||
248 | null, -1, $includeCollections); |
||
249 | } |
||
250 | |||
251 | /** |
||
252 | * Get the backend class for the specified item type |
||
253 | * |
||
254 | * @param string $itemType |
||
255 | * @return \OCP\Share_Backend |
||
256 | * @throws \Exception |
||
257 | */ |
||
258 | public static function getBackend($itemType) { |
||
259 | $l = \OC::$server->getL10N('lib'); |
||
260 | $logger = \OC::$server->get(LoggerInterface::class); |
||
261 | if (isset(self::$backends[$itemType])) { |
||
262 | return self::$backends[$itemType]; |
||
263 | } elseif (isset(self::$backendTypes[$itemType]['class'])) { |
||
264 | $class = self::$backendTypes[$itemType]['class']; |
||
265 | if (class_exists($class)) { |
||
266 | self::$backends[$itemType] = new $class; |
||
267 | if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) { |
||
268 | $message = 'Sharing backend %s must implement the interface OCP\Share_Backend'; |
||
269 | $message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', [$class]); |
||
270 | $logger->error(sprintf($message, $class), ['app' => 'OCP\Share']); |
||
271 | throw new \Exception($message_t); |
||
272 | } |
||
273 | return self::$backends[$itemType]; |
||
274 | } else { |
||
275 | $message = 'Sharing backend %s not found'; |
||
276 | $message_t = $l->t('Sharing backend %s not found', [$class]); |
||
277 | $logger->error(sprintf($message, $class), ['app' => 'OCP\Share']); |
||
278 | throw new \Exception($message_t); |
||
279 | } |
||
280 | } |
||
281 | $message = 'Sharing backend for %s not found'; |
||
282 | $message_t = $l->t('Sharing backend for %s not found', [$itemType]); |
||
283 | $logger->error(sprintf($message, $itemType), ['app' => 'OCP\Share']); |
||
284 | throw new \Exception($message_t); |
||
285 | } |
||
286 | |||
287 | /** |
||
288 | * Check if resharing is allowed |
||
289 | * |
||
290 | * @return boolean true if allowed or false |
||
291 | * |
||
292 | * Resharing is allowed by default if not configured |
||
293 | */ |
||
294 | public static function isResharingAllowed() { |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * Get a list of collection item types for the specified item type |
||
307 | * |
||
308 | * @param string $itemType |
||
309 | * @return array|false |
||
310 | */ |
||
311 | private static function getCollectionItemTypes(string $itemType) { |
||
312 | $collectionTypes = [$itemType]; |
||
313 | foreach (self::$backendTypes as $type => $backend) { |
||
314 | if (in_array($backend['collectionOf'], $collectionTypes)) { |
||
315 | $collectionTypes[] = $type; |
||
316 | } |
||
317 | } |
||
318 | // TODO Add option for collections to be collection of themselves, only 'folder' does it now... |
||
319 | if (isset(self::$backendTypes[$itemType]) && (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder')) { |
||
320 | unset($collectionTypes[0]); |
||
321 | } |
||
322 | // Return array if collections were found or the item type is a |
||
323 | // collection itself - collections can be inside collections |
||
324 | if (count($collectionTypes) > 0) { |
||
325 | return $collectionTypes; |
||
326 | } |
||
327 | return false; |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * Get shared items from the database |
||
332 | * |
||
333 | * @param string $itemType |
||
334 | * @param string $item Item source or target (optional) |
||
335 | * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique |
||
336 | * @param string $shareWith User or group the item is being shared with |
||
337 | * @param string $uidOwner User that is the owner of shared items (optional) |
||
338 | * @param int $format Format to convert items to with formatItems() (optional) |
||
339 | * @param mixed $parameters to pass to formatItems() (optional) |
||
340 | * @param int $limit Number of items to return, -1 to return all matches (optional) |
||
341 | * @param boolean $includeCollections Include collection item types (optional) |
||
342 | * @param boolean $itemShareWithBySource (optional) |
||
343 | * @param boolean $checkExpireDate |
||
344 | * @return array |
||
345 | * |
||
346 | * See public functions getItem(s)... for parameter usage |
||
347 | * |
||
348 | * Refactoring notes: |
||
349 | * * defacto $limit, $itemsShareWithBySource, $checkExpireDate, $parameters and $format is always the default and therefore is removed in the subsequent call |
||
350 | */ |
||
351 | public static function getItems($itemType, ?string $item = null, ?int $shareType = null, $shareWith = null, |
||
352 | $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, |
||
353 | $includeCollections = false, $itemShareWithBySource = false, $checkExpireDate = true) { |
||
354 | if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_enabled', 'yes') != 'yes') { |
||
355 | return []; |
||
356 | } |
||
357 | $fileDependent = $itemType == 'file' || $itemType == 'folder'; |
||
358 | $qb = self::getSelectStatement(self::FORMAT_NONE, $fileDependent, $uidOwner); |
||
359 | $qb->from('share', 's'); |
||
360 | |||
361 | $backend = self::getBackend($itemType); |
||
362 | $collectionTypes = false; |
||
363 | // Get filesystem root to add it to the file target and remove from the |
||
364 | // file source, match file_source with the file cache |
||
365 | if ($fileDependent) { |
||
366 | if (!is_null($uidOwner)) { |
||
367 | $root = \OC\Files\Filesystem::getRoot(); |
||
368 | } else { |
||
369 | $root = ''; |
||
370 | } |
||
371 | if (isset($item)) { |
||
372 | $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('file_source', 'f.fileid')); |
||
373 | } else { |
||
374 | $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->andX( |
||
375 | $qb->expr()->eq('file_source', 'f.fileid'), |
||
376 | $qb->expr()->isNotNull('file_target') |
||
377 | )); |
||
378 | } |
||
379 | $qb->innerJoin('s', 'storages', 'st', $qb->expr()->eq('numeric_id', 'f.storage')); |
||
380 | } else { |
||
381 | $root = ''; |
||
382 | $collectionTypes = self::getCollectionItemTypes($itemType); |
||
383 | if ($includeCollections && !isset($item) && $collectionTypes) { |
||
384 | // If includeCollections is true, find collections of this item type, e.g. a music album contains songs |
||
385 | if (!in_array($itemType, $collectionTypes)) { |
||
386 | $itemTypes = array_merge([$itemType], $collectionTypes); |
||
387 | } else { |
||
388 | $itemTypes = $collectionTypes; |
||
389 | } |
||
390 | $qb->where($qb->expr()->in('item_type', $qb->createNamedParameter($itemTypes, IQueryBuilder::PARAM_STR_ARRAY))); |
||
391 | } else { |
||
392 | $qb->where($qb->expr()->eq('item_type', $qb->createNamedParameter($itemType))); |
||
393 | } |
||
394 | } |
||
395 | if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') { |
||
396 | $qb->andWhere($qb->expr()->neq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK, IQueryBuilder::PARAM_INT))); |
||
397 | } |
||
398 | if (isset($shareType)) { |
||
399 | // Include all user and group items |
||
400 | if ($shareType === self::$shareTypeUserAndGroups && isset($shareWith)) { |
||
401 | $qb->andWhere($qb->expr()->andX( |
||
402 | $qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_USER, self::$shareTypeGroupUserUnique], IQueryBuilder::PARAM_INT_ARRAY)), |
||
403 | $qb->expr()->eq('share_with', $qb->createNamedParameter($shareWith)) |
||
404 | )); |
||
405 | |||
406 | $user = \OC::$server->getUserManager()->get($shareWith); |
||
407 | $groups = []; |
||
408 | if ($user) { |
||
409 | $groups = \OC::$server->getGroupManager()->getUserGroupIds($user); |
||
410 | } |
||
411 | if (!empty($groups)) { |
||
412 | $qb->orWhere($qb->expr()->andX( |
||
413 | $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP, IQueryBuilder::PARAM_INT)), |
||
414 | $qb->expr()->in('share_with', $qb->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY)) |
||
415 | )); |
||
416 | } |
||
417 | |||
418 | // Don't include own group shares |
||
419 | $qb->andWhere($qb->expr()->neq('uid_owner', $qb->createNamedParameter($shareWith))); |
||
420 | } else { |
||
421 | $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType, IQueryBuilder::PARAM_INT))); |
||
422 | if (isset($shareWith)) { |
||
423 | $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($shareWith, IQueryBuilder::PARAM_STR))); |
||
424 | } |
||
425 | } |
||
426 | } |
||
427 | if (isset($uidOwner)) { |
||
428 | $qb->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uidOwner))); |
||
429 | if (!isset($shareType)) { |
||
430 | // Prevent unique user targets for group shares from being selected |
||
431 | $qb->andWhere($qb->expr()->neq('share_type', $qb->createNamedParameter(self::$shareTypeGroupUserUnique, IQueryBuilder::PARAM_INT))); |
||
432 | } |
||
433 | if ($fileDependent) { |
||
434 | $column = 'file_source'; |
||
435 | } else { |
||
436 | $column = 'item_source'; |
||
437 | } |
||
438 | } else { |
||
439 | if ($fileDependent) { |
||
440 | $column = 'file_target'; |
||
441 | } else { |
||
442 | $column = 'item_target'; |
||
443 | } |
||
444 | } |
||
445 | if (isset($item)) { |
||
446 | $collectionTypes = self::getCollectionItemTypes($itemType); |
||
447 | // If looking for own shared items, check item_source else check item_target |
||
448 | if (isset($uidOwner)) { |
||
449 | // If item type is a file, file source needs to be checked in case the item was converted |
||
450 | if ($fileDependent) { |
||
451 | $expr = $qb->expr()->eq('file_source', $qb->createNamedParameter($item)); |
||
452 | $column = 'file_source'; |
||
453 | } else { |
||
454 | $expr = $qb->expr()->eq('item_source', $qb->createNamedParameter($item)); |
||
455 | $column = 'item_source'; |
||
456 | } |
||
457 | } else { |
||
458 | if ($fileDependent) { |
||
459 | $item = \OC\Files\Filesystem::normalizePath($item); |
||
460 | $expr = $qb->expr()->eq('file_target', $qb->createNamedParameter($item)); |
||
461 | } else { |
||
462 | $expr = $qb->expr()->eq('item_target', $qb->createNamedParameter($item)); |
||
463 | } |
||
464 | } |
||
465 | if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) { |
||
466 | $qb->andWhere($qb->expr()->orX( |
||
467 | $expr, |
||
468 | $qb->expr()->in('item_type', $qb->createNamedParameter($collectionTypes, IQueryBuilder::PARAM_STR_ARRAY)) |
||
469 | )); |
||
470 | } else { |
||
471 | $qb->andWhere($expr); |
||
472 | } |
||
473 | } |
||
474 | $qb->orderBy('s.id', 'ASC'); |
||
475 | try { |
||
476 | $result = $qb->executeQuery(); |
||
477 | } catch (\Exception $e) { |
||
478 | \OCP\Server::get(LoggerInterface::class)->error( |
||
479 | 'Error while selecting shares: ' . $qb->getSQL(), |
||
480 | [ |
||
481 | 'app' => 'files_sharing', |
||
482 | 'exception' => $e |
||
483 | ]); |
||
484 | throw new \RuntimeException('Wrong SQL query', 500, $e); |
||
485 | } |
||
486 | |||
487 | $root = strlen($root); |
||
488 | $items = []; |
||
489 | $targets = []; |
||
490 | $switchedItems = []; |
||
491 | $mounts = []; |
||
492 | while ($row = $result->fetch()) { |
||
493 | //var_dump($row); |
||
494 | self::transformDBResults($row); |
||
495 | // Filter out duplicate group shares for users with unique targets |
||
496 | if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) { |
||
497 | continue; |
||
498 | } |
||
499 | if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) { |
||
500 | $row['share_type'] = IShare::TYPE_GROUP; |
||
501 | $row['unique_name'] = true; // remember that we use a unique name for this user |
||
502 | $row['share_with'] = $items[$row['parent']]['share_with']; |
||
503 | // if the group share was unshared from the user we keep the permission, otherwise |
||
504 | // we take the permission from the parent because this is always the up-to-date |
||
505 | // permission for the group share |
||
506 | if ($row['permissions'] > 0) { |
||
507 | $row['permissions'] = $items[$row['parent']]['permissions']; |
||
508 | } |
||
509 | // Remove the parent group share |
||
510 | unset($items[$row['parent']]); |
||
511 | if ($row['permissions'] == 0) { |
||
512 | continue; |
||
513 | } |
||
514 | } elseif (!isset($uidOwner)) { |
||
515 | // Check if the same target already exists |
||
516 | if (isset($targets[$row['id']])) { |
||
517 | // Check if the same owner shared with the user twice |
||
518 | // through a group and user share - this is allowed |
||
519 | $id = $targets[$row['id']]; |
||
520 | if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) { |
||
521 | // Switch to group share type to ensure resharing conditions aren't bypassed |
||
522 | if ($items[$id]['share_type'] != IShare::TYPE_GROUP) { |
||
523 | $items[$id]['share_type'] = IShare::TYPE_GROUP; |
||
524 | $items[$id]['share_with'] = $row['share_with']; |
||
525 | } |
||
526 | // Switch ids if sharing permission is granted on only |
||
527 | // one share to ensure correct parent is used if resharing |
||
528 | if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE |
||
529 | && (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) { |
||
530 | $items[$row['id']] = $items[$id]; |
||
531 | $switchedItems[$id] = $row['id']; |
||
532 | unset($items[$id]); |
||
533 | $id = $row['id']; |
||
534 | } |
||
535 | $items[$id]['permissions'] |= (int)$row['permissions']; |
||
536 | } |
||
537 | continue; |
||
538 | } elseif (!empty($row['parent'])) { |
||
539 | $targets[$row['parent']] = $row['id']; |
||
540 | } |
||
541 | } |
||
542 | // Remove root from file source paths if retrieving own shared items |
||
543 | if (isset($uidOwner) && isset($row['path'])) { |
||
544 | if (isset($row['parent'])) { |
||
545 | $query = \OC::$server->getDatabaseConnection()->getQueryBuilder(); |
||
546 | $query->select('file_target') |
||
547 | ->from('share') |
||
548 | ->where($query->expr()->eq('id', $query->createNamedParameter($row['parent']))); |
||
549 | |||
550 | $parentRow = false; |
||
551 | try { |
||
552 | $parentResult = $query->executeQuery(); |
||
553 | $parentRow = $parentResult->fetchOne(); |
||
554 | $parentResult->closeCursor(); |
||
555 | |||
556 | $tmpPath = $parentRow['file_target']; |
||
557 | // find the right position where the row path continues from the target path |
||
558 | $pos = strrpos($row['path'], $parentRow['file_target']); |
||
559 | $subPath = substr($row['path'], $pos); |
||
560 | $splitPath = explode('/', $subPath); |
||
561 | foreach (array_slice($splitPath, 2) as $pathPart) { |
||
562 | $tmpPath = $tmpPath . '/' . $pathPart; |
||
563 | } |
||
564 | $row['path'] = $tmpPath; |
||
565 | } catch (Exception $e) { |
||
566 | \OCP\Server::get(LoggerInterface::class) |
||
567 | ->error('Can\'t select parent :' . $e->getMessage() . ' query=' . $query->getSQL(), [ |
||
568 | 'exception' => $e, |
||
569 | 'app' => 'core' |
||
570 | ]); |
||
571 | } |
||
572 | } else { |
||
573 | if (!isset($mounts[$row['storage']])) { |
||
574 | $mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']); |
||
575 | if (is_array($mountPoints) && !empty($mountPoints)) { |
||
576 | $mounts[$row['storage']] = current($mountPoints); |
||
577 | } |
||
578 | } |
||
579 | if (!empty($mounts[$row['storage']])) { |
||
580 | $path = $mounts[$row['storage']]->getMountPoint() . $row['path']; |
||
581 | $relPath = substr($path, $root); // path relative to data/user |
||
582 | $row['path'] = rtrim($relPath, '/'); |
||
583 | } |
||
584 | } |
||
585 | } |
||
586 | |||
587 | // Check if resharing is allowed, if not remove share permission |
||
588 | if (isset($row['permissions']) && (!self::isResharingAllowed() | \OCP\Util::isSharingDisabledForUser())) { |
||
589 | $row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE; |
||
590 | } |
||
591 | // Add display names to result |
||
592 | $row['share_with_displayname'] = $row['share_with']; |
||
593 | if (isset($row['share_with']) && $row['share_with'] != '' && |
||
594 | $row['share_type'] === IShare::TYPE_USER) { |
||
595 | $shareWithUser = \OC::$server->getUserManager()->get($row['share_with']); |
||
596 | $row['share_with_displayname'] = $shareWithUser === null ? $row['share_with'] : $shareWithUser->getDisplayName(); |
||
597 | } elseif (isset($row['share_with']) && $row['share_with'] != '' && |
||
598 | $row['share_type'] === IShare::TYPE_REMOTE) { |
||
599 | $addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD'], [ |
||
600 | 'limit' => 1, |
||
601 | 'enumeration' => false, |
||
602 | 'fullmatch' => false, |
||
603 | 'strict_search' => true, |
||
604 | ]); |
||
605 | foreach ($addressBookEntries as $entry) { |
||
606 | foreach ($entry['CLOUD'] as $cloudID) { |
||
607 | if ($cloudID === $row['share_with']) { |
||
608 | $row['share_with_displayname'] = $entry['FN']; |
||
609 | } |
||
610 | } |
||
611 | } |
||
612 | } |
||
613 | if (isset($row['uid_owner']) && $row['uid_owner'] != '') { |
||
614 | $ownerUser = \OC::$server->getUserManager()->get($row['uid_owner']); |
||
615 | $row['displayname_owner'] = $ownerUser === null ? $row['uid_owner'] : $ownerUser->getDisplayName(); |
||
616 | } |
||
617 | |||
618 | if ($row['permissions'] > 0) { |
||
619 | $items[$row['id']] = $row; |
||
620 | } |
||
621 | } |
||
622 | $result->closeCursor(); |
||
623 | |||
624 | // group items if we are looking for items shared with the current user |
||
625 | if (isset($shareWith) && $shareWith === \OC_User::getUser()) { |
||
626 | $items = self::groupItems($items, $itemType); |
||
627 | } |
||
628 | |||
629 | if (!empty($items)) { |
||
630 | $collectionItems = []; |
||
631 | foreach ($items as &$row) { |
||
632 | // Check if this is a collection of the requested item type |
||
633 | if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) { |
||
634 | if (($collectionBackend = self::getBackend($row['item_type'])) |
||
635 | && $collectionBackend instanceof \OCP\Share_Backend_Collection) { |
||
636 | // Collections can be inside collections, check if the item is a collection |
||
637 | if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) { |
||
638 | $collectionItems[] = $row; |
||
639 | } else { |
||
640 | $collection = []; |
||
641 | $collection['item_type'] = $row['item_type']; |
||
642 | if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { |
||
643 | $collection['path'] = basename($row['path']); |
||
644 | } |
||
645 | $row['collection'] = $collection; |
||
646 | // Fetch all the children sources |
||
647 | $children = $collectionBackend->getChildren($row[$column]); |
||
648 | foreach ($children as $child) { |
||
649 | $childItem = $row; |
||
650 | $childItem['item_type'] = $itemType; |
||
651 | if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') { |
||
652 | $childItem['item_source'] = $child['source']; |
||
653 | $childItem['item_target'] = $child['target']; |
||
654 | } |
||
655 | if ($backend instanceof \OCP\Share_Backend_File_Dependent) { |
||
656 | if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { |
||
657 | $childItem['file_source'] = $child['source']; |
||
658 | } else { // TODO is this really needed if we already know that we use the file backend? |
||
659 | $meta = \OC\Files\Filesystem::getFileInfo($child['file_path']); |
||
660 | $childItem['file_source'] = $meta['fileid']; |
||
661 | } |
||
662 | $childItem['file_target'] = |
||
663 | \OC\Files\Filesystem::normalizePath($child['file_path']); |
||
664 | } |
||
665 | if (isset($item)) { |
||
666 | if ($childItem[$column] == $item) { |
||
667 | $collectionItems[] = $childItem; |
||
668 | } |
||
669 | } else { |
||
670 | $collectionItems[] = $childItem; |
||
671 | } |
||
672 | } |
||
673 | } |
||
674 | } |
||
675 | // Remove collection item |
||
676 | $toRemove = $row['id']; |
||
677 | if (array_key_exists($toRemove, $switchedItems)) { |
||
678 | $toRemove = $switchedItems[$toRemove]; |
||
679 | } |
||
680 | unset($items[$toRemove]); |
||
681 | } elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) { |
||
682 | // FIXME: Thats a dirty hack to improve file sharing performance, |
||
683 | // see github issue #10588 for more details |
||
684 | // Need to find a solution which works for all back-ends |
||
685 | $collectionBackend = self::getBackend($row['item_type']); |
||
686 | $sharedParents = $collectionBackend->getParents($row['item_source']); |
||
687 | foreach ($sharedParents as $parent) { |
||
688 | $collectionItems[] = $parent; |
||
689 | } |
||
690 | } |
||
691 | } |
||
692 | if (!empty($collectionItems)) { |
||
693 | $collectionItems = array_unique($collectionItems, SORT_REGULAR); |
||
694 | $items = array_merge($items, $collectionItems); |
||
695 | } |
||
696 | |||
697 | // filter out invalid items, these can appear when subshare entries exist |
||
698 | // for a group in which the requested user isn't a member any more |
||
699 | $items = array_filter($items, function ($item) { |
||
700 | return $item['share_type'] !== self::$shareTypeGroupUserUnique; |
||
701 | }); |
||
702 | |||
703 | return self::formatResult($items, $column, $backend); |
||
704 | } elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) { |
||
705 | // FIXME: Thats a dirty hack to improve file sharing performance, |
||
706 | // see github issue #10588 for more details |
||
707 | // Need to find a solution which works for all back-ends |
||
708 | $collectionItems = []; |
||
709 | $collectionBackend = self::getBackend('folder'); |
||
710 | $sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner); |
||
711 | foreach ($sharedParents as $parent) { |
||
712 | $collectionItems[] = $parent; |
||
713 | } |
||
714 | return self::formatResult($collectionItems, $column, $backend); |
||
715 | } |
||
716 | |||
717 | return []; |
||
718 | } |
||
719 | |||
720 | /** |
||
721 | * group items with link to the same source |
||
722 | * |
||
723 | * @param array $items |
||
724 | * @param string $itemType |
||
725 | * @return array of grouped items |
||
726 | */ |
||
727 | protected static function groupItems($items, $itemType) { |
||
728 | $fileSharing = $itemType === 'file' || $itemType === 'folder'; |
||
729 | |||
730 | $result = []; |
||
731 | |||
732 | foreach ($items as $item) { |
||
733 | $grouped = false; |
||
734 | foreach ($result as $key => $r) { |
||
735 | // for file/folder shares we need to compare file_source, otherwise we compare item_source |
||
736 | // only group shares if they already point to the same target, otherwise the file where shared |
||
737 | // before grouping of shares was added. In this case we don't group them to avoid confusions |
||
738 | if (($fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) || |
||
739 | (!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) { |
||
740 | // add the first item to the list of grouped shares |
||
741 | if (!isset($result[$key]['grouped'])) { |
||
742 | $result[$key]['grouped'][] = $result[$key]; |
||
743 | } |
||
744 | $result[$key]['permissions'] = (int)$item['permissions'] | (int)$r['permissions']; |
||
745 | $result[$key]['grouped'][] = $item; |
||
746 | $grouped = true; |
||
747 | break; |
||
748 | } |
||
749 | } |
||
750 | |||
751 | if (!$grouped) { |
||
752 | $result[] = $item; |
||
753 | } |
||
754 | } |
||
755 | |||
756 | return $result; |
||
757 | } |
||
758 | |||
759 | /** |
||
760 | * Construct select statement |
||
761 | * |
||
762 | * @param bool $fileDependent ist it a file/folder share or a general share |
||
763 | */ |
||
764 | private static function getSelectStatement(int $format, bool $fileDependent, ?string $uidOwner = null): IQueryBuilder { |
||
765 | /** @var IDBConnection $connection */ |
||
766 | $connection = \OC::$server->get(IDBConnection::class); |
||
767 | $qb = $connection->getQueryBuilder(); |
||
768 | if ($format == self::FORMAT_STATUSES) { |
||
769 | if ($fileDependent) { |
||
770 | return $qb->select( |
||
771 | 's.id', |
||
772 | 's.parent', |
||
773 | 'share_type', |
||
774 | 'path', |
||
775 | 'storage', |
||
776 | 'share_with', |
||
777 | 'uid_owner', |
||
778 | 'file_source', |
||
779 | 'stime', |
||
780 | 's.permissions', |
||
781 | 'uid_initiator' |
||
782 | )->selectAlias('st.id', 'storage_id') |
||
783 | ->selectAlias('f.parent', 'file_parent'); |
||
784 | } |
||
785 | return $qb->select('id', 'parent', 'share_type', 'share_with', 'uid_owner', 'item_source', 'stime', 's.permissions'); |
||
786 | } |
||
787 | |||
788 | if (isset($uidOwner)) { |
||
789 | if ($fileDependent) { |
||
790 | return $qb->select( |
||
791 | 's.id', |
||
792 | 'item_type', |
||
793 | 'item_source', |
||
794 | 's.parent', |
||
795 | 'share_type', |
||
796 | 'share_with', |
||
797 | 'file_source', |
||
798 | 'file_target', |
||
799 | 'path', |
||
800 | 's.permissions', |
||
801 | 'stime', |
||
802 | 'expiration', |
||
803 | 'token', |
||
804 | 'storage', |
||
805 | 'mail_send', |
||
806 | 'uid_owner', |
||
807 | 'uid_initiator' |
||
808 | )->selectAlias('st.id', 'storage_id') |
||
809 | ->selectAlias('f.parent', 'file_parent'); |
||
810 | } |
||
811 | return $qb->select('id', 'item_type', 'item_source', 'parent', 'share_type', |
||
812 | 'share_with', 'uid_owner', 'file_source', 'stime', 's.permissions', |
||
813 | 'expiration', 'token', 'mail_send'); |
||
814 | } |
||
815 | |||
816 | if ($fileDependent) { |
||
817 | if ($format == File::FORMAT_GET_FOLDER_CONTENTS || $format == File::FORMAT_FILE_APP_ROOT) { |
||
818 | return $qb->select( |
||
819 | 's.id', |
||
820 | 'item_type', |
||
821 | 'item_source', |
||
822 | 's.parent', |
||
823 | 'uid_owner', |
||
824 | 'share_type', |
||
825 | 'share_with', |
||
826 | 'file_source', |
||
827 | 'path', |
||
828 | 'file_target', |
||
829 | 's.permissions', |
||
830 | 'stime', |
||
831 | 'expiration', |
||
832 | 'storage', |
||
833 | 'name', |
||
834 | 'mtime', |
||
835 | 'mimepart', |
||
836 | 'size', |
||
837 | 'encrypted', |
||
838 | 'etag', |
||
839 | 'mail_send' |
||
840 | )->selectAlias('f.parent', 'file_parent'); |
||
841 | } |
||
842 | return $qb->select( |
||
843 | 's.id', |
||
844 | 'item_type', |
||
845 | 'item_source', |
||
846 | 'item_target', |
||
847 | 's.parent', |
||
848 | 'share_type', |
||
849 | 'share_with', |
||
850 | 'uid_owner', |
||
851 | 'file_source', |
||
852 | 'path', |
||
853 | 'file_target', |
||
854 | 's.permissions', |
||
855 | 'stime', |
||
856 | 'expiration', |
||
857 | 'token', |
||
858 | 'storage', |
||
859 | 'mail_send', |
||
860 | )->selectAlias('f.parent', 'file_parent') |
||
861 | ->selectAlias('st.id', 'storage_id'); |
||
862 | } |
||
863 | return $qb->select('*'); |
||
864 | } |
||
865 | |||
866 | |||
867 | /** |
||
868 | * transform db results |
||
869 | * |
||
870 | * @param array $row result |
||
871 | */ |
||
872 | private static function transformDBResults(&$row) { |
||
873 | if (isset($row['id'])) { |
||
874 | $row['id'] = (int)$row['id']; |
||
875 | } |
||
876 | if (isset($row['share_type'])) { |
||
877 | $row['share_type'] = (int)$row['share_type']; |
||
878 | } |
||
879 | if (isset($row['parent'])) { |
||
880 | $row['parent'] = (int)$row['parent']; |
||
881 | } |
||
882 | if (isset($row['file_parent'])) { |
||
883 | $row['file_parent'] = (int)$row['file_parent']; |
||
884 | } |
||
885 | if (isset($row['file_source'])) { |
||
886 | $row['file_source'] = (int)$row['file_source']; |
||
887 | } |
||
888 | if (isset($row['permissions'])) { |
||
889 | $row['permissions'] = (int)$row['permissions']; |
||
890 | } |
||
891 | if (isset($row['storage'])) { |
||
892 | $row['storage'] = (int)$row['storage']; |
||
893 | } |
||
894 | if (isset($row['stime'])) { |
||
895 | $row['stime'] = (int)$row['stime']; |
||
896 | } |
||
897 | if (isset($row['expiration']) && $row['share_type'] !== IShare::TYPE_LINK) { |
||
898 | // discard expiration date for non-link shares, which might have been |
||
899 | // set by ancient bugs |
||
900 | $row['expiration'] = null; |
||
901 | } |
||
902 | } |
||
903 | |||
904 | /** |
||
905 | * format result |
||
906 | * |
||
907 | * @param array $items result |
||
908 | * @param string $column is it a file share or a general share ('file_target' or 'item_target') |
||
909 | * @param \OCP\Share_Backend $backend sharing backend |
||
910 | * @param int $format |
||
911 | * @param array $parameters additional format parameters |
||
912 | * @return array format result |
||
913 | */ |
||
914 | private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE, $parameters = null) { |
||
915 | if ($format === self::FORMAT_NONE) { |
||
916 | return $items; |
||
917 | } elseif ($format === self::FORMAT_STATUSES) { |
||
918 | $statuses = []; |
||
919 | foreach ($items as $item) { |
||
920 | if ($item['share_type'] === IShare::TYPE_LINK) { |
||
921 | if ($item['uid_initiator'] !== \OC::$server->getUserSession()->getUser()->getUID()) { |
||
922 | continue; |
||
923 | } |
||
924 | $statuses[$item[$column]]['link'] = true; |
||
925 | } elseif (!isset($statuses[$item[$column]])) { |
||
926 | $statuses[$item[$column]]['link'] = false; |
||
927 | } |
||
928 | if (!empty($item['file_target'])) { |
||
929 | $statuses[$item[$column]]['path'] = $item['path']; |
||
930 | } |
||
931 | } |
||
932 | return $statuses; |
||
933 | } else { |
||
934 | return $backend->formatItems($items, $format, $parameters); |
||
935 | } |
||
936 | } |
||
937 | |||
938 | /** |
||
939 | * remove protocol from URL |
||
940 | * |
||
941 | * @param string $url |
||
942 | * @return string |
||
943 | */ |
||
944 | public static function removeProtocolFromUrl($url) { |
||
952 | } |
||
953 | |||
954 | |||
955 | /** |
||
956 | * @return int |
||
957 | */ |
||
958 | public static function getExpireInterval() { |
||
960 | } |
||
961 | |||
962 | /** |
||
963 | * Checks whether the given path is reachable for the given owner |
||
964 | * |
||
965 | * @param string $path path relative to files |
||
966 | * @param string $ownerStorageId storage id of the owner |
||
967 | * |
||
968 | * @return boolean true if file is reachable, false otherwise |
||
969 | */ |
||
970 | private static function isFileReachable($path, $ownerStorageId) { |
||
985 | } |
||
986 | } |
||
987 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.