diff --git a/apps/federatedfilesharing/appinfo/app.php b/apps/federatedfilesharing/appinfo/app.php
index 4666d343f7e9e811848a9a6d4528dc9eab39959e..c33a887c6d6172df65b05940ccfd959e28e62d85 100644
--- a/apps/federatedfilesharing/appinfo/app.php
+++ b/apps/federatedfilesharing/appinfo/app.php
@@ -20,4 +20,21 @@
  */
 
 $app = new \OCA\FederatedFileSharing\AppInfo\Application('federatedfilesharing');
+
+use OCA\FederatedFileSharing\Notifier;
+
+$l = \OC::$server->getL10N('files_sharing');
+
 $app->registerSettings();
+
+$manager = \OC::$server->getNotificationManager();
+$manager->registerNotifier(function() {
+	return new Notifier(
+		\OC::$server->getL10NFactory()
+	);
+}, function() use ($l) {
+	return [
+		'id' => 'files_sharing',
+		'name' => $l->t('Federated sharing'),
+	];
+});
diff --git a/apps/federatedfilesharing/appinfo/database.xml b/apps/federatedfilesharing/appinfo/database.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1dbe8ee2ec90432bccc94b5438f58025fb8026e9
--- /dev/null
+++ b/apps/federatedfilesharing/appinfo/database.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<!--
+Keep a mapping of the share ID stored in the local oc_share table
+and the share ID stored in the remote servers oc_share table.
+This is needed in order to send updates in both directions between
+the servers (e.g. permissions change, unshare,...)
+-->
+
+<database>
+	<name>*dbname*</name>
+	<create>true</create>
+	<overwrite>false</overwrite>
+	<charset>utf8</charset>
+	<table>
+		<name>*dbprefix*federated_reshares</name>
+		<declaration>
+			<field>
+				<name>share_id</name>
+				<type>integer</type>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+			<field>
+				<name>remote_id</name>
+				<type>integer</type>
+				<notnull>true</notnull>
+				<length>4</length>
+				<comments>share ID at the remote server</comments>
+			</field>
+			<index>
+				<name>share_id_index</name>
+				<unique>true</unique>
+				<field>
+					<name>share_id</name>
+					<sorting>ascending</sorting>
+				</field>
+			</index>
+		</declaration>
+	</table>
+</database>
diff --git a/apps/federatedfilesharing/appinfo/info.xml b/apps/federatedfilesharing/appinfo/info.xml
index 643281bd145f11dace9eabbc29a0d6a9f443f01b..5cf4039f1967edf3561ef1718beb72b721d0493b 100644
--- a/apps/federatedfilesharing/appinfo/info.xml
+++ b/apps/federatedfilesharing/appinfo/info.xml
@@ -5,7 +5,7 @@
     <description>Provide federated file sharing across ownCloud servers</description>
     <licence>AGPL</licence>
     <author>Bjoern Schiessle, Roeland Jago Douma</author>
-    <version>0.2.0</version>
+    <version>0.3.0</version>
     <namespace>FederatedFileSharing</namespace>
     <category>other</category>
     <dependencies>
diff --git a/apps/federatedfilesharing/lib/AppInfo/Application.php b/apps/federatedfilesharing/lib/AppInfo/Application.php
index 5a213aec8e2ea5cb64f3862fe26f43098e58ad46..d1b0646ba5bbb97fd673243c5e8b6b5c71cd2e9c 100644
--- a/apps/federatedfilesharing/lib/AppInfo/Application.php
+++ b/apps/federatedfilesharing/lib/AppInfo/Application.php
@@ -81,7 +81,8 @@ class Application extends App {
 			\OC::$server->getL10N('federatedfilesharing'),
 			\OC::$server->getLogger(),
 			\OC::$server->getLazyRootFolder(),
-			\OC::$server->getConfig()
+			\OC::$server->getConfig(),
+			\OC::$server->getUserManager()
 		);
 	}
 
diff --git a/apps/federatedfilesharing/lib/BackgroundJob/UnShare.php b/apps/federatedfilesharing/lib/BackgroundJob/RetryJob.php
similarity index 81%
rename from apps/federatedfilesharing/lib/BackgroundJob/UnShare.php
rename to apps/federatedfilesharing/lib/BackgroundJob/RetryJob.php
index b056db4eac7d9858c01baf2c358a03738e332997..109a607bff0649c2b988c2346071ed398a74c4d5 100644
--- a/apps/federatedfilesharing/lib/BackgroundJob/UnShare.php
+++ b/apps/federatedfilesharing/lib/BackgroundJob/RetryJob.php
@@ -32,26 +32,26 @@ use OCP\BackgroundJob\IJobList;
 use OCP\ILogger;
 
 /**
- * Class UnShare
+ * Class RetryJob
  *
- * Background job to re-send the un-share notification to the remote server in
+ * Background job to re-send update of federated re-shares to the remote server in
  * case the server was not available on the first try
  *
  * @package OCA\FederatedFileSharing\BackgroundJob
  */
-class UnShare extends Job {
+class RetryJob extends Job {
 
 	/** @var  bool */
 	private $retainJob = true;
-	
+
 	/** @var Notifications */
 	private $notifications;
 
-	/** @var int max number of attempts to send the un-share request */
-	private $maxTry = 10;
+	/** @var int max number of attempts to send the request */
+	private $maxTry = 20;
 
-	/** @var int how much time should be between two tries (12 hours) */
-	private $interval = 43200;
+	/** @var int how much time should be between two tries (10 minutes) */
+	private $interval = 600;
 
 	/**
 	 * UnShare constructor.
@@ -77,7 +77,7 @@ class UnShare extends Job {
 				\OC::$server->getJobList()
 			);
 		}
-		
+
 	}
 
 	/**
@@ -99,12 +99,14 @@ class UnShare extends Job {
 
 	protected function run($argument) {
 		$remote = $argument['remote'];
-		$id = (int)$argument['id'];
+		$remoteId = $argument['remoteId'];
 		$token = $argument['token'];
+		$action = $argument['action'];
+		$data = json_decode($argument['data'], true);
 		$try = (int)$argument['try'] + 1;
 
-		$result = $this->notifications->sendRemoteUnShare($remote, $id, $token, $try);
-
+		$result = $this->notifications->sendUpdateToRemote($remote, $remoteId, $token, $action, $data, $try);
+		
 		if ($result === true || $try > $this->maxTry) {
 			$this->retainJob = false;
 		}
@@ -117,11 +119,13 @@ class UnShare extends Job {
 	 * @param array $argument
 	 */
 	protected function reAddJob(IJobList $jobList, array $argument) {
-		$jobList->add('OCA\FederatedFileSharing\BackgroundJob\UnShare',
+		$jobList->add('OCA\FederatedFileSharing\BackgroundJob\RetryJob',
 			[
 				'remote' => $argument['remote'],
-				'id' => $argument['id'],
+				'remoteId' => $argument['remoteId'],
 				'token' => $argument['token'],
+				'data' => $argument['data'],
+				'action' => $argument['action'],
 				'try' => (int)$argument['try'] + 1,
 				'lastRun' => time()
 			]
diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php
index d014a6219a3165f74ba4ff7a2f7c124b65236e77..76fed01c30864f068bd94aa8a1638ba83d7bfbc7 100644
--- a/apps/federatedfilesharing/lib/FederatedShareProvider.php
+++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php
@@ -25,10 +25,10 @@ namespace OCA\FederatedFileSharing;
 
 use OC\Share20\Share;
 use OCP\Files\IRootFolder;
-use OCP\IAppConfig;
 use OCP\IConfig;
 use OCP\IL10N;
 use OCP\ILogger;
+use OCP\IUserManager;
 use OCP\Share\IShare;
 use OCP\Share\IShareProvider;
 use OC\Share20\Exception\InvalidShare;
@@ -70,6 +70,12 @@ class FederatedShareProvider implements IShareProvider {
 	/** @var IConfig */
 	private $config;
 
+	/** @var string */
+	private $externalShareTable = 'share_external';
+
+	/** @var IUserManager */
+	private $userManager;
+
 	/**
 	 * DefaultShareProvider constructor.
 	 *
@@ -81,6 +87,7 @@ class FederatedShareProvider implements IShareProvider {
 	 * @param ILogger $logger
 	 * @param IRootFolder $rootFolder
 	 * @param IConfig $config
+	 * @param IUserManager $userManager
 	 */
 	public function __construct(
 			IDBConnection $connection,
@@ -90,7 +97,8 @@ class FederatedShareProvider implements IShareProvider {
 			IL10N $l10n,
 			ILogger $logger,
 			IRootFolder $rootFolder,
-			IConfig $config
+			IConfig $config,
+			IUserManager $userManager
 	) {
 		$this->dbConnection = $connection;
 		$this->addressHandler = $addressHandler;
@@ -100,6 +108,7 @@ class FederatedShareProvider implements IShareProvider {
 		$this->logger = $logger;
 		$this->rootFolder = $rootFolder;
 		$this->config = $config;
+		$this->userManager = $userManager;
 	}
 
 	/**
@@ -124,10 +133,9 @@ class FederatedShareProvider implements IShareProvider {
 		$shareWith = $share->getSharedWith();
 		$itemSource = $share->getNodeId();
 		$itemType = $share->getNodeType();
-		$uidOwner = $share->getShareOwner();
 		$permissions = $share->getPermissions();
 		$sharedBy = $share->getSharedBy();
-
+		
 		/*
 		 * Check if file is not already shared with the remote user
 		 */
@@ -151,31 +159,136 @@ class FederatedShareProvider implements IShareProvider {
 			throw new \Exception($message_t);
 		}
 
-		$token = $this->tokenHandler->generateToken();
+		$share->setSharedWith($user . '@' . $remote);
+
+		try {
+			$remoteShare = $this->getShareFromExternalShareTable($share);
+		} catch (ShareNotFound $e) {
+			$remoteShare = null;
+		}
 
-		$shareWith = $user . '@' . $remote;
+		if ($remoteShare) {
+			try {
+				$uidOwner = $remoteShare['owner'] . '@' . $remoteShare['remote'];
+				$shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, 'tmp_token_' . time());
+				$share->setId($shareId);
+				list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
+				// remote share was create successfully if we get a valid token as return
+				$send = is_string($token) && $token !== '';
+			} catch (\Exception $e) {
+				// fall back to old re-share behavior if the remote server
+				// doesn't support flat re-shares (was introduced with ownCloud 9.1)
+				$this->removeShareFromTable($share);
+				$shareId = $this->createFederatedShare($share);
+			}
+			if ($send) {
+				$this->updateSuccessfulReshare($shareId, $token);
+				$this->storeRemoteId($shareId, $remoteId);
+			} else {
+				$this->removeShareFromTable($share);
+				$message_t = $this->l->t('File is already shared with %s', [$shareWith]);
+				throw new \Exception($message_t);
+			}
 
-		$shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
+		} else {
+			$shareId = $this->createFederatedShare($share);
+		}
 
+		$data = $this->getRawShare($shareId);
+		return $this->createShareObject($data);
+	}
+
+	/**
+	 * create federated share and inform the recipient
+	 *
+	 * @param IShare $share
+	 * @return int
+	 * @throws ShareNotFound
+	 * @throws \Exception
+	 */
+	protected function createFederatedShare(IShare $share) {
+		$token = $this->tokenHandler->generateToken();
+		$shareId = $this->addShareToDB(
+			$share->getNodeId(),
+			$share->getNodeType(),
+			$share->getSharedWith(),
+			$share->getSharedBy(),
+			$share->getShareOwner(),
+			$share->getPermissions(),
+			$token
+		);
+		$sharedByFederatedId = $share->getSharedBy();
+		if ($this->userManager->userExists($sharedByFederatedId)) {
+			$sharedByFederatedId = $sharedByFederatedId . '@' . $this->addressHandler->generateRemoteURL();
+		}
 		$send = $this->notifications->sendRemoteShare(
 			$token,
-			$shareWith,
+			$share->getSharedWith(),
 			$share->getNode()->getName(),
 			$shareId,
-			$share->getSharedBy()
+			$share->getShareOwner(),
+			$share->getShareOwner() . '@' . $this->addressHandler->generateRemoteURL(),
+			$share->getSharedBy(),
+			$sharedByFederatedId
 		);
 
-		$data = $this->getRawShare($shareId);
-		$share = $this->createShare($data);
-
 		if ($send === false) {
-			$this->delete($share);
+			$data = $this->getRawShare($shareId);
+			$share = $this->createShareObject($data);
+			$this->removeShareFromTable($share);
 			$message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.',
-				[$share->getNode()->getName(), $shareWith]);
+				[$share->getNode()->getName(), $share->getSharedWith()]);
 			throw new \Exception($message_t);
 		}
 
-		return $share;
+		return $shareId;
+	}
+
+	/**
+	 * @param string $shareWith
+	 * @param IShare $share
+	 * @param string $shareId internal share Id
+	 * @return array
+	 * @throws \Exception
+	 */
+	protected function askOwnerToReShare($shareWith, IShare $share, $shareId) {
+
+		$remoteShare = $this->getShareFromExternalShareTable($share);
+		$token = $remoteShare['share_token'];
+		$remoteId = $remoteShare['remote_id'];
+		$remote = $remoteShare['remote'];
+
+		list($token, $remoteId) = $this->notifications->requestReShare(
+			$token,
+			$remoteId,
+			$shareId,
+			$remote,
+			$shareWith,
+			$share->getPermissions()
+		);
+
+		return [$token, $remoteId];
+	}
+
+	/**
+	 * get federated share from the share_external table but exclude mounted link shares
+	 *
+	 * @param IShare $share
+	 * @return array
+	 * @throws ShareNotFound
+	 */
+	protected function getShareFromExternalShareTable(IShare $share) {
+		$query = $this->dbConnection->getQueryBuilder();
+		$query->select('*')->from($this->externalShareTable)
+			->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner())))
+			->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
+		$result = $query->execute()->fetchAll();
+
+		if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
+			return $result[0];
+		}
+
+		throw new ShareNotFound('share not found in share_external table');
 	}
 
 	/**
@@ -234,9 +347,85 @@ class FederatedShareProvider implements IShareProvider {
 				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
 				->execute();
 
+		// send the updated permission to the owner/initiator, if they are not the same
+		if ($share->getShareOwner() !== $share->getSharedBy()) {
+			$this->sendPermissionUpdate($share);
+		}
+
 		return $share;
 	}
 
+	/**
+	 * send the updated permission to the owner/initiator, if they are not the same
+	 *
+	 * @param IShare $share
+	 * @throws ShareNotFound
+	 * @throws \OC\HintException
+	 */
+	protected function sendPermissionUpdate(IShare $share) {
+		$remoteId = $this->getRemoteId($share);
+		// if the local user is the owner we send the permission change to the initiator
+		if ($this->userManager->userExists($share->getShareOwner())) {
+			list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
+		} else { // ... if not we send the permission change to the owner
+			list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
+		}
+		$this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions());
+	}
+
+
+	/**
+	 * update successful reShare with the correct token
+	 *
+	 * @param int $shareId
+	 * @param string $token
+	 */
+	protected function updateSuccessfulReShare($shareId, $token) {
+		$query = $this->dbConnection->getQueryBuilder();
+		$query->update('share')
+			->where($query->expr()->eq('id', $query->createNamedParameter($shareId)))
+			->set('token', $query->createNamedParameter($token))
+			->execute();
+	}
+
+	/**
+	 * store remote ID in federated reShare table
+	 *
+	 * @param $shareId
+	 * @param $remoteId
+	 */
+	public function storeRemoteId($shareId, $remoteId) {
+		$query = $this->dbConnection->getQueryBuilder();
+		$query->insert('federated_reshares')
+			->values(
+				[
+					'share_id' =>  $query->createNamedParameter($shareId),
+					'remote_id' => $query->createNamedParameter($remoteId),
+				]
+			);
+		$query->execute();
+	}
+
+	/**
+	 * get share ID on remote server for federated re-shares
+	 *
+	 * @param IShare $share
+	 * @return int
+	 * @throws ShareNotFound
+	 */
+	public function getRemoteId(IShare $share) {
+		$query = $this->dbConnection->getQueryBuilder();
+		$query->select('remote_id')->from('federated_reshares')
+			->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
+		$data = $query->execute()->fetch();
+
+		if (!is_array($data) || !isset($data['remote_id'])) {
+			throw new ShareNotFound();
+		}
+
+		return (int)$data['remote_id'];
+	}
+
 	/**
 	 * @inheritdoc
 	 */
@@ -266,7 +455,7 @@ class FederatedShareProvider implements IShareProvider {
 
 		$cursor = $qb->execute();
 		while($data = $cursor->fetch()) {
-			$children[] = $this->createShare($data);
+			$children[] = $this->createShareObject($data);
 		}
 		$cursor->closeCursor();
 
@@ -274,18 +463,77 @@ class FederatedShareProvider implements IShareProvider {
 	}
 
 	/**
-	 * Delete a share
+	 * Delete a share (owner unShares the file)
 	 *
 	 * @param IShare $share
 	 */
 	public function delete(IShare $share) {
+
+		list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
+
+		$isOwner = false;
+
+		// if the local user is the owner we can send the unShare request directly...
+		if ($this->userManager->userExists($share->getShareOwner())) {
+			$this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
+			$this->revokeShare($share, true);
+			$isOwner = true;
+		} else { // ... if not we need to correct ID for the unShare request
+			$remoteId = $this->getRemoteId($share);
+			$this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken());
+			$this->revokeShare($share, false);
+		}
+
+		// send revoke notification to the other user, if initiator and owner are not the same user
+		if ($share->getShareOwner() !== $share->getSharedBy()) {
+			$remoteId = $this->getRemoteId($share);
+			if ($isOwner) {
+				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
+			} else {
+				list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
+			}
+			$this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
+		}
+
+		$this->removeShareFromTable($share);
+	}
+
+	/**
+	 * in case of a re-share we need to send the other use (initiator or owner)
+	 * a message that the file was unshared
+	 *
+	 * @param IShare $share
+	 * @param bool $isOwner the user can either be the owner or the user who re-sahred it
+	 * @throws ShareNotFound
+	 * @throws \OC\HintException
+	 */
+	protected function revokeShare($share, $isOwner) {
+		// also send a unShare request to the initiator, if this is a different user than the owner
+		if ($share->getShareOwner() !== $share->getSharedBy()) {
+			if ($isOwner) {
+				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
+			} else {
+				list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
+			}
+			$remoteId = $this->getRemoteId($share);
+			$this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
+		}
+	}
+
+	/**
+	 * remove share from table
+	 *
+	 * @param IShare $share
+	 */
+	public function removeShareFromTable(IShare $share) {
 		$qb = $this->dbConnection->getQueryBuilder();
 		$qb->delete('share')
 			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())));
 		$qb->execute();
 
-		list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
-		$this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
+		$qb->delete('federated_reshares')
+			->where($qb->expr()->eq('share_id', $qb->createNamedParameter($share->getId())));
+		$qb->execute();
 	}
 
 	/**
@@ -348,7 +596,7 @@ class FederatedShareProvider implements IShareProvider {
 		$cursor = $qb->execute();
 		$shares = [];
 		while($data = $cursor->fetch()) {
-			$shares[] = $this->createShare($data);
+			$shares[] = $this->createShareObject($data);
 		}
 		$cursor->closeCursor();
 
@@ -375,7 +623,7 @@ class FederatedShareProvider implements IShareProvider {
 		}
 
 		try {
-			$share = $this->createShare($data);
+			$share = $this->createShareObject($data);
 		} catch (InvalidShare $e) {
 			throw new ShareNotFound();
 		}
@@ -400,7 +648,7 @@ class FederatedShareProvider implements IShareProvider {
 
 		$shares = [];
 		while($data = $cursor->fetch()) {
-			$shares[] = $this->createShare($data);
+			$shares[] = $this->createShareObject($data);
 		}
 		$cursor->closeCursor();
 
@@ -439,7 +687,7 @@ class FederatedShareProvider implements IShareProvider {
 		$cursor = $qb->execute();
 
 		while($data = $cursor->fetch()) {
-			$shares[] = $this->createShare($data);
+			$shares[] = $this->createShareObject($data);
 		}
 		$cursor->closeCursor();
 
@@ -470,7 +718,7 @@ class FederatedShareProvider implements IShareProvider {
 		}
 
 		try {
-			$share = $this->createShare($data);
+			$share = $this->createShareObject($data);
 		} catch (InvalidShare $e) {
 			throw new ShareNotFound();
 		}
@@ -512,9 +760,9 @@ class FederatedShareProvider implements IShareProvider {
 	 * @throws InvalidShare
 	 * @throws ShareNotFound
 	 */
-	private function createShare($data) {
+	private function createShareObject($data) {
 
-		$share = new Share($this->rootFolder);
+		$share = new Share($this->rootFolder, $this->userManager);
 		$share->setId((int)$data['id'])
 			->setShareType((int)$data['share_type'])
 			->setPermissions((int)$data['permissions'])
diff --git a/apps/federatedfilesharing/lib/Notifications.php b/apps/federatedfilesharing/lib/Notifications.php
index 9cdc7760361c772d211fc2e04c4f9eac4ffc1036..bf9e0fc9634cc061930023021dd3b968fc6aac12 100644
--- a/apps/federatedfilesharing/lib/Notifications.php
+++ b/apps/federatedfilesharing/lib/Notifications.php
@@ -23,6 +23,7 @@
 
 namespace OCA\FederatedFileSharing;
 
+use OCP\AppFramework\Http;
 use OCP\BackgroundJob\IJobList;
 use OCP\Http\Client\IClientService;
 
@@ -67,9 +68,14 @@ class Notifications {
 	 * @param string $name
 	 * @param int $remote_id
 	 * @param string $owner
+	 * @param string $ownerFederatedId
+	 * @param string $sharedBy
+	 * @param string $sharedByFederatedId
 	 * @return bool
+	 * @throws \OC\HintException
+	 * @throws \OC\ServerNotAvailableException
 	 */
-	public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner) {
+	public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId) {
 
 		list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);
 
@@ -83,6 +89,9 @@ class Notifications {
 				'name' => $name,
 				'remoteId' => $remote_id,
 				'owner' => $owner,
+				'ownerFederatedId' => $ownerFederatedId,
+				'sharedBy' => $sharedBy,
+				'sharedByFederatedId' => $sharedByFederatedId,
 				'remote' => $local,
 			);
 
@@ -100,35 +109,143 @@ class Notifications {
 		return false;
 	}
 
+	/**
+	 * ask owner to re-share the file with the given user
+	 *
+	 * @param string $token
+	 * @param int $id remote Id
+	 * @param int $shareId internal share Id
+	 * @param string $remote remote address of the owner
+	 * @param string $shareWith
+	 * @param int $permission
+	 * @return bool
+	 * @throws \OC\HintException
+	 * @throws \OC\ServerNotAvailableException
+	 */
+	public function requestReShare($token, $id, $shareId, $remote, $shareWith, $permission) {
+
+		$fields = array(
+			'shareWith' => $shareWith,
+			'token' => $token,
+			'permission' => $permission,
+			'remoteId' => $shareId
+		);
+
+		$url = $this->addressHandler->removeProtocolFromUrl($remote);
+		$result = $this->tryHttpPostToShareEndpoint(rtrim($url, '/'), '/' . $id . '/reshare', $fields);
+		$status = json_decode($result['result'], true);
+
+		$httpRequestSuccessful = $result['success'];
+		$ocsCallSuccessful = $status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200;
+		$validToken = isset($status['ocs']['data']['token']) && is_string($status['ocs']['data']['token']);
+		$validRemoteId = isset($status['ocs']['data']['remoteId']);
+
+		if ($httpRequestSuccessful && $ocsCallSuccessful && $validToken && $validRemoteId) {
+			return [
+				$status['ocs']['data']['token'],
+				(int)$status['ocs']['data']['remoteId']
+			];
+		}
+
+		return false;
+	}
+
 	/**
 	 * send server-to-server unshare to remote server
 	 *
 	 * @param string $remote url
 	 * @param int $id share id
 	 * @param string $token
-	 * @param int $try how often did we already tried to send the un-share request
 	 * @return bool
 	 */
-	public function sendRemoteUnShare($remote, $id, $token, $try = 0) {
-		$url = rtrim($remote, '/');
-		$fields = array('token' => $token, 'format' => 'json');
-		$url = $this->addressHandler->removeProtocolFromUrl($url);
-		$result = $this->tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields);
+	public function sendRemoteUnShare($remote, $id, $token) {
+		$this->sendUpdateToRemote($remote, $id, $token, 'unshare');
+	}
+
+	/**
+	 * send server-to-server unshare to remote server
+	 *
+	 * @param string $remote url
+	 * @param int $id share id
+	 * @param string $token
+	 * @return bool
+	 */
+	public function sendRevokeShare($remote, $id, $token) {
+		$this->sendUpdateToRemote($remote, $id, $token, 'revoke');
+	}
+
+	/**
+	 * send notification to remote server if the permissions was changed
+	 *
+	 * @param string $remote
+	 * @param int $remoteId
+	 * @param string $token
+	 * @param int $permissions
+	 * @return bool
+	 */
+	public function sendPermissionChange($remote, $remoteId, $token, $permissions) {
+		$this->sendUpdateToRemote($remote, $remoteId, $token, 'permissions', ['permissions' => $permissions]);
+	}
+
+	/**
+	 * forward accept reShare to remote server
+	 * 
+	 * @param string $remote
+	 * @param int $remoteId
+	 * @param string $token
+	 */
+	public function sendAcceptShare($remote, $remoteId, $token) {
+		$this->sendUpdateToRemote($remote, $remoteId, $token, 'accept');
+	}
+
+	/**
+	 * forward decline reShare to remote server
+	 *
+	 * @param string $remote
+	 * @param int $remoteId
+	 * @param string $token
+	 */
+	public function sendDeclineShare($remote, $remoteId, $token) {
+		$this->sendUpdateToRemote($remote, $remoteId, $token, 'decline');
+	}
+
+	/**
+	 * inform remote server whether server-to-server share was accepted/declined
+	 *
+	 * @param string $remote
+	 * @param string $token
+	 * @param int $remoteId Share id on the remote host
+	 * @param string $action possible actions: accept, decline, unshare, revoke, permissions
+	 * @param array $data
+	 * @param int $try
+	 * @return boolean
+	 */
+	public function sendUpdateToRemote($remote, $remoteId, $token, $action, $data = [], $try = 0) {
+
+		$fields = array('token' => $token);
+		foreach ($data as $key => $value) {
+			$fields[$key] = $value;
+		}
+
+		$url = $this->addressHandler->removeProtocolFromUrl($remote);
+		$result = $this->tryHttpPostToShareEndpoint(rtrim($url, '/'), '/' . $remoteId . '/' . $action, $fields);
 		$status = json_decode($result['result'], true);
 
-		if ($result['success'] && 
-			($status['ocs']['meta']['statuscode'] === 100 || 
+		if ($result['success'] &&
+			($status['ocs']['meta']['statuscode'] === 100 ||
 				$status['ocs']['meta']['statuscode'] === 200
 			)
 		) {
 			return true;
 		} elseif ($try === 0) {
 			// only add new job on first try
-			$this->jobList->add('OCA\FederatedFileSharing\BackgroundJob\UnShare',
+			$this->jobList->add('OCA\FederatedFileSharing\BackgroundJob\RetryJob',
 				[
 					'remote' => $remote,
-					'id' => $id,
+					'remoteId' => $remoteId,
 					'token' => $token,
+					'action' => $action,
+					'data' => json_encode($data),
 					'try' => $try,
 					'lastRun' => $this->getTimestamp()
 				]
@@ -138,6 +255,7 @@ class Notifications {
 		return false;
 	}
 
+
 	/**
 	 * return current timestamp
 	 *
@@ -154,6 +272,7 @@ class Notifications {
 	 * @param string $urlSuffix
 	 * @param array $fields post parameters
 	 * @return array
+	 * @throws \Exception
 	 */
 	protected function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) {
 		$client = $this->httpClientService->newClient();
@@ -174,6 +293,12 @@ class Notifications {
 				$result['success'] = true;
 				break;
 			} catch (\Exception $e) {
+				// if flat re-sharing is not supported by the remote server
+				// we re-throw the exception and fall back to the old behaviour.
+				// (flat re-shares has been introduced in ownCloud 9.1)
+				if ($e->getCode() === Http::STATUS_INTERNAL_SERVER_ERROR) {
+					throw $e;
+				}
 				$try++;
 				$protocol = 'http://';
 			}
diff --git a/apps/federatedfilesharing/lib/RequestHandler.php b/apps/federatedfilesharing/lib/RequestHandler.php
new file mode 100644
index 0000000000000000000000000000000000000000..01ab96822d80348578ca793da1d73d9f84a00197
--- /dev/null
+++ b/apps/federatedfilesharing/lib/RequestHandler.php
@@ -0,0 +1,580 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\FederatedFileSharing;
+
+use OCA\Files_Sharing\Activity;
+use OCP\AppFramework\Http;
+use OCP\Constants;
+use OCP\Files\NotFoundException;
+use OCP\IDBConnection;
+use OCP\IRequest;
+use OCP\IUserManager;
+use OCP\Share;
+
+/**
+ * Class RequestHandler
+ * 
+ * handles OCS Request to the federated share API
+ *
+ * @package OCA\FederatedFileSharing\API
+ */
+class RequestHandler {
+
+	/** @var FederatedShareProvider */
+	private $federatedShareProvider;
+
+	/** @var IDBConnection */
+	private $connection;
+
+	/** @var Share\IManager */
+	private $shareManager;
+
+	/** @var IRequest */
+	private $request;
+
+	/** @var Notifications */
+	private $notifications;
+
+	/** @var AddressHandler */
+	private $addressHandler;
+
+	/** @var  IUserManager */
+	private $userManager;
+
+	/** @var string */
+	private $shareTable = 'share';
+
+	/**
+	 * Server2Server constructor.
+	 *
+	 * @param FederatedShareProvider $federatedShareProvider
+	 * @param IDBConnection $connection
+	 * @param Share\IManager $shareManager
+	 * @param IRequest $request
+	 * @param Notifications $notifications
+	 * @param AddressHandler $addressHandler
+	 * @param IUserManager $userManager
+	 */
+	public function __construct(FederatedShareProvider $federatedShareProvider,
+								IDBConnection $connection,
+								Share\IManager $shareManager,
+								IRequest $request,
+								Notifications $notifications,
+								AddressHandler $addressHandler,
+								IUserManager $userManager
+	) {
+		$this->federatedShareProvider = $federatedShareProvider;
+		$this->connection = $connection;
+		$this->shareManager = $shareManager;
+		$this->request = $request;
+		$this->notifications = $notifications;
+		$this->addressHandler = $addressHandler;
+		$this->userManager = $userManager;
+	}
+
+	/**
+	 * create a new share
+	 *
+	 * @param array $params
+	 * @return \OC_OCS_Result
+	 */
+	public function createShare($params) {
+
+		if (!$this->isS2SEnabled(true)) {
+			return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing');
+		}
+
+		$remote = isset($_POST['remote']) ? $_POST['remote'] : null;
+		$token = isset($_POST['token']) ? $_POST['token'] : null;
+		$name = isset($_POST['name']) ? $_POST['name'] : null;
+		$owner = isset($_POST['owner']) ? $_POST['owner'] : null;
+		$sharedBy = isset($_POST['sharedBy']) ? $_POST['sharedBy'] : null;
+		$shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null;
+		$remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null;
+		$sharedByFederatedId = isset($_POST['sharedByFederatedId']) ? $_POST['sharedByFederatedId'] : null;
+		$ownerFederatedId = isset($_POST['ownerFederatedId']) ? $_POST['ownerFederatedId'] : null;
+
+		if ($remote && $token && $name && $owner && $remoteId && $shareWith) {
+
+			if(!\OCP\Util::isValidFileName($name)) {
+				return new \OC_OCS_Result(null, 400, 'The mountpoint name contains invalid characters.');
+			}
+
+			// FIXME this should be a method in the user management instead
+			\OCP\Util::writeLog('files_sharing', 'shareWith before, ' . $shareWith, \OCP\Util::DEBUG);
+			\OCP\Util::emitHook(
+				'\OCA\Files_Sharing\API\Server2Server',
+				'preLoginNameUsedAsUserName',
+				array('uid' => &$shareWith)
+			);
+			\OCP\Util::writeLog('files_sharing', 'shareWith after, ' . $shareWith, \OCP\Util::DEBUG);
+
+			if (!\OCP\User::userExists($shareWith)) {
+				return new \OC_OCS_Result(null, 400, 'User does not exists');
+			}
+
+			\OC_Util::setupFS($shareWith);
+
+			$discoveryManager = new DiscoveryManager(
+				\OC::$server->getMemCacheFactory(),
+				\OC::$server->getHTTPClientService()
+			);
+			$externalManager = new \OCA\Files_Sharing\External\Manager(
+					\OC::$server->getDatabaseConnection(),
+					\OC\Files\Filesystem::getMountManager(),
+					\OC\Files\Filesystem::getLoader(),
+					\OC::$server->getHTTPHelper(),
+					\OC::$server->getNotificationManager(),
+					$discoveryManager,
+					$shareWith
+				);
+
+			try {
+				$externalManager->addShare($remote, $token, '', $name, $owner, false, $shareWith, $remoteId);
+				$shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external');
+
+				if ($ownerFederatedId === null) {
+					$ownerFederatedId = $owner . '@' . $this->cleanupRemote($remote);
+				}
+				// if the owner of the share and the initiator are the same user
+				// we also complete the federated share ID for the initiator
+				if ($sharedByFederatedId === null && $owner === $sharedBy) {
+					$sharedByFederatedId = $ownerFederatedId;
+				}
+
+				\OC::$server->getActivityManager()->publishActivity(
+					Activity::FILES_SHARING_APP, Activity::SUBJECT_REMOTE_SHARE_RECEIVED, array($ownerFederatedId, trim($name, '/')), '', array(),
+					'', '', $shareWith, Activity::TYPE_REMOTE_SHARE, Activity::PRIORITY_LOW);
+
+				$urlGenerator = \OC::$server->getURLGenerator();
+
+				$notificationManager = \OC::$server->getNotificationManager();
+				$notification = $notificationManager->createNotification();
+				$notification->setApp('files_sharing')
+					->setUser($shareWith)
+					->setDateTime(new \DateTime())
+					->setObject('remote_share', $shareId)
+					->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]);
+
+				$declineAction = $notification->createAction();
+				$declineAction->setLabel('decline')
+					->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v1.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE');
+				$notification->addAction($declineAction);
+
+				$acceptAction = $notification->createAction();
+				$acceptAction->setLabel('accept')
+					->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v1.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST');
+				$notification->addAction($acceptAction);
+
+				$notificationManager->notify($notification);
+
+				return new \OC_OCS_Result();
+			} catch (\Exception $e) {
+				\OCP\Util::writeLog('files_sharing', 'server can not add remote share, ' . $e->getMessage(), \OCP\Util::ERROR);
+				return new \OC_OCS_Result(null, 500, 'internal server error, was not able to add share from ' . $remote);
+			}
+		}
+
+		return new \OC_OCS_Result(null, 400, 'server can not add remote share, missing parameter');
+	}
+
+	/**
+	 * create re-share on behalf of another user
+	 *
+	 * @param $params
+	 * @return \OC_OCS_Result
+	 */
+	public function reShare($params) {
+
+		$id = isset($params['id']) ? (int)$params['id'] : null;
+		$token = $this->request->getParam('token', null);
+		$shareWith = $this->request->getParam('shareWith', null);
+		$permission = (int)$this->request->getParam('permission', null);
+		$remoteId = (int)$this->request->getParam('remoteId', null);
+
+		if ($id === null ||
+			$token === null ||
+			$shareWith === null ||
+			$permission === null ||
+			$remoteId === null
+		) {
+			return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST);
+		}
+
+		try {
+			$share = $this->federatedShareProvider->getShareById($id);
+		} catch (Share\Exceptions\ShareNotFound $e) {
+			return new \OC_OCS_Result(null, Http::STATUS_NOT_FOUND);
+		}
+
+		// don't allow to share a file back to the owner
+		list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);
+		$owner = $share->getShareOwner();
+		$currentServer = $this->addressHandler->generateRemoteURL();
+		if ($this->addressHandler->compareAddresses($user, $remote,$owner , $currentServer)) {
+			return new \OC_OCS_Result(null, Http::STATUS_FORBIDDEN);
+		}
+
+		if ($this->verifyShare($share, $token)) {
+
+			// check if re-sharing is allowed
+			if ($share->getPermissions() | ~Constants::PERMISSION_SHARE) {
+				$share->setPermissions($share->getPermissions() & $permission);
+				// the recipient of the initial share is now the initiator for the re-share
+				$share->setSharedBy($share->getSharedWith());
+				$share->setSharedWith($shareWith);
+				try {
+					$result = $this->federatedShareProvider->create($share);
+					$this->federatedShareProvider->storeRemoteId((int)$result->getId(), $remoteId);
+					return new \OC_OCS_Result(['token' => $result->getToken(), 'remoteId' => $result->getId()]);
+				} catch (\Exception $e) {
+					return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST);
+				}
+			} else {
+				return new \OC_OCS_Result(null, Http::STATUS_FORBIDDEN);
+			}
+		}
+		return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST);
+
+	}
+
+	/**
+	 * accept server-to-server share
+	 *
+	 * @param array $params
+	 * @return \OC_OCS_Result
+	 */
+	public function acceptShare($params) {
+
+		if (!$this->isS2SEnabled()) {
+			return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing');
+		}
+
+		$id = $params['id'];
+		$token = isset($_POST['token']) ? $_POST['token'] : null;
+
+		try {
+			$share = $this->federatedShareProvider->getShareById($id);
+		} catch (Share\Exceptions\ShareNotFound $e) {
+			return new \OC_OCS_Result();
+		}
+
+		if ($this->verifyShare($share, $token)) {
+			$this->executeAcceptShare($share);
+			if ($share->getShareOwner() !== $share->getSharedBy()) {
+				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
+				$remoteId = $this->federatedShareProvider->getRemoteId($share);
+				$this->notifications->sendAcceptShare($remote, $remoteId, $share->getToken());
+			}
+		}
+
+		return new \OC_OCS_Result();
+	}
+
+	protected function executeAcceptShare(Share\IShare $share) {
+		list($file, $link) = $this->getFile($this->getCorrectUid($share), $share->getNode()->getId());
+
+		$event = \OC::$server->getActivityManager()->generateEvent();
+		$event->setApp(Activity::FILES_SHARING_APP)
+			->setType(Activity::TYPE_REMOTE_SHARE)
+			->setAffectedUser($this->getCorrectUid($share))
+			->setSubject(Activity::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), basename($file)])
+			->setObject('files', $share->getNode()->getId(), $file)
+			->setLink($link);
+		\OC::$server->getActivityManager()->publish($event);
+	}
+
+	/**
+	 * decline server-to-server share
+	 *
+	 * @param array $params
+	 * @return \OC_OCS_Result
+	 */
+	public function declineShare($params) {
+
+		if (!$this->isS2SEnabled()) {
+			return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing');
+		}
+
+		$id = (int)$params['id'];
+		$token = isset($_POST['token']) ? $_POST['token'] : null;
+
+		try {
+			$share = $this->federatedShareProvider->getShareById($id);
+		} catch (Share\Exceptions\ShareNotFound $e) {
+			return new \OC_OCS_Result();
+		}
+
+		if($this->verifyShare($share, $token)) {
+			if ($share->getShareOwner() !== $share->getSharedBy()) {
+				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
+				$remoteId = $this->federatedShareProvider->getRemoteId($share);
+				$this->notifications->sendDeclineShare($remote, $remoteId, $share->getToken());
+			}
+			$this->executeDeclineShare($share);
+		}
+
+		return new \OC_OCS_Result();
+	}
+
+	/**
+	 * delete declined share and create a activity
+	 *
+	 * @param Share\IShare $share
+	 */
+	protected function executeDeclineShare(Share\IShare $share) {
+		$this->federatedShareProvider->removeShareFromTable($share);
+		list($file, $link) = $this->getFile($this->getCorrectUid($share), $share->getNode()->getId());
+
+		$event = \OC::$server->getActivityManager()->generateEvent();
+		$event->setApp(Activity::FILES_SHARING_APP)
+			->setType(Activity::TYPE_REMOTE_SHARE)
+			->setAffectedUser($this->getCorrectUid($share))
+			->setSubject(Activity::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), basename($file)])
+			->setObject('files', $share->getNode()->getId(), $file)
+			->setLink($link);
+		\OC::$server->getActivityManager()->publish($event);
+
+	}
+
+	/**
+	 * check if we are the initiator or the owner of a re-share and return the correct UID
+	 *
+	 * @param Share\IShare $share
+	 * @return string
+	 */
+	protected function getCorrectUid(Share\IShare $share) {
+		if($this->userManager->userExists($share->getShareOwner())) {
+			return $share->getShareOwner();
+		}
+
+		return $share->getSharedBy();
+	}
+
+	/**
+	 * remove server-to-server share if it was unshared by the owner
+	 *
+	 * @param array $params
+	 * @return \OC_OCS_Result
+	 */
+	public function unshare($params) {
+
+		if (!$this->isS2SEnabled()) {
+			return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing');
+		}
+
+		$id = $params['id'];
+		$token = isset($_POST['token']) ? $_POST['token'] : null;
+
+		$query = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share_external` WHERE `remote_id` = ? AND `share_token` = ?');
+		$query->execute(array($id, $token));
+		$share = $query->fetchRow();
+
+		if ($token && $id && !empty($share)) {
+
+			$remote = $this->cleanupRemote($share['remote']);
+
+			$owner = $share['owner'] . '@' . $remote;
+			$mountpoint = $share['mountpoint'];
+			$user = $share['user'];
+
+			$query = \OCP\DB::prepare('DELETE FROM `*PREFIX*share_external` WHERE `remote_id` = ? AND `share_token` = ?');
+			$query->execute(array($id, $token));
+
+			if ($share['accepted']) {
+				$path = trim($mountpoint, '/');
+			} else {
+				$path = trim($share['name'], '/');
+			}
+
+			$notificationManager = \OC::$server->getNotificationManager();
+			$notification = $notificationManager->createNotification();
+			$notification->setApp('files_sharing')
+				->setUser($share['user'])
+				->setObject('remote_share', (int) $share['id']);
+			$notificationManager->markProcessed($notification);
+
+			\OC::$server->getActivityManager()->publishActivity(
+				Activity::FILES_SHARING_APP, Activity::SUBJECT_REMOTE_SHARE_UNSHARED, array($owner, $path), '', array(),
+				'', '', $user, Activity::TYPE_REMOTE_SHARE, Activity::PRIORITY_MEDIUM);
+		}
+
+		return new \OC_OCS_Result();
+	}
+
+	private function cleanupRemote($remote) {
+		$remote = substr($remote, strpos($remote, '://') + 3);
+
+		return rtrim($remote, '/');
+	}
+
+
+	/**
+	 * federated share was revoked, either by the owner or the re-sharer
+	 *
+	 * @param $params
+	 * @return \OC_OCS_Result
+	 */
+	public function revoke($params) {
+		$id = (int)$params['id'];
+		$token = $this->request->getParam('token');
+		
+		$share = $this->federatedShareProvider->getShareById($id);
+		
+		if ($this->verifyShare($share, $token)) {
+			$this->federatedShareProvider->removeShareFromTable($share);
+			return new \OC_OCS_Result();
+		}
+
+	return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST);
+
+	}
+	
+	/**
+	 * get share
+	 *
+	 * @param int $id
+	 * @param string $token
+	 * @return array|bool
+	 */
+	protected function getShare($id, $token) {
+		$query = $this->connection->getQueryBuilder();
+		$query->select('*')->from($this->shareTable)
+			->where($query->expr()->eq('token', $query->createNamedParameter($token)))
+			->andWhere($query->expr()->eq('share_type', $query->createNamedParameter(FederatedShareProvider::SHARE_TYPE_REMOTE)))
+			->andWhere($query->expr()->eq('id', $query->createNamedParameter($id)));
+
+		$result = $query->execute()->fetchAll();
+
+		if (!empty($result) && isset($result[0])) {
+			return $result[0];
+		}
+
+		return false;
+	}
+
+	/**
+	 * get file
+	 *
+	 * @param string $user
+	 * @param int $fileSource
+	 * @return array with internal path of the file and a absolute link to it
+	 */
+	private function getFile($user, $fileSource) {
+		\OC_Util::setupFS($user);
+
+		try {
+			$file = \OC\Files\Filesystem::getPath($fileSource);
+		} catch (NotFoundException $e) {
+			$file = null;
+		}
+		$args = \OC\Files\Filesystem::is_dir($file) ? array('dir' => $file) : array('dir' => dirname($file), 'scrollto' => $file);
+		$link = \OCP\Util::linkToAbsolute('files', 'index.php', $args);
+
+		return array($file, $link);
+
+	}
+
+	/**
+	 * check if server-to-server sharing is enabled
+	 *
+	 * @param bool $incoming
+	 * @return bool
+	 */
+	private function isS2SEnabled($incoming = false) {
+
+		$result = \OCP\App::isEnabled('files_sharing');
+
+		if ($incoming) {
+			$result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled();
+		} else {
+			$result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
+		}
+
+		return $result;
+	}
+
+	/**
+	 * check if we got the right share
+	 *
+	 * @param Share\IShare $share
+	 * @param string $token
+	 * @return bool
+	 */
+	protected function verifyShare(Share\IShare $share, $token) {
+		if (
+			$share->getShareType() === FederatedShareProvider::SHARE_TYPE_REMOTE &&
+			$share->getToken() === $token
+		) {
+			return true;
+		}
+
+		return false;
+	}
+
+	/**
+	 * update share information to keep federated re-shares in sync
+	 *
+	 * @param array $params
+	 * @return \OC_OCS_Result
+	 */
+	public function updatePermissions($params) {
+		$id = (int)$params['id'];
+		$token = $this->request->getParam('token', null);
+		$permissions = $this->request->getParam('permissions', null);
+
+		try {
+			$share = $this->federatedShareProvider->getShareById($id);
+		} catch (Share\Exceptions\ShareNotFound $e) {
+			return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST);
+		}
+
+		$validPermission = ctype_digit($permissions);
+		$validToken = $this->verifyShare($share, $token);
+		if ($validPermission && $validToken) {
+			$this->updatePermissionsInDatabase($share, (int)$permissions);
+		} else {
+			return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST);
+		}
+
+		return new \OC_OCS_Result();
+	}
+
+	/**
+	 * update permissions in database
+	 *
+	 * @param IShare $share
+	 * @param int $permissions
+	 */
+	protected function updatePermissionsInDatabase(IShare $share, $permissions) {
+		$query = $this->connection->getQueryBuilder();
+		$query->update('share')
+			->where($query->expr()->eq('id', $query->createNamedParameter($share->getId())))
+			->set('permissions', $query->createNamedParameter($permissions))
+			->execute();
+	}
+
+}
diff --git a/apps/files_sharing/lib/notifier.php b/apps/federatedfilesharing/lib/notifier.php
similarity index 86%
rename from apps/files_sharing/lib/notifier.php
rename to apps/federatedfilesharing/lib/notifier.php
index 27e4e2565f2968a2255e55f07e3a79df872d4ffe..1c8d92c7ecad491c54cd6a647f342177affdfee1 100644
--- a/apps/files_sharing/lib/notifier.php
+++ b/apps/federatedfilesharing/lib/notifier.php
@@ -19,7 +19,7 @@
  *
  */
 
-namespace OCA\Files_Sharing;
+namespace OCA\FederatedFileSharing;
 
 
 use OCP\Notification\INotification;
@@ -54,9 +54,15 @@ class Notifier implements INotifier {
 			// Deal with known subjects
 			case 'remote_share':
 				$params = $notification->getSubjectParameters();
-				$notification->setParsedSubject(
-					(string) $l->t('You received "/%2$s" as a remote share from %1$s', $params)
-				);
+				if ($params[0] !== $params[1] && $params[1] !== null) {
+					$notification->setParsedSubject(
+						(string) $l->t('You received "/%3$s" as a remote share from %1$s (on behalf of %2$s)', $params)
+					);
+				} else {
+					$notification->setParsedSubject(
+						(string)$l->t('You received "/%3$s" as a remote share from %1$s', $params)
+					);
+				}
 
 				// Deal with the actions for a known subject
 				foreach ($notification->getActions() as $action) {
diff --git a/apps/federatedfilesharing/tests/AddressHandlerTest.php b/apps/federatedfilesharing/tests/AddressHandlerTest.php
index 9f7d8c49b4da69557a170df71f321e85c4d063b7..bb1c2c5a25ae4f07daef0a8edf20c758e58d02b9 100644
--- a/apps/federatedfilesharing/tests/AddressHandlerTest.php
+++ b/apps/federatedfilesharing/tests/AddressHandlerTest.php
@@ -27,9 +27,8 @@ namespace OCA\FederatedFileSharing\Tests;
 use OCA\FederatedFileSharing\AddressHandler;
 use OCP\IL10N;
 use OCP\IURLGenerator;
-use Test\TestCase;
 
-class AddressHandlerTest extends TestCase {
+class AddressHandlerTest extends \Test\TestCase {
 
 	/** @var  AddressHandler */
 	private $addressHandler;
diff --git a/apps/federatedfilesharing/tests/DiscoveryManagerTest.php b/apps/federatedfilesharing/tests/DiscoveryManagerTest.php
index 9ae62b1ae4da374609ffcfffc759a4c9ef4b1a06..5af6b394dd234a52d1bd9f15a28ab8f59a24432f 100644
--- a/apps/federatedfilesharing/tests/DiscoveryManagerTest.php
+++ b/apps/federatedfilesharing/tests/DiscoveryManagerTest.php
@@ -26,9 +26,8 @@ use OCP\Http\Client\IClient;
 use OCP\Http\Client\IClientService;
 use OCP\ICache;
 use OCP\ICacheFactory;
-use Test\TestCase;
 
-class DiscoveryManagerTest extends TestCase {
+class DiscoveryManagerTest extends \Test\TestCase {
 	/** @var ICache */
 	private $cache;
 	/** @var IClient */
diff --git a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php
index 1fbae90a46fede4d460888f29322d4a22a67e6f6..9b3edf0398dba71a203661001decec04e4e33acf 100644
--- a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php
+++ b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php
@@ -30,8 +30,8 @@ use OCP\IConfig;
 use OCP\IDBConnection;
 use OCP\IL10N;
 use OCP\ILogger;
+use OCP\IUserManager;
 use OCP\Share\IManager;
-use Test\TestCase;
 
 /**
  * Class FederatedShareProviderTest
@@ -39,7 +39,7 @@ use Test\TestCase;
  * @package OCA\FederatedFileSharing\Tests
  * @group DB
  */
-class FederatedShareProviderTest extends TestCase {
+class FederatedShareProviderTest extends \Test\TestCase {
 
 	/** @var IDBConnection */
 	protected $connection;
@@ -57,6 +57,8 @@ class FederatedShareProviderTest extends TestCase {
 	protected $rootFolder;
 	/** @var  IConfig | \PHPUnit_Framework_MockObject_MockObject */
 	protected $config;
+	/** @var  IUserManager | \PHPUnit_Framework_MockObject_MockObject */
+	protected $userManager;
 
 	/** @var IManager */
 	protected $shareManager;
@@ -82,7 +84,11 @@ class FederatedShareProviderTest extends TestCase {
 		$this->logger = $this->getMock('OCP\ILogger');
 		$this->rootFolder = $this->getMock('OCP\Files\IRootFolder');
 		$this->config = $this->getMock('OCP\IConfig');
-		$this->addressHandler = new AddressHandler(\OC::$server->getURLGenerator(), $this->l);
+		$this->userManager = $this->getMock('OCP\IUserManager');
+		//$this->addressHandler = new AddressHandler(\OC::$server->getURLGenerator(), $this->l);
+		$this->addressHandler = $this->getMockBuilder('OCA\FederatedFileSharing\AddressHandler')->disableOriginalConstructor()->getMock();
+
+		$this->userManager->expects($this->any())->method('userExists')->willReturn(true);
 
 		$this->provider = new FederatedShareProvider(
 			$this->connection,
@@ -92,7 +98,8 @@ class FederatedShareProviderTest extends TestCase {
 			$this->l,
 			$this->logger,
 			$this->rootFolder,
-			$this->config
+			$this->config,
+			$this->userManager
 		);
 
 		$this->shareManager = \OC::$server->getShareManager();
@@ -119,6 +126,11 @@ class FederatedShareProviderTest extends TestCase {
 
 		$this->tokenHandler->method('generateToken')->willReturn('token');
 
+		$this->addressHandler->expects($this->any())->method('generateRemoteURL')
+			->willReturn('http://localhost/');
+		$this->addressHandler->expects($this->any())->method('splitUserRemote')
+			->willReturn(['user', 'server.com']);
+
 		$this->notifications->expects($this->once())
 			->method('sendRemoteShare')
 			->with(
@@ -126,7 +138,10 @@ class FederatedShareProviderTest extends TestCase {
 				$this->equalTo('user@server.com'),
 				$this->equalTo('myFile'),
 				$this->anything(),
-				'sharedBy'
+				'shareOwner',
+				'shareOwner@http://localhost/',
+				'sharedBy',
+				'sharedBy@http://localhost/'
 			)->willReturn(true);
 
 		$this->rootFolder->expects($this->never())->method($this->anything());
@@ -182,6 +197,11 @@ class FederatedShareProviderTest extends TestCase {
 
 		$this->tokenHandler->method('generateToken')->willReturn('token');
 
+		$this->addressHandler->expects($this->any())->method('generateRemoteURL')
+			->willReturn('http://localhost/');
+		$this->addressHandler->expects($this->any())->method('splitUserRemote')
+			->willReturn(['user', 'server.com']);
+
 		$this->notifications->expects($this->once())
 			->method('sendRemoteShare')
 			->with(
@@ -189,7 +209,10 @@ class FederatedShareProviderTest extends TestCase {
 				$this->equalTo('user@server.com'),
 				$this->equalTo('myFile'),
 				$this->anything(),
-				'sharedBy'
+				'shareOwner',
+				'shareOwner@http://localhost/',
+				'sharedBy',
+				'sharedBy@http://localhost/'
 			)->willReturn(false);
 
 		$this->rootFolder->expects($this->once())
@@ -226,7 +249,10 @@ class FederatedShareProviderTest extends TestCase {
 		$node->method('getId')->willReturn(42);
 		$node->method('getName')->willReturn('myFile');
 
-		$shareWith = 'sharedBy@' . $this->addressHandler->generateRemoteURL();
+		$this->addressHandler->expects($this->any())->method('compareAddresses')
+			->willReturn(true);
+
+		$shareWith = 'sharedBy@localhost';
 
 		$share->setSharedWith($shareWith)
 			->setSharedBy('sharedBy')
@@ -262,6 +288,10 @@ class FederatedShareProviderTest extends TestCase {
 		$node->method('getId')->willReturn(42);
 		$node->method('getName')->willReturn('myFile');
 
+
+		$this->addressHandler->expects($this->any())->method('splitUserRemote')
+			->willReturn(['user', 'server.com']);
+
 		$share->setSharedWith('user@server.com')
 			->setSharedBy('sharedBy')
 			->setShareOwner('shareOwner')
@@ -270,6 +300,9 @@ class FederatedShareProviderTest extends TestCase {
 
 		$this->tokenHandler->method('generateToken')->willReturn('token');
 
+		$this->addressHandler->expects($this->any())->method('generateRemoteURL')
+			->willReturn('http://localhost/');
+
 		$this->notifications->expects($this->once())
 			->method('sendRemoteShare')
 			->with(
@@ -277,7 +310,10 @@ class FederatedShareProviderTest extends TestCase {
 				$this->equalTo('user@server.com'),
 				$this->equalTo('myFile'),
 				$this->anything(),
-				'sharedBy'
+				'shareOwner',
+				'shareOwner@http://localhost/',
+				'sharedBy',
+				'sharedBy@http://localhost/'
 			)->willReturn(true);
 
 		$this->rootFolder->expects($this->never())->method($this->anything());
@@ -291,20 +327,46 @@ class FederatedShareProviderTest extends TestCase {
 		}
 	}
 
-	public function testUpdate() {
+	/**
+	 * @dataProvider datatTestUpdate
+	 *
+	 */
+	public function testUpdate($owner, $sharedBy) {
+
+		$this->provider = $this->getMockBuilder('OCA\FederatedFileSharing\FederatedShareProvider')
+			->setConstructorArgs(
+				[
+					$this->connection,
+					$this->addressHandler,
+					$this->notifications,
+					$this->tokenHandler,
+					$this->l,
+					$this->logger,
+					$this->rootFolder,
+					$this->config,
+					$this->userManager
+				]
+			)->setMethods(['sendPermissionUpdate'])->getMock();
+
 		$share = $this->shareManager->newShare();
 
 		$node = $this->getMock('\OCP\Files\File');
 		$node->method('getId')->willReturn(42);
 		$node->method('getName')->willReturn('myFile');
 
+
+		$this->addressHandler->expects($this->any())->method('splitUserRemote')
+			->willReturn(['user', 'server.com']);
+
 		$share->setSharedWith('user@server.com')
-			->setSharedBy('sharedBy')
-			->setShareOwner('shareOwner')
+			->setSharedBy($sharedBy)
+			->setShareOwner($owner)
 			->setPermissions(19)
 			->setNode($node);
 
 		$this->tokenHandler->method('generateToken')->willReturn('token');
+		$this->addressHandler->expects($this->any())->method('generateRemoteURL')
+			->willReturn('http://localhost/');
 
 		$this->notifications->expects($this->once())
 			->method('sendRemoteShare')
@@ -313,9 +375,18 @@ class FederatedShareProviderTest extends TestCase {
 				$this->equalTo('user@server.com'),
 				$this->equalTo('myFile'),
 				$this->anything(),
-				'sharedBy'
+				$owner,
+				$owner . '@http://localhost/',
+				$sharedBy,
+				$sharedBy . '@http://localhost/'
 			)->willReturn(true);
 
+		if($owner === $sharedBy) {
+			$this->provider->expects($this->never())->method('sendPermissionUpdate');
+		} else {
+			$this->provider->expects($this->once())->method('sendPermissionUpdate');
+		}
+
 		$this->rootFolder->expects($this->never())->method($this->anything());
 
 		$share = $this->provider->create($share);
@@ -328,11 +399,24 @@ class FederatedShareProviderTest extends TestCase {
 		$this->assertEquals(1, $share->getPermissions());
 	}
 
+	public function datatTestUpdate() {
+		return [
+			['sharedBy', 'shareOwner'],
+			['shareOwner', 'shareOwner']
+		];
+	}
+
 	public function testGetSharedBy() {
 		$node = $this->getMock('\OCP\Files\File');
 		$node->method('getId')->willReturn(42);
 		$node->method('getName')->willReturn('myFile');
 
+		$this->addressHandler->expects($this->at(0))->method('splitUserRemote')
+			->willReturn(['user', 'server.com']);
+
+		$this->addressHandler->expects($this->at(1))->method('splitUserRemote')
+			->willReturn(['user2', 'server.com']);
+
 		$this->tokenHandler->method('generateToken')->willReturn('token');
 		$this->notifications
 			->method('sendRemoteShare')
@@ -439,6 +523,14 @@ class FederatedShareProviderTest extends TestCase {
 		$node->method('getId')->willReturn(42);
 		$node->method('getName')->willReturn('myFile');
 
+		$this->addressHandler->expects($this->any())->method('splitUserRemote')
+			->willReturnCallback(function ($uid) {
+				if ($uid === 'user@server.com') {
+					return ['user', 'server.com'];
+				}
+				return ['user2', 'server.com'];
+		});
+
 		$this->tokenHandler->method('generateToken')->willReturn('token');
 		$this->notifications
 			->method('sendRemoteShare')
diff --git a/apps/federatedfilesharing/tests/NotificationsTest.php b/apps/federatedfilesharing/tests/NotificationsTest.php
index bde69a82bad242a5e6807dcf6bf636d30bfa2636..50a62e9829efdcbf9528c7c391ecf5b97b7b2cca 100644
--- a/apps/federatedfilesharing/tests/NotificationsTest.php
+++ b/apps/federatedfilesharing/tests/NotificationsTest.php
@@ -28,9 +28,8 @@ use OCA\FederatedFileSharing\DiscoveryManager;
 use OCA\FederatedFileSharing\Notifications;
 use OCP\BackgroundJob\IJobList;
 use OCP\Http\Client\IClientService;
-use Test\TestCase;
 
-class NotificationsTest extends TestCase {
+class NotificationsTest extends \Test\TestCase {
 
 	/** @var  AddressHandler | \PHPUnit_Framework_MockObject_MockObject */
 	private $addressHandler;
@@ -85,14 +84,15 @@ class NotificationsTest extends TestCase {
 		return $instance;
 	}
 
+
 	/**
-	 * @dataProvider dataTestSendRemoteUnShare
+	 * @dataProvider dataTestSendUpdateToRemote
 	 *
 	 * @param int $try
 	 * @param array $httpRequestResult
 	 * @param bool $expected
 	 */
-	public function testSendRemoteUnShare($try, $httpRequestResult, $expected) {
+	public function testSendUpdateToRemote($try, $httpRequestResult, $expected) {
 		$remote = 'remote';
 		$id = 42;
 		$timestamp = 63576;
@@ -102,20 +102,22 @@ class NotificationsTest extends TestCase {
 		$instance->expects($this->any())->method('getTimestamp')->willReturn($timestamp);
 
 		$instance->expects($this->once())->method('tryHttpPostToShareEndpoint')
-			->with($remote, '/'.$id.'/unshare', ['token' => $token, 'format' => 'json'])
+			->with($remote, '/'.$id.'/unshare', ['token' => $token, 'data1Key' => 'data1Value'])
 			->willReturn($httpRequestResult);
 
 		$this->addressHandler->expects($this->once())->method('removeProtocolFromUrl')
 			->with($remote)->willReturn($remote);
-		
+
 		// only add background job on first try
 		if ($try === 0 && $expected === false) {
 			$this->jobList->expects($this->once())->method('add')
 				->with(
-					'OCA\FederatedFileSharing\BackgroundJob\UnShare',
+					'OCA\FederatedFileSharing\BackgroundJob\RetryJob',
 					[
 						'remote' => $remote,
-						'id' => $id,
+						'remoteId' => $id,
+						'action' => 'unshare',
+						'data' => json_encode(['data1Key' => 'data1Value']),
 						'token' => $token,
 						'try' => $try,
 						'lastRun' => $timestamp
@@ -124,14 +126,15 @@ class NotificationsTest extends TestCase {
 		} else {
 			$this->jobList->expects($this->never())->method('add');
 		}
-		
+
 		$this->assertSame($expected,
-			$instance->sendRemoteUnShare($remote, $id, $token, $try)
+			$instance->sendUpdateToRemote($remote, $id, $token, 'unshare', ['data1Key' => 'data1Value'], $try)
 		);
-		
+
 	}
-	
-	public function dataTestSendRemoteUnshare() {
+
+
+	public function dataTestSendUpdateToRemote() {
 		return [
 			// test if background job is added correctly
 			[0, ['success' => true, 'result' => json_encode(['ocs' => ['meta' => ['statuscode' => 200]]])], true],
diff --git a/apps/files_sharing/tests/server2server.php b/apps/federatedfilesharing/tests/RequestHandlerTest.php
similarity index 60%
rename from apps/files_sharing/tests/server2server.php
rename to apps/federatedfilesharing/tests/RequestHandlerTest.php
index 1c8b5ed7a17ccdf40c4dd353f3c4d050c04aa626..14da756a0ef868bf4b92488276d8720d8b148bf0 100644
--- a/apps/files_sharing/tests/server2server.php
+++ b/apps/federatedfilesharing/tests/RequestHandlerTest.php
@@ -23,14 +23,22 @@
  *
  */
 
-use OCA\Files_Sharing\Tests\TestCase;
+namespace OCA\FederatedFileSharing\Tests;
+
+use OC\Files\Filesystem;
+use OCA\FederatedFileSharing\DiscoveryManager;
+use OCA\FederatedFileSharing\FederatedShareProvider;
+use OCA\FederatedFileSharing\RequestHandler;
+use OCP\IUserManager;
+use OCP\Share\IShare;
 
 /**
- * Class Test_Files_Sharing_Api
+ * Class RequestHandlerTest
  *
+ * @package OCA\FederatedFileSharing\Tests
  * @group DB
  */
-class Test_Files_Sharing_S2S_OCS_API extends TestCase {
+class RequestHandlerTest extends TestCase {
 
 	const TEST_FOLDER_NAME = '/folder_share_api_test';
 
@@ -40,13 +48,25 @@ class Test_Files_Sharing_S2S_OCS_API extends TestCase {
 	private $connection;
 
 	/**
-	 * @var \OCA\Files_Sharing\API\Server2Server
+	 * @var RequestHandler
 	 */
 	private $s2s;
 
 	/** @var  \OCA\FederatedFileSharing\FederatedShareProvider | PHPUnit_Framework_MockObject_MockObject */
 	private $federatedShareProvider;
 
+	/** @var  \OCA\FederatedFileSharing\Notifications | PHPUnit_Framework_MockObject_MockObject */
+	private $notifications;
+
+	/** @var  \OCA\FederatedFileSharing\AddressHandler | PHPUnit_Framework_MockObject_MockObject */
+	private $addressHandler;
+	
+	/** @var  IUserManager | \PHPUnit_Framework_MockObject_MockObject */
+	private $userManager;
+
+	/** @var  IShare | \PHPUnit_Framework_MockObject_MockObject */
+	private $share;
+
 	protected function setUp() {
 		parent::setUp();
 
@@ -60,16 +80,33 @@ class Test_Files_Sharing_S2S_OCS_API extends TestCase {
 				->setConstructorArgs([$config, $clientService])
 				->getMock();
 		$httpHelperMock->expects($this->any())->method('post')->with($this->anything())->will($this->returnValue(true));
+		$this->share = $this->getMock('\OCP\Share\IShare');
 		$this->federatedShareProvider = $this->getMockBuilder('OCA\FederatedFileSharing\FederatedShareProvider')
 			->disableOriginalConstructor()->getMock();
 		$this->federatedShareProvider->expects($this->any())
 			->method('isOutgoingServer2serverShareEnabled')->willReturn(true);
 		$this->federatedShareProvider->expects($this->any())
 			->method('isIncomingServer2serverShareEnabled')->willReturn(true);
+		$this->federatedShareProvider->expects($this->any())->method('getShareById')
+			->willReturn($this->share);
 
+		$this->notifications = $this->getMockBuilder('OCA\FederatedFileSharing\Notifications')
+			->disableOriginalConstructor()->getMock();
+		$this->addressHandler = $this->getMockBuilder('OCA\FederatedFileSharing\AddressHandler')
+			->disableOriginalConstructor()->getMock();
+		$this->userManager = $this->getMock('OCP\IUserManager');
+		
 		$this->registerHttpHelper($httpHelperMock);
 
-		$this->s2s = new \OCA\Files_Sharing\API\Server2Server($this->federatedShareProvider);
+		$this->s2s = new RequestHandler(
+			$this->federatedShareProvider,
+			\OC::$server->getDatabaseConnection(),
+			\OC::$server->getShareManager(),
+			\OC::$server->getRequest(),
+			$this->notifications,
+			$this->addressHandler,
+			$this->userManager
+		);
 
 		$this->connection = \OC::$server->getDatabaseConnection();
 	}
@@ -78,6 +115,9 @@ class Test_Files_Sharing_S2S_OCS_API extends TestCase {
 		$query = \OCP\DB::prepare('DELETE FROM `*PREFIX*share_external`');
 		$query->execute();
 
+		$query = \OCP\DB::prepare('DELETE FROM `*PREFIX*share`');
+		$query->execute();
+
 		$this->restoreHttpHelper();
 
 		parent::tearDown();
@@ -135,28 +175,34 @@ class Test_Files_Sharing_S2S_OCS_API extends TestCase {
 
 
 	function testDeclineShare() {
-		$dummy = \OCP\DB::prepare('
-			INSERT INTO `*PREFIX*share`
-			(`share_type`, `uid_owner`, `item_type`, `item_source`, `item_target`, `file_source`, `file_target`, `permissions`, `stime`, `token`, `share_with`)
-			VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
-			');
-		$dummy->execute(array(\OCP\Share::SHARE_TYPE_REMOTE, self::TEST_FILES_SHARING_API_USER1, 'test', '1', '/1', '1', '/test.txt', '1', time(), 'token', 'foo@bar'));
 
-		$verify = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share`');
-		$result = $verify->execute();
-		$data = $result->fetchAll();
-		$this->assertSame(1, count($data));
+		$this->s2s = $this->getMockBuilder('\OCA\FederatedFileSharing\RequestHandler')
+			->setConstructorArgs(
+				[
+					$this->federatedShareProvider,
+					\OC::$server->getDatabaseConnection(),
+					\OC::$server->getShareManager(),
+					\OC::$server->getRequest(),
+					$this->notifications,
+					$this->addressHandler,
+					$this->userManager
+				]
+			)->setMethods(['executeDeclineShare', 'verifyShare'])->getMock();
+
+		$this->s2s->expects($this->once())->method('executeDeclineShare');
+
+		$this->s2s->expects($this->any())->method('verifyShare')->willReturn(true);
 
 		$_POST['token'] = 'token';
-		$this->s2s->declineShare(array('id' => $data[0]['id']));
 
-		$verify = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share`');
-		$result = $verify->execute();
-		$data = $result->fetchAll();
-		$this->assertEmpty($data);
+		$this->s2s->declineShare(array('id' => 42));
+
 	}
 
-	function testDeclineShareMultiple() {
+	function XtestDeclineShareMultiple() {
+
+		$this->share->expects($this->any())->method('verifyShare')->willReturn(true);
+
 		$dummy = \OCP\DB::prepare('
 			INSERT INTO `*PREFIX*share`
 			(`share_type`, `uid_owner`, `item_type`, `item_source`, `item_target`, `file_source`, `file_target`, `permissions`, `stime`, `token`, `share_with`)
@@ -194,14 +240,14 @@ class Test_Files_Sharing_S2S_OCS_API extends TestCase {
 	function testDeleteUser($toDelete, $expected, $remainingUsers) {
 		$this->createDummyS2SShares();
 
-		$discoveryManager = new \OCA\FederatedFileSharing\DiscoveryManager(
+		$discoveryManager = new DiscoveryManager(
 			\OC::$server->getMemCacheFactory(),
 			\OC::$server->getHTTPClientService()
 		);
-		$manager = new OCA\Files_Sharing\External\Manager(
+		$manager = new \OCA\Files_Sharing\External\Manager(
 			\OC::$server->getDatabaseConnection(),
-			\OC\Files\Filesystem::getMountManager(),
-			\OC\Files\Filesystem::getLoader(),
+			Filesystem::getMountManager(),
+			Filesystem::getLoader(),
 			\OC::$server->getHTTPHelper(),
 			\OC::$server->getNotificationManager(),
 			$discoveryManager,
@@ -260,4 +306,76 @@ class Test_Files_Sharing_S2S_OCS_API extends TestCase {
 		$this->assertSame(10, count($dummyEntries));
 	}
 
+	/**
+	 * @dataProvider dataTestGetShare
+	 *
+	 * @param bool $found
+	 * @param bool $correctId
+	 * @param bool $correctToken
+	 */
+	public function testGetShare($found, $correctId, $correctToken) {
+
+		$connection = \OC::$server->getDatabaseConnection();
+		$query = $connection->getQueryBuilder();
+		$stime = time();
+		$query->insert('share')
+			->values(
+				[
+					'share_type' => $query->createNamedParameter(FederatedShareProvider::SHARE_TYPE_REMOTE),
+					'uid_owner' => $query->createNamedParameter(self::TEST_FILES_SHARING_API_USER1),
+					'uid_initiator' => $query->createNamedParameter(self::TEST_FILES_SHARING_API_USER2),
+					'item_type' => $query->createNamedParameter('test'),
+					'item_source' => $query->createNamedParameter('1'),
+					'item_target' => $query->createNamedParameter('/1'),
+					'file_source' => $query->createNamedParameter('1'),
+					'file_target' => $query->createNamedParameter('/test.txt'),
+					'permissions' => $query->createNamedParameter('1'),
+					'stime' => $query->createNamedParameter($stime),
+					'token' => $query->createNamedParameter('token'),
+					'share_with' => $query->createNamedParameter('foo@bar'),
+				]
+			)->execute();
+		$id = $query->getLastInsertId();
+
+		$expected = [
+			'share_type' => (string)FederatedShareProvider::SHARE_TYPE_REMOTE,
+			'uid_owner' => self::TEST_FILES_SHARING_API_USER1,
+			'item_type' => 'test',
+			'item_source' => '1',
+			'item_target' => '/1',
+			'file_source' => '1',
+			'file_target' => '/test.txt',
+			'permissions' => '1',
+			'stime' => (string)$stime,
+			'token' => 'token',
+			'share_with' => 'foo@bar',
+			'id' => (string)$id,
+			'uid_initiator' => self::TEST_FILES_SHARING_API_USER2,
+			'parent' => null,
+			'accepted' => '0',
+			'expiration' => null,
+			'mail_send' => '0'
+		];
+
+		$searchToken = $correctToken ? 'token' : 'wrongToken';
+		$searchId = $correctId ? $id : -1;
+
+		$result = $this->invokePrivate($this->s2s, 'getShare', [$searchId, $searchToken]);
+
+		if ($found) {
+			$this->assertEquals($expected, $result);
+		} else {
+			$this->assertSame(false, $result);
+		}
+	}
+
+	public function dataTestGetShare() {
+		return [
+			[true, true, true],
+			[false, false, true],
+			[false, true, false],
+			[false, false, false],
+		];
+	}
+
 }
diff --git a/apps/federatedfilesharing/tests/TestCase.php b/apps/federatedfilesharing/tests/TestCase.php
new file mode 100644
index 0000000000000000000000000000000000000000..64c6d0455980c1451bcc590d60adfb51f9db72f4
--- /dev/null
+++ b/apps/federatedfilesharing/tests/TestCase.php
@@ -0,0 +1,132 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Jörn Friedrich Dreyer <jfd@butonic.de>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Roeland Jago Douma <rullzer@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\FederatedFileSharing\Tests;
+
+use OC\Files\Filesystem;
+use OCA\Files\Share;
+
+/**
+ * Class Test_Files_Sharing_Base
+ *
+ * @group DB
+ *
+ * Base class for sharing tests.
+ */
+abstract class TestCase extends \Test\TestCase {
+
+	const TEST_FILES_SHARING_API_USER1 = "test-share-user1";
+	const TEST_FILES_SHARING_API_USER2 = "test-share-user2";
+
+	public static function setUpBeforeClass() {
+		parent::setUpBeforeClass();
+
+		// reset backend
+		\OC_User::clearBackends();
+		\OC_Group::clearBackends();
+
+		// create users
+		$backend = new \Test\Util\User\Dummy();
+		\OC_User::useBackend($backend);
+		$backend->createUser(self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER1);
+		$backend->createUser(self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_USER2);
+	}
+
+	protected function setUp() {
+		parent::setUp();
+
+		//login as user1
+		self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
+	}
+
+	public static function tearDownAfterClass() {
+		// cleanup users
+		$user = \OC::$server->getUserManager()->get(self::TEST_FILES_SHARING_API_USER1);
+		if ($user !== null) {
+			$user->delete();
+		}
+		$user = \OC::$server->getUserManager()->get(self::TEST_FILES_SHARING_API_USER2);
+		if ($user !== null) {
+			$user->delete();
+		}
+
+		\OC_Util::tearDownFS();
+		\OC_User::setUserId('');
+		Filesystem::tearDown();
+
+		// reset backend
+		\OC_User::clearBackends();
+		\OC_User::useBackend('database');
+		\OC_Group::clearBackends();
+		\OC_Group::useBackend(new \OC_Group_Database());
+
+		parent::tearDownAfterClass();
+	}
+
+	/**
+	 * @param string $user
+	 * @param bool $create
+	 * @param bool $password
+	 */
+	protected static function loginHelper($user, $create = false, $password = false) {
+
+		if ($password === false) {
+			$password = $user;
+		}
+
+		if ($create) {
+			\OC::$server->getUserManager()->createUser($user, $password);
+			\OC_Group::createGroup('group');
+			\OC_Group::addToGroup($user, 'group');
+		}
+
+		self::resetStorage();
+
+		\OC_Util::tearDownFS();
+		\OC::$server->getUserSession()->setUser(null);
+		\OC\Files\Filesystem::tearDown();
+		\OC::$server->getUserSession()->login($user, $password);
+		\OC::$server->getUserFolder($user);
+
+		\OC_Util::setupFS($user);
+	}
+
+	/**
+	 * reset init status for the share storage
+	 */
+	protected static function resetStorage() {
+		$storage = new \ReflectionClass('\OC\Files\Storage\Shared');
+		$isInitialized = $storage->getProperty('initialized');
+		$isInitialized->setAccessible(true);
+		$isInitialized->setValue($storage, false);
+		$isInitialized->setAccessible(false);
+	}
+
+}
diff --git a/apps/federatedfilesharing/tests/TokenHandlerTest.php b/apps/federatedfilesharing/tests/TokenHandlerTest.php
index 490c0d95d7bc6629c4b8f943a967e1cba67d6fb4..004a3a61933b8b7700c9e2f044743451958e366e 100644
--- a/apps/federatedfilesharing/tests/TokenHandlerTest.php
+++ b/apps/federatedfilesharing/tests/TokenHandlerTest.php
@@ -26,9 +26,8 @@ namespace OCA\FederatedFileSharing\Tests;
 
 use OCA\FederatedFileSharing\TokenHandler;
 use OCP\Security\ISecureRandom;
-use Test\TestCase;
 
-class TokenHandlerTest extends TestCase {
+class TokenHandlerTest extends \Test\TestCase {
 
 	/** @var  TokenHandler */
 	private $tokenHandler;
diff --git a/apps/files_sharing/api/server2server.php b/apps/files_sharing/api/server2server.php
deleted file mode 100644
index 034ec5105e4145f46254cd94ce155fe2b436ba6a..0000000000000000000000000000000000000000
--- a/apps/files_sharing/api/server2server.php
+++ /dev/null
@@ -1,325 +0,0 @@
-<?php
-/**
- * @author Arthur Schiwon <blizzz@owncloud.com>
- * @author Björn Schießle <schiessle@owncloud.com>
- * @author Joas Schilling <nickvergessen@owncloud.com>
- * @author Lukas Reschke <lukas@owncloud.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- *
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program 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, version 3,
- * along with this program.  If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-namespace OCA\Files_Sharing\API;
-
-use OCA\FederatedFileSharing\DiscoveryManager;
-use OCA\FederatedFileSharing\FederatedShareProvider;
-use OCA\Files_Sharing\Activity;
-use OCP\Files\NotFoundException;
-
-class Server2Server {
-
-	/** @var FederatedShareProvider */
-	private $federatedShareProvider;
-
-
-	/**
-	 * Server2Server constructor.
-	 *
-	 * @param FederatedShareProvider $federatedShareProvider
-	 */
-	public function __construct(FederatedShareProvider $federatedShareProvider) {
-		$this->federatedShareProvider = $federatedShareProvider;
-	}
-
-	/**
-	 * create a new share
-	 *
-	 * @param array $params
-	 * @return \OC_OCS_Result
-	 */
-	public function createShare($params) {
-
-		if (!$this->isS2SEnabled(true)) {
-			return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing');
-		}
-
-		$remote = isset($_POST['remote']) ? $_POST['remote'] : null;
-		$token = isset($_POST['token']) ? $_POST['token'] : null;
-		$name = isset($_POST['name']) ? $_POST['name'] : null;
-		$owner = isset($_POST['owner']) ? $_POST['owner'] : null;
-		$shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null;
-		$remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null;
-
-		if ($remote && $token && $name && $owner && $remoteId && $shareWith) {
-
-			if(!\OCP\Util::isValidFileName($name)) {
-				return new \OC_OCS_Result(null, 400, 'The mountpoint name contains invalid characters.');
-			}
-
-			// FIXME this should be a method in the user management instead
-			\OCP\Util::writeLog('files_sharing', 'shareWith before, ' . $shareWith, \OCP\Util::DEBUG);
-			\OCP\Util::emitHook(
-				'\OCA\Files_Sharing\API\Server2Server',
-				'preLoginNameUsedAsUserName',
-				array('uid' => &$shareWith)
-			);
-			\OCP\Util::writeLog('files_sharing', 'shareWith after, ' . $shareWith, \OCP\Util::DEBUG);
-
-			if (!\OCP\User::userExists($shareWith)) {
-				return new \OC_OCS_Result(null, 400, 'User does not exists');
-			}
-
-			\OC_Util::setupFS($shareWith);
-
-			$discoveryManager = new DiscoveryManager(
-				\OC::$server->getMemCacheFactory(),
-				\OC::$server->getHTTPClientService()
-			);
-			$externalManager = new \OCA\Files_Sharing\External\Manager(
-					\OC::$server->getDatabaseConnection(),
-					\OC\Files\Filesystem::getMountManager(),
-					\OC\Files\Filesystem::getLoader(),
-					\OC::$server->getHTTPHelper(),
-					\OC::$server->getNotificationManager(),
-					$discoveryManager,
-					$shareWith
-				);
-
-			try {
-				$externalManager->addShare($remote, $token, '', $name, $owner, false, $shareWith, $remoteId);
-				$shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external');
-
-				$user = $owner . '@' . $this->cleanupRemote($remote);
-
-				\OC::$server->getActivityManager()->publishActivity(
-					Activity::FILES_SHARING_APP, Activity::SUBJECT_REMOTE_SHARE_RECEIVED, array($user, trim($name, '/')), '', array(),
-					'', '', $shareWith, Activity::TYPE_REMOTE_SHARE, Activity::PRIORITY_LOW);
-
-				$urlGenerator = \OC::$server->getURLGenerator();
-
-				$notificationManager = \OC::$server->getNotificationManager();
-				$notification = $notificationManager->createNotification();
-				$notification->setApp('files_sharing')
-					->setUser($shareWith)
-					->setDateTime(new \DateTime())
-					->setObject('remote_share', $shareId)
-					->setSubject('remote_share', [$user, trim($name, '/')]);
-
-				$declineAction = $notification->createAction();
-				$declineAction->setLabel('decline')
-					->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v1.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE');
-				$notification->addAction($declineAction);
-
-				$acceptAction = $notification->createAction();
-				$acceptAction->setLabel('accept')
-					->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v1.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST');
-				$notification->addAction($acceptAction);
-
-				$notificationManager->notify($notification);
-
-				return new \OC_OCS_Result();
-			} catch (\Exception $e) {
-				\OCP\Util::writeLog('files_sharing', 'server can not add remote share, ' . $e->getMessage(), \OCP\Util::ERROR);
-				return new \OC_OCS_Result(null, 500, 'internal server error, was not able to add share from ' . $remote);
-			}
-		}
-
-		return new \OC_OCS_Result(null, 400, 'server can not add remote share, missing parameter');
-	}
-
-	/**
-	 * accept server-to-server share
-	 *
-	 * @param array $params
-	 * @return \OC_OCS_Result
-	 */
-	public function acceptShare($params) {
-
-		if (!$this->isS2SEnabled()) {
-			return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing');
-		}
-
-		$id = $params['id'];
-		$token = isset($_POST['token']) ? $_POST['token'] : null;
-		$share = self::getShare($id, $token);
-
-		if ($share) {
-			list($file, $link) = self::getFile($share['uid_owner'], $share['file_source']);
-
-			$event = \OC::$server->getActivityManager()->generateEvent();
-			$event->setApp(Activity::FILES_SHARING_APP)
-				->setType(Activity::TYPE_REMOTE_SHARE)
-				->setAffectedUser($share['uid_owner'])
-				->setSubject(Activity::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share['share_with'], basename($file)])
-				->setObject('files', $share['file_source'], $file)
-				->setLink($link);
-			\OC::$server->getActivityManager()->publish($event);
-		}
-
-		return new \OC_OCS_Result();
-	}
-
-	/**
-	 * decline server-to-server share
-	 *
-	 * @param array $params
-	 * @return \OC_OCS_Result
-	 */
-	public function declineShare($params) {
-
-		if (!$this->isS2SEnabled()) {
-			return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing');
-		}
-
-		$id = $params['id'];
-		$token = isset($_POST['token']) ? $_POST['token'] : null;
-
-		$share = $this->getShare($id, $token);
-
-		if ($share) {
-			// userId must be set to the user who unshares
-			\OCP\Share::unshare($share['item_type'], $share['item_source'], $share['share_type'], $share['share_with'], $share['uid_owner']);
-
-			list($file, $link) = $this->getFile($share['uid_owner'], $share['file_source']);
-
-			$event = \OC::$server->getActivityManager()->generateEvent();
-			$event->setApp(Activity::FILES_SHARING_APP)
-				->setType(Activity::TYPE_REMOTE_SHARE)
-				->setAffectedUser($share['uid_owner'])
-				->setSubject(Activity::SUBJECT_REMOTE_SHARE_DECLINED, [$share['share_with'], basename($file)])
-				->setObject('files', $share['file_source'], $file)
-				->setLink($link);
-			\OC::$server->getActivityManager()->publish($event);
-		}
-
-		return new \OC_OCS_Result();
-	}
-
-	/**
-	 * remove server-to-server share if it was unshared by the owner
-	 *
-	 * @param array $params
-	 * @return \OC_OCS_Result
-	 */
-	public function unshare($params) {
-
-		if (!$this->isS2SEnabled()) {
-			return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing');
-		}
-
-		$id = $params['id'];
-		$token = isset($_POST['token']) ? $_POST['token'] : null;
-
-		$query = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share_external` WHERE `remote_id` = ? AND `share_token` = ?');
-		$query->execute(array($id, $token));
-		$share = $query->fetchRow();
-
-		if ($token && $id && !empty($share)) {
-
-			$remote = $this->cleanupRemote($share['remote']);
-
-			$owner = $share['owner'] . '@' . $remote;
-			$mountpoint = $share['mountpoint'];
-			$user = $share['user'];
-
-			$query = \OCP\DB::prepare('DELETE FROM `*PREFIX*share_external` WHERE `remote_id` = ? AND `share_token` = ?');
-			$query->execute(array($id, $token));
-
-			if ($share['accepted']) {
-				$path = trim($mountpoint, '/');
-			} else {
-				$path = trim($share['name'], '/');
-			}
-
-			$notificationManager = \OC::$server->getNotificationManager();
-			$notification = $notificationManager->createNotification();
-			$notification->setApp('files_sharing')
-				->setUser($share['user'])
-				->setObject('remote_share', (int) $share['id']);
-			$notificationManager->markProcessed($notification);
-
-			\OC::$server->getActivityManager()->publishActivity(
-				Activity::FILES_SHARING_APP, Activity::SUBJECT_REMOTE_SHARE_UNSHARED, array($owner, $path), '', array(),
-				'', '', $user, Activity::TYPE_REMOTE_SHARE, Activity::PRIORITY_MEDIUM);
-		}
-
-		return new \OC_OCS_Result();
-	}
-
-	private function cleanupRemote($remote) {
-		$remote = substr($remote, strpos($remote, '://') + 3);
-
-		return rtrim($remote, '/');
-	}
-
-	/**
-	 * get share
-	 *
-	 * @param int $id
-	 * @param string $token
-	 * @return array
-	 */
-	private function getShare($id, $token) {
-		$query = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `id` = ? AND `token` = ? AND `share_type` = ?');
-		$query->execute(array($id, $token, \OCP\Share::SHARE_TYPE_REMOTE));
-		$share = $query->fetchRow();
-
-		return $share;
-	}
-
-	/**
-	 * get file
-	 *
-	 * @param string $user
-	 * @param int $fileSource
-	 * @return array with internal path of the file and a absolute link to it
-	 */
-	private function getFile($user, $fileSource) {
-		\OC_Util::setupFS($user);
-
-		try {
-			$file = \OC\Files\Filesystem::getPath($fileSource);
-		} catch (NotFoundException $e) {
-			$file = null;
-		}
-		$args = \OC\Files\Filesystem::is_dir($file) ? array('dir' => $file) : array('dir' => dirname($file), 'scrollto' => $file);
-		$link = \OCP\Util::linkToAbsolute('files', 'index.php', $args);
-
-		return array($file, $link);
-
-	}
-
-	/**
-	 * check if server-to-server sharing is enabled
-	 *
-	 * @param bool $incoming
-	 * @return bool
-	 */
-	private function isS2SEnabled($incoming = false) {
-
-		$result = \OCP\App::isEnabled('files_sharing');
-
-		if ($incoming) {
-			$result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled();
-		} else {
-			$result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
-		}
-
-		return $result;
-	}
-
-}
diff --git a/apps/files_sharing/api/share20ocs.php b/apps/files_sharing/api/share20ocs.php
index af76284532690d24676f40c6df9e9d82ac92d125..28166b943b8525f9dbb40e7ccc88959208071ad8 100644
--- a/apps/files_sharing/api/share20ocs.php
+++ b/apps/files_sharing/api/share20ocs.php
@@ -99,7 +99,15 @@ class Share20OCS {
 	 */
 	protected function formatShare(\OCP\Share\IShare $share) {
 		$sharedBy = $this->userManager->get($share->getSharedBy());
-		$shareOwner = $this->userManager->get($share->getShareOwner());
+		// for federated shares the owner can be a remote user, in this
+		// case we use the initiator
+		if ($this->userManager->userExists($share->getShareOwner())) {
+			$shareOwner = $this->userManager->get($share->getShareOwner());
+			$localUser = $share->getShareOwner();
+		} else {
+			$shareOwner = $this->userManager->get($share->getSharedBy());
+			$localUser = $share->getSharedBy();
+		}
 		$result = [
 			'id' => $share->getId(),
 			'share_type' => $share->getShareType(),
@@ -115,7 +123,7 @@ class Share20OCS {
 		];
 
 		$node = $share->getNode();
-		$result['path'] = $this->rootFolder->getUserFolder($share->getShareOwner())->getRelativePath($node->getPath());
+		$result['path'] = $this->rootFolder->getUserFolder($localUser)->getRelativePath($node->getPath());
 		if ($node instanceOf \OCP\Files\Folder) {
 			$result['item_type'] = 'folder';
 		} else {
diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php
index da573d11ec5328aebef2c8db4b2656641cca23c5..32eee9b6c9c6cd5197c49a0eb1ad50a9f5cc265e 100644
--- a/apps/files_sharing/appinfo/app.php
+++ b/apps/files_sharing/appinfo/app.php
@@ -103,15 +103,3 @@ if ($config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes') {
 		}
 	}
 }
-
-$manager = \OC::$server->getNotificationManager();
-$manager->registerNotifier(function() {
-	return new \OCA\Files_Sharing\Notifier(
-		\OC::$server->getL10NFactory()
-	);
-}, function() use ($l) {
-	return [
-		'id' => 'files_sharing',
-		'name' => $l->t('Federated sharing'),
-	];
-});
diff --git a/apps/files_sharing/lib/external/manager.php b/apps/files_sharing/lib/external/manager.php
index 7dc9f66f1147521f55e8b1313d452fc97a22041b..5b7a13f1eb1a402cd9be0d4404959c533c8981d7 100644
--- a/apps/files_sharing/lib/external/manager.php
+++ b/apps/files_sharing/lib/external/manager.php
@@ -325,6 +325,10 @@ class Manager {
 	}
 
 	public function removeShare($mountPoint) {
+
+		$mountPointObj = $this->mountManager->find($mountPoint);
+		$id = $mountPointObj->getStorage()->getCache()->getId();
+
 		$mountPoint = $this->stripPath($mountPoint);
 		$hash = md5($mountPoint);
 
@@ -338,13 +342,43 @@ class Manager {
 			$share = $getShare->fetch();
 			$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
 		}
+		$getShare->closeCursor();
 
 		$query = $this->connection->prepare('
 			DELETE FROM `*PREFIX*share_external`
 			WHERE `mountpoint_hash` = ?
 			AND `user` = ?
 		');
-		return (bool)$query->execute(array($hash, $this->uid));
+		$result = (bool)$query->execute(array($hash, $this->uid));
+
+		if($result) {
+			$this->removeReShares($id);
+		}
+
+		return $result;
+	}
+
+	/**
+	 * remove re-shares from share table and mapping in the federated_reshares table
+	 * 
+	 * @param $mountPointId
+	 */
+	protected function removeReShares($mountPointId) {
+		$selectQuery = $this->connection->getQueryBuilder();
+		$query = $this->connection->getQueryBuilder();
+		$selectQuery->select('id')->from('share')
+			->where($selectQuery->expr()->eq('file_source', $query->createNamedParameter($mountPointId)));
+		$select = $selectQuery->getSQL();
+
+
+		$query->delete('federated_reshares')
+			->where($query->expr()->in('share_id', $query->createFunction('(' . $select . ')')));
+		$query->execute();
+
+		$deleteReShares = $this->connection->getQueryBuilder();
+		$deleteReShares->delete('share')
+			->where($deleteReShares->expr()->eq('file_source', $deleteReShares->createNamedParameter($mountPointId)));
+		$deleteReShares->execute();
 	}
 
 	/**
diff --git a/apps/files_sharing/tests/api/share20ocstest.php b/apps/files_sharing/tests/api/share20ocstest.php
index ffb74da2af788d1de9e7a2b179e03ecb8b073b3c..96ce34f963c610e22bebaf47642112e99b0d2d74 100644
--- a/apps/files_sharing/tests/api/share20ocstest.php
+++ b/apps/files_sharing/tests/api/share20ocstest.php
@@ -82,6 +82,8 @@ class Share20OCSTest extends \Test\TestCase {
 		$this->currentUser = $this->getMock('OCP\IUser');
 		$this->currentUser->method('getUID')->willReturn('currentUser');
 
+		$this->userManager->expects($this->any())->method('userExists')->willReturn(true);
+
 		$this->l = $this->getMock('\OCP\IL10N');
 		$this->l->method('t')
 			->will($this->returnCallback(function($text, $parameters = []) {
diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php
index f0de39fdad356ef3d54728ca8fe57e4dfd94403c..8a39c18a495332b4187d82bf4e7ab570c461ba68 100644
--- a/lib/private/Share20/DefaultShareProvider.php
+++ b/lib/private/Share20/DefaultShareProvider.php
@@ -733,7 +733,7 @@ class DefaultShareProvider implements IShareProvider {
 	 * @throws InvalidShare
 	 */
 	private function createShare($data) {
-		$share = new Share($this->rootFolder);
+		$share = new Share($this->rootFolder, $this->userManager);
 		$share->setId((int)$data['id'])
 			->setShareType((int)$data['share_type'])
 			->setPermissions((int)$data['permissions'])
diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php
index dee9e0cdd212909282937725a157d8090d329661..3568995472a056ad3c4961ee7599b6e016fbe34a 100644
--- a/lib/private/Share20/Manager.php
+++ b/lib/private/Share20/Manager.php
@@ -201,7 +201,12 @@ class Manager implements IManager {
 		}
 
 		// And you can't share your rootfolder
-		if ($this->rootFolder->getUserFolder($share->getSharedBy())->getPath() === $share->getNode()->getPath()) {
+		if ($this->userManager->userExists($share->getSharedBy())) {
+			$sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath();
+		} else {
+			$sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath();
+		}
+		if ($sharedPath === $share->getNode()->getPath()) {
 			throw new \InvalidArgumentException('You can\'t share your root folder');
 		}
 
@@ -713,7 +718,11 @@ class Manager implements IManager {
 		}
 
 		if ($share->getPermissions() !== $originalShare->getPermissions()) {
-			$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
+			if ($this->userManager->userExists($share->getShareOwner())) {
+				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
+			} else {
+				$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
+			}
 			\OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
 				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
 				'itemSource' => $share->getNode()->getId(),
@@ -1107,7 +1116,7 @@ class Manager implements IManager {
 	 * @return \OCP\Share\IShare;
 	 */
 	public function newShare() {
-		return new \OC\Share20\Share($this->rootFolder);
+		return new \OC\Share20\Share($this->rootFolder, $this->userManager);
 	}
 
 	/**
diff --git a/lib/private/Share20/ProviderFactory.php b/lib/private/Share20/ProviderFactory.php
index 0bedfb84fc7e7917ce757a12135d64ccef649365..b436a7bc5f3ffe2e180e561abf3631859e33ad8c 100644
--- a/lib/private/Share20/ProviderFactory.php
+++ b/lib/private/Share20/ProviderFactory.php
@@ -115,7 +115,8 @@ class ProviderFactory implements IProviderFactory {
 				$l,
 				$this->serverContainer->getLogger(),
 				$this->serverContainer->getLazyRootFolder(),
-				$this->serverContainer->getConfig()
+				$this->serverContainer->getConfig(),
+				$this->serverContainer->getUserManager()
 			);
 		}
 
diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php
index c361f01216f995be7d4b52e8cd3840606657e8fb..f56fd94b4092820e2b73d47c047611f0b1590f61 100644
--- a/lib/private/Share20/Share.php
+++ b/lib/private/Share20/Share.php
@@ -24,8 +24,7 @@ use OCP\Files\File;
 use OCP\Files\IRootFolder;
 use OCP\Files\Node;
 use OCP\Files\NotFoundException;
-use OCP\IUser;
-use OCP\IGroup;
+use OCP\IUserManager;
 use OCP\Share\Exceptions\IllegalIDChangeException;
 
 class Share implements \OCP\Share\IShare {
@@ -68,8 +67,12 @@ class Share implements \OCP\Share\IShare {
 	/** @var IRootFolder */
 	private $rootFolder;
 
-	public function __construct(IRootFolder $rootFolder) {
+	/** @var IUserManager */
+	private $userManager;
+
+	public function __construct(IRootFolder $rootFolder, IUserManager $userManager) {
 		$this->rootFolder = $rootFolder;
+		$this->userManager = $userManager;
 	}
 
 	/**
@@ -145,7 +148,13 @@ class Share implements \OCP\Share\IShare {
 				throw new NotFoundException();
 			}
 
-			$userFolder = $this->rootFolder->getUserFolder($this->shareOwner);
+			// for federated shares the owner can be a remote user, in this
+			// case we use the initiator
+			if($this->userManager->userExists($this->shareOwner)) {
+				$userFolder = $this->rootFolder->getUserFolder($this->shareOwner);
+			} else {
+				$userFolder = $this->rootFolder->getUserFolder($this->sharedBy);
+			}
 
 			$nodes = $userFolder->getById($this->fileId);
 			if (empty($nodes)) {
diff --git a/ocs/routes.php b/ocs/routes.php
index af9c3e74137a3029c7550598826c3f35bcf4827c..7f4f78dd35d1f4f7ce6318e336bb52615f91d1e1 100644
--- a/ocs/routes.php
+++ b/ocs/routes.php
@@ -100,7 +100,25 @@ API::register(
 // Server-to-Server Sharing
 if (\OC::$server->getAppManager()->isEnabledForUser('files_sharing')) {
 	$federatedSharingApp = new \OCA\FederatedFileSharing\AppInfo\Application('federatedfilesharing');
-	$s2s = new \OCA\Files_Sharing\API\Server2Server($federatedSharingApp->getFederatedShareProvider());
+	$addressHandler = new \OCA\FederatedFileSharing\AddressHandler(
+		\OC::$server->getURLGenerator(),
+		\OC::$server->getL10N('federatedfilesharing')
+	);
+	$notification = new \OCA\FederatedFileSharing\Notifications(
+		$addressHandler,
+		\OC::$server->getHTTPClientService(),
+		new \OCA\FederatedFileSharing\DiscoveryManager(\OC::$server->getMemCacheFactory(), \OC::$server->getHTTPClientService()),
+		\OC::$server->getJobList()
+	);
+	$s2s = new OCA\FederatedFileSharing\RequestHandler(
+		$federatedSharingApp->getFederatedShareProvider(),
+		\OC::$server->getDatabaseConnection(),
+		\OC::$server->getShareManager(),
+		\OC::$server->getRequest(),
+		$notification,
+		$addressHandler,
+		\OC::$server->getUserManager()
+	);
 	API::register('post',
 		'/cloud/shares',
 		array($s2s, 'createShare'),
@@ -108,6 +126,21 @@ if (\OC::$server->getAppManager()->isEnabledForUser('files_sharing')) {
 		API::GUEST_AUTH
 	);
 
+	API::register('post',
+		'/cloud/shares/{id}/reshare',
+		array($s2s, 'reShare'),
+		'files_sharing',
+		API::GUEST_AUTH
+	);
+
+	API::register('post',
+		'/cloud/shares/{id}/permissions',
+		array($s2s, 'updatePermissions'),
+		'files_sharing',
+		API::GUEST_AUTH
+	);
+
+
 	API::register('post',
 		'/cloud/shares/{id}/accept',
 		array($s2s, 'acceptShare'),
@@ -128,4 +161,11 @@ if (\OC::$server->getAppManager()->isEnabledForUser('files_sharing')) {
 		'files_sharing',
 		API::GUEST_AUTH
 	);
+
+	API::register('post',
+		'/cloud/shares/{id}/revoke',
+		array($s2s, 'revoke'),
+		'files_sharing',
+		API::GUEST_AUTH
+	);
 }
diff --git a/tests/lib/Share20/DefaultShareProviderTest.php b/tests/lib/Share20/DefaultShareProviderTest.php
index 44a48535b9b5ace6af2f85450a11d7839d5e41ca..6ef00721a70e15e0f8df91380c9f97383a0664ae 100644
--- a/tests/lib/Share20/DefaultShareProviderTest.php
+++ b/tests/lib/Share20/DefaultShareProviderTest.php
@@ -57,6 +57,8 @@ class DefaultShareProviderTest extends \Test\TestCase {
 		$this->groupManager = $this->getMock('OCP\IGroupManager');
 		$this->rootFolder = $this->getMock('OCP\Files\IRootFolder');
 
+		$this->userManager->expects($this->any())->method('userExists')->willReturn(true);
+
 		//Empty share table
 		$this->dbConn->getQueryBuilder()->delete('share')->execute();
 
@@ -587,7 +589,7 @@ class DefaultShareProviderTest extends \Test\TestCase {
 	}
 
 	public function testCreateUserShare() {
-		$share = new \OC\Share20\Share($this->rootFolder);
+		$share = new \OC\Share20\Share($this->rootFolder, $this->userManager);
 
 		$shareOwner = $this->getMock('OCP\IUser');
 		$shareOwner->method('getUID')->WillReturn('shareOwner');
@@ -635,7 +637,7 @@ class DefaultShareProviderTest extends \Test\TestCase {
 	}
 
 	public function testCreateGroupShare() {
-		$share = new \OC\Share20\Share($this->rootFolder);
+		$share = new \OC\Share20\Share($this->rootFolder, $this->userManager);
 
 		$shareOwner = $this->getMock('\OCP\IUser');
 		$shareOwner->method('getUID')->willReturn('shareOwner');
@@ -683,7 +685,7 @@ class DefaultShareProviderTest extends \Test\TestCase {
 	}
 
 	public function testCreateLinkShare() {
-		$share = new \OC\Share20\Share($this->rootFolder);
+		$share = new \OC\Share20\Share($this->rootFolder, $this->userManager);
 
 		$shareOwner = $this->getMock('\OCP\IUser');
 		$shareOwner->method('getUID')->willReturn('shareOwner');
diff --git a/tests/lib/Share20/ManagerTest.php b/tests/lib/Share20/ManagerTest.php
index 7d79150449c6f52540642da8672d2ea151335451..a50ea6c892ad40fe479bb8130fc4905f7ea2e5fa 100644
--- a/tests/lib/Share20/ManagerTest.php
+++ b/tests/lib/Share20/ManagerTest.php
@@ -2283,6 +2283,9 @@ class ManagerTest extends \Test\TestCase {
 	}
 
 	public function testUpdateShareUser() {
+
+		$this->userManager->expects($this->any())->method('userExists')->willReturn(true);
+
 		$manager = $this->createManagerMock()
 			->setMethods([
 				'canShare',
@@ -2567,4 +2570,4 @@ class DummyFactory implements IProviderFactory {
 	public function getProviderForType($shareType) {
 		return $this->provider;
 	}
-}
\ No newline at end of file
+}
diff --git a/tests/lib/Share20/ShareTest.php b/tests/lib/Share20/ShareTest.php
index fdfc69f6577bd147331f7f3bf6b4b10f45b38a96..91bd2fe84b6375c7ecbb3fe93e687ef20d866034 100644
--- a/tests/lib/Share20/ShareTest.php
+++ b/tests/lib/Share20/ShareTest.php
@@ -36,7 +36,8 @@ class ShareTest extends \Test\TestCase {
 
 	public function setUp() {
 		$this->rootFolder = $this->getMock('\OCP\Files\IRootFolder');
-		$this->share = new \OC\Share20\Share($this->rootFolder);
+		$this->userManager = $this->getMock('OCP\IUserManager');
+		$this->share = new \OC\Share20\Share($this->rootFolder, $this->userManager);
 	}
 
 	/**