diff options
author | Lukas Reschke <lukas@owncloud.com> | 2014-09-29 20:26:41 +0200 |
---|---|---|
committer | Lukas Reschke <lukas@owncloud.com> | 2014-09-29 20:26:41 +0200 |
commit | 8c8e5c168eefbf1460d5456dde6d09562de8902e (patch) | |
tree | 71d80acf82bb9e2c6188ff8f6cc495876bad0ebc /lib | |
parent | 9489852e9490c6182e3d900c5fba2ca37d281dd1 (diff) | |
parent | 6e7acb8ae083c158fb412fa3e035e64a83063911 (diff) | |
download | nextcloud-server-8c8e5c168eefbf1460d5456dde6d09562de8902e.tar.gz nextcloud-server-8c8e5c168eefbf1460d5456dde6d09562de8902e.zip |
Merge pull request #10107 from owncloud/sharing_group_shares
[sharing] group shares
Diffstat (limited to 'lib')
-rw-r--r-- | lib/private/share/helper.php | 127 | ||||
-rw-r--r-- | lib/private/share/hooks.php | 28 | ||||
-rw-r--r-- | lib/private/share/share.php | 603 | ||||
-rw-r--r-- | lib/public/share.php | 4 |
4 files changed, 398 insertions, 364 deletions
diff --git a/lib/private/share/helper.php b/lib/private/share/helper.php index 46e3c280488..2418535c9d5 100644 --- a/lib/private/share/helper.php +++ b/lib/private/share/helper.php @@ -35,8 +35,8 @@ class Helper extends \OC\Share\Constants { * @throws \Exception * @return string Item target */ - public static function generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, - $suggestedTarget = null, $groupParent = null) { + public static function generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $suggestedTarget = null, $groupParent = null) { + // FIXME: $uidOwner and $groupParent seems to be unused $backend = \OC\Share\Share::getBackend($itemType); if ($shareType == self::SHARE_TYPE_LINK) { if (isset($suggestedTarget)) { @@ -54,91 +54,28 @@ class Helper extends \OC\Share\Constants { if ($shareType == self::SHARE_TYPE_USER) { // Share with is a user, so set share type to user and groups $shareType = self::$shareTypeUserAndGroups; - $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith)); - } else { - $userAndGroups = false; } - $exclude = null; - // Backend has 3 opportunities to generate a unique target - for ($i = 0; $i < 2; $i++) { - // Check if suggested target exists first - if ($i == 0 && isset($suggestedTarget)) { - $target = $suggestedTarget; - } else { - if ($shareType == self::SHARE_TYPE_GROUP) { - $target = $backend->generateTarget($itemSource, false, $exclude); - } else { - $target = $backend->generateTarget($itemSource, $shareWith, $exclude); - } - if (is_array($exclude) && in_array($target, $exclude)) { - break; - } - } - // Check if target already exists - $checkTarget = \OC\Share\Share::getItems($itemType, $target, $shareType, $shareWith); - if (!empty($checkTarget)) { - foreach ($checkTarget as $item) { - // Skip item if it is the group parent row - if (isset($groupParent) && $item['id'] == $groupParent) { - if (count($checkTarget) == 1) { - return $target; - } else { - continue; - } - } - if ($item['uid_owner'] == $uidOwner) { - if ($itemType == 'file' || $itemType == 'folder') { - $meta = \OC\Files\Filesystem::getFileInfo($itemSource); - if ($item['file_source'] == $meta['fileid']) { - return $target; - } - } else if ($item['item_source'] == $itemSource) { - return $target; - } - } - } - if (!isset($exclude)) { - $exclude = array(); - } - // Find similar targets to improve backend's chances to generate a unqiue target - if ($userAndGroups) { - if ($column == 'file_target') { - $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' - .' WHERE `item_type` IN (\'file\', \'folder\')' - .' AND `share_type` IN (?,?,?)' - .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')'); - $result = $checkTargets->execute(array(self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, - self::$shareTypeGroupUserUnique)); - } else { - $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' - .' WHERE `item_type` = ? AND `share_type` IN (?,?,?)' - .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')'); - $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_USER, - self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique)); - } - } else { - if ($column == 'file_target') { - $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' - .' WHERE `item_type` IN (\'file\', \'folder\')' - .' AND `share_type` = ? AND `share_with` = ?'); - $result = $checkTargets->execute(array(self::SHARE_TYPE_GROUP, $shareWith)); - } else { - $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share`' - .' WHERE `item_type` = ? AND `share_type` = ? AND `share_with` = ?'); - $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_GROUP, $shareWith)); - } - } - while ($row = $result->fetchRow()) { - $exclude[] = $row[$column]; - } - } else { - return $target; + $exclude = array(); + + $result = \OCP\Share::getItemsSharedWithUser($itemType, $shareWith); + foreach ($result as $row) { + if ($row['permissions'] > 0) { + $exclude[] = $row[$column]; } } + + // Check if suggested target exists first + if (!isset($suggestedTarget)) { + $suggestedTarget = $itemSource; + } + if ($shareType == self::SHARE_TYPE_GROUP) { + $target = $backend->generateTarget($suggestedTarget, false, $exclude); + } else { + $target = $backend->generateTarget($suggestedTarget, $shareWith, $exclude); + } + + return $target; } - $message = 'Sharing backend registered for '.$itemType.' did not generate a unique target for '.$itemSource; - \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); - throw new \Exception($message); } /** @@ -146,10 +83,12 @@ class Helper extends \OC\Share\Constants { * @param int $parent Id of item to delete * @param bool $excludeParent If true, exclude the parent from the delete (optional) * @param string $uidOwner The user that the parent was shared with (optional) + * @param int $newParent new parent for the childrens */ - public static function delete($parent, $excludeParent = false, $uidOwner = null) { + public static function delete($parent, $excludeParent = false, $uidOwner = null, $newParent = null) { $ids = array($parent); $deletedItems = array(); + $changeParent = array(); $parents = array($parent); while (!empty($parents)) { $parents = "'".implode("','", $parents)."'"; @@ -167,8 +106,6 @@ class Helper extends \OC\Share\Constants { // Reset parents array, only go through loop again if items are found $parents = array(); while ($item = $result->fetchRow()) { - $ids[] = $item['id']; - $parents[] = $item['id']; $tmpItem = array( 'id' => $item['id'], 'shareWith' => $item['share_with'], @@ -179,12 +116,28 @@ class Helper extends \OC\Share\Constants { if (isset($item['file_target'])) { $tmpItem['fileTarget'] = $item['file_target']; } - $deletedItems[] = $tmpItem; + // if we have a new parent for the child we remember the child + // to update the parent, if not we add it to the list of items + // which should be deleted + if ($newParent !== null) { + $changeParent[] = $item['id']; + } else { + $deletedItems[] = $tmpItem; + $ids[] = $item['id']; + $parents[] = $item['id']; + } } } if ($excludeParent) { unset($ids[0]); } + + if (!empty($changeParent)) { + $idList = "'".implode("','", $changeParent)."'"; + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `parent` = ? WHERE `id` IN ('.$idList.')'); + $query->execute(array($newParent)); + } + if (!empty($ids)) { $idList = "'".implode("','", $ids)."'"; $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `id` IN ('.$idList.')'); diff --git a/lib/private/share/hooks.php b/lib/private/share/hooks.php index 9ac64d888ea..66b197b921b 100644 --- a/lib/private/share/hooks.php +++ b/lib/private/share/hooks.php @@ -45,6 +45,7 @@ class Hooks extends \OC\Share\Constants { * @param array $arguments */ public static function post_addToGroup($arguments) { + // Find the group shares and check if the user needs a unique target $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?'); $result = $query->execute(array(self::SHARE_TYPE_GROUP, $arguments['gid'])); @@ -52,18 +53,25 @@ class Hooks extends \OC\Share\Constants { .' `item_target`, `parent`, `share_type`, `share_with`, `uid_owner`, `permissions`,' .' `stime`, `file_source`, `file_target`) VALUES (?,?,?,?,?,?,?,?,?,?,?)'); while ($item = $result->fetchRow()) { - if ($item['item_type'] == 'file' || $item['item_type'] == 'file') { - $itemTarget = null; - } else { - $itemTarget = Helper::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, - $arguments['uid'], $item['uid_owner'], $item['item_target'], $item['id']); - } - if (isset($item['file_source'])) { - $fileTarget = Helper::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, - $arguments['uid'], $item['uid_owner'], $item['file_target'], $item['id']); + + $sourceExists = \OC\Share\Share::getItemSharedWithBySource($item['item_type'], $item['item_source'], self::FORMAT_NONE, null, true, $arguments['uid']); + + if ($sourceExists) { + $fileTarget = $sourceExists['file_target']; + $itemTarget = $sourceExists['item_target']; } else { - $fileTarget = null; + $itemTarget = Helper::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, $arguments['uid'], + $item['owner'], null, $item['parent']); + + // do we also need a file target + if ($item['item_type'] === 'file' || $item['item_type'] === 'folder') { + $fileTarget = Helper::generateTarget('file', $item['file_target'], self::SHARE_TYPE_USER, $arguments['uid'], + $item['owner'], null, $item['parent']); + } else { + $fileTarget = null; + } } + // Insert an extra row for the group share if the item or file target is unique for this user if ($itemTarget != $item['item_target'] || $fileTarget != $item['file_target']) { $query->execute(array($item['item_type'], $item['item_source'], $itemTarget, $item['id'], diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 8441e6a94c4..d861f0510e4 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -294,23 +294,32 @@ class Share extends \OC\Share\Constants { $shares = array(); + $column = ($itemType === 'file' || $itemType === 'folder') ? 'file_source' : 'item_source'; + + $where = ' `' . $column . '` = ? AND `item_type` = ? '; + $arguments = array($itemSource, $itemType); + // for link shares $user === null + if ($user !== null) { + $where .= ' AND `share_with` = ? '; + $arguments[] = $user; + } + // first check if there is a db entry for the specific user $query = \OC_DB::prepare( - 'SELECT `file_target`, `permissions`, `expiration` + 'SELECT * FROM `*PREFIX*share` - WHERE - `item_source` = ? AND `item_type` = ? AND `share_with` = ?' + WHERE' . $where ); - $result = \OC_DB::executeAudited($query, array($itemSource, $itemType, $user)); + $result = \OC_DB::executeAudited($query, $arguments); while ($row = $result->fetchRow()) { $shares[] = $row; } //if didn't found a result than let's look for a group share. - if(empty($shares)) { + if(empty($shares) && $user !== null) { $groups = \OC_Group::getUserGroups($user); $query = \OC_DB::prepare( @@ -318,7 +327,7 @@ class Share extends \OC\Share\Constants { FROM `*PREFIX*share` WHERE - `item_source` = ? AND `item_type` = ? AND `share_with` in (?)' + `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)' ); $result = \OC_DB::executeAudited($query, array($itemSource, $itemType, implode(',', $groups))); @@ -339,11 +348,13 @@ class Share extends \OC\Share\Constants { * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters * @param boolean $includeCollections - * @return mixed Return depends on format + * @param string $shareWith (optional) define against which user should be checked, default: current user + * @return array */ public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE, - $parameters = null, $includeCollections = false) { - return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, + $parameters = null, $includeCollections = false, $shareWith = null) { + $shareWith = ($shareWith === null) ? \OC_User::getUser() : $shareWith; + return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, $shareWith, null, $format, $parameters, 1, $includeCollections, true); } @@ -679,9 +690,31 @@ class Share extends \OC\Share\Constants { * @return boolean true on success or false on failure */ public static function unshare($itemType, $itemSource, $shareType, $shareWith) { - $item = self::getItems($itemType, $itemSource, $shareType, $shareWith, \OC_User::getUser(),self::FORMAT_NONE, null, 1); - if (!empty($item)) { - self::unshareItem($item); + + // check if it is a valid itemType + self::getBackend($itemType); + + $items = self::getItemSharedWithUser($itemType, $itemSource, $shareWith); + + $toDelete = array(); + $newParent = null; + $currentUser = \OC_User::getUser(); + foreach ($items as $item) { + // delete the item with the expected share_type and owner + if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) { + $toDelete = $item; + // if there is more then one result we don't have to delete the children + // but update their parent. For group shares the new parent should always be + // the original group share and not the db entry with the unique name + } else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) { + $newParent = $item['parent']; + } else { + $newParent = $item['id']; + } + } + + if (!empty($toDelete)) { + self::unshareItem($toDelete, $newParent); return true; } return false; @@ -773,7 +806,7 @@ class Share extends \OC\Share\Constants { } } - if (!$itemUnshared && isset($groupShare)) { + if (!$itemUnshared && isset($groupShare) && !isset($uniqueGroupShare)) { $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share`' .' (`item_type`, `item_source`, `item_target`, `parent`, `share_type`,' .' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`, `file_target`)' @@ -1054,9 +1087,10 @@ class Share extends \OC\Share\Constants { /** * Unshares a share given a share data array * @param array $item Share data (usually database row) + * @param int new parent ID * @return null */ - protected static function unshareItem(array $item) { + protected static function unshareItem(array $item, $newParent = null) { // Pass all the vars we have for now, they may be useful $hookParams = array( 'id' => $item['id'], @@ -1073,7 +1107,7 @@ class Share extends \OC\Share\Constants { } \OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams); - $deletedShares = Helper::delete($item['id']); + $deletedShares = Helper::delete($item['id'], false, null, $newParent); $deletedShares[] = $hookParams; $hookParams['deletedShares'] = $deletedShares; \OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams); @@ -1292,13 +1326,17 @@ class Share extends \OC\Share\Constants { $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) { - if ($shareType == self::$shareTypeUserAndGroups) { - // 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'; - } // The limit must be at least 3, because filtering needs to be done if ($limit < 3) { $queryLimit = 3; @@ -1307,7 +1345,6 @@ class Share extends \OC\Share\Constants { } } else { $queryLimit = null; - $where .= ' ORDER BY `*PREFIX*share`.`id` ASC'; } $select = self::createSelectStatement($format, $fileDependent, $uidOwner); $root = strlen($root); @@ -1315,7 +1352,7 @@ class Share extends \OC\Share\Constants { $result = $query->execute($queryArgs); if (\OC_DB::isError($result)) { \OC_Log::write('OCP\Share', - \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where, + \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=', \OC_Log::ERROR); } $items = array(); @@ -1329,6 +1366,12 @@ class Share extends \OC\Share\Constants { $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) { @@ -1336,10 +1379,10 @@ class Share extends \OC\Share\Constants { } } else if (!isset($uidOwner)) { // Check if the same target already exists - if (isset($targets[$row[$column]])) { + 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[$column]]; + $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) { @@ -1355,12 +1398,12 @@ class Share extends \OC\Share\Constants { unset($items[$id]); $id = $row['id']; } - // Combine the permissions for the item $items[$id]['permissions'] |= (int)$row['permissions']; - continue; + } - } else { - $targets[$row[$column]] = $row['id']; + continue; + } elseif (!empty($row['parent'])) { + $targets[$row['parent']] = $row['id']; } } // Remove root from file source paths if retrieving own shared items @@ -1398,6 +1441,7 @@ class Share extends \OC\Share\Constants { } } } + if($checkExpireDate) { if (self::expireItem($row)) { continue; @@ -1415,8 +1459,17 @@ class Share extends \OC\Share\Constants { $row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']); } - $items[$row['id']] = $row; + 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) { @@ -1502,6 +1555,47 @@ class Share extends \OC\Share\Constants { } /** + * group items with link to the same source + * + * @param array $items + * @param string $itemType + * @return array of grouped items + */ + protected static function groupItems($items, $itemType) { + + $fileSharing = ($itemType === 'file' || $itemType === 'folder') ? true : false; + + $result = array(); + + foreach ($items as $item) { + $grouped = false; + foreach ($result as $key => $r) { + // for file/folder shares we need to compare file_source, otherwise we compare item_source + // only group shares if they already point to the same target, otherwise the file where shared + // before grouping of shares was added. In this case we don't group them toi avoid confusions + if (( $fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) || + (!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) { + // add the first item to the list of grouped shares + if (!isset($result[$key]['grouped'])) { + $result[$key]['grouped'][] = $result[$key]; + } + $result[$key]['permissions'] = (int) $item['permissions'] | (int) $r['permissions']; + $result[$key]['grouped'][] = $item; + $grouped = true; + break; + } + } + + if (!$grouped) { + $result[] = $item; + } + + } + + return $result; + } + +/** * Put shared item into the database * @param string $itemType Item type * @param string $itemSource Item source @@ -1518,121 +1612,34 @@ class Share extends \OC\Share\Constants { */ private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $parentFolder = null, $token = null, $itemSourceName = null, \DateTime $expirationDate = null) { - $backend = self::getBackend($itemType); - $l = \OC::$server->getL10N('lib'); - // Check if this is a reshare - if ($checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true)) { - - // Check if attempting to share back to owner - if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) { - $message = 'Sharing %s failed, because the user %s is the original sharer'; - $message_t = $l->t('Sharing %s failed, because the user %s is the original sharer', array($itemSourceName, $shareWith)); - - \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR); - throw new \Exception($message_t); - } - // Check if share permissions is granted - if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\PERMISSION_SHARE) { - if (~(int)$checkReshare['permissions'] & $permissions) { - $message = 'Sharing %s failed, because the permissions exceed permissions granted to %s'; - $message_t = $l->t('Sharing %s failed, because the permissions exceed permissions granted to %s', array($itemSourceName, $uidOwner)); - \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $uidOwner), \OC_Log::ERROR); - throw new \Exception($message_t); - } else { - // TODO Don't check if inside folder - $parent = $checkReshare['id']; - $itemSource = $checkReshare['item_source']; - $fileSource = $checkReshare['file_source']; - $suggestedItemTarget = $checkReshare['item_target']; - $suggestedFileTarget = $checkReshare['file_target']; - $filePath = $checkReshare['file_target']; - $expirationDate = min($expirationDate, $checkReshare['expiration']); - } - } else { - $message = 'Sharing %s failed, because resharing is not allowed'; - $message_t = $l->t('Sharing %s failed, because resharing is not allowed', array($itemSourceName)); + $queriesToExecute = array(); + $suggestedItemTarget = null; - \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName), \OC_Log::ERROR); - throw new \Exception($message_t); - } - } else { - $parent = null; - $suggestedItemTarget = null; - $suggestedFileTarget = null; - if (!$backend->isValidSource($itemSource, $uidOwner)) { - $message = 'Sharing %s failed, because the sharing backend for ' - .'%s could not find its source'; - $message_t = $l->t('Sharing %s failed, because the sharing backend for %s could not find its source', array($itemSource, $itemType)); - \OC_Log::write('OCP\Share', sprintf($message, $itemSource, $itemType), \OC_Log::ERROR); - throw new \Exception($message_t); - } - if ($backend instanceof \OCP\Share_Backend_File_Dependent) { - $filePath = $backend->getFilePath($itemSource, $uidOwner); - if ($itemType == 'file' || $itemType == 'folder') { - $fileSource = $itemSource; - } else { - $meta = \OC\Files\Filesystem::getFileInfo($filePath); - $fileSource = $meta['fileid']; - } - if ($fileSource == -1) { - $message = 'Sharing %s failed, because the file could not be found in the file cache'; - $message_t = $l->t('Sharing %s failed, because the file could not be found in the file cache', array($itemSource)); - - \OC_Log::write('OCP\Share', sprintf($message, $itemSource), \OC_Log::ERROR); - throw new \Exception($message_t); - } - } else { - $filePath = null; - $fileSource = null; - } + $result = self::checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate); + if(!empty($result)) { + $parent = $result['parent']; + $itemSource = $result['itemSource']; + $fileSource = $result['fileSource']; + $suggestedItemTarget = $result['suggestedItemTarget']; + $suggestedFileTarget = $result['suggestedFileTarget']; + $filePath = $result['filePath']; + $expirationDate = $result['expirationDate']; } - // Share with a group + $isGroupShare = false; if ($shareType == self::SHARE_TYPE_GROUP) { - $groupItemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], - $uidOwner, $suggestedItemTarget); - $run = true; - $error = ''; - \OC_Hook::emit('OCP\Share', 'pre_shared', array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $groupItemTarget, - 'shareType' => $shareType, - 'shareWith' => $shareWith['group'], - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'fileSource' => $fileSource, - 'expiration' => $expirationDate, - 'token' => $token, - 'run' => &$run, - 'error' => &$error - )); - - if ($run === false) { - throw new \Exception($error); - } - - if (isset($fileSource)) { - if ($parentFolder) { - if ($parentFolder === true) { - $groupFileTarget = Helper::generateTarget('file', $filePath, $shareType, - $shareWith['group'], $uidOwner, $suggestedFileTarget); - // Set group default file target for future use - $parentFolders[0]['folder'] = $groupFileTarget; - } else { - // Get group default file target - $groupFileTarget = $parentFolder[0]['folder'].$itemSource; - $parent = $parentFolder[0]['id']; - } - } else { - $groupFileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith['group'], - $uidOwner, $suggestedFileTarget); - } - } else { - $groupFileTarget = null; + $isGroupShare = true; + $users = \OC_Group::usersInGroup($shareWith['group']); + // remove current user from list + if (in_array(\OCP\User::getUser(), $users)) { + unset($users[array_search(\OCP\User::getUser(), $users)]); } - $queriesToExecute = array(); + $groupItemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], + $uidOwner, $suggestedItemTarget); + $groupFileTarget = $filePath; + + // add group share to table and remember the id as parent $queriesToExecute['groupShare'] = array( 'itemType' => $itemType, 'itemSource' => $itemSource, @@ -1643,165 +1650,231 @@ class Share extends \OC\Share\Constants { 'permissions' => $permissions, 'shareTime' => time(), 'fileSource' => $fileSource, - 'fileTarget' => $groupFileTarget, + 'fileTarget' => $filePath, 'token' => $token, 'parent' => $parent, 'expiration' => $expirationDate, ); - // Loop through all users of this group in case we need to add an extra row - foreach ($shareWith['users'] as $uid) { - $itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $uid, + + } else { + $users = array($shareWith); + $itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, + $suggestedItemTarget); + } + + $run = true; + $error = ''; + $preHookData = array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'shareType' => $shareType, + 'uidOwner' => $uidOwner, + 'permissions' => $permissions, + 'fileSource' => $fileSource, + 'expiration' => $expirationDate, + 'token' => $token, + 'run' => &$run, + 'error' => &$error + ); + + $preHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget; + $preHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith; + + \OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData); + + if ($run === false) { + throw new \Exception($error); + } + + foreach ($users as $user) { + $sourceId = ($itemType === 'file' || $itemType === 'folder') ? $fileSource : $itemSource; + $sourceExists = self::getItemSharedWithBySource($itemType, $sourceId, self::FORMAT_NONE, null, true, $user); + + $shareType = ($isGroupShare) ? self::$shareTypeGroupUserUnique : $shareType; + + if ($sourceExists) { + $fileTarget = $sourceExists['file_target']; + $itemTarget = $sourceExists['item_target']; + + // for group shares we don't need a additional entry if the target is the same + if($isGroupShare && $groupItemTarget === $itemTarget) { + continue; + } + + } elseif(!$sourceExists && !$isGroupShare) { + + $itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $user, $uidOwner, $suggestedItemTarget, $parent); if (isset($fileSource)) { if ($parentFolder) { if ($parentFolder === true) { - $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid, + $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $user, $uidOwner, $suggestedFileTarget, $parent); if ($fileTarget != $groupFileTarget) { - $parentFolders[$uid]['folder'] = $fileTarget; + $parentFolders[$user]['folder'] = $fileTarget; } - } else if (isset($parentFolder[$uid])) { - $fileTarget = $parentFolder[$uid]['folder'].$itemSource; - $parent = $parentFolder[$uid]['id']; + } else if (isset($parentFolder[$user])) { + $fileTarget = $parentFolder[$user]['folder'].$itemSource; + $parent = $parentFolder[$user]['id']; } } else { $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, - $uid, $uidOwner, $suggestedFileTarget, $parent); + $user, $uidOwner, $suggestedFileTarget, $parent); } } else { $fileTarget = null; } - // Insert an extra row for the group share if the item or file target is unique for this user - if ($itemTarget != $groupItemTarget || (isset($fileSource) && $fileTarget != $groupFileTarget)) { - $queriesToExecute[] = array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $itemTarget, - 'shareType' => self::$shareTypeGroupUserUnique, - 'shareWith' => $uid, - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'shareTime' => time(), - 'fileSource' => $fileSource, - 'fileTarget' => $fileTarget, - 'token' => $token, - //'parent' => $parent, - 'expiration' => $expirationDate, - ); + + } else { + + // group share which doesn't exists until now, check if we need a unique target for this user + + $itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $user, + $uidOwner, $suggestedItemTarget, $parent); + + // do we also need a file target + if (isset($fileSource)) { + $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $user, + $uidOwner, $suggestedFileTarget, $parent); + } else { + $fileTarget = null; + } + + if ($itemTarget === $groupItemTarget && (isset($fileSource) && $fileTarget === $groupItemTarget)) { + continue; } } + $queriesToExecute[] = array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'itemTarget' => $itemTarget, + 'shareType' => $shareType, + 'shareWith' => $user, + 'uidOwner' => $uidOwner, + 'permissions' => $permissions, + 'shareTime' => time(), + 'fileSource' => $fileSource, + 'fileTarget' => $fileTarget, + 'token' => $token, + 'parent' => $parent, + 'expiration' => $expirationDate, + ); + + } + + if ($isGroupShare) { self::insertShare($queriesToExecute['groupShare']); // Save this id, any extra rows for this group share will need to reference it $parent = \OC_DB::insertid('*PREFIX*share'); unset($queriesToExecute['groupShare']); + } - foreach ($queriesToExecute as $shareQuery) { - $shareQuery['parent'] = $parent; - self::insertShare($shareQuery); - } + foreach ($queriesToExecute as $shareQuery) { + $shareQuery['parent'] = $parent; + self::insertShare($shareQuery); + } - \OC_Hook::emit('OCP\Share', 'post_shared', array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $groupItemTarget, - 'parent' => $parent, - 'shareType' => $shareType, - 'shareWith' => $shareWith['group'], - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'fileSource' => $fileSource, - 'fileTarget' => $groupFileTarget, - 'id' => $parent, - 'token' => $token, - 'expirationDate' => $expirationDate, - )); - - if ($parentFolder === true) { - // Return parent folders to preserve file target paths for potential children - return $parentFolders; + $postHookData = array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'parent' => $parent, + 'shareType' => $shareType, + 'uidOwner' => $uidOwner, + 'permissions' => $permissions, + 'fileSource' => $fileSource, + 'id' => $parent, + 'token' => $token, + 'expirationDate' => $expirationDate, + ); + + $postHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith; + $postHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget; + $postHookData['fileTarget'] = ($isGroupShare) ? $groupFileTarget : $fileTarget; + + \OC_Hook::emit('OCP\Share', 'post_shared', $postHookData); + + + return true; + } + + private static function checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate) { + $backend = self::getBackend($itemType); + + $l = \OC::$server->getL10N('lib'); + $result = array(); + + $checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true); + if ($checkReshare) { + // Check if attempting to share back to owner + if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) { + $message = 'Sharing %s failed, because the user %s is the original sharer'; + $message_t = $l->t('Sharing %s failed, because the user %s is the original sharer', array($itemSourceName, $shareWith)); + + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OC_Log::ERROR); + throw new \Exception($message_t); } - } else { - $itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, - $suggestedItemTarget); - $run = true; - $error = ''; - \OC_Hook::emit('OCP\Share', 'pre_shared', array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $itemTarget, - 'shareType' => $shareType, - 'shareWith' => $shareWith, - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'fileSource' => $fileSource, - 'token' => $token, - 'expirationDate' => $expirationDate, - 'run' => &$run, - 'error' => &$error, - )); - - if ($run === false) { - throw new \Exception($error); - } - - if (isset($fileSource)) { - if ($parentFolder) { - if ($parentFolder === true) { - $fileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith, - $uidOwner, $suggestedFileTarget); - $parentFolders['folder'] = $fileTarget; - } else { - $fileTarget = $parentFolder['folder'].$itemSource; - $parent = $parentFolder['id']; - } + + // Check if share permissions is granted + if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\PERMISSION_SHARE) { + if (~(int)$checkReshare['permissions'] & $permissions) { + $message = 'Sharing %s failed, because the permissions exceed permissions granted to %s'; + $message_t = $l->t('Sharing %s failed, because the permissions exceed permissions granted to %s', array($itemSourceName, $uidOwner)); + + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName, $uidOwner), \OC_Log::ERROR); + throw new \Exception($message_t); } else { - $fileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner, - $suggestedFileTarget); + // TODO Don't check if inside folder + $result['parent'] = $checkReshare['id']; + $result['itemSource'] = $checkReshare['item_source']; + $result['fileSource'] = $checkReshare['file_source']; + $result['suggestedItemTarget'] = $checkReshare['item_target']; + $result['suggestedFileTarget'] = $checkReshare['file_target']; + $result['filePath'] = $checkReshare['file_target']; + $result['expirationDate'] = min($expirationDate, $checkReshare['expiration']); } } else { - $fileTarget = null; - } + $message = 'Sharing %s failed, because resharing is not allowed'; + $message_t = $l->t('Sharing %s failed, because resharing is not allowed', array($itemSourceName)); - self::insertShare(array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $itemTarget, - 'shareType' => $shareType, - 'shareWith' => $shareWith, - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'shareTime' => time(), - 'fileSource' => $fileSource, - 'fileTarget' => $fileTarget, - 'token' => $token, - 'parent' => $parent, - 'expiration' => $expirationDate, - )); + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName), \OC_Log::ERROR); + throw new \Exception($message_t); + } + } else { + $result['parent'] = null; + $result['suggestedItemTarget'] = null; + $result['suggestedFileTarget'] = null; + $result['itemSource'] = $itemSource; + $result['expirationDate'] = $expirationDate; + if (!$backend->isValidSource($itemSource, $uidOwner)) { + $message = 'Sharing %s failed, because the sharing backend for ' + .'%s could not find its source'; + $message_t = $l->t('Sharing %s failed, because the sharing backend for %s could not find its source', array($itemSource, $itemType)); + \OC_Log::write('OCP\Share', sprintf($message, $itemSource, $itemType), \OC_Log::ERROR); + throw new \Exception($message_t); + } + if ($backend instanceof \OCP\Share_Backend_File_Dependent) { + $result['filePath'] = $backend->getFilePath($itemSource, $uidOwner); + if ($itemType == 'file' || $itemType == 'folder') { + $result['fileSource'] = $itemSource; + } else { + $meta = \OC\Files\Filesystem::getFileInfo($result['filePath']); + $result['fileSource'] = $meta['fileid']; + } + if ($result['fileSource'] == -1) { + $message = 'Sharing %s failed, because the file could not be found in the file cache'; + $message_t = $l->t('Sharing %s failed, because the file could not be found in the file cache', array($itemSource)); - $id = \OC_DB::insertid('*PREFIX*share'); - \OC_Hook::emit('OCP\Share', 'post_shared', array( - 'itemType' => $itemType, - 'itemSource' => $itemSource, - 'itemTarget' => $itemTarget, - 'parent' => $parent, - 'shareType' => $shareType, - 'shareWith' => $shareWith, - 'uidOwner' => $uidOwner, - 'permissions' => $permissions, - 'fileSource' => $fileSource, - 'fileTarget' => $fileTarget, - 'id' => $id, - 'token' => $token, - 'expirationDate' => $expirationDate, - )); - if ($parentFolder === true) { - $parentFolders['id'] = $id; - // Return parent folder to preserve file target paths for potential children - return $parentFolders; + \OC_Log::write('OCP\Share', sprintf($message, $itemSource), \OC_Log::ERROR); + throw new \Exception($message_t); + } + } else { + $result['filePath'] = null; + $result['fileSource'] = null; } } - return true; + + return $result; } private static function insertShare(array $shareData) @@ -1877,9 +1950,9 @@ class Share extends \OC\Share\Constants { $select = '*'; if ($format == self::FORMAT_STATUSES) { if ($fileDependent) { - $select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, `share_with`, `uid_owner` , `file_source`, `stime`'; + $select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, `share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`'; } else { - $select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`'; + $select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`, `*PREFIX*share`.`permissions`'; } } else { if (isset($uidOwner)) { diff --git a/lib/public/share.php b/lib/public/share.php index c8b64cc187c..449d1fa211e 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -139,7 +139,7 @@ class Share extends \OC\Share\Constants { * @param int $format (optional) Format type must be defined by the backend * @param mixed $parameters * @param bool $includeCollections - * @return mixed Return depends on format + * @return array */ public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { @@ -361,7 +361,7 @@ interface Share_Backend { * Get a unique name of the item for the specified user * @param string $itemSource * @param string|false $shareWith User the item is being shared with - * @param array|null $exclude List of similar item names already existing as shared items + * @param array|null $exclude List of similar item names already existing as shared items @deprecated since version OC7 * @return string Target name * * This function needs to verify that the user does not already have an item with this name. |