diff --git a/.gitignore b/.gitignore
index f25fb52ce2d397d223c9dbdadae2805d672b14a2..964701eea63ae35348a538d5f82c2c3d521dd454 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@
 !/apps/files
 !/apps/federation
 !/apps/federatedfilesharing
+!/apps/sharebymail
 !/apps/encryption
 !/apps/files_external
 !/apps/files_sharing
diff --git a/apps/files/lib/Controller/ApiController.php b/apps/files/lib/Controller/ApiController.php
index d6f88581b9621a5ea5d2d6f140d0ab9186e8dd9c..138b68601cb7b43e070a59e9d7647b74b989e5c5 100644
--- a/apps/files/lib/Controller/ApiController.php
+++ b/apps/files/lib/Controller/ApiController.php
@@ -200,7 +200,8 @@ class ApiController extends Controller {
 			\OCP\Share::SHARE_TYPE_USER,
 			\OCP\Share::SHARE_TYPE_GROUP,
 			\OCP\Share::SHARE_TYPE_LINK,
-			\OCP\Share::SHARE_TYPE_REMOTE
+			\OCP\Share::SHARE_TYPE_REMOTE,
+			\OCP\Share::SHARE_TYPE_EMAIL
 		];
 		foreach ($requestedShareTypes as $requestedShareType) {
 			// one of each type is enough to find out about the types
diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php
index ad9ac6c0851c09660e977f5ddedbdce6118975fb..4f80b8fc966349840e39fd2d1a5868aa55f56738 100644
--- a/apps/files_sharing/lib/Controller/ShareAPIController.php
+++ b/apps/files_sharing/lib/Controller/ShareAPIController.php
@@ -36,7 +36,6 @@ use OCP\IL10N;
 use OCP\IUserManager;
 use OCP\IRequest;
 use OCP\IURLGenerator;
-use OCP\IUser;
 use OCP\Files\IRootFolder;
 use OCP\Lock\LockedException;
 use OCP\Share\IManager;
@@ -186,7 +185,11 @@ class ShareAPIController extends OCSController {
 
 		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
 			$result['share_with'] = $share->getSharedWith();
-			$result['share_with_displayname'] = $share->getSharedWith();
+			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
+			$result['token'] = $share->getToken();
+		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
+			$result['share_with'] = $share->getSharedWith();
+			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
 			$result['token'] = $share->getToken();
 		}
 
@@ -195,6 +198,28 @@ class ShareAPIController extends OCSController {
 		return $result;
 	}
 
+	/**
+	 * Check if one of the users address books knows the exact property, if
+	 * yes we return the full name.
+	 *
+	 * @param string $query
+	 * @param string $property
+	 * @return string
+	 */
+	private function getDisplayNameFromAddressBook($query, $property) {
+		// FIXME: If we inject the contacts manager it gets initialized bofore any address books are registered
+		$result = \OC::$server->getContactsManager()->search($query, [$property]);
+		foreach ($result as $r) {
+			foreach($r[$property] as $value) {
+				if ($value === $query) {
+					return $r['FN'];
+				}
+			}
+		}
+
+		return $query;
+	}
+
 	/**
 	 * Get a specific share by id
 	 *
@@ -400,6 +425,17 @@ class ShareAPIController extends OCSController {
 
 			$share->setSharedWith($shareWith);
 			$share->setPermissions($permissions);
+		} else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) {
+			if ($share->getNodeType() === 'file') {
+				$share->setPermissions(\OCP\Constants::PERMISSION_READ);
+			} else {
+				$share->setPermissions(
+					\OCP\Constants::PERMISSION_READ |
+					\OCP\Constants::PERMISSION_CREATE |
+					\OCP\Constants::PERMISSION_UPDATE |
+					\OCP\Constants::PERMISSION_DELETE);
+			}
+			$share->setSharedWith($shareWith);
 		} else {
 			throw new OCSBadRequestException($this->l->t('Unknown share type'));
 		}
@@ -466,6 +502,9 @@ class ShareAPIController extends OCSController {
 			$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0));
 			$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, false, -1, 0));
 			$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0));
+			if($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
+				$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $node, false, -1, 0));
+			}
 			if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
 				$shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $node, false, -1, 0));
 			}
@@ -541,7 +580,12 @@ class ShareAPIController extends OCSController {
 		$userShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $path, $reshares, -1, 0);
 		$groupShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0);
 		$linkShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0);
-		$shares = array_merge($userShares, $groupShares, $linkShares);
+		if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
+			$mailShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $path, $reshares, -1, 0);
+		} else {
+			$mailShares = [];
+		}
+		$shares = array_merge($userShares, $groupShares, $linkShares, $mailShares);
 
 		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
 			$federatedShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0);
@@ -774,13 +818,24 @@ class ShareAPIController extends OCSController {
 		// First check if it is an internal share.
 		try {
 			$share = $this->shareManager->getShareById('ocinternal:' . $id);
+			return $share;
 		} catch (ShareNotFound $e) {
-			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
-				throw new ShareNotFound();
+			// Do nothing, just try the other share type
+		}
+
+		try {
+			if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
+				$share = $this->shareManager->getShareById('ocMailShare:' . $id);
+				return $share;
 			}
+		} catch (ShareNotFound $e) {
+			// Do nothing, just try the other share type
+		}
 
-			$share = $this->shareManager->getShareById('ocFederatedSharing:' . $id);
+		if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
+			throw new ShareNotFound();
 		}
+		$share = $this->shareManager->getShareById('ocFederatedSharing:' . $id);
 
 		return $share;
 	}
diff --git a/apps/files_sharing/lib/Controller/ShareesAPIController.php b/apps/files_sharing/lib/Controller/ShareesAPIController.php
index 99c6b55240da45bfa76ee9c31044c4f6284bd217..5e01c9bfb09a414fd9d5aecca3d25450a0b54b4b 100644
--- a/apps/files_sharing/lib/Controller/ShareesAPIController.php
+++ b/apps/files_sharing/lib/Controller/ShareesAPIController.php
@@ -273,15 +273,15 @@ class ShareesAPIController extends OCSController {
 
 	/**
 	 * @param string $search
-	 * @return array possible sharees
+	 * @return array
 	 */
 	protected function getRemote($search) {
-		$this->result['remotes'] = [];
+		$result = ['results' => [], 'exact' => []];
 
 		// Search in contacts
 		//@todo Pagination missing
 		$addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']);
-		$foundRemoteById = false;
+		$result['exactIdMatch'] = false;
 		foreach ($addressBookContacts as $contact) {
 			if (isset($contact['isLocalSystemBook'])) {
 				continue;
@@ -295,10 +295,10 @@ class ShareesAPIController extends OCSController {
 					list(, $serverUrl) = $this->splitUserRemote($cloudId);
 					if (strtolower($contact['FN']) === strtolower($search) || strtolower($cloudId) === strtolower($search)) {
 						if (strtolower($cloudId) === strtolower($search)) {
-							$foundRemoteById = true;
+							$result['exactIdMatch'] = true;
 						}
-						$this->result['exact']['remotes'][] = [
-							'label' => $contact['FN'],
+						$result['exact'][] = [
+							'label' => $contact['FN'] . " ($cloudId)",
 							'value' => [
 								'shareType' => Share::SHARE_TYPE_REMOTE,
 								'shareWith' => $cloudId,
@@ -306,8 +306,8 @@ class ShareesAPIController extends OCSController {
 							],
 						];
 					} else {
-						$this->result['remotes'][] = [
-							'label' => $contact['FN'],
+						$result['results'][] = [
+							'label' => $contact['FN'] . " ($cloudId)",
 							'value' => [
 								'shareType' => Share::SHARE_TYPE_REMOTE,
 								'shareWith' => $cloudId,
@@ -320,11 +320,11 @@ class ShareesAPIController extends OCSController {
 		}
 
 		if (!$this->shareeEnumeration) {
-			$this->result['remotes'] = [];
+			$result['results'] = [];
 		}
 
-		if (!$foundRemoteById && substr_count($search, '@') >= 1 && $this->offset === 0) {
-			$this->result['exact']['remotes'][] = [
+		if (!$result['exactIdMatch'] && substr_count($search, '@') >= 1 && $this->offset === 0) {
+			$result['exact'][] = [
 				'label' => $search,
 				'value' => [
 					'shareType' => Share::SHARE_TYPE_REMOTE,
@@ -334,6 +334,8 @@ class ShareesAPIController extends OCSController {
 		}
 
 		$this->reachedEndFor[] = 'remotes';
+
+		return $result;
 	}
 
 	/**
@@ -404,68 +406,6 @@ class ShareesAPIController extends OCSController {
 		return $remote;
 	}
 
-	/**
-	 * @param string $search
-	 */
-	protected function getEmails($search) {
-		$this->result['emails'] = [];
-		$this->result['exact']['emails'] = [];
-
-		$foundEmail = false;
-
-		// Search in contacts
-		//@todo Pagination missing
-		$addressBookContacts = $this->contactsManager->search($search, ['FN', 'EMAIL']);
-		foreach ($addressBookContacts as $contact) {
-			if (!isset($contact['EMAIL'])) {
-				continue;
-			}
-
-			$emails = $contact['EMAIL'];
-			if (!is_array($emails)) {
-				$emails = [$emails];
-			}
-
-			foreach ($emails as $email) {
-				if (strtolower($search) === strtolower($contact['FN']) ||
-					strtolower($search) === strtolower($email)
-				) {
-					if (strtolower($search) === strtolower($email)) {
-						$foundEmail = true;
-					}
-
-					$this->result['exact']['emails'][] = [
-						'label' => $contact['FN'],
-						'value' => [
-							'shareType' => Share::SHARE_TYPE_EMAIL,
-							'shareWith' => $email,
-						],
-					];
-				} else if ($this->shareeEnumeration) {
-					$this->result['emails'][] = [
-						'label' => $contact['FN'],
-						'value' => [
-							'shareType' => Share::SHARE_TYPE_EMAIL,
-							'shareWith' => $email,
-						],
-					];
-				}
-			}
-		}
-
-		if (!$foundEmail && substr_count($search, '@') >= 1 && $this->offset === 0) {
-			$this->result['exact']['emails'][] = [
-				'label' => $search,
-				'value' => [
-					'shareType' => Share::SHARE_TYPE_EMAIL,
-					'shareWith' => $search,
-				],
-			];
-		}
-
-		$this->reachedEndFor[] = 'emails';
-	}
-
 	/**
 	 * @NoAdminRequired
 	 *
@@ -487,17 +427,16 @@ class ShareesAPIController extends OCSController {
 
 		$shareTypes = [
 			Share::SHARE_TYPE_USER,
+			Share::SHARE_TYPE_REMOTE,
+			Share::SHARE_TYPE_EMAIL
 		];
 
 		if ($this->shareManager->allowGroupSharing()) {
 			$shareTypes[] = Share::SHARE_TYPE_GROUP;
 		}
 
-		$shareTypes[] = Share::SHARE_TYPE_EMAIL;
-		$shareTypes[] = Share::SHARE_TYPE_REMOTE;
-
-		if (is_array($shareType)) {
-			$shareTypes = array_intersect($shareTypes, $shareType);
+		if (isset($_GET['shareType']) && is_array($_GET['shareType'])) {
+			$shareTypes = array_intersect($shareTypes, $_GET['shareType']);
 			sort($shareTypes);
 		} else if (is_numeric($shareType)) {
 			$shareTypes = array_intersect($shareTypes, [(int) $shareType]);
@@ -509,6 +448,11 @@ class ShareesAPIController extends OCSController {
 			$shareTypes = array_diff($shareTypes, [Share::SHARE_TYPE_REMOTE]);
 		}
 
+		if (!$this->shareManager->shareProviderExists(Share::SHARE_TYPE_EMAIL)) {
+			// Remove mail shares from type array, because the share provider is not loaded
+			$shareTypes = array_diff($shareTypes, [Share::SHARE_TYPE_EMAIL]);
+		}
+
 		$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
 		$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
 		$this->limit = (int) $perPage;
@@ -560,13 +504,30 @@ class ShareesAPIController extends OCSController {
 		}
 
 		// Get remote
+		$remoteResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
 		if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) {
-			$this->getRemote($search);
+			$remoteResults = $this->getRemote($search);
 		}
 
-		// Get email
+		$mailResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false];
 		if (in_array(Share::SHARE_TYPE_EMAIL, $shareTypes)) {
-			$this->getEmails($search);
+			$mailResults = $this->getEmail($search);
+		}
+
+		// if we have a exact match, either for the federated cloud id or for the
+		// email address we only return the exact match. It is highly unlikely
+		// that the exact same email address and federated cloud id exists
+		if ($mailResults['exactIdMatch'] && !$remoteResults['exactIdMatch']) {
+			$this->result['emails'] = $mailResults['results'];
+			$this->result['exact']['emails'] = $mailResults['exact'];
+		} else if (!$mailResults['exactIdMatch'] && $remoteResults['exactIdMatch']) {
+			$this->result['remotes'] = $remoteResults['results'];
+			$this->result['exact']['remotes'] = $remoteResults['exact'];
+		} else {
+			$this->result['remotes'] = $remoteResults['results'];
+			$this->result['exact']['remotes'] = $remoteResults['exact'];
+			$this->result['emails'] = $mailResults['results'];
+			$this->result['exact']['emails'] = $mailResults['exact'];
 		}
 
 		$response = new Http\DataResponse($this->result);
@@ -583,6 +544,70 @@ class ShareesAPIController extends OCSController {
 		return $response;
 	}
 
+	/**
+	 * @param string $search
+	 * @return array
+	 */
+	protected function getEmail($search) {
+		$result = ['results' => [], 'exact' => []];
+
+		// Search in contacts
+		//@todo Pagination missing
+		$addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']);
+		$result['exactIdMatch'] = false;
+		foreach ($addressBookContacts as $contact) {
+			if (isset($contact['isLocalSystemBook'])) {
+				continue;
+			}
+			if (isset($contact['EMAIL'])) {
+				$emailAddresses = $contact['EMAIL'];
+				if (!is_array($emailAddresses)) {
+					$emailAddresses = [$emailAddresses];
+				}
+				foreach ($emailAddresses as $emailAddress) {
+					if (strtolower($contact['FN']) === strtolower($search) || strtolower($emailAddress) === strtolower($search)) {
+						if (strtolower($emailAddress) === strtolower($search)) {
+							$result['exactIdMatch'] = true;
+						}
+						$result['exact'][] = [
+							'label' => $contact['FN'] . " ($emailAddress)",
+							'value' => [
+								'shareType' => Share::SHARE_TYPE_EMAIL,
+								'shareWith' => $emailAddress,
+							],
+						];
+					} else {
+						$result['results'][] = [
+							'label' => $contact['FN'] . " ($emailAddress)",
+							'value' => [
+								'shareType' => Share::SHARE_TYPE_EMAIL,
+								'shareWith' => $emailAddress,
+							],
+						];
+					}
+				}
+			}
+		}
+
+		if (!$this->shareeEnumeration) {
+			$result['results'] = [];
+		}
+
+		if (!$result['exactIdMatch'] && filter_var($search, FILTER_VALIDATE_EMAIL)) {
+			$result['exact'][] = [
+				'label' => $search,
+				'value' => [
+					'shareType' => Share::SHARE_TYPE_EMAIL,
+					'shareWith' => $search,
+				],
+			];
+		}
+
+		$this->reachedEndFor[] = 'emails';
+
+		return $result;
+	}
+
 	/**
 	 * Generates a bunch of pagination links for the current page
 	 *
diff --git a/apps/files_sharing/tests/ApiTest.php b/apps/files_sharing/tests/ApiTest.php
index 6ab0cb4e1a67f0a9c199a39f38879be987c96982..540905a7dc9067c646ffb29846cd7b353184365d 100644
--- a/apps/files_sharing/tests/ApiTest.php
+++ b/apps/files_sharing/tests/ApiTest.php
@@ -234,6 +234,7 @@ class ApiTest extends TestCase {
 
 	function testEnfoceLinkPassword() {
 
+		$password = md5(time());
 		$appConfig = \OC::$server->getAppConfig();
 		$appConfig->setValue('core', 'shareapi_enforce_links_password', 'yes');
 
@@ -257,14 +258,14 @@ class ApiTest extends TestCase {
 
 		// share with password should succeed
 		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
-		$result = $ocs->createShare($this->folder, \OCP\Constants::PERMISSION_ALL, \OCP\Share::SHARE_TYPE_LINK, null, 'false', 'bar');
+		$result = $ocs->createShare($this->folder, \OCP\Constants::PERMISSION_ALL, \OCP\Share::SHARE_TYPE_LINK, null, 'false', $password);
 		$ocs->cleanup();
 
 		$data = $result->getData();
 
 		// setting new password should succeed
 		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
-		$ocs->updateShare($data['id'], null, 'bar');
+		$ocs->updateShare($data['id'], null, $password);
 		$ocs->cleanup();
 
 		// removing password should fail
@@ -887,6 +888,9 @@ class ApiTest extends TestCase {
 	 * @depends testCreateShareLink
 	 */
 	function testUpdateShare() {
+
+		$password = md5(time());
+
 		$node1 = $this->userFolder->get($this->filename);
 		$share1 = $this->shareManager->newShare();
 		$share1->setNode($node1)
@@ -915,7 +919,7 @@ class ApiTest extends TestCase {
 		$this->assertNull($share2->getPassword());
 
 		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
-		$ocs->updateShare($share2->getId(), null, 'foo');
+		$ocs->updateShare($share2->getId(), null, $password);
 		$ocs->cleanup();
 
 		$share2 = $this->shareManager->getShareById('ocinternal:' . $share2->getId());
diff --git a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php
index 6ad0576b6fa919f36f715017603c24b261f402a6..890fdb6eda0e493e9c488328f4e22e150bfd192f 100644
--- a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php
+++ b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php
@@ -23,8 +23,10 @@
  */
 namespace OCA\Files_Sharing\Tests\Controller;
 
+use OC\ContactsManager;
 use OCP\AppFramework\Http\DataResponse;
 use OCP\AppFramework\OCS\OCSNotFoundException;
+use OCP\Contacts;
 use OCP\Files\Folder;
 use OCP\IL10N;
 use OCA\Files_Sharing\Controller\ShareAPIController;
diff --git a/apps/files_sharing/tests/Controller/ShareesAPIControllerTest.php b/apps/files_sharing/tests/Controller/ShareesAPIControllerTest.php
index 6ee1ff596e4b98709886db40cf272785cd7b3b23..e8ee55d184546f8c62a0def67be7505f0001c6b8 100644
--- a/apps/files_sharing/tests/Controller/ShareesAPIControllerTest.php
+++ b/apps/files_sharing/tests/Controller/ShareesAPIControllerTest.php
@@ -771,28 +771,44 @@ class ShareesAPIControllerTest extends TestCase {
 		$this->assertCount((int) $reachedEnd, $this->invokePrivate($this->sharees, 'reachedEndFor'));
 	}
 
+	/**
+	 * @dataProvider dataGetRemote
+	 *
+	 * @param string $searchTerm
+	 * @param array $contacts
+	 * @param bool $shareeEnumeration
+	 * @param array $expected
+	 * @param bool $reachedEnd
+	 */
+	public function testGetRemote($searchTerm, $contacts, $shareeEnumeration, $expected, $reachedEnd) {
+		$this->invokePrivate($this->sharees, 'shareeEnumeration', [$shareeEnumeration]);
+		$this->contactsManager->expects($this->any())
+			->method('search')
+			->with($searchTerm, ['CLOUD', 'FN'])
+			->willReturn($contacts);
+
+		$result = $this->invokePrivate($this->sharees, 'getRemote', [$searchTerm]);
+
+		$this->assertEquals($expected, $result);
+		$this->assertCount((int) $reachedEnd, $this->invokePrivate($this->sharees, 'reachedEndFor'));
+	}
+
 	public function dataGetRemote() {
 		return [
-			['test', [], true, [], [], true],
-			['test', [], false, [], [], true],
+			['test', [], true, ['results' => [], 'exact' => [], 'exactIdMatch' => false], true],
+			['test', [], false, ['results' => [], 'exact' => [], 'exactIdMatch' => false], true],
 			[
 				'test@remote',
 				[],
 				true,
-				[
-					['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']],
-				],
-				[],
+				['results' => [], 'exact' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]], 'exactIdMatch' => false],
 				true,
 			],
 			[
 				'test@remote',
 				[],
 				false,
-				[
-					['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']],
-				],
-				[],
+				['results' => [], 'exact' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]], 'exactIdMatch' => false],
 				true,
 			],
 			[
@@ -814,10 +830,7 @@ class ShareesAPIControllerTest extends TestCase {
 					],
 				],
 				true,
-				[],
-				[
-					['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']],
-				],
+				['results' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => [], 'exactIdMatch' => false],
 				true,
 			],
 			[
@@ -839,8 +852,7 @@ class ShareesAPIControllerTest extends TestCase {
 					],
 				],
 				false,
-				[],
-				[],
+				['results' => [], 'exact' => [], 'exactIdMatch' => false],
 				true,
 			],
 			[
@@ -862,12 +874,7 @@ class ShareesAPIControllerTest extends TestCase {
 					],
 				],
 				true,
-				[
-					['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']],
-				],
-				[
-					['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']],
-				],
+				['results' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]], 'exactIdMatch' => false],
 				true,
 			],
 			[
@@ -889,10 +896,7 @@ class ShareesAPIControllerTest extends TestCase {
 					],
 				],
 				false,
-				[
-					['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']],
-				],
-				[],
+				['results' => [], 'exact' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]], 'exactIdMatch' => false],
 				true,
 			],
 			[
@@ -914,10 +918,7 @@ class ShareesAPIControllerTest extends TestCase {
 					],
 				],
 				true,
-				[
-					['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']],
-				],
-				[],
+				['results' => [], 'exact' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exactIdMatch' => true],
 				true,
 			],
 			[
@@ -939,10 +940,7 @@ class ShareesAPIControllerTest extends TestCase {
 					],
 				],
 				false,
-				[
-					['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']],
-				],
-				[],
+				['results' => [], 'exact' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exactIdMatch' => true],
 				true,
 			],
 			// contact with space
@@ -965,10 +963,7 @@ class ShareesAPIControllerTest extends TestCase {
 					],
 				],
 				false,
-				[
-					['label' => 'User Name @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user name@localhost', 'server' => 'localhost']],
-				],
-				[],
+				['results' => [], 'exact' => [['label' => 'User Name @ Localhost (user name@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user name@localhost', 'server' => 'localhost']]], 'exactIdMatch' => true],
 				true,
 			],
 			// remote with space, no contact
@@ -991,62 +986,57 @@ class ShareesAPIControllerTest extends TestCase {
 					],
 				],
 				false,
-				[
-					['label' => 'user space@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user space@remote']],
-				],
-				[],
+				['results' => [], 'exact' => [['label' => 'user space@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user space@remote']]], 'exactIdMatch' => false],
 				true,
 			],
 		];
 	}
 
 	/**
-	 * @dataProvider dataGetRemote
+	 * @dataProvider dataGetEmail
 	 *
 	 * @param string $searchTerm
 	 * @param array $contacts
 	 * @param bool $shareeEnumeration
-	 * @param array $exactExpected
 	 * @param array $expected
 	 * @param bool $reachedEnd
 	 */
-	public function testGetRemote($searchTerm, $contacts, $shareeEnumeration, $exactExpected, $expected, $reachedEnd) {
+	public function testGetEmail($searchTerm, $contacts, $shareeEnumeration, $expected, $reachedEnd) {
 		$this->invokePrivate($this->sharees, 'shareeEnumeration', [$shareeEnumeration]);
 		$this->contactsManager->expects($this->any())
 			->method('search')
-			->with($searchTerm, ['CLOUD', 'FN'])
+			->with($searchTerm, ['EMAIL', 'FN'])
 			->willReturn($contacts);
 
-		$this->invokePrivate($this->sharees, 'getRemote', [$searchTerm]);
-		$result = $this->invokePrivate($this->sharees, 'result');
+		$result = $this->invokePrivate($this->sharees, 'getEmail', [$searchTerm]);
 
-		$this->assertEquals($exactExpected, $result['exact']['remotes']);
-		$this->assertEquals($expected, $result['remotes']);
+		$this->assertEquals($expected, $result);
 		$this->assertCount((int) $reachedEnd, $this->invokePrivate($this->sharees, 'reachedEndFor'));
 	}
 
-	public function dataGetEmails() {
+	public function dataGetEmail() {
 		return [
-			['test', [], true, [], [], true],
-			['test', [], false, [], [], true],
+			['test', [], true, ['results' => [], 'exact' => [], 'exactIdMatch' => false], true],
+			['test', [], false, ['results' => [], 'exact' => [], 'exactIdMatch' => false], true],
 			[
 				'test@remote.com',
 				[],
 				true,
-				[
-					['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']],
-				],
+				['results' => [], 'exact' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]], 'exactIdMatch' => false],
+				true,
+			],
+			[ // no valid email address
+				'test@remote',
 				[],
 				true,
+				['results' => [], 'exact' => [], 'exactIdMatch' => false],
+				true,
 			],
 			[
 				'test@remote.com',
 				[],
 				false,
-				[
-					['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']],
-				],
-				[],
+				['results' => [], 'exact' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]], 'exactIdMatch' => false],
 				true,
 			],
 			[
@@ -1063,15 +1053,12 @@ class ShareesAPIControllerTest extends TestCase {
 					[
 						'FN' => 'User @ Localhost',
 						'EMAIL' => [
-							'username@localhost.com',
+							'username@localhost',
 						],
 					],
 				],
 				true,
-				[],
-				[
-					['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost.com']],
-				],
+				['results' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exact' => [], 'exactIdMatch' => false],
 				true,
 			],
 			[
@@ -1088,13 +1075,12 @@ class ShareesAPIControllerTest extends TestCase {
 					[
 						'FN' => 'User @ Localhost',
 						'EMAIL' => [
-							'username@localhost.com',
+							'username@localhost',
 						],
 					],
 				],
 				false,
-				[],
-				[],
+				['results' => [], 'exact' => [], 'exactIdMatch' => false],
 				true,
 			],
 			[
@@ -1111,17 +1097,12 @@ class ShareesAPIControllerTest extends TestCase {
 					[
 						'FN' => 'User @ Localhost',
 						'EMAIL' => [
-							'username@localhost.com',
+							'username@localhost',
 						],
 					],
 				],
 				true,
-				[
-					['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']],
-				],
-				[
-					['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost.com']],
-				],
+				['results' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exact' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]], 'exactIdMatch' => false],
 				true,
 			],
 			[
@@ -1138,19 +1119,16 @@ class ShareesAPIControllerTest extends TestCase {
 					[
 						'FN' => 'User @ Localhost',
 						'EMAIL' => [
-							'username@localhost.com',
+							'username@localhost',
 						],
 					],
 				],
 				false,
-				[
-					['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']],
-				],
-				[],
+				['results' => [], 'exact' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]], 'exactIdMatch' => false],
 				true,
 			],
 			[
-				'username@localhost.com',
+				'username@localhost',
 				[
 					[
 						'FN' => 'User3 @ Localhost',
@@ -1163,19 +1141,16 @@ class ShareesAPIControllerTest extends TestCase {
 					[
 						'FN' => 'User @ Localhost',
 						'EMAIL' => [
-							'username@localhost.com',
+							'username@localhost',
 						],
 					],
 				],
 				true,
-				[
-					['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost.com']],
-				],
-				[],
+				['results' => [], 'exact' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exactIdMatch' => true],
 				true,
 			],
 			[
-				'username@localhost.com',
+				'username@localhost',
 				[
 					[
 						'FN' => 'User3 @ Localhost',
@@ -1188,20 +1163,40 @@ class ShareesAPIControllerTest extends TestCase {
 					[
 						'FN' => 'User @ Localhost',
 						'EMAIL' => [
-							'username@localhost.com',
+							'username@localhost',
 						],
 					],
 				],
 				false,
+				['results' => [], 'exact' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exactIdMatch' => true],
+				true,
+			],
+			// contact with space
+			[
+				'user name@localhost',
 				[
-					['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost.com']],
+					[
+						'FN' => 'User3 @ Localhost',
+					],
+					[
+						'FN' => 'User2 @ Localhost',
+						'EMAIL' => [
+						],
+					],
+					[
+						'FN' => 'User Name @ Localhost',
+						'EMAIL' => [
+							'user name@localhost',
+						],
+					],
 				],
-				[],
+				false,
+				['results' => [], 'exact' => [['label' => 'User Name @ Localhost (user name@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'user name@localhost']]], 'exactIdMatch' => true],
 				true,
 			],
-			// Test single email
+			// remote with space, no contact
 			[
-				'username@localhost.com',
+				'user space@remote.com',
 				[
 					[
 						'FN' => 'User3 @ Localhost',
@@ -1213,137 +1208,106 @@ class ShareesAPIControllerTest extends TestCase {
 					],
 					[
 						'FN' => 'User @ Localhost',
-						'EMAIL' => 'username@localhost.com',
+						'EMAIL' => [
+							'username@localhost',
+						],
 					],
 				],
 				false,
-				[
-					['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost.com']],
-				],
-				[],
+				['results' => [], 'exact' => [], 'exactIdMatch' => false],
 				true,
 			],
 		];
 	}
 
-	/**
-	 * @dataProvider dataGetEmails
-	 *
-	 * @param string $searchTerm
-	 * @param array $contacts
-	 * @param bool $shareeEnumeration
-	 * @param array $exactExpected
-	 * @param array $expected
-	 * @param bool $reachedEnd
-	 */
-	public function testGetEmails($searchTerm, $contacts, $shareeEnumeration, $exactExpected, $expected, $reachedEnd) {
-		$this->invokePrivate($this->sharees, 'shareeEnumeration', [$shareeEnumeration]);
-		$this->contactsManager->expects($this->any())
-			->method('search')
-			->with($searchTerm, ['FN', 'EMAIL'])
-			->willReturn($contacts);
-
-		$this->invokePrivate($this->sharees, 'getEmails', [$searchTerm]);
-		$result = $this->invokePrivate($this->sharees, 'result');
-
-		$this->assertEquals($exactExpected, $result['exact']['emails']);
-		$this->assertEquals($expected, $result['emails']);
-		$this->assertCount((int) $reachedEnd, $this->invokePrivate($this->sharees, 'reachedEndFor'));
-	}
-
 	public function dataSearch() {
-		$allTypes = [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_EMAIL, Share::SHARE_TYPE_REMOTE];
+		$allTypes = [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE, Share::SHARE_TYPE_EMAIL];
 
 		return [
-			[[], '', 'yes', true, '', null, $allTypes, 1, 200, false, true, true],
+			[[], '', 'yes', true, true, $allTypes, false, true, true],
 
 			// Test itemType
 			[[
 				'search' => '',
-			], '', 'yes', true, '', null, $allTypes, 1, 200, false, true, true],
+			], '', 'yes', true, true, $allTypes, false, true, true],
 			[[
 				'search' => 'foobar',
-			], '', 'yes', true, 'foobar', null, $allTypes, 1, 200, false, true, true],
+			], '', 'yes', true, true, $allTypes, false, true, true],
 			[[
 				'search' => 0,
-			], '', 'yes', true, '0', null, $allTypes, 1, 200, false, true, true],
+			], '', 'yes', true, true, $allTypes, false, true, true],
 
 			// Test itemType
 			[[
 				'itemType' => '',
-			], '', 'yes', true, '', '', $allTypes, 1, 200, false, true, true],
+			], '', 'yes', true, true, $allTypes, false, true, true],
 			[[
 				'itemType' => 'folder',
-			], '', 'yes', true, '', 'folder', $allTypes, 1, 200, false, true, true],
+			], '', 'yes', true, true, $allTypes, false, true, true],
 			[[
 				'itemType' => 0,
-			], '', 'yes', true, '', '0', $allTypes, 1, 200, false, true, true],
+			], '', 'yes', true, true, $allTypes, false, true, true],
 
 			// Test shareType
 			[[
-			], '', 'yes', true, '', null, $allTypes, 1, 200, false, true, true],
+			], '', 'yes', true, true, $allTypes, false, true, true],
 			[[
 				'shareType' => 0,
-			], '', 'yes', true, '', null, [0], 1, 200, false, true, true],
+			], '', 'yes', true, false, [0], false, true, true],
 			[[
 				'shareType' => '0',
-			], '', 'yes', true, '', null, [0], 1, 200, false, true, true],
+			], '', 'yes', true, false, [0], false, true, true],
 			[[
 				'shareType' => 1,
-			], '', 'yes', true, '', null, [1], 1, 200, false, true, true],
+			], '', 'yes', true, false, [1], false, true, true],
 			[[
 				'shareType' => 12,
-			], '', 'yes', true, '', null, [], 1, 200, false, true, true],
+			], '', 'yes', true, false, [], false, true, true],
 			[[
 				'shareType' => 'foobar',
-			], '', 'yes', true, '', null, $allTypes, 1, 200, false, true, true],
+			], '', 'yes', true, true, $allTypes, false, true, true],
 			[[
 				'shareType' => [0, 1, 2],
-			], '', 'yes', true, '', null, [0, 1], 1, 200, false, true, true],
+			], '', 'yes', false, false, [0, 1], false, true, true],
 			[[
 				'shareType' => [0, 1],
-			], '', 'yes', true, '', null, [0, 1], 1, 200, false, true, true],
+			], '', 'yes', false, false, [0, 1], false, true, true],
 			[[
 				'shareType' => $allTypes,
-			], '', 'yes', true, '', null, $allTypes, 1, 200, false, true, true],
+			], '', 'yes', true, true, $allTypes, false, true, true],
 			[[
 				'shareType' => $allTypes,
-			], '', 'yes', false, '', null, [0, 1, 4], 1, 200, false, true, true],
+			], '', 'yes', false, false, [0, 1], false, true, true],
 			[[
 				'shareType' => $allTypes,
-			], '', 'yes', true, '', null, [0, 4, 6], 1, 200, false, true, false],
+			], '', 'yes', true, false, [0, 6], false, true, false],
 			[[
 				'shareType' => $allTypes,
-			], '', 'yes', false, '', null, [0, 4], 1, 200, false, true, false],
+			], '', 'yes', false, true, [0, 4], false, true, false],
 
 			// Test pagination
 			[[
 				'page' => 1,
-			], '', 'yes', true, '', null, $allTypes, 1, 200, false, true, true],
+			], '', 'yes', true, true, $allTypes, false, true, true],
 			[[
 				'page' => 10,
-			], '', 'yes', true, '', null, $allTypes, 10, 200, false, true, true],
+			], '', 'yes', true, true, $allTypes, false, true, true],
 
 			// Test perPage
 			[[
 				'perPage' => 1,
-			], '', 'yes', true, '', null, $allTypes, 1, 1, false, true, true],
+			], '', 'yes', true, true, $allTypes, false, true, true],
 			[[
 				'perPage' => 10,
-			], '', 'yes', true, '', null, $allTypes, 1, 10, false, true, true],
+			], '', 'yes', true, true, $allTypes, false, true, true],
 
 			// Test $shareWithGroupOnly setting
-			[[], 'no', 'yes',  true, '', null, $allTypes, 1, 200, false, true, true],
-			[[], 'yes', 'yes', true, '', null, $allTypes, 1, 200, true, true, true],
+			[[], 'no', 'yes',  true, true, $allTypes, false, true, true],
+			[[], 'yes', 'yes', true, true, $allTypes, true, true, true],
 
 			// Test $shareeEnumeration setting
-			[[], 'no', 'yes',  true, '', null, $allTypes, 1, 200, false, true, true],
-			[[], 'no', 'no', true, '', null, $allTypes, 1, 200, false, false, true],
-
-			// Test keep case for search
-			[[
-				'search' => 'foo@example.com/ownCloud',
-			], '', 'yes', true, 'foo@example.com/ownCloud', null, $allTypes, 1, 200, false, true, true],
+			[[], 'no', 'yes',  true, true, $allTypes, false, true, true],
+			[[], 'no', 'no', true, true, $allTypes, false, false, true],
 		];
 	}
 
@@ -1354,16 +1318,12 @@ class ShareesAPIControllerTest extends TestCase {
 	 * @param string $apiSetting
 	 * @param string $enumSetting
 	 * @param bool $remoteSharingEnabled
-	 * @param string $search
-	 * @param string $itemType
 	 * @param array $shareTypes
-	 * @param int $page
-	 * @param int $perPage
 	 * @param bool $shareWithGroupOnly
 	 * @param bool $shareeEnumeration
 	 * @param bool $allowGroupSharing
 	 */
-	public function testSearch($getData, $apiSetting, $enumSetting, $remoteSharingEnabled, $search, $itemType, $shareTypes, $page, $perPage, $shareWithGroupOnly, $shareeEnumeration, $allowGroupSharing) {
+	public function testSearch($getData, $apiSetting, $enumSetting, $remoteSharingEnabled, $emailSharingEnabled, $shareTypes, $shareWithGroupOnly, $shareeEnumeration, $allowGroupSharing) {
 		$search = isset($getData['search']) ? $getData['search'] : '';
 		$itemType = isset($getData['itemType']) ? $getData['itemType'] : null;
 		$page = isset($getData['page']) ? $getData['page'] : 1;
@@ -1399,11 +1359,10 @@ class ShareesAPIControllerTest extends TestCase {
 				$this->getMockBuilder('OCP\ILogger')->disableOriginalConstructor()->getMock(),
 				$this->shareManager
 			])
-			->setMethods(array('searchSharees', 'isRemoteSharingAllowed'))
+			->setMethods(array('searchSharees', 'isRemoteSharingAllowed', 'shareProviderExists'))
 			->getMock();
 		$sharees->expects($this->once())
 			->method('searchSharees')
-			->with($search, $itemType, $shareTypes, $page, $perPage)
 			->willReturnCallback(function
 					($isearch, $iitemType, $ishareTypes, $ipage, $iperPage)
 				use ($search, $itemType, $shareTypes, $page, $perPage) {
@@ -1411,7 +1370,10 @@ class ShareesAPIControllerTest extends TestCase {
 				// We are doing strict comparisons here, so we can differ 0/'' and null on shareType/itemType
 				$this->assertSame($search, $isearch);
 				$this->assertSame($itemType, $iitemType);
-				$this->assertSame($shareTypes, $ishareTypes);
+				$this->assertSame(count($shareTypes), count($ishareTypes));
+				foreach($shareTypes as $expected) {
+					$this->assertTrue(in_array($expected, $ishareTypes));
+				}
 				$this->assertSame($page, $ipage);
 				$this->assertSame($perPage, $iperPage);
 				return new Http\DataResponse();
@@ -1421,6 +1383,11 @@ class ShareesAPIControllerTest extends TestCase {
 			->with($itemType)
 			->willReturn($remoteSharingEnabled);
 
+		$this->shareManager->expects($this->any())
+			->method('shareProviderExists')
+			->with(\OCP\Share::SHARE_TYPE_EMAIL)
+			->willReturn($emailSharingEnabled);
+
 		$this->assertInstanceOf(Http\DataResponse::class, $sharees->search($search, $itemType, $page, $perPage, $shareType));
 
 		$this->assertSame($shareWithGroupOnly, $this->invokePrivate($sharees, 'shareWithGroupOnly'));
@@ -1519,7 +1486,7 @@ class ShareesAPIControllerTest extends TestCase {
 
 	public function dataSearchSharees() {
 		return [
-			['test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, false, [], [], [],
+			['test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, false, [], [], ['results' => [], 'exact' => [], 'exactIdMatch' => false],
 				[
 					'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'emails' => []],
 					'users' => [],
@@ -1527,7 +1494,7 @@ class ShareesAPIControllerTest extends TestCase {
 					'remotes' => [],
 					'emails' => [],
 				], false],
-			['test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, false, [], [], [],
+			['test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, false, [], [], ['results' => [], 'exact' => [], 'exactIdMatch' => false],
 				[
 					'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'emails' => []],
 					'users' => [],
@@ -1541,7 +1508,7 @@ class ShareesAPIControllerTest extends TestCase {
 				], [
 					['label' => 'testgroup1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'testgroup1']],
 				], [
-					['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']],
+					'results' => [['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']]], 'exact' => [], 'exactIdMatch' => false,
 				],
 				[
 					'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'emails' => []],
@@ -1562,7 +1529,7 @@ class ShareesAPIControllerTest extends TestCase {
 				'test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_REMOTE], 1, 2, false, [
 					['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']],
 				], null, [
-					['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']],
+					'results' => [['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']]], 'exact' => [], 'exactIdMatch' => false
 				],
 				[
 					'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'emails' => []],
@@ -1660,14 +1627,11 @@ class ShareesAPIControllerTest extends TestCase {
 				$result['groups'] = $mockedGroupsResult;
 				$this->invokePrivate($sharees, 'result', [$result]);
 			});
+
 		$sharees->expects(($mockedRemotesResult === null) ? $this->never() : $this->once())
 			->method('getRemote')
 			->with($searchTerm)
-			->willReturnCallback(function() use ($sharees, $mockedRemotesResult) {
-				$result = $this->invokePrivate($sharees, 'result');
-				$result['remotes'] = $mockedRemotesResult;
-				$this->invokePrivate($sharees, 'result', [$result]);
-			});
+			->willReturn($mockedRemotesResult);
 
 		$ocs = $this->invokePrivate($sharees, 'searchSharees', [$searchTerm, $itemType, $shareTypes, $page, $perPage, $shareWithGroupOnly]);
 		$this->assertInstanceOf('\OCP\AppFramework\Http\DataResponse', $ocs);
diff --git a/apps/sharebymail/appinfo/app.php b/apps/sharebymail/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..5ef7b6f18cb2827284c5663c1d00ce2de766da43
--- /dev/null
+++ b/apps/sharebymail/appinfo/app.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+$settings = new \OCA\ShareByMail\Settings();
+
+\OCP\Util::connectHook('\OCP\Config', 'js', $settings, 'announceShareProvider');
diff --git a/apps/sharebymail/appinfo/info.xml b/apps/sharebymail/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f1771fc9551f807987a4db809b7a8984f43bc4c0
--- /dev/null
+++ b/apps/sharebymail/appinfo/info.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<info>
+    <id>sharebymail</id>
+    <name>Share by mail</name>
+    <description>Share provider which allows you to share files by mail</description>
+    <licence>AGPL</licence>
+    <author>Bjoern Schiessle</author>
+    <version>1.0.0</version>
+    <namespace>ShareByMail</namespace>
+    <category>other</category>
+    <dependencies>
+        <owncloud min-version="9.2" max-version="9.2" />
+    </dependencies>
+    <default_enable/>
+</info>
diff --git a/apps/sharebymail/lib/Settings.php b/apps/sharebymail/lib/Settings.php
new file mode 100644
index 0000000000000000000000000000000000000000..4ab1622425be9a57d49fb6d6dad308fe107326c9
--- /dev/null
+++ b/apps/sharebymail/lib/Settings.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\ShareByMail;
+
+
+class Settings {
+
+	/**
+	 * announce that the share-by-mail share provider is enabled
+	 *
+	 * @param array $settings
+	 */
+	public function announceShareProvider(array $settings) {
+		$array = json_decode($settings['array']['oc_appconfig'], true);
+		$array['shareByMailEnabled'] = true;
+		$settings['array']['oc_appconfig'] = json_encode($array);
+	}
+}
diff --git a/apps/sharebymail/lib/ShareByMailProvider.php b/apps/sharebymail/lib/ShareByMailProvider.php
new file mode 100644
index 0000000000000000000000000000000000000000..a332bd13d880492f83fa42a1826a08a845c8023b
--- /dev/null
+++ b/apps/sharebymail/lib/ShareByMailProvider.php
@@ -0,0 +1,709 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\ShareByMail;
+
+use OC\HintException;
+use OC\Share20\Exception\InvalidShare;
+use OCP\Files\Folder;
+use OCP\Files\IRootFolder;
+use OCP\Files\Node;
+use OCP\IDBConnection;
+use OCP\IL10N;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+use OCP\IUserManager;
+use OCP\Mail\IMailer;
+use OCP\Security\ISecureRandom;
+use OC\Share20\Share;
+use OCP\Share\Exceptions\ShareNotFound;
+use OCP\Share\IShare;
+use OCP\Share\IShareProvider;
+
+/**
+ * Class ShareByMail
+ *
+ * @package OCA\ShareByMail
+ */
+class ShareByMailProvider implements IShareProvider {
+
+	/** @var  IDBConnection */
+	private $dbConnection;
+
+	/** @var ILogger */
+	private $logger;
+
+	/** @var ISecureRandom */
+	private $secureRandom;
+
+	/** @var IUserManager */
+	private $userManager;
+
+	/** @var IRootFolder */
+	private $rootFolder;
+
+	/** @var IL10N */
+	private $l;
+
+	/** @var IMailer */
+	private $mailer;
+
+	/** @var IURLGenerator */
+	private $urlGenerator;
+
+	/**
+	 * Return the identifier of this provider.
+	 *
+	 * @return string Containing only [a-zA-Z0-9]
+	 */
+	public function identifier() {
+		return 'ocShareByMail';
+	}
+
+	/**
+	 * DefaultShareProvider constructor.
+	 *
+	 * @param IDBConnection $connection
+	 * @param ISecureRandom $secureRandom
+	 * @param IUserManager $userManager
+	 * @param IRootFolder $rootFolder
+	 * @param IL10N $l
+	 * @param ILogger $logger
+	 * @param IMailer $mailer
+	 * @param IURLGenerator $urlGenerator
+	 */
+	public function __construct(
+		IDBConnection $connection,
+		ISecureRandom $secureRandom,
+		IUserManager $userManager,
+		IRootFolder $rootFolder,
+		IL10N $l,
+		ILogger $logger,
+		IMailer $mailer,
+		IURLGenerator $urlGenerator
+	) {
+		$this->dbConnection = $connection;
+		$this->secureRandom = $secureRandom;
+		$this->userManager = $userManager;
+		$this->rootFolder = $rootFolder;
+		$this->l = $l;
+		$this->logger = $logger;
+		$this->mailer = $mailer;
+		$this->urlGenerator = $urlGenerator;
+	}
+
+	/**
+	 * Share a path
+	 *
+	 * @param IShare $share
+	 * @return IShare The share object
+	 * @throws ShareNotFound
+	 * @throws \Exception
+	 */
+	public function create(IShare $share) {
+
+		$shareWith = $share->getSharedWith();
+		/*
+		 * Check if file is not already shared with the remote user
+		 */
+		$alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0);
+		if (!empty($alreadyShared)) {
+			$message = 'Sharing %s failed, this item is already shared with %s';
+			$message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
+			$this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
+			throw new \Exception($message_t);
+		}
+
+		$shareId = $this->createMailShare($share);
+
+		$data = $this->getRawShare($shareId);
+		return $this->createShareObject($data);
+
+	}
+
+	/**
+	 * @param IShare $share
+	 * @return int
+	 * @throws \Exception
+	 */
+	protected function createMailShare(IShare $share) {
+		$share->setToken($this->generateToken());
+		$shareId = $this->addShareToDB(
+			$share->getNodeId(),
+			$share->getNodeType(),
+			$share->getSharedWith(),
+			$share->getSharedBy(),
+			$share->getShareOwner(),
+			$share->getPermissions(),
+			$share->getToken()
+		);
+
+		try {
+			$link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare',
+				['token' => $share->getToken()]);
+			$this->sendMailNotification($share->getNode()->getName(),
+				$link,
+				$share->getShareOwner(),
+				$share->getSharedBy(), $share->getSharedWith());
+		} catch (HintException $hintException) {
+			$this->logger->error('Failed to send share by mail: ' . $hintException->getMessage());
+			$this->removeShareFromTable($shareId);
+			throw $hintException;
+		} catch (\Exception $e) {
+			$this->logger->error('Failed to send share by mail: ' . $e->getMessage());
+			$this->removeShareFromTable($shareId);
+			throw new HintException('Failed to send share by mail',
+				$this->l->t('Failed to send share by E-mail'));
+		}
+
+		return $shareId;
+
+	}
+
+	protected function sendMailNotification($filename, $link, $owner, $initiator, $shareWith) {
+		if ($owner === $initiator) {
+			$subject = (string)$this->l->t('%s shared »%s« with you', array($owner, $filename));
+		} else {
+			$subject = (string)$this->l->t('%s shared »%s« with you on behalf of %s', array($owner, $filename, $initiator));
+		}
+
+		$message = $this->mailer->createMessage();
+		$htmlBody = $this->createMailBody('mail', $filename, $link, $owner, $initiator);
+		$textBody = $this->createMailBody('altmail', $filename, $link, $owner, $initiator);
+		$message->setTo([$shareWith]);
+		$message->setSubject($subject);
+		$message->setBody($textBody, 'text/plain');
+		$message->setHtmlBody($htmlBody);
+		$this->mailer->send($message);
+
+	}
+
+	/**
+	 * create mail body
+	 *
+	 * @param $filename
+	 * @param $link
+	 * @param $owner
+	 * @param $initiator
+	 * @return string plain text mail
+	 * @throws HintException
+	 */
+	protected function createMailBody($template, $filename, $link, $owner, $initiator) {
+
+		$mailBodyTemplate = new \OC_Template('sharebymail', $template, '');
+		$mailBodyTemplate->assign ('filename', $filename);
+		$mailBodyTemplate->assign ('link', $link);
+		$mailBodyTemplate->assign ('owner', $owner);
+		$mailBodyTemplate->assign ('initiator', $initiator);
+		$mailBodyTemplate->assign ('onBehalfOf', $initiator !== $owner);
+		$mailBody = $mailBodyTemplate->fetchPage();
+
+		if (is_string($mailBody)) {
+			return $mailBody;
+		}
+
+		throw new HintException('Failed to create the E-mail',
+			$this->l->t('Failed to create the E-mail'));
+	}
+
+	/**
+	 * generate share token
+	 *
+	 * @return string
+	 */
+	protected function generateToken() {
+		$token = $this->secureRandom->generate(
+			15, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS);
+		return $token;
+	}
+
+	/**
+	 * Get all children of this share
+	 *
+	 * @param IShare $parent
+	 * @return IShare[]
+	 */
+	public function getChildren(IShare $parent) {
+		$children = [];
+
+		$qb = $this->dbConnection->getQueryBuilder();
+		$qb->select('*')
+			->from('share')
+			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
+			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
+			->orderBy('id');
+
+		$cursor = $qb->execute();
+		while($data = $cursor->fetch()) {
+			$children[] = $this->createShareObject($data);
+		}
+		$cursor->closeCursor();
+
+		return $children;
+	}
+
+	/**
+	 * add share to the database and return the ID
+	 *
+	 * @param int $itemSource
+	 * @param string $itemType
+	 * @param string $shareWith
+	 * @param string $sharedBy
+	 * @param string $uidOwner
+	 * @param int $permissions
+	 * @param string $token
+	 * @return int
+	 */
+	protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) {
+		$qb = $this->dbConnection->getQueryBuilder();
+		$qb->insert('share')
+			->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
+			->setValue('item_type', $qb->createNamedParameter($itemType))
+			->setValue('item_source', $qb->createNamedParameter($itemSource))
+			->setValue('file_source', $qb->createNamedParameter($itemSource))
+			->setValue('share_with', $qb->createNamedParameter($shareWith))
+			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
+			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
+			->setValue('permissions', $qb->createNamedParameter($permissions))
+			->setValue('token', $qb->createNamedParameter($token))
+			->setValue('stime', $qb->createNamedParameter(time()));
+
+		/*
+		 * Added to fix https://github.com/owncloud/core/issues/22215
+		 * Can be removed once we get rid of ajax/share.php
+		 */
+		$qb->setValue('file_target', $qb->createNamedParameter(''));
+
+		$qb->execute();
+		$id = $qb->getLastInsertId();
+
+		return (int)$id;
+	}
+
+	/**
+	 * Update a share
+	 *
+	 * @param IShare $share
+	 * @return IShare The share object
+	 */
+	public function update(IShare $share) {
+		/*
+		 * We allow updating the permissions of mail shares
+		 */
+		$qb = $this->dbConnection->getQueryBuilder();
+			$qb->update('share')
+				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
+				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
+				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
+				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
+				->execute();
+
+		return $share;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function move(IShare $share, $recipient) {
+		/**
+		 * nothing to do here, mail shares are only outgoing shares
+		 */
+		return $share;
+	}
+
+	/**
+	 * Delete a share (owner unShares the file)
+	 *
+	 * @param IShare $share
+	 */
+	public function delete(IShare $share) {
+		$this->removeShareFromTable($share->getId());
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function deleteFromSelf(IShare $share, $recipient) {
+		// nothing to do here, mail shares are only outgoing shares
+		return;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
+		$qb = $this->dbConnection->getQueryBuilder();
+		$qb->select('*')
+			->from('share');
+
+		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
+
+		/**
+		 * Reshares for this user are shares where they are the owner.
+		 */
+		if ($reshares === false) {
+			//Special case for old shares created via the web UI
+			$or1 = $qb->expr()->andX(
+				$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
+				$qb->expr()->isNull('uid_initiator')
+			);
+
+			$qb->andWhere(
+				$qb->expr()->orX(
+					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
+					$or1
+				)
+			);
+		} else {
+			$qb->andWhere(
+				$qb->expr()->orX(
+					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
+					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
+				)
+			);
+		}
+
+		if ($node !== null) {
+			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
+		}
+
+		if ($limit !== -1) {
+			$qb->setMaxResults($limit);
+		}
+
+		$qb->setFirstResult($offset);
+		$qb->orderBy('id');
+
+		$cursor = $qb->execute();
+		$shares = [];
+		while($data = $cursor->fetch()) {
+			$shares[] = $this->createShareObject($data);
+		}
+		$cursor->closeCursor();
+
+		return $shares;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function getShareById($id, $recipientId = null) {
+		$qb = $this->dbConnection->getQueryBuilder();
+
+		$qb->select('*')
+			->from('share')
+			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
+			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
+
+		$cursor = $qb->execute();
+		$data = $cursor->fetch();
+		$cursor->closeCursor();
+
+		if ($data === false) {
+			throw new ShareNotFound();
+		}
+
+		try {
+			$share = $this->createShareObject($data);
+		} catch (InvalidShare $e) {
+			throw new ShareNotFound();
+		}
+
+		return $share;
+	}
+
+	/**
+	 * Get shares for a given path
+	 *
+	 * @param \OCP\Files\Node $path
+	 * @return IShare[]
+	 */
+	public function getSharesByPath(Node $path) {
+		$qb = $this->dbConnection->getQueryBuilder();
+
+		$cursor = $qb->select('*')
+			->from('share')
+			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
+			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
+			->execute();
+
+		$shares = [];
+		while($data = $cursor->fetch()) {
+			$shares[] = $this->createShareObject($data);
+		}
+		$cursor->closeCursor();
+
+		return $shares;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
+		/** @var IShare[] $shares */
+		$shares = [];
+
+		//Get shares directly with this user
+		$qb = $this->dbConnection->getQueryBuilder();
+		$qb->select('*')
+			->from('share');
+
+		// Order by id
+		$qb->orderBy('id');
+
+		// Set limit and offset
+		if ($limit !== -1) {
+			$qb->setMaxResults($limit);
+		}
+		$qb->setFirstResult($offset);
+
+		$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
+		$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
+
+		// Filter by node if provided
+		if ($node !== null) {
+			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
+		}
+
+		$cursor = $qb->execute();
+
+		while($data = $cursor->fetch()) {
+			$shares[] = $this->createShareObject($data);
+		}
+		$cursor->closeCursor();
+
+
+		return $shares;
+	}
+
+	/**
+	 * Get a share by token
+	 *
+	 * @param string $token
+	 * @return IShare
+	 * @throws ShareNotFound
+	 */
+	public function getShareByToken($token) {
+		$qb = $this->dbConnection->getQueryBuilder();
+
+		$cursor = $qb->select('*')
+			->from('share')
+			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
+			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
+			->execute();
+
+		$data = $cursor->fetch();
+
+		if ($data === false) {
+			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
+		}
+
+		try {
+			$share = $this->createShareObject($data);
+		} catch (InvalidShare $e) {
+			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
+		}
+
+		return $share;
+	}
+
+	/**
+	 * remove share from table
+	 *
+	 * @param string $shareId
+	 */
+	protected function removeShareFromTable($shareId) {
+		$qb = $this->dbConnection->getQueryBuilder();
+		$qb->delete('share')
+			->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
+		$qb->execute();
+	}
+
+	/**
+	 * Create a share object from an database row
+	 *
+	 * @param array $data
+	 * @return IShare
+	 * @throws InvalidShare
+	 * @throws ShareNotFound
+	 */
+	protected function createShareObject($data) {
+
+		$share = new Share($this->rootFolder, $this->userManager);
+		$share->setId((int)$data['id'])
+			->setShareType((int)$data['share_type'])
+			->setPermissions((int)$data['permissions'])
+			->setTarget($data['file_target'])
+			->setMailSend((bool)$data['mail_send'])
+			->setToken($data['token']);
+
+		$shareTime = new \DateTime();
+		$shareTime->setTimestamp((int)$data['stime']);
+		$share->setShareTime($shareTime);
+		$share->setSharedWith($data['share_with']);
+
+		if ($data['uid_initiator'] !== null) {
+			$share->setShareOwner($data['uid_owner']);
+			$share->setSharedBy($data['uid_initiator']);
+		} else {
+			//OLD SHARE
+			$share->setSharedBy($data['uid_owner']);
+			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
+
+			$owner = $path->getOwner();
+			$share->setShareOwner($owner->getUID());
+		}
+
+		$share->setNodeId((int)$data['file_source']);
+		$share->setNodeType($data['item_type']);
+
+		$share->setProviderId($this->identifier());
+
+		return $share;
+	}
+
+	/**
+	 * Get the node with file $id for $user
+	 *
+	 * @param string $userId
+	 * @param int $id
+	 * @return \OCP\Files\File|\OCP\Files\Folder
+	 * @throws InvalidShare
+	 */
+	private function getNode($userId, $id) {
+		try {
+			$userFolder = $this->rootFolder->getUserFolder($userId);
+		} catch (NotFoundException $e) {
+			throw new InvalidShare();
+		}
+
+		$nodes = $userFolder->getById($id);
+
+		if (empty($nodes)) {
+			throw new InvalidShare();
+		}
+
+		return $nodes[0];
+	}
+
+	/**
+	 * A user is deleted from the system
+	 * So clean up the relevant shares.
+	 *
+	 * @param string $uid
+	 * @param int $shareType
+	 */
+	public function userDeleted($uid, $shareType) {
+		$qb = $this->dbConnection->getQueryBuilder();
+
+		$qb->delete('share')
+			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
+			->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
+			->execute();
+	}
+
+	/**
+	 * This provider does not support group shares
+	 *
+	 * @param string $gid
+	 */
+	public function groupDeleted($gid) {
+		return;
+	}
+
+	/**
+	 * This provider does not support group shares
+	 *
+	 * @param string $uid
+	 * @param string $gid
+	 */
+	public function userDeletedFromGroup($uid, $gid) {
+		return;
+	}
+
+	/**
+	 * get database row of a give share
+	 *
+	 * @param $id
+	 * @return array
+	 * @throws ShareNotFound
+	 */
+	protected function getRawShare($id) {
+
+		// Now fetch the inserted share and create a complete share object
+		$qb = $this->dbConnection->getQueryBuilder();
+		$qb->select('*')
+			->from('share')
+			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
+
+		$cursor = $qb->execute();
+		$data = $cursor->fetch();
+		$cursor->closeCursor();
+
+		if ($data === false) {
+			throw new ShareNotFound;
+		}
+
+		return $data;
+	}
+
+	public function getSharesInFolder($userId, Folder $node, $reshares) {
+		$qb = $this->dbConnection->getQueryBuilder();
+		$qb->select('*')
+			->from('share', 's')
+			->andWhere($qb->expr()->orX(
+				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
+				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
+			))
+			->andWhere(
+				$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
+			);
+
+		/**
+		 * Reshares for this user are shares where they are the owner.
+		 */
+		if ($reshares === false) {
+			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
+		} else {
+			$qb->andWhere(
+				$qb->expr()->orX(
+					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
+					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
+				)
+			);
+		}
+
+		$qb->innerJoin('s', 'filecache' ,'f', 's.file_source = f.fileid');
+		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
+
+		$qb->orderBy('id');
+
+		$cursor = $qb->execute();
+		$shares = [];
+		while ($data = $cursor->fetch()) {
+			$shares[$data['fileid']][] = $this->createShareObject($data);
+		}
+		$cursor->closeCursor();
+
+		return $shares;
+	}
+
+}
diff --git a/apps/sharebymail/templates/altmail.php b/apps/sharebymail/templates/altmail.php
new file mode 100644
index 0000000000000000000000000000000000000000..7b9de6295ff9a0ef852951a0458c43e644a0ad7a
--- /dev/null
+++ b/apps/sharebymail/templates/altmail.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/** @var OC_Theme $theme */
+/** @var array $_ */
+if ($_['onBehalfOf']) {
+	print_unescaped($l->t("Hey there,\n\n%s shared »%s« with you on behalf of %s.\n\n%s\n\n", [$_['owner'], $_['filename'], $_['initiator'], $_['link']]));
+} else {
+	print_unescaped($l->t("Hey there,\n\n%s shared »%s« with you.\n\n%s\n\n", [$_['owner'], $_['filename'], $_['link']]));
+}
+// TRANSLATORS term at the end of a mail
+p($l->t("Cheers!"));
+print_unescaped("\n");
+?>
+
+--
+<?php p($theme->getName() . ' - ' . $theme->getSlogan()); ?>
+<?php print_unescaped("\n".$theme->getBaseUrl());
diff --git a/apps/sharebymail/templates/mail.php b/apps/sharebymail/templates/mail.php
new file mode 100644
index 0000000000000000000000000000000000000000..48f7dcb063a1c9a0bec26e6dcfd0572c83b5f2fc
--- /dev/null
+++ b/apps/sharebymail/templates/mail.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/** @var OC_Theme $theme */
+/** @var array $_ */
+?>
+
+<table cellspacing="0" cellpadding="0" border="0" width="100%">
+	<tr><td>
+			<table cellspacing="0" cellpadding="0" border="0" width="600px">
+				<tr>
+					<td bgcolor="<?php p($theme->getMailHeaderColor());?>" width="20px">&nbsp;</td>
+					<td bgcolor="<?php p($theme->getMailHeaderColor());?>">
+						<img src="<?php p(\OC::$server->getURLGenerator()->getAbsoluteURL(image_path('', 'logo-mail.gif'))); ?>" alt="<?php p($theme->getName()); ?>"/>
+					</td>
+				</tr>
+				<tr><td colspan="2">&nbsp;</td></tr>
+				<tr>
+					<td width="20px">&nbsp;</td>
+					<td style="font-weight:normal; font-size:0.8em; line-height:1.2em; font-family:verdana,'arial',sans;">
+						<?php
+						if ($_['onBehalfOf']) {
+							print_unescaped($l->t('Hey there,<br><br>%s shared <a href="%s">%s</a> with you on behalf of %s.<br><br>', [$_['owner'], $_['link'], $_['filename'], $_['initiator']]));
+						} else {
+							print_unescaped($l->t('Hey there,<br><br>%s shared <a href="%s">%s</a> with you.<br><br>', [$_['owner'], $_['link'], $_['filename']]));
+						}
+						// TRANSLATORS term at the end of a mail
+						p($l->t('Cheers!'));
+						?>
+					</td>
+				</tr>
+				<tr><td colspan="2">&nbsp;</td></tr>
+				<tr>
+					<td width="20px">&nbsp;</td>
+					<td style="font-weight:normal; font-size:0.8em; line-height:1.2em; font-family:verdana,'arial',sans;">--<br>
+						<?php p($theme->getName()); ?> -
+						<?php p($theme->getSlogan()); ?>
+						<br><a href="<?php p($theme->getBaseUrl()); ?>"><?php p($theme->getBaseUrl());?></a>
+					</td>
+				</tr>
+				<tr>
+					<td colspan="2">&nbsp;</td>
+				</tr>
+			</table>
+		</td></tr>
+</table>
diff --git a/apps/sharebymail/tests/SettingsTest.php b/apps/sharebymail/tests/SettingsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..f415421b0cf000fb315e16bfe75bbbbab1f511d7
--- /dev/null
+++ b/apps/sharebymail/tests/SettingsTest.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\ShareByMail\Tests;
+
+
+use OCA\ShareByMail\Settings;
+use Test\TestCase;
+
+class SettingsTest extends TestCase  {
+
+	/** @var  Settings */
+	private $instance;
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->instance = new Settings();
+	}
+
+	public function testAnnounceShareProvider() {
+		$before = [
+			'oc_appconfig' =>
+				json_encode([
+					'key1' => 'value1',
+					'key2' => 'value2'
+				]),
+			'oc_foo' => 'oc_bar'
+		];
+
+		$after = [
+				'oc_appconfig' =>
+					json_encode([
+						'key1' => 'value1',
+						'key2' => 'value2',
+						'shareByMailEnabled' => true
+					]),
+				'oc_foo' => 'oc_bar'
+		];
+
+		$this->instance->announceShareProvider(['array' => &$before]);
+		$this->assertSame($after, $before);
+	}
+
+}
diff --git a/apps/sharebymail/tests/ShareByMailProviderTest.php b/apps/sharebymail/tests/ShareByMailProviderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..eedce286a0360017f9bd34f3d2cf8f0ec5a294c4
--- /dev/null
+++ b/apps/sharebymail/tests/ShareByMailProviderTest.php
@@ -0,0 +1,655 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OCA\ShareByMail\Tests;
+
+
+use OC\HintException;
+use OCA\ShareByMail\ShareByMailProvider;
+use OCP\Files\IRootFolder;
+use OCP\IDBConnection;
+use OCP\IL10N;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+use OCP\IUserManager;
+use OCP\Mail\IMailer;
+use OCP\Security\ISecureRandom;
+use OCP\Share\Exceptions\ShareNotFound;
+use OCP\Share\IManager;
+use OCP\Share\IShare;
+use Test\TestCase;
+
+/**
+ * Class ShareByMailProviderTest
+ *
+ * @package OCA\ShareByMail\Tests
+ * @group DB
+ */
+class ShareByMailProviderTest extends TestCase {
+
+	/** @var  IDBConnection */
+	private $connection;
+
+	/** @var  IManager */
+	private $shareManager;
+
+	/** @var  IL10N | \PHPUnit_Framework_MockObject_MockObject */
+	private $l;
+
+	/** @var  ILogger | \PHPUnit_Framework_MockObject_MockObject */
+	private $logger;
+
+	/** @var  IRootFolder | \PHPUnit_Framework_MockObject_MockObject */
+	private $rootFolder;
+
+	/** @var  IUserManager | \PHPUnit_Framework_MockObject_MockObject */
+	private $userManager;
+
+	/** @var  ISecureRandom | \PHPUnit_Framework_MockObject_MockObject */
+	private $secureRandom;
+
+	/** @var  IMailer | \PHPUnit_Framework_MockObject_MockObject */
+	private $mailer;
+
+	/** @var  IURLGenerator | \PHPUnit_Framework_MockObject_MockObject */
+	private $urlGenerator;
+
+	/** @var  IShare | \PHPUnit_Framework_MockObject_MockObject */
+	private $share;
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->shareManager = \OC::$server->getShareManager();
+		$this->connection = \OC::$server->getDatabaseConnection();
+
+		$this->l = $this->getMockBuilder('OCP\IL10N')->getMock();
+		$this->l->method('t')
+			->will($this->returnCallback(function($text, $parameters = []) {
+				return vsprintf($text, $parameters);
+			}));
+		$this->logger = $this->getMockBuilder('OCP\ILogger')->getMock();
+		$this->rootFolder = $this->getMockBuilder('OCP\Files\IRootFolder')->getMock();
+		$this->userManager = $this->getMockBuilder('OCP\IUserManager')->getMock();
+		$this->secureRandom = $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock();
+		$this->mailer = $this->getMockBuilder('\OCP\Mail\IMailer')->getMock();
+		$this->urlGenerator = $this->getMockBuilder('\OCP\IUrlGenerator')->getMock();
+		$this->share = $this->getMockBuilder('\OCP\Share\IShare')->getMock();
+
+		$this->userManager->expects($this->any())->method('userExists')->willReturn(true);
+	}
+
+	/**
+	 * get instance of Mocked ShareByMailProvider
+	 *
+	 * @param array $mockedMethods internal methods which should be mocked
+	 * @return \PHPUnit_Framework_MockObject_MockObject | ShareByMailProvider
+	 */
+	private function getInstance(array $mockedMethods = []) {
+
+		$instance = $this->getMockBuilder('OCA\ShareByMail\ShareByMailProvider')
+			->setConstructorArgs(
+				[
+					$this->connection,
+					$this->secureRandom,
+					$this->userManager,
+					$this->rootFolder,
+					$this->l,
+					$this->logger,
+					$this->mailer,
+					$this->urlGenerator
+				]
+			);
+
+		if (!empty($mockedMethods)) {
+			$instance->setMethods($mockedMethods);
+			return $instance->getMock();
+		}
+
+		return new ShareByMailProvider(
+			$this->connection,
+			$this->secureRandom,
+			$this->userManager,
+			$this->rootFolder,
+			$this->l,
+			$this->logger,
+			$this->mailer,
+			$this->urlGenerator
+		);
+
+	}
+
+	public function tearDown() {
+		$this->connection->getQueryBuilder()->delete('share')->execute();
+
+		return parent::tearDown();
+	}
+
+	public function testCreate() {
+		$share = $this->getMockBuilder('\OCP\Share\IShare')->getMock();
+		$share->expects($this->once())->method('getSharedWith')->willReturn('user1');
+
+		$instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject']);
+
+		$instance->expects($this->once())->method('getSharedWith')->willReturn([]);
+		$instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42);
+		$instance->expects($this->once())->method('getRawShare')->with(42)->willReturn('rawShare');
+		$instance->expects($this->once())->method('createShareObject')->with('rawShare')->willReturn('shareObject');
+
+		$this->assertSame('shareObject',
+			$instance->create($share)
+		);
+	}
+
+	/**
+	 * @expectedException \Exception
+	 */
+	public function testCreateFailed() {
+		$this->share->expects($this->once())->method('getSharedWith')->willReturn('user1');
+		$node = $this->getMockBuilder('OCP\Files\Node')->getMock();
+		$node->expects($this->any())->method('getName')->willReturn('fileName');
+		$this->share->expects($this->any())->method('getNode')->willReturn($node);
+
+		$instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject']);
+
+		$instance->expects($this->once())->method('getSharedWith')->willReturn(['found']);
+		$instance->expects($this->never())->method('createMailShare');
+		$instance->expects($this->never())->method('getRawShare');
+		$instance->expects($this->never())->method('createShareObject');
+
+		$this->assertSame('shareObject',
+			$instance->create($this->share)
+		);
+	}
+
+	public function testCreateMailShare() {
+		$this->share->expects($this->any())->method('getToken')->willReturn('token');
+		$this->share->expects($this->once())->method('setToken')->with('token');
+		$node = $this->getMockBuilder('OCP\Files\Node')->getMock();
+		$node->expects($this->any())->method('getName')->willReturn('fileName');
+		$this->share->expects($this->any())->method('getNode')->willReturn($node);
+
+		$instance = $this->getInstance(['generateToken', 'addShareToDB', 'sendMailNotification']);
+
+		$instance->expects($this->once())->method('generateToken')->willReturn('token');
+		$instance->expects($this->once())->method('addShareToDB')->willReturn(42);
+		$instance->expects($this->once())->method('sendMailNotification');
+		$this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute')
+			->with('files_sharing.sharecontroller.showShare', ['token' => 'token']);
+		$instance->expects($this->once())->method('sendMailNotification');
+
+		$this->assertSame(42,
+			$this->invokePrivate($instance, 'createMailShare', [$this->share])
+		);
+
+	}
+
+	/**
+	 * @expectedException \OC\HintException
+	 */
+	public function testCreateMailShareFailed() {
+		$this->share->expects($this->any())->method('getToken')->willReturn('token');
+		$this->share->expects($this->once())->method('setToken')->with('token');
+		$node = $this->getMockBuilder('OCP\Files\Node')->getMock();
+		$node->expects($this->any())->method('getName')->willReturn('fileName');
+		$this->share->expects($this->any())->method('getNode')->willReturn($node);
+
+		$instance = $this->getInstance(['generateToken', 'addShareToDB', 'sendMailNotification']);
+
+		$instance->expects($this->once())->method('generateToken')->willReturn('token');
+		$instance->expects($this->once())->method('addShareToDB')->willReturn(42);
+		$instance->expects($this->once())->method('sendMailNotification');
+		$this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute')
+			->with('files_sharing.sharecontroller.showShare', ['token' => 'token']);
+		$instance->expects($this->once())->method('sendMailNotification')
+			->willReturnCallback(
+				function() {
+					throw new \Exception('should be converted to a hint exception');
+				}
+			);
+
+		$this->assertSame(42,
+			$this->invokePrivate($instance, 'createMailShare', [$this->share])
+		);
+
+	}
+
+	public function testGenerateToken() {
+		$instance = $this->getInstance();
+
+		$this->secureRandom->expects($this->once())->method('generate')->willReturn('token');
+
+		$this->assertSame('token',
+			$this->invokePrivate($instance, 'generateToken')
+		);
+	}
+
+	public function testAddShareToDB() {
+		$itemSource = 11;
+		$itemType = 'file';
+		$shareWith = 'user@server.com';
+		$sharedBy = 'user1';
+		$uidOwner = 'user2';
+		$permissions = 1;
+		$token = 'token';
+
+
+		$instance = $this->getInstance();
+		$id = $this->invokePrivate(
+			$instance,
+			'addShareToDB',
+			[
+				$itemSource,
+				$itemType,
+				$shareWith,
+				$sharedBy,
+				$uidOwner,
+				$permissions,
+				$token
+			]
+		);
+
+		$qb = $this->connection->getQueryBuilder();
+		$result = $qb->select('*')
+			->from('share')
+			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
+			->execute()->fetchAll();
+
+		$this->assertSame(1, count($result));
+
+		$this->assertSame($itemSource, (int)$result[0]['item_source']);
+		$this->assertSame($itemType, $result[0]['item_type']);
+		$this->assertSame($shareWith, $result[0]['share_with']);
+		$this->assertSame($sharedBy, $result[0]['uid_initiator']);
+		$this->assertSame($uidOwner, $result[0]['uid_owner']);
+		$this->assertSame($permissions, (int)$result[0]['permissions']);
+		$this->assertSame($token, $result[0]['token']);
+
+	}
+
+	public function testUpdate() {
+
+		$itemSource = 11;
+		$itemType = 'file';
+		$shareWith = 'user@server.com';
+		$sharedBy = 'user1';
+		$uidOwner = 'user2';
+		$permissions = 1;
+		$token = 'token';
+
+
+		$instance = $this->getInstance();
+
+		$id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
+
+		$this->share->expects($this->once())->method('getPermissions')->willReturn($permissions + 1);
+		$this->share->expects($this->once())->method('getShareOwner')->willReturn($uidOwner);
+		$this->share->expects($this->once())->method('getSharedBy')->willReturn($sharedBy);
+		$this->share->expects($this->once())->method('getId')->willReturn($id);
+
+		$this->assertSame($this->share,
+			$instance->update($this->share)
+		);
+
+		$qb = $this->connection->getQueryBuilder();
+		$result = $qb->select('*')
+			->from('share')
+			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
+			->execute()->fetchAll();
+
+		$this->assertSame(1, count($result));
+
+		$this->assertSame($itemSource, (int)$result[0]['item_source']);
+		$this->assertSame($itemType, $result[0]['item_type']);
+		$this->assertSame($shareWith, $result[0]['share_with']);
+		$this->assertSame($sharedBy, $result[0]['uid_initiator']);
+		$this->assertSame($uidOwner, $result[0]['uid_owner']);
+		$this->assertSame($permissions + 1, (int)$result[0]['permissions']);
+		$this->assertSame($token, $result[0]['token']);
+	}
+
+	public function testDelete() {
+		$instance = $this->getInstance(['removeShareFromTable']);
+		$this->share->expects($this->once())->method('getId')->willReturn(42);
+		$instance->expects($this->once())->method('removeShareFromTable')->with(42);
+		$instance->delete($this->share);
+	}
+
+	public function testGetShareById() {
+		$instance = $this->getInstance(['createShareObject']);
+
+		$itemSource = 11;
+		$itemType = 'file';
+		$shareWith = 'user@server.com';
+		$sharedBy = 'user1';
+		$uidOwner = 'user2';
+		$permissions = 1;
+		$token = 'token';
+
+		$this->createDummyShare($itemType, $itemSource, $shareWith, "user1wrong", "user2wrong", $permissions, $token);
+		$id2 = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
+
+		$instance->expects($this->once())->method('createShareObject')
+			->willReturnCallback(
+				function ($data) use ($uidOwner, $sharedBy, $id2) {
+					$this->assertSame($uidOwner, $data['uid_owner']);
+					$this->assertSame($sharedBy, $data['uid_initiator']);
+					$this->assertSame($id2, (int)$data['id']);
+					return $this->share;
+				}
+			);
+
+		$result = $instance->getShareById($id2);
+
+		$this->assertInstanceOf('OCP\Share\IShare', $result);
+	}
+
+	/**
+	 * @expectedException \OCP\Share\Exceptions\ShareNotFound
+	 */
+	public function testGetShareByIdFailed() {
+		$instance = $this->getInstance(['createShareObject']);
+
+		$itemSource = 11;
+		$itemType = 'file';
+		$shareWith = 'user@server.com';
+		$sharedBy = 'user1';
+		$uidOwner = 'user2';
+		$permissions = 1;
+		$token = 'token';
+
+		$id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
+
+		$instance->getShareById($id+1);
+	}
+
+	public function testGetShareByPath() {
+
+		$itemSource = 11;
+		$itemType = 'file';
+		$shareWith = 'user@server.com';
+		$sharedBy = 'user1';
+		$uidOwner = 'user2';
+		$permissions = 1;
+		$token = 'token';
+
+		$node = $this->getMockBuilder('OCP\Files\Node')->getMock();
+		$node->expects($this->once())->method('getId')->willReturn($itemSource);
+
+
+		$instance = $this->getInstance(['createShareObject']);
+
+		$this->createDummyShare($itemType, 111, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
+		$id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
+
+		$instance->expects($this->once())->method('createShareObject')
+			->willReturnCallback(
+				function ($data) use ($uidOwner, $sharedBy, $id) {
+					$this->assertSame($uidOwner, $data['uid_owner']);
+					$this->assertSame($sharedBy, $data['uid_initiator']);
+					$this->assertSame($id, (int)$data['id']);
+					return $this->share;
+				}
+			);
+
+		$result = $instance->getSharesByPath($node);
+
+		$this->assertTrue(is_array($result));
+		$this->assertSame(1, count($result));
+		$this->assertInstanceOf('OCP\Share\IShare', $result[0]);
+	}
+
+	public function testGetShareByToken() {
+
+		$itemSource = 11;
+		$itemType = 'file';
+		$shareWith = 'user@server.com';
+		$sharedBy = 'user1';
+		$uidOwner = 'user2';
+		$permissions = 1;
+		$token = 'token';
+
+		$instance = $this->getInstance(['createShareObject']);
+
+		$idMail = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
+		$idPublic = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, \OCP\Share::SHARE_TYPE_LINK);
+
+		$this->assertTrue($idMail !== $idPublic);
+
+		$instance->expects($this->once())->method('createShareObject')
+			->willReturnCallback(
+				function ($data) use ($idMail) {
+					$this->assertSame($idMail, (int)$data['id']);
+					return $this->share;
+				}
+			);
+
+		$this->assertInstanceOf('OCP\Share\IShare',
+			$instance->getShareByToken('token')
+		);
+	}
+
+	/**
+	 * @expectedException \OCP\Share\Exceptions\ShareNotFound
+	 */
+	public function testGetShareByTokenFailed() {
+
+		$itemSource = 11;
+		$itemType = 'file';
+		$shareWith = 'user@server.com';
+		$sharedBy = 'user1';
+		$uidOwner = 'user2';
+		$permissions = 1;
+		$token = 'token';
+
+		$instance = $this->getInstance(['createShareObject']);
+
+		$idMail = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
+		$idPublic = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, "token2", \OCP\Share::SHARE_TYPE_LINK);
+
+		$this->assertTrue($idMail !== $idPublic);
+
+		$this->assertInstanceOf('OCP\Share\IShare',
+			$instance->getShareByToken('token2')
+		);
+	}
+
+	public function testRemoveShareFromTable() {
+		$itemSource = 11;
+		$itemType = 'file';
+		$shareWith = 'user@server.com';
+		$sharedBy = 'user1';
+		$uidOwner = 'user2';
+		$permissions = 1;
+		$token = 'token';
+
+		$instance = $this->getInstance();
+
+		$id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
+
+		$query = $this->connection->getQueryBuilder();
+		$query->select('*')->from('share')
+			->where($query->expr()->eq('id', $query->createNamedParameter($id)));
+		$before = $query->execute()->fetchAll();
+
+		$this->assertTrue(is_array($before));
+		$this->assertSame(1, count($before));
+
+		$this->invokePrivate($instance, 'removeShareFromTable', [$id]);
+
+		$query = $this->connection->getQueryBuilder();
+		$query->select('*')->from('share')
+			->where($query->expr()->eq('id', $query->createNamedParameter($id)));
+		$after = $query->execute()->fetchAll();
+
+		$this->assertTrue(is_array($after));
+		$this->assertEmpty($after);
+	}
+
+	public function testUserDeleted() {
+
+		$itemSource = 11;
+		$itemType = 'file';
+		$shareWith = 'user@server.com';
+		$sharedBy = 'user1';
+		$uidOwner = 'user2';
+		$permissions = 1;
+		$token = 'token';
+
+		$this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
+		$id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, 'user2Wrong', $permissions, $token);
+
+		$query = $this->connection->getQueryBuilder();
+		$query->select('*')->from('share');
+		$before = $query->execute()->fetchAll();
+
+		$this->assertTrue(is_array($before));
+		$this->assertSame(2, count($before));
+
+
+		$instance = $this->getInstance();
+
+		$instance->userDeleted($uidOwner, \OCP\Share::SHARE_TYPE_EMAIL);
+
+		$query = $this->connection->getQueryBuilder();
+		$query->select('*')->from('share');
+		$after = $query->execute()->fetchAll();
+
+		$this->assertTrue(is_array($after));
+		$this->assertSame(1, count($after));
+		$this->assertSame($id, (int)$after[0]['id']);
+
+	}
+
+	public function testGetRawShare() {
+		$itemSource = 11;
+		$itemType = 'file';
+		$shareWith = 'user@server.com';
+		$sharedBy = 'user1';
+		$uidOwner = 'user2';
+		$permissions = 1;
+		$token = 'token';
+
+		$instance = $this->getInstance();
+
+		$id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
+
+		$result = $this->invokePrivate($instance, 'getRawShare', [$id]);
+
+		$this->assertTrue(is_array($result));
+		$this->assertSame($itemSource, (int)$result['item_source']);
+		$this->assertSame($itemType, $result['item_type']);
+		$this->assertSame($shareWith, $result['share_with']);
+		$this->assertSame($sharedBy, $result['uid_initiator']);
+		$this->assertSame($uidOwner, $result['uid_owner']);
+		$this->assertSame($permissions, (int)$result['permissions']);
+		$this->assertSame($token, $result['token']);
+	}
+
+	/**
+	 * @expectedException \OCP\Share\Exceptions\ShareNotFound
+	 */
+	public function testGetRawShareFailed() {
+		$itemSource = 11;
+		$itemType = 'file';
+		$shareWith = 'user@server.com';
+		$sharedBy = 'user1';
+		$uidOwner = 'user2';
+		$permissions = 1;
+		$token = 'token';
+
+		$instance = $this->getInstance();
+
+		$id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token);
+
+		$this->invokePrivate($instance, 'getRawShare', [$id+1]);
+	}
+
+	private function createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $shareType = \OCP\Share::SHARE_TYPE_EMAIL) {
+		$qb = $this->connection->getQueryBuilder();
+		$qb->insert('share')
+			->setValue('share_type', $qb->createNamedParameter($shareType))
+			->setValue('item_type', $qb->createNamedParameter($itemType))
+			->setValue('item_source', $qb->createNamedParameter($itemSource))
+			->setValue('file_source', $qb->createNamedParameter($itemSource))
+			->setValue('share_with', $qb->createNamedParameter($shareWith))
+			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
+			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
+			->setValue('permissions', $qb->createNamedParameter($permissions))
+			->setValue('token', $qb->createNamedParameter($token))
+			->setValue('stime', $qb->createNamedParameter(time()));
+
+		/*
+		 * Added to fix https://github.com/owncloud/core/issues/22215
+		 * Can be removed once we get rid of ajax/share.php
+		 */
+		$qb->setValue('file_target', $qb->createNamedParameter(''));
+
+		$qb->execute();
+		$id = $qb->getLastInsertId();
+
+		return (int)$id;
+	}
+
+	public function testGetSharesInFolder() {
+		$userManager = \OC::$server->getUserManager();
+		$rootFolder = \OC::$server->getRootFolder();
+
+		$provider = $this->getInstance(['sendMailNotification']);
+
+		$u1 = $userManager->createUser('testFed', md5(time()));
+		$u2 = $userManager->createUser('testFed2', md5(time()));
+
+		$folder1 = $rootFolder->getUserFolder($u1->getUID())->newFolder('foo');
+		$file1 = $folder1->newFile('bar1');
+		$file2 = $folder1->newFile('bar2');
+
+		$share1 = $this->shareManager->newShare();
+		$share1->setSharedWith('user@server.com')
+			->setSharedBy($u1->getUID())
+			->setShareOwner($u1->getUID())
+			->setPermissions(\OCP\Constants::PERMISSION_READ)
+			->setNode($file1);
+		$provider->create($share1);
+
+		$share2 = $this->shareManager->newShare();
+		$share2->setSharedWith('user@server.com')
+			->setSharedBy($u2->getUID())
+			->setShareOwner($u1->getUID())
+			->setPermissions(\OCP\Constants::PERMISSION_READ)
+			->setNode($file2);
+		$provider->create($share2);
+
+		$result = $provider->getSharesInFolder($u1->getUID(), $folder1, false);
+		$this->assertCount(1, $result);
+		$this->assertCount(1, $result[$file1->getId()]);
+
+		$result = $provider->getSharesInFolder($u1->getUID(), $folder1, true);
+		$this->assertCount(2, $result);
+		$this->assertCount(1, $result[$file1->getId()]);
+		$this->assertCount(1, $result[$file2->getId()]);
+
+		$u1->delete();
+		$u2->delete();
+	}
+	
+}
diff --git a/build/integration/features/provisioning-v1.feature b/build/integration/features/provisioning-v1.feature
index 98bf321dc64b057b0a30495764fa68ef7decfa65..9103a71ab9265479c5aeacbba3abff8a5cd2ba8c 100644
--- a/build/integration/features/provisioning-v1.feature
+++ b/build/integration/features/provisioning-v1.feature
@@ -255,7 +255,7 @@ Feature: provisioning
 	Scenario: Delete a user
 		Given As an "admin"
 		And user "brand-new-user" exists
-		When sending "DELETE" to "/cloud/users/brand-new-user" 
+		When sending "DELETE" to "/cloud/users/brand-new-user"
 		Then the OCS status code should be "100"
 		And the HTTP status code should be "200"
 		And user "brand-new-user" does not exist
@@ -291,6 +291,7 @@ Feature: provisioning
 			| files_trashbin |
 			| files_versions |
 			| provisioning_api |
+			| sharebymail |
 			| systemtags |
 			| theming |
 			| twofactor_backupcodes |
diff --git a/core/js/shareconfigmodel.js b/core/js/shareconfigmodel.js
index b04c2acae16b69f665816cd85307b2f0ce52113f..24922d6263682e40bce06ccd73c2bdd03570633e 100644
--- a/core/js/shareconfigmodel.js
+++ b/core/js/shareconfigmodel.js
@@ -25,6 +25,7 @@
 			isDefaultExpireDateEnforced: oc_appconfig.core.defaultExpireDateEnforced === true,
 			isDefaultExpireDateEnabled: oc_appconfig.core.defaultExpireDateEnabled === true,
 			isRemoteShareAllowed: oc_appconfig.core.remoteShareAllowed,
+			isMailShareAllowed: oc_appconfig.shareByMailEnabled !== undefined,
 			defaultExpireDate: oc_appconfig.core.defaultExpireDate,
 			isResharingAllowed: oc_appconfig.core.resharingAllowed,
 			allowGroupSharing: oc_appconfig.core.allowGroupSharing
diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js
index 66ed85eda33f78414e4a3c6c63ac136488250065..775eaa554b04f14dce51b883a98debfff97f6245 100644
--- a/core/js/sharedialogshareelistview.js
+++ b/core/js/sharedialogshareelistview.js
@@ -24,25 +24,28 @@
 					'{{#if avatarEnabled}}' +
 					'<div class="avatar {{#if modSeed}}imageplaceholderseed{{/if}}" data-username="{{shareWith}}" {{#if modSeed}}data-seed="{{shareWith}} {{shareType}}"{{/if}}></div>' +
 					'{{/if}}' +
-					'<span class="has-tooltip username" title="{{shareWith}}">{{shareWithDisplayName}}</span>' +
+					'<span class="has-tooltip username" title="{{shareWithTitle}}">{{shareWithDisplayName}}</span>' +
 					'<span class="sharingOptionsGroup">' +
 						'{{#if editPermissionPossible}}' +
+						'{{#unless isFileSharedByMail}}' +
 						'<span class="shareOption">' +
 							'<input id="canEdit-{{cid}}-{{shareWith}}" type="checkbox" name="edit" class="permissions checkbox" {{#if hasEditPermission}}checked="checked"{{/if}} />' +
 							'<label for="canEdit-{{cid}}-{{shareWith}}">{{canEditLabel}}</label>' +
 						'</span>' +
+						'{{/unless}}' +
 						'{{/if}}' +
+						'{{#unless isMailShare}}' +
 						'<a href="#"><span class="icon icon-more"></span></a>' +
 						'<div class="popovermenu bubble hidden menu">' +
 							'<ul>' +
-								'{{#if isResharingAllowed}} {{#if sharePermissionPossible}}' +
+								'{{#if isResharingAllowed}} {{#if sharePermissionPossible}} {{#unless isMailShare}}' +
 								'<li>' +
 									'<span class="shareOption">' +
 										'<input id="canShare-{{cid}}-{{shareWith}}" type="checkbox" name="share" class="permissions checkbox" {{#if hasSharePermission}}checked="checked"{{/if}} data-permissions="{{sharePermission}}" />' +
 										'<label for="canShare-{{cid}}-{{shareWith}}">{{canShareLabel}}</label>' +
 									'</span>' +
 								'</li>' +
-								'{{/if}} {{/if}}' +
+								'{{/unless}} {{/if}} {{/if}}' +
 								'{{#if isFolder}}' +
 									'{{#if createPermissionPossible}}' +
 									'<li>' +
@@ -74,7 +77,9 @@
 								'</li>' +
 							'</ul>' +
 						'</div>' +
-						'</span>' +
+						'{{/unless}}' +
+						'<a href="#" class="unshare"><span class="icon-loading-small hidden"></span><span class="icon icon-delete"></span><span class="hidden-visually">{{unshareLabel}}</span></a>' +
+					'</span>' +
 				'</li>' +
 			'{{/each}}' +
 			'{{#each linkReshares}}' +
@@ -141,6 +146,7 @@
 		getShareeObject: function(shareIndex) {
 			var shareWith = this.model.getShareWith(shareIndex);
 			var shareWithDisplayName = this.model.getShareWithDisplayName(shareIndex);
+			var shareWithTitle = '';
 			var shareType = this.model.getShareType(shareIndex);
 
 			var hasPermissionOverride = {};
@@ -148,6 +154,16 @@
 				shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'group') + ')';
 			} else if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
 				shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'remote') + ')';
+			} else if (shareType === OC.Share.SHARE_TYPE_EMAIL) {
+				shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'email') + ')';
+			}
+
+			if (shareType === OC.Share.SHARE_TYPE_GROUP) {
+				shareWithTitle = shareWith + " (" + t('core', 'group') + ')';
+			} else if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
+				shareWithTitle = shareWith + " (" + t('core', 'remote') + ')';
+			} else if (shareType === OC.Share.SHARE_TYPE_EMAIL) {
+				shareWithTitle = shareWith + " (" + t('core', 'email') + ')';
 			}
 
 			return _.extend(hasPermissionOverride, {
@@ -160,10 +176,13 @@
 				wasMailSent: this.model.notificationMailWasSent(shareIndex),
 				shareWith: shareWith,
 				shareWithDisplayName: shareWithDisplayName,
+				shareWithTitle: shareWithTitle,
 				shareType: shareType,
 				shareId: this.model.get('shares')[shareIndex].id,
 				modSeed: shareType !== OC.Share.SHARE_TYPE_USER,
-				isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE
+				isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE,
+				isMailShare: shareType === OC.Share.SHARE_TYPE_EMAIL,
+				isFileSharedByMail: shareType === OC.Share.SHARE_TYPE_EMAIL && !this.model.isFolder()
 			});
 		},
 
diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js
index 7efb361f512dd9771aa1023f96d0eb3ceb1506e3..3b2a7480c4736b18acf7ab561031caacbefda755 100644
--- a/core/js/sharedialogview.js
+++ b/core/js/sharedialogview.js
@@ -154,10 +154,16 @@
 						var users   = result.ocs.data.exact.users.concat(result.ocs.data.users);
 						var groups  = result.ocs.data.exact.groups.concat(result.ocs.data.groups);
 						var remotes = result.ocs.data.exact.remotes.concat(result.ocs.data.remotes);
+						if (typeof(result.ocs.data.emails) !== 'undefined') {
+							var emails = result.ocs.data.exact.emails.concat(result.ocs.data.emails);
+						} else {
+							var emails = [];
+						}
 
 						var usersLength;
 						var groupsLength;
 						var remotesLength;
+						var emailsLength;
 
 						var i, j;
 
@@ -212,10 +218,18 @@
 										break;
 									}
 								}
+							} else if (share.share_type === OC.Share.SHARE_TYPE_EMAIL) {
+								emailsLength = emails.length;
+								for (j = 0; j < emailsLength; j++) {
+									if (emails[j].value.shareWith === share.share_with) {
+										emails.splice(j, 1);
+										break;
+									}
+								}
 							}
 						}
 
-						var suggestions = users.concat(groups).concat(remotes);
+						var suggestions = users.concat(groups).concat(remotes).concat(emails);
 
 						if (suggestions.length > 0) {
 							$('.shareWithField').removeClass('error')
@@ -258,16 +272,13 @@
 					sharee: text
 				});
 			} else if (item.value.shareType === OC.Share.SHARE_TYPE_REMOTE) {
-				if (item.value.server) {
-					text = t('core', '{sharee} (at {server})', {
-						sharee: text,
-						server: item.value.server
-					});
-				} else {
 					text = t('core', '{sharee} (remote)', {
 						sharee: text
 					});
-				}
+			} else if (item.value.shareType === OC.Share.SHARE_TYPE_EMAIL) {
+				text = t('core', '{sharee} (email)', {
+					sharee: text
+				});
 			}
 			var insert = $("<div class='share-autocomplete-item'/>");
 			var avatar = $("<div class='avatardiv'></div>").appendTo(insert);
@@ -397,7 +408,7 @@
 				var infoTemplate = this._getRemoteShareInfoTemplate();
 				remoteShareInfo = infoTemplate({
 					docLink: this.configModel.getFederatedShareDocLink(),
-					tooltip: t('core', 'Share with people on other Nextclouds using the syntax username@example.com/nextcloud')
+					tooltip: t('core', 'Share with people on other servers using the syntax username@example.com/nextcloud')
 				});
 			}
 
@@ -405,19 +416,33 @@
 		},
 
 		_renderSharePlaceholderPart: function () {
-			var sharePlaceholder = t('core', 'Share with users…');
+			var allowGroupSharing = this.configModel.get('allowGroupSharing');
+			var allowRemoteSharing = this.configModel.get('isRemoteShareAllowed');
+			var allowMailSharing = this.configModel.get('isMailShareAllowed');
 
-			if (this.configModel.get('allowGroupSharing')) {
-				if (this.configModel.get('isRemoteShareAllowed')) {
-					sharePlaceholder = t('core', 'Share with users, groups or remote users…');
-				} else {
-					sharePlaceholder = t('core', 'Share with users or groups…');
-				}
-			} else if (this.configModel.get('isRemoteShareAllowed')) {
-					sharePlaceholder = t('core', 'Share with users or remote users…');
+			if (!allowGroupSharing && !allowRemoteSharing && allowMailSharing) {
+				return t('core', 'Share with users or by mail...');
+			}
+			if (!allowGroupSharing && allowRemoteSharing && !allowMailSharing) {
+				return t('core', 'Share with users or remote users...');
+			}
+			if (!allowGroupSharing && allowRemoteSharing && allowMailSharing) {
+				return t('core', 'Share with users, remote users or by mail...');
+			}
+			if (allowGroupSharing && !allowRemoteSharing && !allowMailSharing) {
+				return t('core', 'Share with users or groups...');
+			}
+			if (allowGroupSharing && !allowRemoteSharing && allowMailSharing) {
+				return t('core', 'Share with users, groups or by mail...');
+			}
+			if (allowGroupSharing && allowRemoteSharing && !allowMailSharing) {
+				return t('core', 'Share with users, groups or remote users...');
+			}
+			if (allowGroupSharing && allowRemoteSharing && allowMailSharing) {
+				return t('core', 'Share with users, groups, remote users or by mail...');
 			}
 
-			return sharePlaceholder;
+			return 	t('core', 'Share with users...');
 		},
 
 		/**
diff --git a/core/shipped.json b/core/shipped.json
index ee8a2b05ef6985d5323f7bfc2612390a94dea3ba..9855a6638479a47bff3ec1cfc6bd0e0c814f5233 100644
--- a/core/shipped.json
+++ b/core/shipped.json
@@ -25,6 +25,7 @@
     "password_policy",
     "provisioning_api",
     "serverinfo",
+    "sharebymail",
     "survey_client",
     "systemtags",
     "templateeditor",
diff --git a/lib/private/Share/Constants.php b/lib/private/Share/Constants.php
index 13a5a044e8a32cce30da0f130b82d728bb79e86e..f13f83f8ba9aa15795b9cc7e6ab20f5f512f4916 100644
--- a/lib/private/Share/Constants.php
+++ b/lib/private/Share/Constants.php
@@ -29,9 +29,9 @@ class Constants {
 	const SHARE_TYPE_USER = 0;
 	const SHARE_TYPE_GROUP = 1;
 	const SHARE_TYPE_LINK = 3;
-	const SHARE_TYPE_EMAIL = 4;   // ToDo Check if it is still in use otherwise remove it
+	const SHARE_TYPE_EMAIL = 4;
 	const SHARE_TYPE_CONTACT = 5; // ToDo Check if it is still in use otherwise remove it
-	const SHARE_TYPE_REMOTE = 6;  // ToDo Check if it is still in use otherwise remove it
+	const SHARE_TYPE_REMOTE = 6;
 
 	const FORMAT_NONE = -1;
 	const FORMAT_STATUSES = -2;
diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php
index 838650ada1534af3f5b52671ceb33f3bd3145104..0c49d0b6490a3f078939da1947c150ab57eb38ce 100644
--- a/lib/private/Share20/Manager.php
+++ b/lib/private/Share20/Manager.php
@@ -30,6 +30,7 @@ namespace OC\Share20;
 use OC\Cache\CappedMemoryCache;
 use OC\Files\Mount\MoveableMount;
 use OC\HintException;
+use OC\Share20\Exception\ProviderException;
 use OCP\Files\File;
 use OCP\Files\Folder;
 use OCP\Files\IRootFolder;
@@ -185,6 +186,10 @@ class Manager implements IManager {
 			if ($share->getSharedWith() === null) {
 				throw new \InvalidArgumentException('SharedWith should not be empty');
 			}
+		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
+			if ($share->getSharedWith() === null) {
+				throw new \InvalidArgumentException('SharedWith should not be empty');
+			}
 		} else {
 			// We can't handle other types yet
 			throw new \InvalidArgumentException('unkown share type');
@@ -580,6 +585,16 @@ class Manager implements IManager {
 			if ($share->getPassword() !== null) {
 				$share->setPassword($this->hasher->hash($share->getPassword()));
 			}
+		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
+			$this->linkCreateChecks($share);
+			$share->setToken(
+				$this->secureRandom->generate(
+					\OC\Share\Constants::TOKEN_LENGTH,
+					\OCP\Security\ISecureRandom::CHAR_LOWER.
+					\OCP\Security\ISecureRandom::CHAR_UPPER.
+					\OCP\Security\ISecureRandom::CHAR_DIGITS
+				)
+			);
 		}
 
 		// Cannot share with the owner
@@ -1034,6 +1049,16 @@ class Manager implements IManager {
 		// If it is not a link share try to fetch a federated share by token
 		if ($share === null) {
 			$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE);
+			try {
+				$share = $provider->getShareByToken($token);
+			} catch (ShareNotFound $e) {
+				$share = null;
+			}
+		}
+
+		// If it is not a link share try to fetch a federated share by token
+		if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
+			$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL);
 			$share = $provider->getShareByToken($token);
 		}
 
@@ -1277,4 +1302,17 @@ class Manager implements IManager {
 		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
 	}
 
+	/**
+	 * @inheritdoc
+	 */
+	public function shareProviderExists($shareType) {
+		try {
+			$this->factory->getProviderForType($shareType);
+		} catch (ProviderException $e) {
+			return false;
+		}
+
+		return true;
+	}
+
 }
diff --git a/lib/private/Share20/ProviderFactory.php b/lib/private/Share20/ProviderFactory.php
index 5cdc9a51a222c5849c7b81d06c39343e30c6fe6b..457cf117c6994e6278b44d387aaea3bde4bcebb4 100644
--- a/lib/private/Share20/ProviderFactory.php
+++ b/lib/private/Share20/ProviderFactory.php
@@ -28,6 +28,7 @@ use OCA\FederatedFileSharing\DiscoveryManager;
 use OCA\FederatedFileSharing\FederatedShareProvider;
 use OCA\FederatedFileSharing\Notifications;
 use OCA\FederatedFileSharing\TokenHandler;
+use OCA\ShareByMail\ShareByMailProvider;
 use OCP\Share\IProviderFactory;
 use OC\Share20\Exception\ProviderException;
 use OCP\IServerContainer;
@@ -45,6 +46,8 @@ class ProviderFactory implements IProviderFactory {
 	private $defaultProvider = null;
 	/** @var FederatedShareProvider */
 	private $federatedProvider = null;
+	/** @var  ShareByMailProvider */
+	private $shareByMailProvider;
 
 	/**
 	 * IProviderFactory constructor.
@@ -125,6 +128,39 @@ class ProviderFactory implements IProviderFactory {
 		return $this->federatedProvider;
 	}
 
+	/**
+	 * Create the federated share provider
+	 *
+	 * @return FederatedShareProvider
+	 */
+	protected function getShareByMailProvider() {
+		if ($this->shareByMailProvider === null) {
+			/*
+			 * Check if the app is enabled
+			 */
+			$appManager = $this->serverContainer->getAppManager();
+			if (!$appManager->isEnabledForUser('sharebymail')) {
+				return null;
+			}
+
+			$l = $this->serverContainer->getL10N('sharebymail');
+
+			$this->shareByMailProvider = new ShareByMailProvider(
+				$this->serverContainer->getDatabaseConnection(),
+				$this->serverContainer->getSecureRandom(),
+				$this->serverContainer->getUserManager(),
+				$this->serverContainer->getLazyRootFolder(),
+				$l,
+				$this->serverContainer->getLogger(),
+				$this->serverContainer->getMailer(),
+				$this->serverContainer->getURLGenerator()
+			);
+		}
+
+		return $this->shareByMailProvider;
+	}
+
+
 	/**
 	 * @inheritdoc
 	 */
@@ -134,6 +170,8 @@ class ProviderFactory implements IProviderFactory {
 			$provider = $this->defaultShareProvider();
 		} else if ($id === 'ocFederatedSharing') {
 			$provider = $this->federatedShareProvider();
+		} else if ($id = 'ocMailShare') {
+			$provider = $this->getShareByMailProvider();
 		}
 
 		if ($provider === null) {
@@ -155,6 +193,8 @@ class ProviderFactory implements IProviderFactory {
 			$provider = $this->defaultShareProvider();
 		} else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
 			$provider = $this->federatedShareProvider();
+		} else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) {
+			$provider = $this->getShareByMailProvider();
 		}
 
 		if ($provider === null) {
diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php
index a74ab5fe7968306cb525c4ae9c96dc7be3083095..137dc30928004450e510dda39255e6a1c136922a 100644
--- a/lib/public/Share/IManager.php
+++ b/lib/public/Share/IManager.php
@@ -286,4 +286,12 @@ interface IManager {
 	 */
 	public function outgoingServer2ServerSharesAllowed();
 
+	/**
+	 * Check if a given share provider exists
+	 * @param int $shareType
+	 * @return bool
+	 * @since 9.2.0
+	 */
+	public function shareProviderExists($shareType);
+
 }
diff --git a/tests/lib/Share20/ManagerTest.php b/tests/lib/Share20/ManagerTest.php
index c0c7b48b35dbbc427da1e6f66ee25f731767edfe..bd85e3c73aa922a462ea6aed1cb9a211498c1115 100644
--- a/tests/lib/Share20/ManagerTest.php
+++ b/tests/lib/Share20/ManagerTest.php
@@ -2530,6 +2530,46 @@ class ManagerTest extends \Test\TestCase {
 		$this->manager->moveShare($share, 'recipient');
 	}
 
+
+	/**
+	 * @dataProvider dataTestShareProviderExists
+	 */
+	public function testShareProviderExists($shareType, $expected) {
+
+		$factory = $this->getMockBuilder('OCP\Share\IProviderFactory')->getMock();
+		$factory->expects($this->any())->method('getProviderForType')
+			->willReturnCallback(function ($id) {
+				if ($id === \OCP\Share::SHARE_TYPE_USER) {
+					return true;
+				}
+				throw new Exception\ProviderException();
+			});
+
+		$manager = new Manager(
+			$this->logger,
+			$this->config,
+			$this->secureRandom,
+			$this->hasher,
+			$this->mountManager,
+			$this->groupManager,
+			$this->l,
+			$factory,
+			$this->userManager,
+			$this->rootFolder,
+			$this->eventDispatcher
+		);
+		$this->assertSame($expected,
+			$manager->shareProviderExists($shareType)
+		);
+	}
+
+	public function dataTestShareProviderExists() {
+		return [
+			[\OCP\Share::SHARE_TYPE_USER, true],
+			[42, false],
+		];
+	}
+
 	public function testGetSharesInFolder() {
 		$factory = new DummyFactory2($this->createMock(IServerContainer::class));