From b6026625781b2fda8ee1b23c3984be4a064adccc Mon Sep 17 00:00:00 2001
From: Bjoern Schiessle <schiessle@owncloud.com>
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 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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
 	 *
-- 
GitLab