From b6026625781b2fda8ee1b23c3984be4a064adccc Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Tue, 18 Feb 2014 15:07:03 +0100 Subject: [PATCH] add a "helper" and a "hooks" class. Move constants needed by multiple classes to a "constants" class --- lib/base.php | 8 +- lib/private/share/constants.php | 44 +++++ lib/private/share/helper.php | 202 ++++++++++++++++++++ lib/private/share/hooks.php | 108 +++++++++++ lib/private/share/share.php | 320 ++------------------------------ lib/public/share.php | 50 +---- 6 files changed, 377 insertions(+), 355 deletions(-) create mode 100644 lib/private/share/constants.php create mode 100644 lib/private/share/helper.php create mode 100644 lib/private/share/hooks.php diff --git a/lib/base.php b/lib/base.php index 2515b9657c..b3f6776e6d 100644 --- a/lib/base.php +++ b/lib/base.php @@ -659,10 +659,10 @@ class OC { */ public static function registerShareHooks() { if (\OC_Config::getValue('installed')) { - OC_Hook::connect('OC_User', 'post_deleteUser', 'OCP\Share', 'post_deleteUser'); - OC_Hook::connect('OC_User', 'post_addToGroup', 'OCP\Share', 'post_addToGroup'); - OC_Hook::connect('OC_User', 'post_removeFromGroup', 'OCP\Share', 'post_removeFromGroup'); - OC_Hook::connect('OC_User', 'post_deleteGroup', 'OCP\Share', 'post_deleteGroup'); + OC_Hook::connect('OC_User', 'post_deleteUser', 'OC\Share\Hooks', 'post_deleteUser'); + OC_Hook::connect('OC_User', 'post_addToGroup', 'OC\Share\Hooks', 'post_addToGroup'); + OC_Hook::connect('OC_User', 'post_removeFromGroup', 'OC\Share\Hooks', 'post_removeFromGroup'); + OC_Hook::connect('OC_User', 'post_deleteGroup', 'OC\Share\Hooks', 'post_deleteGroup'); } } diff --git a/lib/private/share/constants.php b/lib/private/share/constants.php new file mode 100644 index 0000000000..7e4223d10f --- /dev/null +++ b/lib/private/share/constants.php @@ -0,0 +1,44 @@ + + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + */ + +namespace OC\Share; + +class Constants { + + const SHARE_TYPE_USER = 0; + const SHARE_TYPE_GROUP = 1; + const SHARE_TYPE_LINK = 3; + const SHARE_TYPE_EMAIL = 4; + const SHARE_TYPE_CONTACT = 5; + const SHARE_TYPE_REMOTE = 6; + + const FORMAT_NONE = -1; + const FORMAT_STATUSES = -2; + const FORMAT_SOURCES = -3; + + const TOKEN_LENGTH = 32; // see db_structure.xml + + protected static $shareTypeUserAndGroups = -1; + protected static $shareTypeGroupUserUnique = 2; + protected static $backends = array(); + protected static $backendTypes = array(); + protected static $isResharingAllowed; +} diff --git a/lib/private/share/helper.php b/lib/private/share/helper.php new file mode 100644 index 0000000000..fde5566728 --- /dev/null +++ b/lib/private/share/helper.php @@ -0,0 +1,202 @@ + + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + */ + +namespace OC\Share; + +class Helper extends \OC\Share\Constants { + + /** + * Generate a unique target for the item + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK + * @param string User or group the item is being shared with + * @param string User that is the owner of shared item + * @param string The suggested target originating from a reshare (optional) + * @param int The id of the parent group share (optional) + * @return string Item target + */ + public static function generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, + $suggestedTarget = null, $groupParent = null) { + $backend = \OC\Share\Share::getBackend($itemType); + if ($shareType == self::SHARE_TYPE_LINK) { + if (isset($suggestedTarget)) { + return $suggestedTarget; + } + return $backend->generateTarget($itemSource, false); + } else { + if ($itemType == 'file' || $itemType == 'folder') { + $column = 'file_target'; + $columnSource = 'file_source'; + } else { + $column = 'item_target'; + $columnSource = 'item_source'; + } + 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; + } + } + } + $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); + } + + /** + * Delete all reshares of an item + * @param int Id of item to delete + * @param bool If true, exclude the parent from the delete (optional) + * @param string The user that the parent was shared with (optinal) + */ + public static function delete($parent, $excludeParent = false, $uidOwner = null) { + $ids = array($parent); + $parents = array($parent); + while (!empty($parents)) { + $parents = "'".implode("','", $parents)."'"; + // Check the owner on the first search of reshares, useful for + // finding and deleting the reshares by a single user of a group share + if (count($ids) == 1 && isset($uidOwner)) { + $query = \OC_DB::prepare('SELECT `id`, `uid_owner`, `item_type`, `item_target`, `parent`' + .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.') AND `uid_owner` = ?'); + $result = $query->execute(array($uidOwner)); + } else { + $query = \OC_DB::prepare('SELECT `id`, `item_type`, `item_target`, `parent`, `uid_owner`' + .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.')'); + $result = $query->execute(); + } + // Reset parents array, only go through loop again if items are found + $parents = array(); + while ($item = $result->fetchRow()) { + // Search for a duplicate parent share, this occurs when an + // item is shared to the same user through a group and user or the + // same item is shared by different users + $userAndGroups = array_merge(array($item['uid_owner']), \OC_Group::getUserGroups($item['uid_owner'])); + $query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`' + .' WHERE `item_type` = ?' + .' AND `item_target` = ?' + .' AND `share_type` IN (?,?,?)' + .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')' + .' AND `uid_owner` != ? AND `id` != ?'); + $duplicateParent = $query->execute(array($item['item_type'], $item['item_target'], + self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique, + $item['uid_owner'], $item['parent']))->fetchRow(); + if ($duplicateParent) { + // Change the parent to the other item id if share permission is granted + if ($duplicateParent['permissions'] & \OCP\PERMISSION_SHARE) { + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `parent` = ? WHERE `id` = ?'); + $query->execute(array($duplicateParent['id'], $item['id'])); + continue; + } + } + $ids[] = $item['id']; + $parents[] = $item['id']; + } + } + if ($excludeParent) { + unset($ids[0]); + } + if (!empty($ids)) { + $ids = "'".implode("','", $ids)."'"; + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `id` IN ('.$ids.')'); + $query->execute(); + } + } +} diff --git a/lib/private/share/hooks.php b/lib/private/share/hooks.php new file mode 100644 index 0000000000..a33c71eedd --- /dev/null +++ b/lib/private/share/hooks.php @@ -0,0 +1,108 @@ + + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + */ + +namespace OC\Share; + +class Hooks extends \OC\Share\Constants { + /** + * Function that is called after a user is deleted. Cleans up the shares of that user. + * @param array arguments + */ + public static function post_deleteUser($arguments) { + // Delete any items shared with the deleted user + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share`' + .' WHERE `share_with` = ? AND `share_type` = ? OR `share_type` = ?'); + $result = $query->execute(array($arguments['uid'], self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique)); + // Delete any items the deleted user shared + $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `uid_owner` = ?'); + $result = $query->execute(array($arguments['uid'])); + while ($item = $result->fetchRow()) { + Helper::delete($item['id']); + } + } + + /** + * Function that is called after a user is added to a group. + * TODO what does it do? + * @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'])); + $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`) 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']); + } 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'], + self::$shareTypeGroupUserUnique, $arguments['uid'], $item['uid_owner'], $item['permissions'], + $item['stime'], $item['file_source'], $fileTarget)); + \OC_DB::insertid('*PREFIX*share'); + } + } + } + + /** + * Function that is called after a user is removed from a group. Shares are cleaned up. + * @param array arguments + */ + public static function post_removeFromGroup($arguments) { + $sql = 'SELECT `id`, `share_type` FROM `*PREFIX*share`' + .' WHERE (`share_type` = ? AND `share_with` = ?) OR (`share_type` = ? AND `share_with` = ?)'; + $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid'], + self::$shareTypeGroupUserUnique, $arguments['uid'])); + while ($item = $result->fetchRow()) { + if ($item['share_type'] == self::SHARE_TYPE_GROUP) { + // Delete all reshares by this user of the group share + Helper::delete($item['id'], true, $arguments['uid']); + } else { + Helper::delete($item['id']); + } + } + } + + /** + * Function that is called after a group is removed. Cleans up the shares to that group. + * @param array arguments + */ + public static function post_deleteGroup($arguments) { + $sql = 'SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?'; + $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid'])); + while ($item = $result->fetchRow()) { + Helper::delete($item['id']); + } + } + +} diff --git a/lib/private/share/share.php b/lib/private/share/share.php index b44d362672..ef0ed257c5 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -29,14 +29,7 @@ namespace OC\Share; * It provides the following hooks: * - post_shared */ -class Share { - - const SHARE_TYPE_USER = 0; - const SHARE_TYPE_GROUP = 1; - const SHARE_TYPE_LINK = 3; - const SHARE_TYPE_EMAIL = 4; - const SHARE_TYPE_CONTACT = 5; - const SHARE_TYPE_REMOTE = 6; +class Share extends \OC\Share\Constants { /** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask * Construct permissions for share() and setPermissions with Or (|) e.g. @@ -53,18 +46,6 @@ class Share { * @see lib/public/constants.php */ - const FORMAT_NONE = -1; - const FORMAT_STATUSES = -2; - const FORMAT_SOURCES = -3; - - const TOKEN_LENGTH = 32; // see db_structure.xml - - private static $shareTypeUserAndGroups = -1; - private static $shareTypeGroupUserUnique = 2; - private static $backends = array(); - private static $backendTypes = array(); - private static $isResharingAllowed; - /** * Register a sharing backend class that implements OCP\Share_Backend for an item type * @param string Item type @@ -540,7 +521,7 @@ class Share { $oldToken = $checkExists['token']; $oldPermissions = $checkExists['permissions']; //delete the old share - self::delete($checkExists['id']); + Helper::delete($checkExists['id']); } // Generate hash of password - same method as user passwords @@ -656,14 +637,14 @@ class Share { $item['file_target'])); \OC_DB::insertid('*PREFIX*share'); // Delete all reshares by this user of the group share - self::delete($item['id'], true, \OC_User::getUser()); + Helper::delete($item['id'], true, \OC_User::getUser()); } else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) { // Set permission to 0 to prevent it from showing up for the user $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?'); $query->execute(array(0, $item['id'])); - self::delete($item['id'], true); + Helper::delete($item['id'], true); } else { - self::delete($item['id']); + Helper::delete($item['id']); } return true; } @@ -732,7 +713,7 @@ class Share { if ($item['permissions'] & ~$permissions) { // If share permission is removed all reshares must be deleted if (($item['permissions'] & \OCP\PERMISSION_SHARE) && (~$permissions & \OCP\PERMISSION_SHARE)) { - self::delete($item['id'], true); + Helper::delete($item['id'], true); } else { $ids = array(); $parents = array($item['id']); @@ -839,7 +820,7 @@ class Share { \OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams + array( 'fileSource' => $item['file_source'], )); - self::delete($item['id']); + Helper::delete($item['id']); \OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams); } @@ -931,7 +912,7 @@ class Share { * See public functions getItem(s)... for parameter usage * */ - private static function getItems($itemType, $item = null, $shareType = null, $shareWith = null, + 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()) { @@ -1417,7 +1398,7 @@ class Share { .' `file_target`, `token`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'); // Share with a group if ($shareType == self::SHARE_TYPE_GROUP) { - $groupItemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], + $groupItemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], $uidOwner, $suggestedItemTarget); $run = true; $error = ''; @@ -1442,7 +1423,7 @@ class Share { if (isset($fileSource)) { if ($parentFolder) { if ($parentFolder === true) { - $groupFileTarget = self::generateTarget('file', $filePath, $shareType, + $groupFileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith['group'], $uidOwner, $suggestedFileTarget); // Set group default file target for future use $parentFolders[0]['folder'] = $groupFileTarget; @@ -1452,7 +1433,7 @@ class Share { $parent = $parentFolder[0]['id']; } } else { - $groupFileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith['group'], + $groupFileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith['group'], $uidOwner, $suggestedFileTarget); } } else { @@ -1464,12 +1445,12 @@ class Share { $parent = \OC_DB::insertid('*PREFIX*share'); // Loop through all users of this group in case we need to add an extra row foreach ($shareWith['users'] as $uid) { - $itemTarget = self::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $uid, + $itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $uid, $uidOwner, $suggestedItemTarget, $parent); if (isset($fileSource)) { if ($parentFolder) { if ($parentFolder === true) { - $fileTarget = self::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid, + $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid, $uidOwner, $suggestedFileTarget, $parent); if ($fileTarget != $groupFileTarget) { $parentFolders[$uid]['folder'] = $fileTarget; @@ -1479,7 +1460,7 @@ class Share { $parent = $parentFolder[$uid]['id']; } } else { - $fileTarget = self::generateTarget('file', $filePath, self::SHARE_TYPE_USER, + $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid, $uidOwner, $suggestedFileTarget, $parent); } } else { @@ -1513,7 +1494,7 @@ class Share { return $parentFolders; } } else { - $itemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, + $itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $suggestedItemTarget); $run = true; $error = ''; @@ -1538,7 +1519,7 @@ class Share { if (isset($fileSource)) { if ($parentFolder) { if ($parentFolder === true) { - $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith, + $fileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner, $suggestedFileTarget); $parentFolders['folder'] = $fileTarget; } else { @@ -1546,7 +1527,7 @@ class Share { $parent = $parentFolder['id']; } } else { - $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner, + $fileTarget = Helper::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner, $suggestedFileTarget); } } else { @@ -1578,183 +1559,6 @@ class Share { return true; } - /** - * Generate a unique target for the item - * @param string Item type - * @param string Item source - * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK - * @param string User or group the item is being shared with - * @param string User that is the owner of shared item - * @param string The suggested target originating from a reshare (optional) - * @param int The id of the parent group share (optional) - * @return string Item target - */ - private static function generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, - $suggestedTarget = null, $groupParent = null) { - $backend = self::getBackend($itemType); - if ($shareType == self::SHARE_TYPE_LINK) { - if (isset($suggestedTarget)) { - return $suggestedTarget; - } - return $backend->generateTarget($itemSource, false); - } else { - if ($itemType == 'file' || $itemType == 'folder') { - $column = 'file_target'; - $columnSource = 'file_source'; - } else { - $column = 'item_target'; - $columnSource = 'item_source'; - } - 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 = self::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; - } - } - } - $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); - } - - /** - * Delete all reshares of an item - * @param int Id of item to delete - * @param bool If true, exclude the parent from the delete (optional) - * @param string The user that the parent was shared with (optinal) - */ - private static function delete($parent, $excludeParent = false, $uidOwner = null) { - $ids = array($parent); - $parents = array($parent); - while (!empty($parents)) { - $parents = "'".implode("','", $parents)."'"; - // Check the owner on the first search of reshares, useful for - // finding and deleting the reshares by a single user of a group share - if (count($ids) == 1 && isset($uidOwner)) { - $query = \OC_DB::prepare('SELECT `id`, `uid_owner`, `item_type`, `item_target`, `parent`' - .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.') AND `uid_owner` = ?'); - $result = $query->execute(array($uidOwner)); - } else { - $query = \OC_DB::prepare('SELECT `id`, `item_type`, `item_target`, `parent`, `uid_owner`' - .' FROM `*PREFIX*share` WHERE `parent` IN ('.$parents.')'); - $result = $query->execute(); - } - // Reset parents array, only go through loop again if items are found - $parents = array(); - while ($item = $result->fetchRow()) { - // Search for a duplicate parent share, this occurs when an - // item is shared to the same user through a group and user or the - // same item is shared by different users - $userAndGroups = array_merge(array($item['uid_owner']), \OC_Group::getUserGroups($item['uid_owner'])); - $query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`' - .' WHERE `item_type` = ?' - .' AND `item_target` = ?' - .' AND `share_type` IN (?,?,?)' - .' AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')' - .' AND `uid_owner` != ? AND `id` != ?'); - $duplicateParent = $query->execute(array($item['item_type'], $item['item_target'], - self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique, - $item['uid_owner'], $item['parent']))->fetchRow(); - if ($duplicateParent) { - // Change the parent to the other item id if share permission is granted - if ($duplicateParent['permissions'] & \OCP\PERMISSION_SHARE) { - $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `parent` = ? WHERE `id` = ?'); - $query->execute(array($duplicateParent['id'], $item['id'])); - continue; - } - } - $ids[] = $item['id']; - $parents[] = $item['id']; - } - } - if ($excludeParent) { - unset($ids[0]); - } - if (!empty($ids)) { - $ids = "'".implode("','", $ids)."'"; - $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `id` IN ('.$ids.')'); - $query->execute(); - } - } - /** * Delete all shares with type SHARE_TYPE_LINK */ @@ -1763,95 +1567,7 @@ class Share { $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ?'); $result = $query->execute(array(self::SHARE_TYPE_LINK)); while ($item = $result->fetchRow()) { - self::delete($item['id']); - } - } - - /** - * Hook Listeners - */ - - /** - * Function that is called after a user is deleted. Cleans up the shares of that user. - * @param array arguments - */ - public static function post_deleteUser($arguments) { - // Delete any items shared with the deleted user - $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share`' - .' WHERE `share_with` = ? AND `share_type` = ? OR `share_type` = ?'); - $result = $query->execute(array($arguments['uid'], self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique)); - // Delete any items the deleted user shared - $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `uid_owner` = ?'); - $result = $query->execute(array($arguments['uid'])); - while ($item = $result->fetchRow()) { - self::delete($item['id']); - } - } - - /** - * Function that is called after a user is added to a group. - * TODO what does it do? - * @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'])); - $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`) VALUES (?,?,?,?,?,?,?,?,?,?,?)'); - while ($item = $result->fetchRow()) { - if ($item['item_type'] == 'file' || $item['item_type'] == 'file') { - $itemTarget = null; - } else { - $itemTarget = self::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 = self::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, - $arguments['uid'], $item['uid_owner'], $item['file_target'], $item['id']); - } 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'], - self::$shareTypeGroupUserUnique, $arguments['uid'], $item['uid_owner'], $item['permissions'], - $item['stime'], $item['file_source'], $fileTarget)); - \OC_DB::insertid('*PREFIX*share'); - } - } - } - - /** - * Function that is called after a user is removed from a group. Shares are cleaned up. - * @param array arguments - */ - public static function post_removeFromGroup($arguments) { - // TODO Don't call if user deleted? - $sql = 'SELECT `id`, `share_type` FROM `*PREFIX*share`' - .' WHERE (`share_type` = ? AND `share_with` = ?) OR (`share_type` = ? AND `share_with` = ?)'; - $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid'], - self::$shareTypeGroupUserUnique, $arguments['uid'])); - while ($item = $result->fetchRow()) { - if ($item['share_type'] == self::SHARE_TYPE_GROUP) { - // Delete all reshares by this user of the group share - self::delete($item['id'], true, $arguments['uid']); - } else { - self::delete($item['id']); - } - } - } - - /** - * Function that is called after a group is removed. Cleans up the shares to that group. - * @param array arguments - */ - public static function post_deleteGroup($arguments) { - $sql = 'SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?'; - $result = \OC_DB::executeAudited($sql, array(self::SHARE_TYPE_GROUP, $arguments['gid'])); - while ($item = $result->fetchRow()) { - self::delete($item['id']); + Helper::delete($item['id']); } } diff --git a/lib/public/share.php b/lib/public/share.php index adc02dfe8c..fcc61b2f4f 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -37,18 +37,7 @@ namespace OCP; * It provides the following hooks: * - post_shared */ -class Share { - - const FORMAT_NONE = -1; - const FORMAT_STATUSES = -2; - const FORMAT_SOURCES = -3; - - const SHARE_TYPE_USER = 0; - const SHARE_TYPE_GROUP = 1; - const SHARE_TYPE_LINK = 3; - const SHARE_TYPE_EMAIL = 4; - const SHARE_TYPE_CONTACT = 5; - const SHARE_TYPE_REMOTE = 6; +class Share extends \OC\Share\Constants { /** * Register a sharing backend class that implements OCP\Share_Backend for an item type @@ -326,43 +315,6 @@ class Share { return \OC\Share\Share::removeAllLinkShares(); } - /** - * Hook Listeners - */ - - /** - * Function that is called after a user is deleted. Cleans up the shares of that user. - * @param array arguments - */ - public static function post_deleteUser($arguments) { - return \OC\Share\Share::post_deleteUser($arguments); - } - - /** - * Function that is called after a user is added to a group. - * TODO what does it do? - * @param array arguments - */ - public static function post_addToGroup($arguments) { - return \OC\Share\Share::post_addToGroup($arguments); - } - - /** - * Function that is called after a user is removed from a group. Shares are cleaned up. - * @param array arguments - */ - public static function post_removeFromGroup($arguments) { - return \OC\Share\Share::post_removeFromGroup($arguments); - } - - /** - * Function that is called after a group is removed. Cleans up the shares to that group. - * @param array arguments - */ - public static function post_deleteGroup($arguments) { - return \OC\Share\Share::post_deleteGroup($arguments); - } - /** * In case a password protected link is not yet authenticated this function will return false *