Example #1
0
 /**
  * Get shared items from the database
  * @param string $itemType
  * @param string $item Item source or target (optional)
  * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique
  * @param string $shareWith User or group the item is being shared with
  * @param string $uidOwner User that is the owner of shared items (optional)
  * @param int $format Format to convert items to with formatItems() (optional)
  * @param mixed $parameters to pass to formatItems() (optional)
  * @param int $limit Number of items to return, -1 to return all matches (optional)
  * @param boolean $includeCollections Include collection item types (optional)
  * @param boolean $itemShareWithBySource (optional)
  * @param boolean $checkExpireDate
  * @return array
  *
  * See public functions getItem(s)... for parameter usage
  *
  */
 public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null, $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false, $itemShareWithBySource = false, $checkExpireDate = true)
 {
     if (!self::isEnabled()) {
         return array();
     }
     $backend = self::getBackend($itemType);
     $collectionTypes = false;
     // Get filesystem root to add it to the file target and remove from the
     // file source, match file_source with the file cache
     if ($itemType == 'file' || $itemType == 'folder') {
         if (!is_null($uidOwner)) {
             $root = \OC\Files\Filesystem::getRoot();
         } else {
             $root = '';
         }
         $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
         if (!isset($item)) {
             $where .= ' AND `file_target` IS NOT NULL ';
         }
         $where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
         $fileDependent = true;
         $queryArgs = array();
     } else {
         $fileDependent = false;
         $root = '';
         $collectionTypes = self::getCollectionItemTypes($itemType);
         if ($includeCollections && !isset($item) && $collectionTypes) {
             // If includeCollections is true, find collections of this item type, e.g. a music album contains songs
             if (!in_array($itemType, $collectionTypes)) {
                 $itemTypes = array_merge(array($itemType), $collectionTypes);
             } else {
                 $itemTypes = $collectionTypes;
             }
             $placeholders = join(',', array_fill(0, count($itemTypes), '?'));
             $where = ' WHERE `item_type` IN (' . $placeholders . '))';
             $queryArgs = $itemTypes;
         } else {
             $where = ' WHERE `item_type` = ?';
             $queryArgs = array($itemType);
         }
     }
     if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
         $where .= ' AND `share_type` != ?';
         $queryArgs[] = self::SHARE_TYPE_LINK;
     }
     if (isset($shareType)) {
         // Include all user and group items
         if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
             $where .= ' AND ((`share_type` in (?, ?) AND `share_with` = ?) ';
             $queryArgs[] = self::SHARE_TYPE_USER;
             $queryArgs[] = self::$shareTypeGroupUserUnique;
             $queryArgs[] = $shareWith;
             $groups = \OC_Group::getUserGroups($shareWith);
             $sharing_groups = \OCA\Sharing_Group\Data::readUserGroups($shareWith);
             if (!empty($groups)) {
                 $placeholders = join(',', array_fill(0, count($groups), '?'));
                 $where .= ' OR (`share_type` = ? AND `share_with` IN (' . $placeholders . ')) ';
                 $queryArgs[] = self::SHARE_TYPE_GROUP;
                 $queryArgs = array_merge($queryArgs, $groups);
             }
             if (!empty($sharing_groups)) {
                 $placeholders = join(',', array_fill(0, count($sharing_groups), '?'));
                 $where .= ' OR (`share_type` = ? AND `share_with` IN (' . $placeholders . ')) ';
                 $queryArgs[] = self::SHARE_TYPE_SHARING_GROUP;
                 $queryArgs = array_merge($queryArgs, $sharing_groups);
             }
             $where .= ')';
             // Don't include own group shares
             $where .= ' AND `uid_owner` != ?';
             $queryArgs[] = $shareWith;
         } else {
             $where .= ' AND `share_type` = ?';
             $queryArgs[] = $shareType;
             if (isset($shareWith)) {
                 $where .= ' AND `share_with` = ?';
                 $queryArgs[] = $shareWith;
             }
         }
     }
     if (isset($uidOwner)) {
         $where .= ' AND `uid_owner` = ?';
         $queryArgs[] = $uidOwner;
         if (!isset($shareType)) {
             // Prevent unique user targets for group shares from being selected
             $where .= ' AND `share_type` != ?';
             $queryArgs[] = self::$shareTypeGroupUserUnique;
         }
         if ($fileDependent) {
             $column = 'file_source';
         } else {
             $column = 'item_source';
         }
     } else {
         if ($fileDependent) {
             $column = 'file_target';
         } else {
             $column = 'item_target';
         }
     }
     if (isset($item)) {
         $collectionTypes = self::getCollectionItemTypes($itemType);
         if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
             $where .= ' AND (';
         } else {
             $where .= ' AND';
         }
         // If looking for own shared items, check item_source else check item_target
         if (isset($uidOwner) || $itemShareWithBySource) {
             // If item type is a file, file source needs to be checked in case the item was converted
             if ($fileDependent) {
                 $where .= ' `file_source` = ?';
                 $column = 'file_source';
             } else {
                 $where .= ' `item_source` = ?';
                 $column = 'item_source';
             }
         } else {
             if ($fileDependent) {
                 $where .= ' `file_target` = ?';
                 $item = \OC\Files\Filesystem::normalizePath($item);
             } else {
                 $where .= ' `item_target` = ?';
             }
         }
         $queryArgs[] = $item;
         if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
             $placeholders = join(',', array_fill(0, count($collectionTypes), '?'));
             $where .= ' OR `item_type` IN (' . $placeholders . '))';
             $queryArgs = array_merge($queryArgs, $collectionTypes);
         }
     }
     if ($shareType == self::$shareTypeUserAndGroups && $limit === 1) {
         // Make sure the unique user target is returned if it exists,
         // unique targets should follow the group share in the database
         // If the limit is not 1, the filtering can be done later
         $where .= ' ORDER BY `*PREFIX*share`.`id` DESC';
     } else {
         $where .= ' ORDER BY `*PREFIX*share`.`id` ASC';
     }
     if ($limit != -1 && !$includeCollections) {
         // The limit must be at least 3, because filtering needs to be done
         if ($limit < 3) {
             $queryLimit = 3;
         } else {
             $queryLimit = $limit;
         }
     } else {
         $queryLimit = null;
     }
     $select = self::createSelectStatement($format, $fileDependent, $uidOwner);
     $root = strlen($root);
     $query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where, $queryLimit);
     $result = $query->execute($queryArgs);
     if (\OC_DB::isError($result)) {
         \OCP\Util::writeLog('OCP\\Share', \OC_DB::getErrorMessage() . ', select=' . $select . ' where=', \OCP\Util::ERROR);
     }
     $items = array();
     $targets = array();
     $switchedItems = array();
     $mounts = array();
     while ($row = $result->fetchRow()) {
         self::transformDBResults($row);
         // Filter out duplicate group shares for users with unique targets
         if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
             continue;
         }
         if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']]) && $items[$row['parent']]['share_type'] == self::SHARE_TYPE_GROUP) {
             $row['share_type'] = self::SHARE_TYPE_GROUP;
             $row['unique_name'] = true;
             // remember that we use a unique name for this user
             $row['share_with'] = $items[$row['parent']]['share_with'];
             // if the group share was unshared from the user we keep the permission, otherwise
             // we take the permission from the parent because this is always the up-to-date
             // permission for the group share
             if ($row['permissions'] > 0) {
                 $row['permissions'] = $items[$row['parent']]['permissions'];
             }
             // Remove the parent group share
             unset($items[$row['parent']]);
             if ($row['permissions'] == 0) {
                 continue;
             }
         } else {
             if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']]) && $items[$row['parent']]['share_type'] == self::SHARE_TYPE_SHARING_GROUP) {
                 $row['share_type'] = self::SHARE_TYPE_SHARING_GROUP;
                 $row['unique_name'] = true;
                 // remember that we use a unique name for this user
                 $row['share_with'] = $items[$row['parent']]['share_with'];
                 // if the group share was unshared from the user we keep the permission, otherwise
                 // we take the permission from the parent because this is always the up-to-date
                 // permission for the group share
                 if ($row['permissions'] > 0) {
                     $row['permissions'] = $items[$row['parent']]['permissions'];
                 }
                 // Remove the parent group share
                 unset($items[$row['parent']]);
                 if ($row['permissions'] == 0) {
                     continue;
                 }
             } else {
                 if (!isset($uidOwner)) {
                     // Check if the same target already exists
                     if (isset($targets[$row['id']])) {
                         // Check if the same owner shared with the user twice
                         // through a group and user share - this is allowed
                         $id = $targets[$row['id']];
                         if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
                             // Switch to group share type to ensure resharing conditions aren't bypassed
                             if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) {
                                 $items[$id]['share_type'] = self::SHARE_TYPE_GROUP;
                                 $items[$id]['share_with'] = $row['share_with'];
                             }
                             // Switch ids if sharing permission is granted on only
                             // one share to ensure correct parent is used if resharing
                             if (~(int) $items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE && (int) $row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
                                 $items[$row['id']] = $items[$id];
                                 $switchedItems[$id] = $row['id'];
                                 unset($items[$id]);
                                 $id = $row['id'];
                             }
                             $items[$id]['permissions'] |= (int) $row['permissions'];
                         }
                         continue;
                     } elseif (!empty($row['parent'])) {
                         $targets[$row['parent']] = $row['id'];
                     }
                 }
             }
         }
         // Remove root from file source paths if retrieving own shared items
         if (isset($uidOwner) && isset($row['path'])) {
             if (isset($row['parent'])) {
                 $query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?');
                 $parentResult = $query->execute(array($row['parent']));
                 if (\OC_DB::isError($result)) {
                     \OCP\Util::writeLog('OCP\\Share', 'Can\'t select parent: ' . \OC_DB::getErrorMessage() . ', select=' . $select . ' where=' . $where, \OCP\Util::ERROR);
                 } else {
                     $parentRow = $parentResult->fetchRow();
                     $tmpPath = $parentRow['file_target'];
                     // find the right position where the row path continues from the target path
                     $pos = strrpos($row['path'], $parentRow['file_target']);
                     $subPath = substr($row['path'], $pos);
                     $splitPath = explode('/', $subPath);
                     foreach (array_slice($splitPath, 2) as $pathPart) {
                         $tmpPath = $tmpPath . '/' . $pathPart;
                     }
                     $row['path'] = $tmpPath;
                 }
             } else {
                 if (!isset($mounts[$row['storage']])) {
                     $mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']);
                     if (is_array($mountPoints) && !empty($mountPoints)) {
                         $mounts[$row['storage']] = current($mountPoints);
                     }
                 }
                 if (!empty($mounts[$row['storage']])) {
                     $path = $mounts[$row['storage']]->getMountPoint() . $row['path'];
                     $relPath = substr($path, $root);
                     // path relative to data/user
                     $row['path'] = rtrim($relPath, '/');
                 }
             }
         }
         if ($checkExpireDate) {
             if (self::expireItem($row)) {
                 continue;
             }
         }
         // Check if resharing is allowed, if not remove share permission
         if (isset($row['permissions']) && !self::isResharingAllowed() | \OC_Util::isSharingDisabledForUser()) {
             $row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE;
         }
         // Add display names to result
         $row['share_with_displayname'] = $row['share_with'];
         if (isset($row['share_with']) && $row['share_with'] != '' && $row['share_type'] === self::SHARE_TYPE_USER) {
             $row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']);
         } else {
             if (isset($row['share_with']) && $row['share_with'] != '' && $row['share_type'] === self::SHARE_TYPE_REMOTE) {
                 $addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD']);
                 foreach ($addressBookEntries as $entry) {
                     foreach ($entry['CLOUD'] as $cloudID) {
                         if ($cloudID === $row['share_with']) {
                             $row['share_with_displayname'] = $entry['FN'];
                         }
                     }
                 }
             }
         }
         if (isset($row['uid_owner']) && $row['uid_owner'] != '') {
             $row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']);
         }
         if ($row['permissions'] > 0) {
             $items[$row['id']] = $row;
         }
     }
     // group items if we are looking for items shared with the current user
     if (isset($shareWith) && $shareWith === \OCP\User::getUser()) {
         $items = self::groupItems($items, $itemType);
     }
     if (!empty($items)) {
         $collectionItems = array();
         foreach ($items as &$row) {
             // Return only the item instead of a 2-dimensional array
             if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) {
                 if ($format == self::FORMAT_NONE) {
                     return $row;
                 } else {
                     break;
                 }
             }
             // Check if this is a collection of the requested item type
             if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) {
                 if (($collectionBackend = self::getBackend($row['item_type'])) && $collectionBackend instanceof \OCP\Share_Backend_Collection) {
                     // Collections can be inside collections, check if the item is a collection
                     if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
                         $collectionItems[] = $row;
                     } else {
                         $collection = array();
                         $collection['item_type'] = $row['item_type'];
                         if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
                             $collection['path'] = basename($row['path']);
                         }
                         $row['collection'] = $collection;
                         // Fetch all of the children sources
                         $children = $collectionBackend->getChildren($row[$column]);
                         foreach ($children as $child) {
                             $childItem = $row;
                             $childItem['item_type'] = $itemType;
                             if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
                                 $childItem['item_source'] = $child['source'];
                                 $childItem['item_target'] = $child['target'];
                             }
                             if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
                                 if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
                                     $childItem['file_source'] = $child['source'];
                                 } else {
                                     // TODO is this really needed if we already know that we use the file backend?
                                     $meta = \OC\Files\Filesystem::getFileInfo($child['file_path']);
                                     $childItem['file_source'] = $meta['fileid'];
                                 }
                                 $childItem['file_target'] = \OC\Files\Filesystem::normalizePath($child['file_path']);
                             }
                             if (isset($item)) {
                                 if ($childItem[$column] == $item) {
                                     // Return only the item instead of a 2-dimensional array
                                     if ($limit == 1) {
                                         if ($format == self::FORMAT_NONE) {
                                             return $childItem;
                                         } else {
                                             // Unset the items array and break out of both loops
                                             $items = array();
                                             $items[] = $childItem;
                                             break 2;
                                         }
                                     } else {
                                         $collectionItems[] = $childItem;
                                     }
                                 }
                             } else {
                                 $collectionItems[] = $childItem;
                             }
                         }
                     }
                 }
                 // Remove collection item
                 $toRemove = $row['id'];
                 if (array_key_exists($toRemove, $switchedItems)) {
                     $toRemove = $switchedItems[$toRemove];
                 }
                 unset($items[$toRemove]);
             } elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
                 // FIXME: Thats a dirty hack to improve file sharing performance,
                 // see github issue #10588 for more details
                 // Need to find a solution which works for all back-ends
                 $collectionBackend = self::getBackend($row['item_type']);
                 $sharedParents = $collectionBackend->getParents($row['item_source']);
                 foreach ($sharedParents as $parent) {
                     $collectionItems[] = $parent;
                 }
             }
         }
         if (!empty($collectionItems)) {
             $items = array_merge($items, $collectionItems);
         }
         // filter out invalid items, these can appear when subshare entries exist
         // for a group in which the requested user isn't a member any more
         $items = array_filter($items, function ($item) {
             return $item['share_type'] !== self::$shareTypeGroupUserUnique;
         });
         return self::formatResult($items, $column, $backend, $format, $parameters);
     } elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) {
         // FIXME: Thats a dirty hack to improve file sharing performance,
         // see github issue #10588 for more details
         // Need to find a solution which works for all back-ends
         $collectionItems = array();
         $collectionBackend = self::getBackend('folder');
         $sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner);
         foreach ($sharedParents as $parent) {
             $collectionItems[] = $parent;
         }
         if ($limit === 1) {
             return reset($collectionItems);
         }
         return self::formatResult($collectionItems, $column, $backend, $format, $parameters);
     }
     return array();
 }