diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php
index 531481a84c00e98f46aaeb07ca730d0c91b4568e..40da32b223a4cf37259476dafc714f3617db80ef 100644
--- a/apps/files/ajax/rawlist.php
+++ b/apps/files/ajax/rawlist.php
@@ -3,10 +3,6 @@
 // only need filesystem apps
 $RUNTIME_APPTYPES=array('filesystem');
 
-// Init owncloud
-
-require_once 'lib/template.php';
-
 OCP\JSON::checkLoggedIn();
 
 // Load the files
diff --git a/apps/files/css/files.css b/apps/files/css/files.css
index b3ecd1dab93e4cb62cb167881960c7b6aba9f49b..138b15db04fff6c8fb950738e1a62209155b38cb 100644
--- a/apps/files/css/files.css
+++ b/apps/files/css/files.css
@@ -7,68 +7,54 @@
 .actions input, .actions button, .actions .button { margin:0; float:left; }
 .actions .button a { color: #555; }
 .actions .button a:hover, .actions .button a:active { color: #333; }
-#new {
-	height:17px;  margin:0 0 0 1em; z-index:1010; float:left;
+#new, #trash {
+	z-index: 1010;
+	float: left;
+	padding: 0 !important; /* override default control bar button padding */
+}
+#trash {
+	margin: 0 1em;
+	float: right;
+}
+#new>a, #trash>a {
+	padding: 14px 10px;
+	position: relative;
+	top: 7px;
+}
+#new.active {
+	border-bottom-left-radius: 0;
+	border-bottom-right-radius: 0;
+	border-bottom: none;
 }
-#new.active { border-bottom-left-radius:0; border-bottom-right-radius:0; border-bottom:none; }
-#new>a { padding:.5em 1.2em .3em; }
 #new>ul {
-	display:none; position:fixed; min-width:7em; z-index:10;
-	padding:.5em; padding-bottom:0; margin-top:.075em; margin-left:-.5em;
+	display: none;
+	position: fixed;
+	min-width: 7em;
+	z-index: 10;
+	padding: .5em;
+	padding-bottom: 0;
+	margin-top: 14px;
+	margin-left: -1px;
 	text-align:left;
-	background:#f8f8f8; border:1px solid #ddd; border-radius:10px; border-top-left-radius:0;
+	background: #f8f8f8;
+	border: 1px solid #ddd;
+	border-radius: 5px;
+	border-top-left-radius: 0;
 	box-shadow:0 2px 7px rgba(170,170,170,.4);
 }
 #new>ul>li { height:36px; margin:.3em; padding-left:3em; padding-bottom:0.1em;
 		background-repeat:no-repeat; cursor:pointer; }
 #new>ul>li>p { cursor:pointer; padding-top: 7px; padding-bottom: 7px;}
-#new>ul>li>form>input {
-	padding: 5px;
-	margin: 2px 0;
-}
 
-#trash { margin: 0 1em; z-index:1010; float: right; }
-
-#upload {
-	height:27px; padding:0; margin-left:0.2em; overflow:hidden;
-}
-#upload a {
-	position:relative; display:block; width:100%; height:27px;
-	cursor:pointer; z-index:10;
-	background-image:url('%webroot%/core/img/actions/upload.svg');
-	background-repeat:no-repeat;
-	background-position:7px 6px;
-	opacity:0.65;
-}
-.file_upload_target { display:none; }
-.file_upload_form { display:inline; float:left; margin:0; padding:0; cursor:pointer; overflow:visible; }
-#file_upload_start {
-	left:0; top:0; width:28px; height:27px; padding:0;
-	font-size:1em;
-	-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0;
-	z-index:20; position:relative; cursor:pointer; overflow:hidden;
-}
-
-#uploadprogresswrapper {
-	position: relative;
-	display: inline;
-}
-#uploadprogressbar {
-	position:relative;
-	float: left;
-	margin-left: 12px;
-	width: 130px;
-	height: 26px;
-	display:inline-block;
-}
-#uploadprogressbar + stop {
-	font-size: 13px;
-}
 
 
 /* FILE TABLE */
 
-#filestable { position: relative; top:37px; width:100%; }
+#filestable {
+	position: relative;
+	top: 44px;
+	width: 100%;
+}
 #filestable tbody tr { background-color:#fff; height:2.5em; }
 #filestable tbody tr:hover, tbody tr:active {
 	background-color: rgb(240,240,240);
@@ -122,9 +108,18 @@ table th#headerDate, table td.date {
 
 /* Multiselect bar */
 #filestable.multiselect {
-	top: 88px;
+	top: 95px;
+}
+table.multiselect thead {
+	position: fixed;
+	top: 89px;
+	z-index: 1;
+	-moz-box-sizing: border-box;
+	box-sizing: border-box;
+	left: 0;
+	padding-left: 80px;
+	width: 100%;
 }
-table.multiselect thead { position:fixed; top:82px; z-index:1; -moz-box-sizing: border-box; box-sizing: border-box; left: 0; padding-left: 80px; width:100%; }
 
 table.multiselect thead th {
 	background-color: rgba(210,210,210,.7);
@@ -228,6 +223,12 @@ table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; }
 	-webkit-transition:background-image 500ms; -moz-transition:background-image 500ms; -o-transition:background-image 500ms; transition:background-image 500ms;
 }
 
+#fileList tr td.filename a.name label {
+	position: absolute;
+	width: 100%;
+	height: 50px;
+}
+
 #uploadsize-message,#delete-confirm { display:none; }
 
 /* File actions */
@@ -319,8 +320,6 @@ a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; }
 
 #scanning-message{ top:40%; left:40%; position:absolute; display:none; }
 
-div.crumb a{ padding:0.9em 0 0.7em 0; color:#555; }
-
 table.dragshadow {
 	width:auto;
 }
diff --git a/apps/files/css/upload.css b/apps/files/css/upload.css
index 2d11e41ba88e3b09520d421d4decf28a95a6c339..ef0435690945e0f9878f085f8024d6ac7c30c981 100644
--- a/apps/files/css/upload.css
+++ b/apps/files/css/upload.css
@@ -1,38 +1,63 @@
-
 #upload {
-	height:27px; padding:0; margin-left:0.2em; overflow:hidden;
+	-moz-box-sizing: border-box;
+	-webkit-box-sizing: border-box;
+	box-sizing: border-box;
+	height: 36px;
+	width: 39px;
+	padding: 0 !important; /* override default control bar button padding */
+	margin-left: .2em;
+	overflow: hidden;
 	vertical-align: top;
 }
 #upload a {
-	position:relative; display:block; width:100%; height:27px;
-	cursor:pointer; z-index:10;
-	background-image:url('%webroot%/core/img/actions/upload.svg');
-	background-repeat:no-repeat;
-	background-position:7px 6px;
-	opacity:0.65;
+	position: relative;
+	display: block;
+	width: 100%;
+	height: 44px;
+	width: 44px;
+	margin: -5px -3px;
+	cursor: pointer;
+	z-index: 10;
+	background-image: url('%webroot%/core/img/actions/upload.svg');
+	background-repeat: no-repeat;
+	background-position: center;
+	opacity: .65;
 }
 .file_upload_target { display:none; }
 .file_upload_form { display:inline; float:left; margin:0; padding:0; cursor:pointer; overflow:visible; }
 #file_upload_start {
-	float: left;
-	left:0; top:0; width:28px; height:27px; padding:0;
-	font-size:1em;
+	position: relative;
+	left: 0;
+	top: 0;
+	width: 44px;
+	height: 44px;
+	margin: -5px -3px;
+	padding: 0;
+	font-size: 1em;
 	-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0;
-	z-index:20; position:relative; cursor:pointer; overflow:hidden;
+	z-index: 20;
+	cursor: pointer;
+	overflow: hidden;
 }
 
 #uploadprogresswrapper {
+	-moz-box-sizing: border-box;
+	-webkit-box-sizing: border-box;
+	box-sizing: border-box;
 	display: inline-block;
 	vertical-align: top;
-	margin:0.3em;
-	height: 29px;
+	height: 36px;
+	box-sizing: border-box;
+}
+#uploadprogresswrapper > input[type='button'] {
+	height: 36px;
 }
 #uploadprogressbar {
 	position:relative;
 	float: left;
 	margin-left: 12px;
 	width: 130px;
-	height: 26px;
+	height: 36px;
 	display:inline-block;
 }
 #uploadprogressbar + stop {
diff --git a/apps/files/index.php b/apps/files/index.php
index 6f22fdfdc19bb4fccfbfae5d0b8b5d2c7b2dd9d5..8d877be8ac981a9a2f0ffb6e4861dcf8ed6653d7 100644
--- a/apps/files/index.php
+++ b/apps/files/index.php
@@ -104,8 +104,12 @@ if ($needUpgrade) {
 	$storageInfo=OC_Helper::getStorageInfo($dir);
 	$maxUploadFilesize=OCP\Util::maxUploadFilesize($dir);
 	$publicUploadEnabled = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes');
+	// if the encryption app is disabled, than everything is fine (INIT_SUCCESSFUL status code)
+	$encryptionInitStatus = 2;
 	if (OC_App::isEnabled('files_encryption')) {
 		$publicUploadEnabled = 'no';
+		$session = new \OCA\Encryption\Session(new \OC\Files\View('/'));
+		$encryptionInitStatus = $session->getInitialized();
 	}
 
 	$trashEnabled = \OCP\App::isEnabled('files_trashbin');
@@ -113,7 +117,7 @@ if ($needUpgrade) {
 	if ($trashEnabled) {
 		$trashEmpty = \OCA\Files_Trashbin\Trashbin::isEmpty($user);
 	}
-	
+
 	OCP\Util::addscript('files', 'fileactions');
 	OCP\Util::addscript('files', 'files');
 	OCP\Util::addscript('files', 'keyboardshortcuts');
@@ -133,7 +137,10 @@ if ($needUpgrade) {
 	$tmpl->assign('isPublic', false);
 	$tmpl->assign('publicUploadEnabled', $publicUploadEnabled);
 	$tmpl->assign("encryptedFiles", \OCP\Util::encryptedFiles());
+	$tmpl->assign("mailNotificationEnabled", \OC_Appconfig::getValue('core', 'shareapi_allow_mail_notification', 'yes'));
+	$tmpl->assign("encryptionInitStatus", $encryptionInitStatus);
 	$tmpl->assign('disableSharing', false);
 	$tmpl->assign('ajaxLoad', $ajaxLoad);
+
 	$tmpl->printPage();
 }
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 4fc1b95a0ab5612bc48560b2328589cc83ab3179..3c99e3876c745166c935a42b3b13e05d50183e4f 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -895,6 +895,10 @@ $(document).ready(function(){
 		$(window).trigger('beforeunload');
 	});
 
+	function decodeQuery(query){
+		return query.replace(/\+/g, ' ');
+	}
+
 	function parseHashQuery(){
 		var hash = window.location.hash,
 			pos = hash.indexOf('?'),
@@ -911,11 +915,11 @@ $(document).ready(function(){
 			dir = '/';
 		// try and parse from URL hash first
 		if (query){
-			params = OC.parseQueryString(query);
+			params = OC.parseQueryString(decodeQuery(query));
 		}
 		// else read from query attributes
 		if (!params){
-			params = OC.parseQueryString(location.search);
+			params = OC.parseQueryString(decodeQuery(location.search));
 		}
 		return (params && params.dir) || '/';
 	}
diff --git a/apps/files/js/files.js b/apps/files/js/files.js
index ec688eaf63efff3ea5d0dc70db8e85e08a45f2ac..899bc6469e503a74bf8b99a2bc7381ca5e169878 100644
--- a/apps/files/js/files.js
+++ b/apps/files/js/files.js
@@ -63,6 +63,15 @@ Files={
 		}
 
 		var encryptedFiles = $('#encryptedFiles').val();
+		var initStatus = $('#encryptionInitStatus').val();
+		if (initStatus === '0') { // enc not initialized, but should be
+			OC.Notification.show(t('files_encryption', 'Encryption App is enabled but your keys are not initialized, please log-out and log-in again'));
+			return;
+		}
+		if (initStatus === '1') { // encryption tried to init but failed
+			OC.Notification.showHtml(t('files_encryption', 'Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files.'));
+			return;
+		}
 		if (encryptedFiles === '1') {
 			OC.Notification.show(t('files_encryption', 'Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files.'));
 			return;
diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php
index 96a80738989667198f4605f7705a0b55b6e9e858..7067b854f50a619dc8fb73f185658088d237228f 100644
--- a/apps/files/templates/index.php
+++ b/apps/files/templates/index.php
@@ -9,7 +9,7 @@
 						data-type='file'><p><?php p($l->t('Text file'));?></p></li>
 					<li style="background-image:url('<?php p(OCP\mimetype_icon('dir')) ?>')"
 						data-type='folder'><p><?php p($l->t('Folder'));?></p></li>
-					<li style="background-image:url('<?php p(OCP\image_path('core', 'filetypes/web.svg')) ?>')"
+					<li style="background-image:url('<?php p(OCP\image_path('core', 'places/link.svg')) ?>')"
 						data-type='web'><p><?php p($l->t('From link'));?></p></li>
 				</ul>
 			</div>
@@ -116,3 +116,5 @@
 <input type="hidden" name="allowZipDownload" id="allowZipDownload" value="<?php p($_['allowZipDownload']); ?>" />
 <input type="hidden" name="usedSpacePercent" id="usedSpacePercent" value="<?php p($_['usedSpacePercent']); ?>" />
 <input type="hidden" name="encryptedFiles" id="encryptedFiles" value="<?php $_['encryptedFiles'] ? p('1') : p('0'); ?>" />
+<input type="hidden" name="encryptedInitStatus" id="encryptionInitStatus" value="<?php p($_['encryptionInitStatus']) ?>" />
+<input type="hidden" name="mailNotificationEnabled" id="mailNotificationEnabled" value="<?php p($_['mailNotificationEnabled']) ?>" />
diff --git a/apps/files/templates/part.list.php b/apps/files/templates/part.list.php
index 1e4d4d11c98dbfa6344abe6ec9a1f48e993d7588..0679da334dedbe907a3b683552bcae6a619230be 100644
--- a/apps/files/templates/part.list.php
+++ b/apps/files/templates/part.list.php
@@ -30,16 +30,15 @@ $totalsize = 0; ?>
 		<?php endif; ?>
 		<?php if($file['type'] == 'dir'): ?>
 			<a class="name" href="<?php p(rtrim($_['baseURL'],'/').'/'.trim($directory,'/').'/'.$name); ?>" title="">
+				<span class="nametext">
+					<?php print_unescaped(htmlspecialchars($file['name']));?>
+				</span>
 		<?php else: ?>
-			<a class="name" href="<?php p(rtrim($_['downloadURL'],'/').'/'.trim($directory,'/').'/'.$name); ?>" title="">
+			<a class="name" href="<?php p(rtrim($_['downloadURL'],'/').'/'.trim($directory,'/').'/'.$name); ?>">
+				<label class="filetext" title="" for="select-<?php p($file['fileid']); ?>"></label>
+				<span class="nametext"><?php print_unescaped(htmlspecialchars($file['basename']));?><span class='extension'><?php p($file['extension']);?></span>
+			</a>
 		<?php endif; ?>
-			<span class="nametext">
-				<?php if($file['type'] == 'dir'):?>
-					<?php print_unescaped(htmlspecialchars($file['name']));?>
-				<?php else:?>
-					<?php print_unescaped(htmlspecialchars($file['basename']));?><span class='extension'><?php p($file['extension']);?></span>
-				<?php endif;?>
-			</span>
 			<?php if($file['type'] == 'dir'):?>
 				<span class="uploadtext" currentUploads="0">
 				</span>
diff --git a/apps/files_encryption/ajax/updatePrivateKeyPassword.php b/apps/files_encryption/ajax/updatePrivateKeyPassword.php
index 1e6644da576f56bd8d8daae01506f2032942658a..29c72952ae9de4457cc5d63340c4caef9658615f 100644
--- a/apps/files_encryption/ajax/updatePrivateKeyPassword.php
+++ b/apps/files_encryption/ajax/updatePrivateKeyPassword.php
@@ -48,6 +48,7 @@ if ($decryptedKey) {
 
 // success or failure
 if ($return) {
+	$session->setInitialized(\OCA\Encryption\Session::INIT_SUCCESSFUL);
 	\OCP\JSON::success(array('data' => array('message' => $l->t('Private key password successfully updated.'))));
 } else {
 	\OCP\JSON::error(array('data' => array('message' => $l->t('Could not update the private key password. Maybe the old password was not correct.'))));
diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php
index 5b62b84e22366a2ed5233d6fa1bcf72b4bb2e7a7..c930ac9eca8be5cb9b88e3651a8771428613bf4c 100644
--- a/apps/files_encryption/appinfo/app.php
+++ b/apps/files_encryption/appinfo/app.php
@@ -43,23 +43,6 @@ if (!OC_Config::getValue('maintenance', false)) {
 		if($sessionReady) {
 			$session = new \OCA\Encryption\Session($view);
 		}
-
-		$user = \OCP\USER::getUser();
-		// check if user has a private key
-		if ($sessionReady === false
-			|| (!$view->file_exists('/' . $user . '/files_encryption/' . $user . '.private.key')
-				&& OCA\Encryption\Crypt::mode() === 'server')
-		) {
-
-			// Force the user to log-in again if the encryption key isn't unlocked
-			// (happens when a user is logged in before the encryption app is
-			// enabled)
-			OCP\User::logout();
-
-			header("Location: " . OC::$WEBROOT . '/');
-
-			exit();
-		}
 	}
 } else {
 	// logout user if we are in maintenance to force re-login
diff --git a/apps/files_encryption/appinfo/version b/apps/files_encryption/appinfo/version
index bd73f47072b1fe4b9914ec14a7f6d47fcc8f816a..2eb3c4fe4eebcdea3da0790cc0ba74cb286ec4f4 100644
--- a/apps/files_encryption/appinfo/version
+++ b/apps/files_encryption/appinfo/version
@@ -1 +1 @@
-0.4
+0.5
diff --git a/apps/files_encryption/files/error.php b/apps/files_encryption/files/error.php
index 2dd27257abe45381c9cf357ffa51e23da9f1df91..ac0c0269164fd9ecc6437c617e143d3e16d56e2d 100644
--- a/apps/files_encryption/files/error.php
+++ b/apps/files_encryption/files/error.php
@@ -1,23 +1,33 @@
 <?php
+
 if (!isset($_)) { //also provide standalone error page
 	require_once __DIR__ . '/../../../lib/base.php';
 
 	$l = OC_L10N::get('files_encryption');
 
-	$errorMsg = $l->t('Your private key is not valid! Likely your password was changed outside the ownCloud system (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files.');
+	if (isset($_GET['i']) && $_GET['i'] === '0') {
+		$errorMsg = $l->t('Encryption app not initialized! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app.');
+		$init = '0';
+	} else {
+		$errorMsg = $l->t('Your private key is not valid! Likely your password was changed outside the ownCloud system (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files.');
+		$init = '1';
+	}
 
-	if(isset($_GET['p']) && $_GET['p'] === '1') {
+	if (isset($_GET['p']) && $_GET['p'] === '1') {
 		header('HTTP/1.0 404 ' . $errorMsg);
 	}
 
-	// check if ajax request
-	if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
+// check if ajax request
+	if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
 		\OCP\JSON::error(array('data' => array('message' => $errorMsg)));
 	} else {
 		header('HTTP/1.0 404 ' . $errorMsg);
 		$tmpl = new OC_Template('files_encryption', 'invalid_private_key', 'guest');
+		$tmpl->assign('message', $errorMsg);
+		$tmpl->assign('init', $init);
 		$tmpl->printPage();
 	}
 
 	exit;
 }
+
diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php
index d9221c6e828a2539ed61ffe184f7e589ca978444..2df860a8e5772d49526e3bf8ac32f13d0c1b3ee3 100644
--- a/apps/files_encryption/hooks/hooks.php
+++ b/apps/files_encryption/hooks/hooks.php
@@ -159,7 +159,6 @@ class Hooks {
 	 * @param array $params keys: uid, password
 	 */
 	public static function setPassphrase($params) {
-
 		// Only attempt to change passphrase if server-side encryption
 		// is in use (client-side encryption does not have access to
 		// the necessary keys)
@@ -543,14 +542,18 @@ class Hooks {
 	}
 
 	/**
-	 * set migration status back to '0' so that all new files get encrypted
+	 * set migration status and the init status back to '0' so that all new files get encrypted
 	 * if the app gets enabled again
 	 * @param array $params contains the app ID
 	 */
 	public static function preDisable($params) {
 		if ($params['app'] === 'files_encryption') {
-			$query = \OC_DB::prepare('UPDATE `*PREFIX*encryption` SET `migration_status`=0');
-			$query->execute();
+
+			$setMigrationStatus = \OC_DB::prepare('UPDATE `*PREFIX*encryption` SET `migration_status`=0');
+			$setMigrationStatus->execute();
+
+			$session = new \OCA\Encryption\Session(new \OC\Files\View('/'));
+			$session->setInitialized(\OCA\Encryption\Session::NOT_INITIALIZED);
 		}
 	}
 
diff --git a/apps/files_encryption/js/settings-admin.js b/apps/files_encryption/js/settings-admin.js
index 6647c621e7b90f3f12c94ed779de577023fd0904..c2140a6f1eb152692e8da411e21099660d5e329c 100644
--- a/apps/files_encryption/js/settings-admin.js
+++ b/apps/files_encryption/js/settings-admin.js
@@ -1,6 +1,8 @@
 /**
- * Copyright (c) 2013, Sam Tuke <samtuke@owncloud.com>, Robin Appelman 
- * <icewind1991@gmail.com>
+ * Copyright (c) 2013
+ *  Sam Tuke <samtuke@owncloud.com>
+ *  Robin Appelman <icewind1991@gmail.com>
+ *  Bjoern Schiessle <schiessle@owncloud.com>
  * This file is licensed under the Affero General Public License version 3 or later.
  * See the COPYING-README file.
  */
@@ -31,22 +33,23 @@ $(document).ready(function(){
 	// Trigger ajax on recoveryAdmin status change
 	var enabledStatus = $('#adminEnableRecovery').val();
 
-	$('input:password[name="recoveryPassword"]').keyup(function(event) {
-		var recoveryPassword = $( '#recoveryPassword' ).val();
+	$('input:password[name="encryptionRecoveryPassword"]').keyup(function(event) {
+		var recoveryPassword = $( '#encryptionRecoveryPassword' ).val();
+		var recoveryPasswordRepeated = $( '#repeatEncryptionRecoveryPassword' ).val();
 		var checkedButton = $('input:radio[name="adminEnableRecovery"]:checked').val();
 		var uncheckedValue = (1+parseInt(checkedButton)) % 2;
-		if (recoveryPassword != '' ) {
+		if (recoveryPassword !== '' && recoveryPassword === recoveryPasswordRepeated) {
 			$('input:radio[name="adminEnableRecovery"][value="'+uncheckedValue.toString()+'"]').removeAttr("disabled");
 		} else {
 			$('input:radio[name="adminEnableRecovery"][value="'+uncheckedValue.toString()+'"]').attr("disabled", "true");
 		}
 	});
 
-	$( 'input:radio[name="adminEnableRecovery"]' ).change( 
+	$( 'input:radio[name="adminEnableRecovery"]' ).change(
 		function() {
 			var recoveryStatus = $( this ).val();
 			var oldStatus = (1+parseInt(recoveryStatus)) % 2;
-			var recoveryPassword = $( '#recoveryPassword' ).val();
+			var recoveryPassword = $( '#encryptionRecoveryPassword' ).val();
 			$.post(
 				OC.filePath( 'files_encryption', 'ajax', 'adminrecovery.php' )
 				, { adminEnableRecovery: recoveryStatus, recoveryPassword: recoveryPassword }
@@ -57,11 +60,10 @@ $(document).ready(function(){
 					} else {
 						OC.Notification.hide();
 						if (recoveryStatus === "0") {
-							$('button:button[name="submitChangeRecoveryKey"]').attr("disabled", "true");
-							$('input:password[name="changeRecoveryPassword"]').attr("disabled", "true");
-							$('input:password[name="changeRecoveryPassword"]').val("");
+							$('p[name="changeRecoveryPasswordBlock"]').addClass("hidden");
 						} else {
-							$('input:password[name="changeRecoveryPassword"]').removeAttr("disabled");
+							$('input:password[name="changeRecoveryPassword"]').val("");
+							$('p[name="changeRecoveryPasswordBlock"]').removeClass("hidden");
 						}
 					}
 				}
@@ -72,9 +74,11 @@ $(document).ready(function(){
 	// change recovery password
 
 	$('input:password[name="changeRecoveryPassword"]').keyup(function(event) {
-		var oldRecoveryPassword = $('input:password[id="oldRecoveryPassword"]').val();
-		var newRecoveryPassword = $('input:password[id="newRecoveryPassword"]').val();
-		if (newRecoveryPassword != '' && oldRecoveryPassword != '' ) {
+		var oldRecoveryPassword = $('#oldEncryptionRecoveryPassword').val();
+		var newRecoveryPassword = $('#newEncryptionRecoveryPassword').val();
+		var newRecoveryPasswordRepeated = $('#repeatedNewEncryptionRecoveryPassword').val();
+
+		if (newRecoveryPassword !== '' && oldRecoveryPassword !== '' && newRecoveryPassword === newRecoveryPasswordRepeated) {
 			$('button:button[name="submitChangeRecoveryKey"]').removeAttr("disabled");
 		} else {
 			$('button:button[name="submitChangeRecoveryKey"]').attr("disabled", "true");
@@ -83,8 +87,8 @@ $(document).ready(function(){
 
 
 	$('button:button[name="submitChangeRecoveryKey"]').click(function() {
-		var oldRecoveryPassword = $('input:password[id="oldRecoveryPassword"]').val();
-		var newRecoveryPassword = $('input:password[id="newRecoveryPassword"]').val();
+		var oldRecoveryPassword = $('#oldEncryptionRecoveryPassword').val();
+		var newRecoveryPassword = $('#newEncryptionRecoveryPassword').val();
 		OC.msg.startSaving('#encryption .msg');
 		$.post(
 		OC.filePath( 'files_encryption', 'ajax', 'changeRecoveryPassword.php' )
@@ -98,5 +102,5 @@ $(document).ready(function(){
 			}
 		);
 	});
-	
+
 });
diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php
index 445d7ff8ca796ed911833df22685935578d2b316..ebfc00157f71820982baa7806b26cd49ee687486 100755
--- a/apps/files_encryption/lib/helper.php
+++ b/apps/files_encryption/lib/helper.php
@@ -199,12 +199,12 @@ class Helper {
 	public static function stripUserFilesPath($path) {
 		$trimmed = ltrim($path, '/');
 		$split = explode('/', $trimmed);
-		
+
 		// it is not a file relative to data/user/files
 		if (count($split) < 3 || $split[1] !== 'files') {
 			return false;
 		}
-		
+
 		$sliced = array_slice($split, 2);
 		$relPath = implode('/', $sliced);
 
@@ -219,30 +219,33 @@ class Helper {
 	public static function getPathToRealFile($path) {
 		$trimmed = ltrim($path, '/');
 		$split = explode('/', $trimmed);
-		
+
 		if (count($split) < 3 || $split[1] !== "files_versions") {
 			return false;
 		}
-		
+
 		$sliced = array_slice($split, 2);
 		$realPath = implode('/', $sliced);
 		//remove the last .v
 		$realPath = substr($realPath, 0, strrpos($realPath, '.v'));
 
 		return $realPath;
-	}	
-	
+	}
+
 	/**
 	 * @brief redirect to a error page
 	 */
-	public static function redirectToErrorPage() {
+	public static function redirectToErrorPage($session) {
+
+		$init = $session->getInitialized();
+
 		$location = \OC_Helper::linkToAbsolute('apps/files_encryption/files', 'error.php');
 		$post = 0;
 		if(count($_POST) > 0) {
 			$post = 1;
-		}
-		header('Location: ' . $location . '?p=' . $post);
-		exit();
+			}
+			header('Location: ' . $location . '?p=' . $post . '&i=' . $init);
+			exit();
 	}
 
 	/**
@@ -259,7 +262,7 @@ class Helper {
 
 		return (bool) $result;
 	}
-	
+
 	/**
 	 * check some common errors if the server isn't configured properly for encryption
 	 * @return bool true if configuration seems to be OK
diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php
index 4ec810a51996e817cca39324e8a9bbf0ec4fe6c5..6f630c83a3f5b277477155e461331aaeef840a07 100644
--- a/apps/files_encryption/lib/proxy.php
+++ b/apps/files_encryption/lib/proxy.php
@@ -317,7 +317,7 @@ class Proxy extends \OC_FileProxy {
 	public function postGetFileInfo($path, $data) {
 
 		// if path is a folder do nothing
-		if (is_array($data) && array_key_exists('size', $data)) {
+		if (\OCP\App::isEnabled('files_encryption') && is_array($data) && array_key_exists('size', $data)) {
 
 			// Disable encryption proxy to prevent recursive calls
 			$proxyStatus = \OC_FileProxy::$enabled;
diff --git a/apps/files_encryption/lib/session.php b/apps/files_encryption/lib/session.php
index 1911386cd12f06832d2406071a4092942191c7d7..25f2198181f3db0baddd7924db9c5806da10179e 100644
--- a/apps/files_encryption/lib/session.php
+++ b/apps/files_encryption/lib/session.php
@@ -30,6 +30,11 @@ class Session {
 
 	private $view;
 
+	const NOT_INITIALIZED = '0';
+	const INIT_EXECUTED = '1';
+	const INIT_SUCCESSFUL = '2';
+
+
 	/**
 	 * @brief if session is started, check if ownCloud key pair is set up, if not create it
 	 * @param \OC_FilesystemView $view
@@ -112,6 +117,36 @@ class Session {
 
 	}
 
+	/**
+	 * @brief Sets status of encryption app
+	 * @param string $init  INIT_SUCCESSFUL, INIT_EXECUTED, NOT_INOITIALIZED
+	 * @return bool
+	 *
+	 * @note this doesn not indicate of the init was successful, we just remeber the try!
+	 */
+	public function setInitialized($init) {
+
+		\OC::$session->set('encryptionInitialized', $init);
+
+		return true;
+
+	}
+
+
+	/**
+	 * @brief Gets status if we already tried to initialize the encryption app
+	 * @returns init status INIT_SUCCESSFUL, INIT_EXECUTED, NOT_INOITIALIZED
+	 *
+	 * @note this doesn not indicate of the init was successful, we just remeber the try!
+	 */
+	public function getInitialized() {
+		if (!is_null(\OC::$session->get('encryptionInitialized'))) {
+			return \OC::$session->get('encryptionInitialized');
+		} else {
+			return self::NOT_INITIALIZED;
+		}
+	}
+
 	/**
 	 * @brief Gets user or public share private key from session
 	 * @returns string $privateKey The user's plaintext private key
diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php
index 083b33c03cbf2be40e674a8e509c3d5743975fb7..02955bb064e982b9020a90b788021e95c59011a0 100644
--- a/apps/files_encryption/lib/stream.php
+++ b/apps/files_encryption/lib/stream.php
@@ -131,7 +131,7 @@ class Stream {
 
 			if($this->privateKey === false) {
 				// if private key is not valid redirect user to a error page
-				\OCA\Encryption\Helper::redirectToErrorPage();
+				\OCA\Encryption\Helper::redirectToErrorPage($this->session);
 			}
 
 			$this->size = $this->rootView->filesize($this->rawPath, $mode);
diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php
index df4d35cab0b42076108403b380e0249425f13e33..53d58fbf40d601c6951d052f2f643f3a5b0a9b24 100644
--- a/apps/files_encryption/lib/util.php
+++ b/apps/files_encryption/lib/util.php
@@ -37,7 +37,6 @@ class Util {
 	const MIGRATION_IN_PROGRESS = -1; // migration is running
 	const MIGRATION_OPEN = 0;         // user still needs to be migrated
 
-
 	private $view; // OC_FilesystemView object for filesystem operations
 	private $userId; // ID of the currently logged-in user
 	private $client; // Client side encryption mode flag
@@ -1752,6 +1751,11 @@ class Util {
 	 */
 	public function initEncryption($params) {
 
+		$session = new \OCA\Encryption\Session($this->view);
+
+		// we tried to initialize the encryption app for this session
+		$session->setInitialized(\OCA\Encryption\Session::INIT_EXECUTED);
+
 		$encryptedKey = Keymanager::getPrivateKey($this->view, $params['uid']);
 
 		$privateKey = Crypt::decryptPrivateKey($encryptedKey, $params['password']);
@@ -1762,9 +1766,8 @@ class Util {
 			return false;
 		}
 
-		$session = new \OCA\Encryption\Session($this->view);
-
 		$session->setPrivateKey($privateKey);
+		$session->setInitialized(\OCA\Encryption\Session::INIT_SUCCESSFUL);
 
 		return $session;
 	}
diff --git a/apps/files_encryption/settings-personal.php b/apps/files_encryption/settings-personal.php
index 589219f32ada5c05fcc112140861fe535ae62ce9..ffcb99602e2f6350affd7523a8b46d0d432572fa 100644
--- a/apps/files_encryption/settings-personal.php
+++ b/apps/files_encryption/settings-personal.php
@@ -16,7 +16,9 @@ $view = new \OC_FilesystemView('/');
 $util = new \OCA\Encryption\Util($view, $user);
 $session = new \OCA\Encryption\Session($view);
 
-$privateKeySet = $session->getPrivateKey() !== false;
+$privateKeySet = $session->getPrivateKey() !== false;
+// did we tried to initialize the keys for this session?
+$initialized = $session->getInitialized();
 
 $recoveryAdminEnabled = OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled');
 $recoveryEnabledForUser = $util->recoveryEnabledForUser();
@@ -31,6 +33,7 @@ if ($recoveryAdminEnabled || !$privateKeySet) {
 	$tmpl->assign('recoveryEnabled', $recoveryAdminEnabled);
 	$tmpl->assign('recoveryEnabledForUser', $recoveryEnabledForUser);
 	$tmpl->assign('privateKeySet', $privateKeySet);
+	$tmpl->assign('initialized', $initialized);
 
 	$result = $tmpl->fetchPage();
 }
diff --git a/apps/files_encryption/templates/invalid_private_key.php b/apps/files_encryption/templates/invalid_private_key.php
index 5c086d6514c1297fb80d5aaea13b9cf90ff3a5ba..9af65f831b43949b8c9c2060351e73079ad154f8 100644
--- a/apps/files_encryption/templates/invalid_private_key.php
+++ b/apps/files_encryption/templates/invalid_private_key.php
@@ -2,9 +2,11 @@
 	<li class='error'>
 		<?php $location = \OC_Helper::linkToRoute( "settings_personal" ).'#changePKPasswd' ?>
 
-		<?php p($l->t('Your private key is not valid! Maybe the your password was changed from outside.')); ?>
+		<?php p($_['message']); ?>
 		<br/>
-		<?php p($l->t('You can unlock your private key in your ')); ?> <a href="<?php echo $location?>"><?php p($l->t('personal settings')); ?>.</a>
+		<?php if($_['init']): ?>
+			<?php>p($l->t('Go directly to your ')); ?> <a href="<?php echo $location?>"><?php p($l->t('personal settings')); ?>.</a>
+		<?php endif; ?>
 		<br/>
 	</li>
 </ul>
diff --git a/apps/files_encryption/templates/settings-admin.php b/apps/files_encryption/templates/settings-admin.php
index f5f7582c2a69d141e49232f76e49e9fc14c708ac..3a6adc09f4b55ea2fee868aaee61de506891a033 100644
--- a/apps/files_encryption/templates/settings-admin.php
+++ b/apps/files_encryption/templates/settings-admin.php
@@ -10,14 +10,17 @@
 			<?php p($l->t("Enable recovery key (allow to recover users files in case of password loss):")); ?>
 			<br/>
 			<br/>
-			<input type="password" name="recoveryPassword" id="recoveryPassword"/>
+			<input type="password" name="encryptionRecoveryPassword" id="encryptionRecoveryPassword"/>
 			<label for="recoveryPassword"><?php p($l->t("Recovery key password")); ?></label>
 			<br/>
+			<input type="password" name="encryptionRecoveryPassword" id="repeatEncryptionRecoveryPassword"/>
+			<label for="repeatEncryptionRecoveryPassword"><?php p($l->t("Repeat Recovery key password")); ?></label>
+			<br/>
 			<input
 				type='radio'
 				name='adminEnableRecovery'
 				value='1'
-				<?php echo($_["recoveryEnabled"] == 1 ? 'checked="checked"' : 'disabled'); ?> />
+				<?php echo($_["recoveryEnabled"] === '1' ? 'checked="checked"' : 'disabled'); ?> />
 			<?php p($l->t("Enabled")); ?>
 			<br/>
 
@@ -25,27 +28,32 @@
 				type='radio'
 				name='adminEnableRecovery'
 				value='0'
-				<?php echo($_["recoveryEnabled"] == 0 ? 'checked="checked"' : 'disabled'); ?> />
+				<?php echo($_["recoveryEnabled"] === '0' ? 'checked="checked"' : 'disabled'); ?> />
 			<?php p($l->t("Disabled")); ?>
 		</p>
 		<br/><br/>
 
-		<p>
+		<p name="changeRecoveryPasswordBlock" <?php if ($_['recoveryEnabled'] === '0') print_unescaped('class="hidden"');?>>
 			<strong><?php p($l->t("Change recovery key password:")); ?></strong>
 			<br/><br/>
 			<input
 				type="password"
 				name="changeRecoveryPassword"
-				id="oldRecoveryPassword"
-				<?php echo($_["recoveryEnabled"] == 0 ? 'disabled' : ''); ?> />
-			<label for="oldRecoveryPassword"><?php p($l->t("Old Recovery key password")); ?></label>
+				id="oldEncryptionRecoveryPassword"
+			<label for="oldEncryptionRecoveryPassword"><?php p($l->t("Old Recovery key password")); ?></label>
+			<br/>
+			<br/>
+			<input
+				type="password"
+				name="changeRecoveryPassword"
+				id="newEncryptionRecoveryPassword"
+			<label for="newEncryptionRecoveryPassword"><?php p($l->t("New Recovery key password")); ?></label>
 			<br/>
 			<input
 				type="password"
 				name="changeRecoveryPassword"
-				id="newRecoveryPassword"
-				<?php echo($_["recoveryEnabled"] == 0 ? 'disabled' : ''); ?> />
-			<label for="newRecoveryPassword"><?php p($l->t("New Recovery key password")); ?></label>
+				id="repeatedNewEncryptionRecoveryPassword"
+			<label for="repeatEncryptionRecoveryPassword"><?php p($l->t("Repeat New Recovery key password")); ?></label>
 			<br/>
 			<button
 				type="button"
diff --git a/apps/files_encryption/templates/settings-personal.php b/apps/files_encryption/templates/settings-personal.php
index 38512453207a0af2ab836589d427f1abc394ef16..ff04556dd53ef9e9d5e104e6aa05fc6db857562c 100644
--- a/apps/files_encryption/templates/settings-personal.php
+++ b/apps/files_encryption/templates/settings-personal.php
@@ -4,7 +4,7 @@
 			<?php p( $l->t( 'Encryption' ) ); ?>
 		</legend>
 
-		<?php if ( ! $_["privateKeySet"] ): ?>
+		<?php if ( ! $_["privateKeySet"] && $_["initialized"] ): ?>
 			<p>
 				<a name="changePKPasswd" />
 				<label for="changePrivateKeyPasswd">
@@ -39,22 +39,22 @@
 		<?php endif; ?>
 
 		<br />
-		
+
 		<?php if ( $_["recoveryEnabled"] && $_["privateKeySet"] ): ?>
 			<p>
 				<label for="userEnableRecovery"><?php p( $l->t( "Enable password recovery:" ) ); ?></label>
 				<br />
 				<em><?php p( $l->t( "Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" ) ); ?></em>
 				<br />
-				<input 
+				<input
 				type='radio'
 				name='userEnableRecovery'
 				value='1'
 				<?php echo ( $_["recoveryEnabledForUser"] == 1 ? 'checked="checked"' : '' ); ?> />
 				<?php p( $l->t( "Enabled" ) ); ?>
 				<br />
-				
-				<input 
+
+				<input
 				type='radio'
 				name='userEnableRecovery'
 				value='0'
diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php
index 895d446a33650b3b3ec35460e99c2dac3d73117a..ffdcbf05109aa70f57ed02c75cb006a7576f061a 100644
--- a/apps/files_sharing/appinfo/app.php
+++ b/apps/files_sharing/appinfo/app.php
@@ -7,6 +7,7 @@ OC::$CLASSPATH['OC\Files\Cache\Shared_Cache'] = 'files_sharing/lib/cache.php';
 OC::$CLASSPATH['OC\Files\Cache\Shared_Permissions'] = 'files_sharing/lib/permissions.php';
 OC::$CLASSPATH['OC\Files\Cache\Shared_Updater'] = 'files_sharing/lib/updater.php';
 OC::$CLASSPATH['OC\Files\Cache\Shared_Watcher'] = 'files_sharing/lib/watcher.php';
+OC::$CLASSPATH['OCA\Files\Share\Api'] = 'files_sharing/lib/api.php';
 OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup');
 OCP\Share::registerBackend('file', 'OC_Share_Backend_File');
 OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file');
diff --git a/apps/files_sharing/appinfo/routes.php b/apps/files_sharing/appinfo/routes.php
index 02815b5eb42919936c19278333517faf562c1cb1..3469829b6f7dbc72bb34642edda9b8485ece5089 100644
--- a/apps/files_sharing/appinfo/routes.php
+++ b/apps/files_sharing/appinfo/routes.php
@@ -2,4 +2,33 @@
 $this->create('core_ajax_public_preview', '/publicpreview.png')->action(
 function() {
 	require_once __DIR__ . '/../ajax/publicpreview.php';
-});
\ No newline at end of file
+});
+
+// OCS API
+
+//TODO: SET: mail notification, waiting for PR #4689 to be accepted
+
+OC_API::register('get',
+		'/apps/files_sharing/api/v1/shares',
+		array('\OCA\Files\Share\Api', 'getAllShares'),
+		'files_sharing');
+
+OC_API::register('post',
+		'/apps/files_sharing/api/v1/shares',
+		array('\OCA\Files\Share\Api', 'createShare'),
+		'files_sharing');
+
+OC_API::register('get',
+		'/apps/files_sharing/api/v1/shares/{id}',
+		array('\OCA\Files\Share\Api', 'getShare'),
+		'files_sharing');
+
+OC_API::register('put',
+		'/apps/files_sharing/api/v1/shares/{id}',
+		array('\OCA\Files\Share\Api', 'updateShare'),
+		'files_sharing');
+
+OC_API::register('delete',
+		'/apps/files_sharing/api/v1/shares/{id}',
+		array('\OCA\Files\Share\Api', 'deleteShare'),
+		'files_sharing');
diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php
new file mode 100644
index 0000000000000000000000000000000000000000..e6624624898233966e897cab41d553214e8d94b1
--- /dev/null
+++ b/apps/files_sharing/lib/api.php
@@ -0,0 +1,467 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2013 Bjoern Schiessle schiessle@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files\Share;
+
+class Api {
+
+	/**
+	 * @brief get all shares
+	 *
+	 * @param array $params option 'file' to limit the result to a specific file/folder
+	 * @return \OC_OCS_Result share information
+	 */
+	public static function getAllShares($params) {
+
+		// if a file is specified, get the share for this file
+		if (isset($_GET['path'])) {
+			$params['itemSource'] = self::getFileId($_GET['path']);
+			$params['path'] = $_GET['path'];
+			if (isset($_GET['subfiles']) && $_GET['subfiles'] === 'true') {
+				return self::getSharesFromFolder($params);
+			}
+			return self::getShare($params);
+		}
+
+		$share = \OCP\Share::getItemShared('file', null);
+
+		if ($share === false) {
+			return new \OC_OCS_Result(null, 404, 'could not get shares');
+		} else {
+			return new \OC_OCS_Result($share);
+		}
+
+	}
+
+	/**
+	 * @brief get share information for a given share
+	 *
+	 * @param array $params which contains a 'id'
+	 * @return \OC_OCS_Result share information
+	 */
+	public static function getShare($params) {
+
+		// either the $params already contains a itemSource if we come from
+		//  getAllShare() or we need to translate the shareID to a itemSource
+		if(isset($params['itemSource'])) {
+			$itemSource = $params['itemSource'];
+			$getSpecificShare = true;
+		} else {
+			$s = self::getShareFromId($params['id']);
+			$itemSource = $s['item_source'];
+			$getSpecificShare = false;
+		}
+
+		if ($itemSource !== null) {
+			$shares = \OCP\Share::getItemShared('file', $itemSource);
+			// if a specific share was specified only return this one
+			if ($getSpecificShare === false) {
+				foreach ($shares as $share) {
+					if ($share['id'] === (int)$params['id']) {
+						$shares = array('element' => $share);
+						break;
+					}
+				}
+			}
+		} else {
+			$shares = null;
+		}
+
+		if ($shares === null || empty($shares)) {
+			return new \OC_OCS_Result(null, 404, 'share doesn\'t exist');
+		} else {
+			return new \OC_OCS_Result($shares);
+		}
+	}
+
+	/**
+	 * @brief get share from all files in a given folder (non-recursive)
+	 * @param array $params contains 'path' to the folder
+	 * @return \OC_OCS_Result
+	 */
+	private static function getSharesFromFolder($params) {
+		$path = $params['path'];
+		$view = new \OC\Files\View('/'.\OCP\User::getUser().'/files');
+
+		if(!$view->is_dir($path)) {
+			return new \OC_OCS_Result(null, 404, "not a directory");
+		}
+
+		$content = $view->getDirectoryContent($path);
+
+		$result = array();
+		foreach ($content as $file) {
+			$share = \OCP\Share::getItemShared('file', $file['fileid']);
+			if ($share) {
+				$share['filename'] = $file['name'];
+				$result[] = $share;
+			}
+		}
+
+		return new \OC_OCS_Result($result);
+	}
+
+	/**
+	 * @breif create a new share
+	 * @param array $params
+	 * @return \OC_OCS_Result
+	 */
+	public static function createShare($params) {
+
+		$path = isset($_POST['path']) ? $_POST['path'] : null;
+
+		if($path === null) {
+			return new \OC_OCS_Result(null, 400, "please specify a file or folder path");
+		}
+
+		$itemSource = self::getFileId($path);
+		$itemType = self::getItemType($path);
+
+		if($itemSource === null) {
+			return new \OC_OCS_Result(null, 404, "wrong path, file/folder doesn't exist.");
+		}
+
+		$shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null;
+		$shareType = isset($_POST['shareType']) ? (int)$_POST['shareType'] : null;
+
+		switch($shareType) {
+			case \OCP\Share::SHARE_TYPE_USER:
+				$permissions = isset($_POST['permissions']) ? (int)$_POST['permissions'] : 31;
+				break;
+			case \OCP\Share::SHARE_TYPE_GROUP:
+				$permissions = isset($_POST['permissions']) ? (int)$_POST['permissions'] : 31;
+				break;
+			case \OCP\Share::SHARE_TYPE_LINK:
+				//allow password protection
+				$shareWith = isset($_POST['password']) ? $_POST['password'] : null;
+				//check public link share
+				$publicUploadEnabled = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes');
+				$encryptionEnabled = \OC_App::isEnabled('files_encryption');
+				if(isset($_POST['publicUpload']) &&
+						($encryptionEnabled || $publicUploadEnabled !== 'yes')) {
+					return new \OC_OCS_Result(null, 404, "public upload disabled by the administrator");
+				}
+				$publicUpload = isset($_POST['publicUpload']) ? $_POST['publicUpload'] : 'false';
+				// read, create, update (7) if public upload is enabled or
+				// read (1) if public upload is disabled
+				$permissions = $publicUpload === 'true' ? 7 : 1;
+				break;
+			default:
+				return new \OC_OCS_Result(null, 404, "unknown share type");
+		}
+
+		try	{
+			$token = \OCP\Share::shareItem(
+					$itemType,
+					$itemSource,
+					$shareType,
+					$shareWith,
+					$permissions
+					);
+		} catch (\Exception $e) {
+			return new \OC_OCS_Result(null, 404, $e->getMessage());
+		}
+
+		if ($token) {
+			$data = array();
+			$data['id'] = 'unknown';
+			$shares = \OCP\Share::getItemShared('file', $itemSource);
+			if(is_string($token)) { //public link share
+				foreach ($shares as $share) {
+					if ($share['token'] === $token) {
+						$data['id'] = $share['id'];
+						break;
+					}
+				}
+				$url = \OCP\Util::linkToPublic('files&t='.$token);
+				$data['url'] = $url; // '&' gets encoded to $amp;
+				$data['token'] = $token;
+
+			} else {
+				foreach ($shares as $share) {
+					if ($share['share_with'] === $shareWith && $share['share_type'] === $shareType) {
+						$data['id'] = $share['id'];
+						break;
+					}
+				}
+			}
+			return new \OC_OCS_Result($data);
+		} else {
+			return new \OC_OCS_Result(null, 404, "couldn't share file");
+		}
+	}
+
+	/**
+	 * update shares, e.g. password, permissions, etc
+	 * @param array $params shareId 'id' and the parameter we want to update
+	 *                      currently supported: permissions, password, publicUpload
+	 * @return \OC_OCS_Result
+	 */
+	public static function updateShare($params) {
+
+		$share = self::getShareFromId($params['id']);
+		$itemSource = isset($share['item_source']) ? $share['item_source'] : null;
+
+		if($itemSource === null) {
+			return new \OC_OCS_Result(null, 404, "wrong share Id, share doesn't exist.");
+		}
+
+		try {
+			if(isset($params['_put']['permissions'])) {
+				return self::updatePermissions($share, $params);
+			} elseif (isset($params['_put']['password'])) {
+				return self::updatePassword($share, $params);
+			} elseif (isset($params['_put']['publicUpload'])) {
+				return self::updatePublicUpload($share, $params);
+			}
+		} catch (\Exception $e) {
+			return new \OC_OCS_Result(null, 400, $e->getMessage());
+		}
+
+		return new \OC_OCS_Result(null, 400, "Wrong or no update parameter given");
+
+	}
+
+	/**
+	 * @brief update permissions for a share
+	 * @param array $share information about the share
+	 * @param array $params contains 'permissions'
+	 * @return \OC_OCS_Result
+	 */
+	private static function updatePermissions($share, $params) {
+
+		$itemSource = $share['item_source'];
+		$itemType = $share['item_type'];
+		$shareWith = $share['share_with'];
+		$shareType = $share['share_type'];
+		$permissions = isset($params['_put']['permissions']) ? (int)$params['_put']['permissions'] : null;
+
+		$publicUploadStatus = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes');
+		$encryptionEnabled = \OC_App::isEnabled('files_encryption');
+		$publicUploadEnabled = false;
+		if(!$encryptionEnabled && $publicUploadStatus === 'yes') {
+			$publicUploadEnabled = true;
+		}
+
+		// only change permissions for public shares if public upload is enabled
+		// and we want to set permissions to 1 (read only) or 7 (allow upload)
+		if ( (int)$shareType === \OCP\Share::SHARE_TYPE_LINK ) {
+			if ($publicUploadEnabled === false || ($permissions !== 7 && $permissions !== 1)) {
+				return new \OC_OCS_Result(null, 400, "can't change permission for public link share");
+			}
+		}
+
+		try {
+			$return = \OCP\Share::setPermissions(
+					$itemType,
+					$itemSource,
+					$shareType,
+					$shareWith,
+					$permissions
+					);
+		} catch (\Exception $e) {
+			return new \OC_OCS_Result(null, 404, $e->getMessage());
+		}
+
+		if ($return) {
+			return new \OC_OCS_Result();
+		} else {
+			return new \OC_OCS_Result(null, 404, "couldn't set permissions");
+		}
+	}
+
+	/**
+	 * @brief enable/disable public upload
+	 * @param array $share information about the share
+	 * @param array $params contains 'publicUpload' which can be 'yes' or 'no'
+	 * @return \OC_OCS_Result
+	 */
+	private static function updatePublicUpload($share, $params) {
+
+		$publicUploadEnabled = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes');
+		$encryptionEnabled = \OC_App::isEnabled('files_encryption');
+		if($encryptionEnabled || $publicUploadEnabled !== 'yes') {
+			return new \OC_OCS_Result(null, 404, "public upload disabled by the administrator");
+		}
+
+		if ($share['item_type'] !== 'folder' ||
+				(int)$share['share_type'] !== \OCP\Share::SHARE_TYPE_LINK ) {
+			return new \OC_OCS_Result(null, 404, "public upload is only possible for public shared folders");
+		}
+
+		// read, create, update (7) if public upload is enabled or
+		// read (1) if public upload is disabled
+		$params['_put']['permissions'] = $params['_put']['publicUpload'] === 'true' ? 7 : 1;
+
+		return self::updatePermissions($share, $params);
+
+	}
+
+	/**
+	 * @brief update password for public link share
+	 * @param array $share information about the share
+	 * @param type $params 'password'
+	 * @return \OC_OCS_Result
+	 */
+	private static function updatePassword($share, $params) {
+
+		$itemSource = $share['item_source'];
+		$itemType = $share['item_type'];
+
+		if( (int)$share['share_type'] !== \OCP\Share::SHARE_TYPE_LINK) {
+			return  new \OC_OCS_Result(null, 400, "password protection is only supported for public shares");
+		}
+
+		$shareWith = isset($params['_put']['password']) ? $params['_put']['password'] : null;
+
+		if($shareWith === '') {
+			$shareWith = null;
+		}
+
+		$items = \OCP\Share::getItemShared($itemType, $itemSource);
+
+		$checkExists = false;
+		foreach ($items as $item) {
+			if($item['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
+				$checkExists = true;
+				$permissions = $item['permissions'];
+			}
+		}
+
+		if (!$checkExists) {
+			return  new \OC_OCS_Result(null, 404, "share doesn't exists, can't change password");
+		}
+
+		$result = \OCP\Share::shareItem(
+				$itemType,
+				$itemSource,
+				\OCP\Share::SHARE_TYPE_LINK,
+				$shareWith,
+				$permissions
+				);
+		if($result) {
+			return new \OC_OCS_Result();
+		}
+
+		return new \OC_OCS_Result(null, 404, "couldn't set password");
+	}
+
+	/**
+	 * @brief unshare a file/folder
+	 * @param array $params contains the shareID 'id' which should be unshared
+	 * @return \OC_OCS_Result
+	 */
+	public static function deleteShare($params) {
+
+		$share = self::getShareFromId($params['id']);
+		$itemSource = isset($share['item_source']) ? $share['item_source'] : null;
+		$itemType = isset($share['item_type']) ? $share['item_type'] : null;;
+
+		if($itemSource === null) {
+			return new \OC_OCS_Result(null, 404, "wrong share ID, share doesn't exist.");
+		}
+
+		$shareWith = isset($share['share_with']) ? $share['share_with'] : null;
+		$shareType = isset($share['share_type']) ? (int)$share['share_type'] : null;
+
+		if( $shareType === \OCP\Share::SHARE_TYPE_LINK) {
+			$shareWith = null;
+		}
+
+		try {
+			$return = \OCP\Share::unshare(
+					$itemType,
+					$itemSource,
+					$shareType,
+					$shareWith);
+		} catch (\Exception $e) {
+			return new \OC_OCS_Result(null, 404, $e->getMessage());
+		}
+
+		if ($return) {
+			return new \OC_OCS_Result();
+		} else {
+			$msg = "Unshare Failed";
+			return new \OC_OCS_Result(null, 404, $msg);
+		}
+	}
+
+	/**
+	 * @brief get file ID from a given path
+	 * @param string $path
+	 * @return string fileID or null
+	 */
+	private static function getFileId($path) {
+
+		$view = new \OC\Files\View('/'.\OCP\User::getUser().'/files');
+		$fileId = null;
+
+		$fileInfo = $view->getFileInfo($path);
+		if ($fileInfo) {
+			$fileId = $fileInfo['fileid'];
+		}
+
+		return $fileId;
+	}
+
+	/**
+	 * @brief get itemType
+	 * @param string $path
+	 * @return string type 'file', 'folder' or null of file/folder doesn't exists
+	 */
+	private static function getItemType($path) {
+		$view = new \OC\Files\View('/'.\OCP\User::getUser().'/files');
+		$itemType = null;
+
+		if ($view->is_dir($path)) {
+			$itemType = "folder";
+		} elseif ($view->is_file($path)) {
+			$itemType = "file";
+		}
+
+		return $itemType;
+	}
+
+	/**
+	 * @brief get some information from a given share
+	 * @param int $shareID
+	 * @return array with: item_source, share_type, share_with, item_type, permissions
+	 */
+	private static function getShareFromId($shareID) {
+		$sql = 'SELECT `item_source`, `share_type`, `share_with`, `item_type`, `permissions` FROM `*PREFIX*share` WHERE `id` = ?';
+		$args = array($shareID);
+		$query = \OCP\DB::prepare($sql);
+		$result = $query->execute($args);
+
+		if (\OCP\DB::isError($result)) {
+			\OCP\Util::writeLog('files_sharing', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
+			return null;
+		}
+		if ($share = $result->fetchRow()) {
+			return $share;
+		}
+
+		return null;
+
+	}
+
+}
diff --git a/apps/files_sharing/tests/api.php b/apps/files_sharing/tests/api.php
new file mode 100644
index 0000000000000000000000000000000000000000..c55c186f089a92787df0b21129307b4e8e640971
--- /dev/null
+++ b/apps/files_sharing/tests/api.php
@@ -0,0 +1,537 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2013 Bjoern Schiessle <schiessle@owncloud.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+require_once __DIR__ . '/../../../lib/base.php';
+
+use OCA\Files\Share;
+
+/**
+ * Class Test_Files_Sharing_Api
+ */
+class Test_Files_Sharing_Api extends \PHPUnit_Framework_TestCase {
+
+	const TEST_FILES_SHARING_API_USER1 = "test-share-user1";
+	const TEST_FILES_SHARING_API_USER2 = "test-share-user2";
+
+	public $stateFilesEncryption;
+	public $filename;
+	public $data;
+	/**
+	 * @var OC_FilesystemView
+	 */
+	public $view;
+	public $folder;
+
+	public static function setUpBeforeClass() {
+		// reset backend
+		\OC_User::clearBackends();
+		\OC_User::useBackend('database');
+
+		// clear share hooks
+		\OC_Hook::clear('OCP\\Share');
+		\OC::registerShareHooks();
+		\OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup');
+
+		// create users
+		self::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1, true);
+		self::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, true);
+
+	}
+
+	function setUp() {
+		$this->data = 'foobar';
+		$this->view = new \OC_FilesystemView('/' . \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1 . '/files');
+
+		$this->folder = '/folder_share_api_test';
+
+		$this->filename = 'share-api-test.txt';
+
+		// remember files_encryption state
+		$this->stateFilesEncryption = \OC_App::isEnabled('files_encryption');
+
+		 //we don't want to tests with app files_encryption enabled
+		\OC_App::disable('files_encryption');
+
+
+		$this->assertTrue(!\OC_App::isEnabled('files_encryption'));
+
+		// save file with content
+		$this->view->file_put_contents($this->filename, $this->data);
+		$this->view->mkdir($this->folder);
+		$this->view->file_put_contents($this->folder.'/'.$this->filename, $this->data);
+
+	}
+
+	function tearDown() {
+		$this->view->unlink($this->filename);
+		$this->view->deleteAll($this->folder);
+		// reset app files_encryption
+		if ($this->stateFilesEncryption) {
+			\OC_App::enable('files_encryption');
+		} else {
+			\OC_App::disable('files_encryption');
+		}
+	}
+
+	public static function tearDownAfterClass() {
+
+		// cleanup users
+		\OC_User::deleteUser(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1);
+		\OC_User::deleteUser(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+	}
+
+	/**
+	 * @medium
+	 */
+	function testCreateShare() {
+
+		//login as user1
+		\Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1);
+
+		// share to user
+
+		// simulate a post request
+		$_POST['path'] = $this->filename;
+		$_POST['shareWith'] = \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2;
+		$_POST['shareType'] = \OCP\Share::SHARE_TYPE_USER;
+
+		$result = Share\Api::createShare(array());
+
+		$this->assertTrue($result->succeeded());
+		$data = $result->getData();
+
+		$share = $this->getShareFromId($data['id']);
+
+		$items = \OCP\Share::getItemShared('file', $share['item_source']);
+
+		$this->assertTrue(!empty($items));
+
+		// share link
+
+		// simulate a post request
+		$_POST['path'] = $this->folder;
+		$_POST['shareType'] = \OCP\Share::SHARE_TYPE_LINK;
+
+		$result = Share\Api::createShare(array());
+
+		// check if API call was successful
+		$this->assertTrue($result->succeeded());
+
+		$data = $result->getData();
+
+		// check if we have a token
+		$this->assertTrue(is_string($data['token']));
+
+		$share = $this->getShareFromId($data['id']);
+
+		$items = \OCP\Share::getItemShared('file', $share['item_source']);
+
+		$this->assertTrue(!empty($items));
+
+		$fileinfo = $this->view->getFileInfo($this->filename);
+
+		\OCP\Share::unshare('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+				\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+
+		$fileinfo = $this->view->getFileInfo($this->folder);
+
+		\OCP\Share::unshare('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
+
+
+
+	}
+
+	/**
+	 * @medium
+	 * @depends testCreateShare
+	 */
+	function testGetAllShares() {
+
+		$fileinfo = $this->view->getFileInfo($this->filename);
+
+		\OCP\Share::shareItem('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+		\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
+
+		$result = Share\Api::getAllShares(array());
+
+		$this->assertTrue($result->succeeded());
+
+        // test should return two shares created from testCreateShare()
+		$this->assertTrue(count($result->getData()) === 1);
+
+		\OCP\Share::unshare('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+				\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+	}
+
+	/**
+	 * @medium
+	 * @depends testCreateShare
+	 */
+	function testGetShareFromSource() {
+
+		$fileInfo = $this->view->getFileInfo($this->filename);
+
+		\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+				\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
+
+		\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK,
+				null, 1);
+
+		$params = array('itemSource' => $fileInfo['fileid']);
+
+		$result = Share\Api::getShare($params);
+
+		$this->assertTrue($result->succeeded());
+
+        // test should return one share created from testCreateShare()
+		$this->assertTrue(count($result->getData()) === 2);
+
+		\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+				\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+
+		\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
+
+	}
+
+	/**
+	 * @medium
+	 * @depends testCreateShare
+	 */
+	function testGetShareFromId() {
+
+		$fileInfo = $this->view->getFileInfo($this->filename);
+
+		$result = \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+			\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
+
+		// share was successful?
+		$this->assertTrue($result);
+
+		// get item to determine share ID
+		$result = \OCP\Share::getItemShared('file', $fileInfo['fileid']);
+
+		$this->assertEquals(1, count($result));
+
+		// get first element
+		$share = reset($result);
+
+		// call getShare() with share ID
+		$params = array('id' => $share['id']);
+		$result = Share\Api::getShare($params);
+
+		$this->assertTrue($result->succeeded());
+
+		// test should return one share created from testCreateShare()
+		$this->assertEquals(1, count($result->getData()));
+
+		\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+			\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+
+	}
+
+	/**
+	 * @medium
+	 */
+	function testGetShareFromFolder() {
+
+		$fileInfo1 = $this->view->getFileInfo($this->filename);
+		$fileInfo2 = $this->view->getFileInfo($this->folder.'/'.$this->filename);
+
+		$result = \OCP\Share::shareItem('file', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER,
+				\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
+
+		// share was successful?
+		$this->assertTrue($result);
+
+		$result = \OCP\Share::shareItem('folder', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK,
+				null, 1);
+
+		// share was successful?
+		$this->assertTrue(is_string($result));
+
+		$_GET['path'] = $this->folder;
+		$_GET['subfiles'] = 'true';
+
+		$result = Share\Api::getAllShares(array());
+
+		$this->assertTrue($result->succeeded());
+
+        // test should return one share within $this->folder
+		$this->assertTrue(count($result->getData()) === 1);
+
+		\OCP\Share::unshare('file', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER,
+				\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+
+		\OCP\Share::unshare('folder', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
+
+	}
+
+	/**
+	 * @medium
+	 */
+	function testGetShareFromUnknownId() {
+
+		$params = array('id' => 0);
+
+		$result = Share\Api::getShare($params);
+
+		$this->assertEquals(404, $result->getStatusCode());
+		$this->assertEquals('share doesn\'t exist', $result->getMeta()['message']);
+
+	}
+
+	/**
+	 * @medium
+	 * @depends testCreateShare
+	 */
+	function testUpdateShare() {
+
+		$fileInfo = $this->view->getFileInfo($this->filename);
+
+		$result = \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+				\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
+
+		// share was successful?
+		$this->assertTrue($result);
+
+		$result = \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK,
+				null, 1);
+
+		// share was successful?
+		$this->assertTrue(is_string($result));
+
+		$items = \OCP\Share::getItemShared('file', null);
+
+		// make sure that we found a link share and a user share
+		$this->assertEquals(count($items), 2);
+
+		$linkShare = null;
+		$userShare = null;
+
+		foreach ($items as $item) {
+			if ($item['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
+				$linkShare = $item;
+			}
+			if ($item['share_type'] === \OCP\Share::SHARE_TYPE_USER) {
+				$userShare = $item;
+			}
+		}
+
+		// make sure that we found a link share and a user share
+		$this->assertTrue(is_array($linkShare));
+		$this->assertTrue(is_array($userShare));
+
+		// update permissions
+
+		$this->assertEquals('31', $userShare['permissions']);
+
+		$params = array();
+		$params['id'] = $userShare['id'];
+		$params['_put'] = array();
+		$params['_put']['permissions'] = 1;
+
+		$result = Share\Api::updateShare($params);
+
+		$this->assertTrue($result->succeeded(), $result->getMeta()['message']);
+
+		$items = \OCP\Share::getItemShared('file', $userShare['file_source']);
+
+		$newUserShare = null;
+		foreach ($items as $item) {
+			if ($item['share_with'] === \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2) {
+				$newUserShare = $item;
+				break;
+			}
+		}
+
+		$this->assertTrue(is_array($newUserShare));
+
+		$this->assertEquals('1', $newUserShare['permissions']);
+
+		// update password for link share
+
+		$this->assertTrue(empty($linkShare['share_with']));
+
+		$params = array();
+		$params['id'] = $linkShare['id'];
+		$params['_put'] = array();
+		$params['_put']['password'] = 'foo';
+
+		$result = Share\Api::updateShare($params);
+
+		$this->assertTrue($result->succeeded());
+
+		$items = \OCP\Share::getItemShared('file', $linkShare['file_source']);
+
+		$newLinkShare = null;
+		foreach ($items as $item) {
+			if ($item['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
+				$newLinkShare = $item;
+				break;
+			}
+		}
+
+		$this->assertTrue(is_array($newLinkShare));
+		$this->assertTrue(!empty($newLinkShare['share_with']));
+
+		\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+				\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
+
+		\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
+
+	}
+
+	/**
+	 * @medium
+	 */
+	function testUpdateShareUpload() {
+
+		$fileInfo = $this->view->getFileInfo($this->folder);
+
+		$result = \OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK,
+				null, 1);
+
+		// share was successful?
+		$this->assertTrue(is_string($result));
+
+		$items = \OCP\Share::getItemShared('file', null);
+
+		// make sure that we found a link share and a user share
+		$this->assertEquals(count($items), 1);
+
+		$linkShare = null;
+
+		foreach ($items as $item) {
+			if ($item['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
+				$linkShare = $item;
+			}
+		}
+
+		// make sure that we found a link share
+		$this->assertTrue(is_array($linkShare));
+
+		// update public upload
+
+		$params = array();
+		$params['id'] = $linkShare['id'];
+		$params['_put'] = array();
+		$params['_put']['publicUpload'] = 'true';
+
+		$result = Share\Api::updateShare($params);
+
+		$this->assertTrue($result->succeeded());
+
+		$items = \OCP\Share::getItemShared('file', $linkShare['file_source']);
+
+		$updatedLinkShare = null;
+		foreach ($items as $item) {
+			if ($item['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
+				$updatedLinkShare = $item;
+				break;
+			}
+		}
+
+		$this->assertTrue(is_array($updatedLinkShare));
+		$this->assertEquals(7, $updatedLinkShare['permissions']);
+
+		// cleanup
+
+		\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
+
+	}
+
+	/**
+	 * @medium
+	 * @depends testCreateShare
+	 */
+	function testDeleteShare() {
+
+		$fileInfo = $this->view->getFileInfo($this->filename);
+
+		\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
+				\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
+
+		\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK,
+				null, 1);
+
+		$items = \OCP\Share::getItemShared('file', null);
+
+		$this->assertEquals(2, count($items));
+
+		foreach ($items as $item) {
+			$result = Share\Api::deleteShare(array('id' => $item['id']));
+
+			$this->assertTrue($result->succeeded());
+		}
+
+		$itemsAfterDelete = \OCP\Share::getItemShared('file', null);
+
+		$this->assertTrue(empty($itemsAfterDelete));
+
+	}
+
+	/**
+	 * @param $user
+	 * @param bool $create
+	 * @param bool $password
+	 */
+	private static function loginHelper($user, $create = false, $password = false) {
+		if ($create) {
+			\OC_User::createUser($user, $user);
+		}
+
+		if ($password === false) {
+			$password = $user;
+		}
+
+		\OC_Util::tearDownFS();
+		\OC_User::setUserId('');
+		\OC\Files\Filesystem::tearDown();
+		\OC_Util::setupFS($user);
+		\OC_User::setUserId($user);
+
+		$params['uid'] = $user;
+		$params['password'] = $password;
+	}
+
+	/**
+	 * @brief get some information from a given share
+	 * @param int $shareID
+	 * @return array with: item_source, share_type, share_with, item_type, permissions
+	 */
+	private function getShareFromId($shareID) {
+		$sql = 'SELECT `item_source`, `share_type`, `share_with`, `item_type`, `permissions` FROM `*PREFIX*share` WHERE `id` = ?';
+		$args = array($shareID);
+		$query = \OCP\DB::prepare($sql);
+		$result = $query->execute($args);
+
+		$share = Null;
+
+		if ($result && $result->numRows() > 0) {
+				$share = $result->fetchRow();
+		}
+
+		return $share;
+
+	}
+
+}
diff --git a/apps/files_versions/ajax/preview.php b/apps/files_versions/ajax/preview.php
new file mode 100644
index 0000000000000000000000000000000000000000..c24134df5347559513a2b53e344d05b33fb9a78d
--- /dev/null
+++ b/apps/files_versions/ajax/preview.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+\OC_Util::checkLoggedIn();
+
+if(!\OC_App::isEnabled('files_versions')){
+	exit;
+}
+
+$file = array_key_exists('file', $_GET) ? (string) urldecode($_GET['file']) : '';
+$maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : 44;
+$maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : 44;
+$version = array_key_exists('version', $_GET) ? $_GET['version'] : '';
+$scalingUp = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true;
+
+if($file === '' && $version === '') {
+	\OC_Response::setStatus(400); //400 Bad Request
+	\OC_Log::write('core-preview', 'No file parameter was passed', \OC_Log::DEBUG);
+	exit;
+}
+
+if($maxX === 0 || $maxY === 0) {
+	\OC_Response::setStatus(400); //400 Bad Request
+	\OC_Log::write('core-preview', 'x and/or y set to 0', \OC_Log::DEBUG);
+	exit;
+}
+
+try{
+	$preview = new \OC\Preview(\OC_User::getUser(), 'files_versions');
+	$preview->setFile($file.'.v'.$version);
+	$preview->setMaxX($maxX);
+	$preview->setMaxY($maxY);
+	$preview->setScalingUp($scalingUp);
+
+	$preview->showPreview();
+}catch(\Exception $e) {
+	\OC_Response::setStatus(500);
+	\OC_Log::write('core', $e->getmessage(), \OC_Log::DEBUG);
+}
diff --git a/apps/files_versions/appinfo/routes.php b/apps/files_versions/appinfo/routes.php
index 38c288adf9d2a6ecc91970d62820b9dbb6def254..8d2abaa89e5ed80cc8a5585e41394a6774d62f9b 100644
--- a/apps/files_versions/appinfo/routes.php
+++ b/apps/files_versions/appinfo/routes.php
@@ -7,3 +7,8 @@
 
 // Register with the capabilities API
 OC_API::register('get', '/cloud/capabilities', array('OCA\Files_Versions\Capabilities', 'getCapabilities'), 'files_versions', OC_API::USER_AUTH);
+
+$this->create('core_ajax_versions_preview', '/preview.png')->action(
+function() {
+	require_once __DIR__ . '/../ajax/preview.php';
+});
diff --git a/apps/files_versions/css/versions.css b/apps/files_versions/css/versions.css
index 6a9b3a95698a8cb8da9f4d3624a934c9980a39a2..c53935711c71c5d15b5a121d2b87f221b24fd905 100644
--- a/apps/files_versions/css/versions.css
+++ b/apps/files_versions/css/versions.css
@@ -1,11 +1,11 @@
 #dropdown.drop-versions {
-	width:22em;
+	width:24em;
 }
 
 #found_versions li {
 	width: 100%;
 	cursor: default;
-	height: 36px;
+	height: 56px;
 	float: left;
 	border-bottom: 1px solid rgba(100,100,100,.1);
 }
@@ -21,6 +21,12 @@
 	filter: alpha(opacity=50);
 	opacity: .5;
 }
+
+#found_versions li > a,
+#found_versions li > span {
+	padding: 17px 7px;
+}
+
 #found_versions li > *:hover,
 #found_versions li > *:focus {
 	-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
@@ -33,6 +39,11 @@
 	padding-right: 4px;
 }
 
+#found_versions img.preview {
+	cursor: default;
+	opacity: 1;
+}
+
 #found_versions .versionDate {
 	min-width: 100px;
 	vertical-align: text-bottom;
diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js
index f57e931bad9aa328dd2d6c504ee86f82567e61bc..3f56a3eb69857f0f86b3049bbeabe5b1d7f76607 100644
--- a/apps/files_versions/js/versions.js
+++ b/apps/files_versions/js/versions.js
@@ -129,6 +129,8 @@ function createVersionsDropdown(filename, files) {
 
 		var path = OC.filePath('files_versions', '', 'download.php');
 
+		var preview = '<img class="preview" src="'+revision.preview+'"/>';
+
 		var download ='<a href="' + path + "?file=" + files + '&revision=' + revision.version + '">';
 		download+='<img';
 		download+=' src="' + OC.imagePath('core', 'actions/download') + '"';
@@ -146,7 +148,7 @@ function createVersionsDropdown(filename, files) {
 
 		var version=$('<li/>');
 		version.attr('value', revision.version);
-		version.html(download + revert);
+		version.html(preview + download + revert);
 
 		version.appendTo('#found_versions');
 	}
diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php
index 0b4699dc5c032b109c83a0de7ed3be4d3af57bbc..fc8d0365c71da502084bd0355cf8b4b582cf30aa 100644
--- a/apps/files_versions/lib/versions.php
+++ b/apps/files_versions/lib/versions.php
@@ -266,6 +266,7 @@ class Storage {
 				$versions[$key]['version'] = $version;
 				$versions[$key]['humanReadableTimestamp'] = self::getHumanReadableTimestamp($version);
 				$versions[$key]['path'] = $filename;
+				$versions[$key]['preview'] = \OCP\Util::linkToRoute('core_ajax_versions_preview', array('file' => $filename, 'version' => $version));
 				$versions[$key]['size'] = $versions_fileview->filesize($filename.'.v'.$version);
 
 				// if file with modified date exists, flag it in array as currently enabled version
diff --git a/apps/user_ldap/ajax/getConfiguration.php b/apps/user_ldap/ajax/getConfiguration.php
index baca588976fc8a8a7755d059d88173477f26dab3..fc51b459a25b0b672900815ff9f68fc8a731e4ed 100644
--- a/apps/user_ldap/ajax/getConfiguration.php
+++ b/apps/user_ldap/ajax/getConfiguration.php
@@ -27,5 +27,6 @@ OCP\JSON::checkAppEnabled('user_ldap');
 OCP\JSON::callCheck();
 
 $prefix = $_POST['ldap_serverconfig_chooser'];
-$connection = new \OCA\user_ldap\lib\Connection($prefix);
+$ldapWrapper = new OCA\user_ldap\lib\LDAP();
+$connection = new \OCA\user_ldap\lib\Connection($ldapWrapper, $prefix);
 OCP\JSON::success(array('configuration' => $connection->getConfiguration()));
diff --git a/apps/user_ldap/ajax/setConfiguration.php b/apps/user_ldap/ajax/setConfiguration.php
index d850bda24706830145d965059b7bb1be8e375f22..94de8835fbcf9540367c2dd3c6d2cd060e940498 100644
--- a/apps/user_ldap/ajax/setConfiguration.php
+++ b/apps/user_ldap/ajax/setConfiguration.php
@@ -27,7 +27,8 @@ OCP\JSON::checkAppEnabled('user_ldap');
 OCP\JSON::callCheck();
 
 $prefix = $_POST['ldap_serverconfig_chooser'];
-$connection = new \OCA\user_ldap\lib\Connection($prefix);
+$ldapWrapper = new OCA\user_ldap\lib\LDAP();
+$connection = new \OCA\user_ldap\lib\Connection($ldapWrapper, $prefix);
 $connection->setConfiguration($_POST);
 $connection->saveConfiguration();
 OCP\JSON::success();
diff --git a/apps/user_ldap/ajax/testConfiguration.php b/apps/user_ldap/ajax/testConfiguration.php
index 7ce1258a7967e7b8cd711f021b4f0fe4346b213d..0b8e4ccfe2048dac6248a43eb667dc75b64b5ddf 100644
--- a/apps/user_ldap/ajax/testConfiguration.php
+++ b/apps/user_ldap/ajax/testConfiguration.php
@@ -28,7 +28,8 @@ OCP\JSON::callCheck();
 
 $l=OC_L10N::get('user_ldap');
 
-$connection = new \OCA\user_ldap\lib\Connection('', null);
+$ldapWrapper = new OCA\user_ldap\lib\LDAP();
+$connection = new \OCA\user_ldap\lib\Connection($ldapWrapper, '', null);
 if($connection->setConfiguration($_POST)) {
 	//Configuration is okay
 	if($connection->bind()) {
diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php
index 593e846bc03f8dea4514bd05cf32c99672e90717..9d6327181afb3b67181eac2afa88267ad6789399 100644
--- a/apps/user_ldap/appinfo/app.php
+++ b/apps/user_ldap/appinfo/app.php
@@ -24,15 +24,15 @@
 OCP\App::registerAdmin('user_ldap', 'settings');
 
 $configPrefixes = OCA\user_ldap\lib\Helper::getServerConfigurationPrefixes(true);
+$ldapWrapper = new OCA\user_ldap\lib\LDAP();
 if(count($configPrefixes) === 1) {
-	$connector = new OCA\user_ldap\lib\Connection($configPrefixes[0]);
-	$userBackend  = new OCA\user_ldap\USER_LDAP();
-	$userBackend->setConnector($connector);
-	$groupBackend = new OCA\user_ldap\GROUP_LDAP();
-	$groupBackend->setConnector($connector);
+	$connector = new OCA\user_ldap\lib\Connection($ldapWrapper, $configPrefixes[0]);
+	$ldapAccess = new OCA\user_ldap\lib\Access($connector, $ldapWrapper);
+	$userBackend  = new OCA\user_ldap\USER_LDAP($ldapAccess);
+	$groupBackend = new OCA\user_ldap\GROUP_LDAP($ldapAccess);
 } else {
-	$userBackend  = new OCA\user_ldap\User_Proxy($configPrefixes);
-	$groupBackend  = new OCA\user_ldap\Group_Proxy($configPrefixes);
+	$userBackend  = new OCA\user_ldap\User_Proxy($configPrefixes, $ldapWrapper);
+	$groupBackend  = new OCA\user_ldap\Group_Proxy($configPrefixes, $ldapWrapper);
 }
 
 if(count($configPrefixes) > 0) {
diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php
index 04ff392f9205ab88f692e4994abc47a19b080a6d..32e2cec5960ade394ef892a661e2a9b6d1f8c4eb 100644
--- a/apps/user_ldap/group_ldap.php
+++ b/apps/user_ldap/group_ldap.php
@@ -23,13 +23,16 @@
 
 namespace OCA\user_ldap;
 
-class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
+use OCA\user_ldap\lib\Access;
+use OCA\user_ldap\lib\BackendUtility;
+
+class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
 	protected $enabled = false;
 
-	public function setConnector(lib\Connection &$connection) {
-		parent::setConnector($connection);
-		$filter = $this->connection->ldapGroupFilter;
-		$gassoc = $this->connection->ldapGroupMemberAssocAttr;
+	public function __construct(Access $access) {
+		parent::__construct($access);
+		$filter = $this->access->connection->ldapGroupFilter;
+		$gassoc = $this->access->connection->ldapGroupMemberAssocAttr;
 		if(!empty($filter) && !empty($gassoc)) {
 			$this->enabled = true;
 		}
@@ -47,30 +50,31 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
 		if(!$this->enabled) {
 			return false;
 		}
-		if($this->connection->isCached('inGroup'.$uid.':'.$gid)) {
-			return $this->connection->getFromCache('inGroup'.$uid.':'.$gid);
+		if($this->access->connection->isCached('inGroup'.$uid.':'.$gid)) {
+			return $this->access->connection->getFromCache('inGroup'.$uid.':'.$gid);
 		}
-		$dn_user = $this->username2dn($uid);
-		$dn_group = $this->groupname2dn($gid);
+		$dn_user = $this->access->username2dn($uid);
+		$dn_group = $this->access->groupname2dn($gid);
 		// just in case
 		if(!$dn_group || !$dn_user) {
-			$this->connection->writeToCache('inGroup'.$uid.':'.$gid, false);
+			$this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, false);
 			return false;
 		}
 		//usually, LDAP attributes are said to be case insensitive. But there are exceptions of course.
-		$members = $this->readAttribute($dn_group, $this->connection->ldapGroupMemberAssocAttr);
+		$members = $this->access->readAttribute($dn_group,
+						$this->access->connection->ldapGroupMemberAssocAttr);
 		if(!$members) {
-			$this->connection->writeToCache('inGroup'.$uid.':'.$gid, false);
+			$this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, false);
 			return false;
 		}
 
 		//extra work if we don't get back user DNs
 		//TODO: this can be done with one LDAP query
-		if(strtolower($this->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
+		if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
 			$dns = array();
 			foreach($members as $mid) {
-				$filter = str_replace('%uid', $mid, $this->connection->ldapLoginFilter);
-				$ldap_users = $this->fetchListOfUsers($filter, 'dn');
+				$filter = str_replace('%uid', $mid, $this->access->connection->ldapLoginFilter);
+				$ldap_users = $this->access->fetchListOfUsers($filter, 'dn');
 				if(count($ldap_users) < 1) {
 					continue;
 				}
@@ -80,7 +84,7 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
 		}
 
 		$isInGroup = in_array($dn_user, $members);
-		$this->connection->writeToCache('inGroup'.$uid.':'.$gid, $isInGroup);
+		$this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, $isInGroup);
 
 		return $isInGroup;
 	}
@@ -98,35 +102,36 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
 			return array();
 		}
 		$cacheKey = 'getUserGroups'.$uid;
-		if($this->connection->isCached($cacheKey)) {
-			return $this->connection->getFromCache($cacheKey);
+		if($this->access->connection->isCached($cacheKey)) {
+			return $this->access->connection->getFromCache($cacheKey);
 		}
-		$userDN = $this->username2dn($uid);
+		$userDN = $this->access->username2dn($uid);
 		if(!$userDN) {
-			$this->connection->writeToCache($cacheKey, array());
+			$this->access->connection->writeToCache($cacheKey, array());
 			return array();
 		}
 
 		//uniqueMember takes DN, memberuid the uid, so we need to distinguish
-		if((strtolower($this->connection->ldapGroupMemberAssocAttr) === 'uniquemember')
-			|| (strtolower($this->connection->ldapGroupMemberAssocAttr) === 'member')
+		if((strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'uniquemember')
+			|| (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'member')
 		) {
 			$uid = $userDN;
-		} else if(strtolower($this->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
-			$result = $this->readAttribute($userDN, 'uid');
+		} else if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
+			$result = $this->access->readAttribute($userDN, 'uid');
 			$uid = $result[0];
 		} else {
 			// just in case
 			$uid = $userDN;
 		}
 
-		$filter = $this->combineFilterWithAnd(array(
-			$this->connection->ldapGroupFilter,
-			$this->connection->ldapGroupMemberAssocAttr.'='.$uid
+		$filter = $this->access->combineFilterWithAnd(array(
+			$this->access->connection->ldapGroupFilter,
+			$this->access->connection->ldapGroupMemberAssocAttr.'='.$uid
 		));
-		$groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName, 'dn'));
-		$groups = array_unique($this->ownCloudGroupNames($groups), SORT_LOCALE_STRING);
-		$this->connection->writeToCache($cacheKey, $groups);
+		$groups = $this->access->fetchListOfGroups($filter,
+				array($this->access->connection->ldapGroupDisplayName, 'dn'));
+		$groups = array_unique($this->access->ownCloudGroupNames($groups), SORT_LOCALE_STRING);
+		$this->access->connection->writeToCache($cacheKey, $groups);
 
 		return $groups;
 	}
@@ -144,70 +149,71 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
 		}
 		$cachekey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset;
 		// check for cache of the exact query
-		$groupUsers = $this->connection->getFromCache($cachekey);
+		$groupUsers = $this->access->connection->getFromCache($cachekey);
 		if(!is_null($groupUsers)) {
 			return $groupUsers;
 		}
 
 		// check for cache of the query without limit and offset
-		$groupUsers = $this->connection->getFromCache('usersInGroup-'.$gid.'-'.$search);
+		$groupUsers = $this->access->connection->getFromCache('usersInGroup-'.$gid.'-'.$search);
 		if(!is_null($groupUsers)) {
 			$groupUsers = array_slice($groupUsers, $offset, $limit);
-			$this->connection->writeToCache($cachekey, $groupUsers);
+			$this->access->connection->writeToCache($cachekey, $groupUsers);
 			return $groupUsers;
 		}
 
 		if($limit === -1) {
 			$limit = null;
 		}
-		$groupDN = $this->groupname2dn($gid);
+		$groupDN = $this->access->groupname2dn($gid);
 		if(!$groupDN) {
 			// group couldn't be found, return empty resultset
-			$this->connection->writeToCache($cachekey, array());
+			$this->access->connection->writeToCache($cachekey, array());
 			return array();
 		}
 
-		$members = $this->readAttribute($groupDN, $this->connection->ldapGroupMemberAssocAttr);
+		$members = $this->access->readAttribute($groupDN,
+						$this->access->connection->ldapGroupMemberAssocAttr);
 		if(!$members) {
 			//in case users could not be retrieved, return empty resultset
-			$this->connection->writeToCache($cachekey, array());
+			$this->access->connection->writeToCache($cachekey, array());
 			return array();
 		}
 
 		$groupUsers = array();
-		$isMemberUid = (strtolower($this->connection->ldapGroupMemberAssocAttr) === 'memberuid');
+		$isMemberUid = (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid');
 		foreach($members as $member) {
 			if($isMemberUid) {
 				//we got uids, need to get their DNs to 'tranlsate' them to usernames
-				$filter = $this->combineFilterWithAnd(array(
+				$filter = $this->access->combineFilterWithAnd(array(
 					\OCP\Util::mb_str_replace('%uid', $member,
-						$this->connection->ldapLoginFilter, 'UTF-8'),
-					$this->getFilterPartForUserSearch($search)
+						$this->access->connection->ldapLoginFilter, 'UTF-8'),
+					$this->access->getFilterPartForUserSearch($search)
 				));
-				$ldap_users = $this->fetchListOfUsers($filter, 'dn');
+				$ldap_users = $this->access->fetchListOfUsers($filter, 'dn');
 				if(count($ldap_users) < 1) {
 					continue;
 				}
-				$groupUsers[] = $this->dn2username($ldap_users[0]);
+				$groupUsers[] = $this->access->dn2username($ldap_users[0]);
 			} else {
 				//we got DNs, check if we need to filter by search or we can give back all of them
 				if(!empty($search)) {
-					if(!$this->readAttribute($member,
-						$this->connection->ldapUserDisplayName,
-						$this->getFilterPartForUserSearch($search))) {
+					if(!$this->access->readAttribute($member,
+						$this->access->connection->ldapUserDisplayName,
+						$this->access->getFilterPartForUserSearch($search))) {
 						continue;
 					}
 				}
 				// dn2username will also check if the users belong to the allowed base
-				if($ocname = $this->dn2username($member)) {
+				if($ocname = $this->access->dn2username($member)) {
 					$groupUsers[] = $ocname;
 				}
 			}
 		}
 		natsort($groupUsers);
-		$this->connection->writeToCache('usersInGroup-'.$gid.'-'.$search, $groupUsers);
+		$this->access->connection->writeToCache('usersInGroup-'.$gid.'-'.$search, $groupUsers);
 		$groupUsers = array_slice($groupUsers, $offset, $limit);
-		$this->connection->writeToCache($cachekey, $groupUsers);
+		$this->access->connection->writeToCache($cachekey, $groupUsers);
 
 		return $groupUsers;
 	}
@@ -245,7 +251,7 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
 
 		//Check cache before driving unnecessary searches
 		\OCP\Util::writeLog('user_ldap', 'getGroups '.$cachekey, \OCP\Util::DEBUG);
-		$ldap_groups = $this->connection->getFromCache($cachekey);
+		$ldap_groups = $this->access->connection->getFromCache($cachekey);
 		if(!is_null($ldap_groups)) {
 			return $ldap_groups;
 		}
@@ -255,16 +261,18 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
 		if($limit <= 0) {
 			$limit = null;
 		}
-		$filter = $this->combineFilterWithAnd(array(
-			$this->connection->ldapGroupFilter,
-			$this->getFilterPartForGroupSearch($search)
+		$filter = $this->access->combineFilterWithAnd(array(
+			$this->access->connection->ldapGroupFilter,
+			$this->access->getFilterPartForGroupSearch($search)
 		));
 		\OCP\Util::writeLog('user_ldap', 'getGroups Filter '.$filter, \OCP\Util::DEBUG);
-		$ldap_groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName, 'dn'),
-			$limit, $offset);
-		$ldap_groups = $this->ownCloudGroupNames($ldap_groups);
+		$ldap_groups = $this->access->fetchListOfGroups($filter,
+				array($this->access->connection->ldapGroupDisplayName, 'dn'),
+				$limit,
+				$offset);
+		$ldap_groups = $this->access->ownCloudGroupNames($ldap_groups);
 
-		$this->connection->writeToCache($cachekey, $ldap_groups);
+		$this->access->connection->writeToCache($cachekey, $ldap_groups);
 		return $ldap_groups;
 	}
 
@@ -278,25 +286,26 @@ class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface {
 	 * @return bool
 	 */
 	public function groupExists($gid) {
-		if($this->connection->isCached('groupExists'.$gid)) {
-			return $this->connection->getFromCache('groupExists'.$gid);
+		if($this->access->connection->isCached('groupExists'.$gid)) {
+			return $this->access->connection->getFromCache('groupExists'.$gid);
 		}
 
-		//getting dn, if false the group does not exist. If dn, it may be mapped only, requires more checking.
-		$dn = $this->groupname2dn($gid);
+		//getting dn, if false the group does not exist. If dn, it may be mapped
+		//only, requires more checking.
+		$dn = $this->access->groupname2dn($gid);
 		if(!$dn) {
-			$this->connection->writeToCache('groupExists'.$gid, false);
+			$this->access->connection->writeToCache('groupExists'.$gid, false);
 			return false;
 		}
 
 		//if group really still exists, we will be able to read its objectclass
-		$objcs = $this->readAttribute($dn, 'objectclass');
+		$objcs = $this->access->readAttribute($dn, 'objectclass');
 		if(!$objcs || empty($objcs)) {
-			$this->connection->writeToCache('groupExists'.$gid, false);
+			$this->access->connection->writeToCache('groupExists'.$gid, false);
 			return false;
 		}
 
-		$this->connection->writeToCache('groupExists'.$gid, true);
+		$this->access->connection->writeToCache('groupExists'.$gid, true);
 		return true;
 	}
 
diff --git a/apps/user_ldap/group_proxy.php b/apps/user_ldap/group_proxy.php
index eb6f176c58ce6e5e70da2d151f086299562690c1..acc563c9532e14ebde97a792686e2b9dce76e178 100644
--- a/apps/user_ldap/group_proxy.php
+++ b/apps/user_ldap/group_proxy.php
@@ -23,6 +23,8 @@
 
 namespace OCA\user_ldap;
 
+use OCA\user_ldap\lib\ILDAPWrapper;
+
 class Group_Proxy extends lib\Proxy implements \OCP\GroupInterface {
 	private $backends = array();
 	private $refBackend = null;
@@ -31,12 +33,11 @@ class Group_Proxy extends lib\Proxy implements \OCP\GroupInterface {
 	 * @brief Constructor
 	 * @param $serverConfigPrefixes array containing the config Prefixes
 	 */
-	public function __construct($serverConfigPrefixes) {
-		parent::__construct();
+	public function __construct($serverConfigPrefixes, ILDAPWrapper $ldap) {
+		parent::__construct($ldap);
 		foreach($serverConfigPrefixes as $configPrefix) {
-		    $this->backends[$configPrefix] = new \OCA\user_ldap\GROUP_LDAP();
-		    $connector = $this->getConnector($configPrefix);
-			$this->backends[$configPrefix]->setConnector($connector);
+		    $this->backends[$configPrefix] =
+				new \OCA\user_ldap\GROUP_LDAP($this->getAccess($configPrefix));
 			if(is_null($this->refBackend)) {
 				$this->refBackend = &$this->backends[$configPrefix];
 			}
diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php
index 52aa39012fd0b2788403169ef45d4d0c658d73c0..fdf9c24612d13e11907d4a29d93477521b007c44 100644
--- a/apps/user_ldap/lib/access.php
+++ b/apps/user_ldap/lib/access.php
@@ -23,12 +23,13 @@
 
 namespace OCA\user_ldap\lib;
 
-abstract class Access {
-	protected $connection;
+class Access extends LDAPUtility {
+	public $connection;
 	//never ever check this var directly, always use getPagedSearchResultState
 	protected $pagedSearchedSuccessful;
 
-	public function setConnector(Connection &$connection) {
+	public function __construct(Connection $connection, ILDAPWrapper $ldap) {
+		parent::__construct($ldap);
 		$this->connection = $connection;
 	}
 
@@ -54,14 +55,14 @@ abstract class Access {
 			return false;
 		}
 		$cr = $this->connection->getConnectionResource();
-		if(!is_resource($cr)) {
+		if(!$this->ldap->isResource($cr)) {
 			//LDAP not available
 			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
 			return false;
 		}
 		$dn = $this->DNasBaseParameter($dn);
-		$rr = @ldap_read($cr, $dn, $filter, array($attr));
-		if(!is_resource($rr)) {
+		$rr = @$this->ldap->read($cr, $dn, $filter, array($attr));
+		if(!$this->ldap->isResource($rr)) {
 			if(!empty($attr)) {
 				//do not throw this message on userExists check, irritates
 				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG);
@@ -73,13 +74,14 @@ abstract class Access {
 			\OCP\Util::writeLog('user_ldap', 'readAttribute: '.$dn.' found', \OCP\Util::DEBUG);
 			return array();
 		}
-		$er = ldap_first_entry($cr, $rr);
-		if(!is_resource($er)) {
+		$er = $this->ldap->firstEntry($cr, $rr);
+		if(!$this->ldap->isResource($er)) {
 			//did not match the filter, return false
 			return false;
 		}
 		//LDAP attributes are not case sensitive
-		$result = \OCP\Util::mb_array_change_key_case(ldap_get_attributes($cr, $er), MB_CASE_LOWER, 'UTF-8');
+		$result = \OCP\Util::mb_array_change_key_case(
+				$this->ldap->getAttributes($cr, $er), MB_CASE_LOWER, 'UTF-8');
 		$attr = mb_strtolower($attr, 'UTF-8');
 
 		if(isset($result[$attr]) && $result[$attr]['count'] > 0) {
@@ -653,7 +655,7 @@ abstract class Access {
 
 		// See if we have a resource, in case not cancel with message
 		$link_resource = $this->connection->getConnectionResource();
-		if(!is_resource($link_resource)) {
+		if(!$this->ldap->isResource($link_resource)) {
 			// Seems like we didn't find any resource.
 			// Return an empty array just like before.
 			\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
@@ -664,11 +666,12 @@ abstract class Access {
 		$pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, $limit, $offset);
 
 		$linkResources = array_pad(array(), count($base), $link_resource);
-		$sr = ldap_search($linkResources, $base, $filter, $attr);
-		$error = ldap_errno($link_resource);
+		$sr = $this->ldap->search($linkResources, $base, $filter, $attr);
+		$error = $this->ldap->errno($link_resource);
 		if(!is_array($sr) || $error !== 0) {
 			\OCP\Util::writeLog('user_ldap',
-				'Error when searching: '.ldap_error($link_resource).' code '.ldap_errno($link_resource),
+				'Error when searching: '.$this->ldap->error($link_resource).
+					' code '.$this->ldap->errno($link_resource),
 				\OCP\Util::ERROR);
 			\OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), \OCP\Util::ERROR);
 			return array();
@@ -677,19 +680,19 @@ abstract class Access {
 		// Do the server-side sorting
 		foreach(array_reverse($attr) as $sortAttr){
 			foreach($sr as $searchResource) {
-				ldap_sort($link_resource, $searchResource, $sortAttr);
+				$this->ldap->sort($link_resource, $searchResource, $sortAttr);
 			}
 		}
 
 		$findings = array();
 		foreach($sr as $key => $res) {
-		    $findings = array_merge($findings, ldap_get_entries($link_resource, $res ));
+		    $findings = array_merge($findings, $this->ldap->getEntries($link_resource, $res ));
 		}
 		if($pagedSearchOK) {
 			\OCP\Util::writeLog('user_ldap', 'Paged search successful', \OCP\Util::INFO);
 			foreach($sr as $key => $res) {
 				$cookie = null;
-				if(ldap_control_paged_result_response($link_resource, $res, $cookie)) {
+				if($this->ldap->controlPagedResultResponse($link_resource, $res, $cookie)) {
 					\OCP\Util::writeLog('user_ldap', 'Set paged search cookie', \OCP\Util::INFO);
 					$this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
 				}
@@ -1103,8 +1106,9 @@ abstract class Access {
 					if($offset > 0) {
 						\OCP\Util::writeLog('user_ldap', 'Cookie '.$cookie, \OCP\Util::INFO);
 					}
-					$pagedSearchOK = ldap_control_paged_result($this->connection->getConnectionResource(),
-						$limit, false, $cookie);
+					$pagedSearchOK = $this->ldap->controlPagedResult(
+						$this->connection->getConnectionResource(), $limit,
+						false, $cookie);
 					if(!$pagedSearchOK) {
 						return false;
 					}
diff --git a/apps/user_ldap/lib/backendutility.php b/apps/user_ldap/lib/backendutility.php
new file mode 100644
index 0000000000000000000000000000000000000000..815757a1a11682bb360bbb923348fe7331662d45
--- /dev/null
+++ b/apps/user_ldap/lib/backendutility.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * ownCloud – LDAP BackendUtility
+ *
+ * @author Arthur Schiwon
+ * @copyright 2013 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap\lib;
+
+use OCA\user_ldap\lib\Access;
+
+abstract class BackendUtility {
+	protected $access;
+
+	/**
+	 * @brief constructor, make sure the subclasses call this one!
+	 * @param $access an instance of Access for LDAP interaction
+	 */
+	public function __construct(Access $access) {
+		$this->access = $access;
+	}
+}
diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php
index e5d9b4d5b40eaf99135c3333f4d87b747a165d9c..a53022c27b3231b23c5aee02a92288402e490c56 100644
--- a/apps/user_ldap/lib/connection.php
+++ b/apps/user_ldap/lib/connection.php
@@ -23,7 +23,7 @@
 
 namespace OCA\user_ldap\lib;
 
-class Connection {
+class Connection extends LDAPUtility {
 	private $ldapConnectionRes = null;
 	private $configPrefix;
 	private $configID;
@@ -60,7 +60,7 @@ class Connection {
 		'ldapQuotaDefault' => null,
 		'ldapEmailAttribute' => null,
 		'ldapCacheTTL' => null,
-		'ldapUuidAttribute' => null,
+		'ldapUuidAttribute' => 'auto',
 		'ldapOverrideUuidAttribute' => null,
 		'ldapOverrideMainServer' => false,
 		'ldapConfigurationActive' => false,
@@ -77,7 +77,8 @@ class Connection {
 	 * @param $configPrefix a string with the prefix for the configkey column (appconfig table)
 	 * @param $configID a string with the value for the appid column (appconfig table) or null for on-the-fly connections
 	 */
-	public function __construct($configPrefix = '', $configID = 'user_ldap') {
+	public function __construct(ILDAPWrapper $ldap, $configPrefix = '', $configID = 'user_ldap') {
+		parent::__construct($ldap);
 		$this->configPrefix = $configPrefix;
 		$this->configID = $configID;
 		$memcache = new \OC\Memcache\Factory();
@@ -86,13 +87,14 @@ class Connection {
 		} else {
 			$this->cache = \OC_Cache::getGlobalCache();
 		}
-		$this->config['hasPagedResultSupport'] = (function_exists('ldap_control_paged_result')
-			&& function_exists('ldap_control_paged_result_response'));
+		$this->config['hasPagedResultSupport'] =
+			$this->ldap->hasPagedResultSupport();
 	}
 
 	public function __destruct() {
-		if(!$this->dontDestruct && is_resource($this->ldapConnectionRes)) {
-			@ldap_unbind($this->ldapConnectionRes);
+		if(!$this->dontDestruct &&
+			$this->ldap->isResource($this->ldapConnectionRes)) {
+			@$this->ldap->unbind($this->ldapConnectionRes);
 		};
 	}
 
@@ -148,7 +150,7 @@ class Connection {
 	public function getConnectionResource() {
 		if(!$this->ldapConnectionRes) {
 			$this->init();
-		} else if(!is_resource($this->ldapConnectionRes)) {
+		} else if(!$this->ldap->isResource($this->ldapConnectionRes)) {
 			$this->ldapConnectionRes = null;
 			$this->establishConnection();
 		}
@@ -361,6 +363,14 @@ class Connection {
 					&& $params[$parameter] === 'homeFolderNamingRule'))
 				&& !empty($value)) {
 				$value = 'attr:'.$value;
+			} else if (strpos($parameter, 'ldapBase') !== false
+				|| (isset($params[$parameter])
+					&& strpos($params[$parameter], 'ldapBase') !== false)) {
+				$this->readBase($params[$parameter], $value);
+				if(is_array($setParameters)) {
+					$setParameters[] = $parameter;
+				}
+				continue;
 			}
 		    if(isset($this->config[$parameter])) {
 				$this->config[$parameter] = $value;
@@ -386,7 +396,8 @@ class Connection {
 	public function saveConfiguration() {
 		$trans = array_flip($this->getConfigTranslationArray());
 		foreach($this->config as $key => $value) {
-			\OCP\Util::writeLog('user_ldap', 'LDAP: storing key '.$key.' value '.$value, \OCP\Util::DEBUG);
+			\OCP\Util::writeLog('user_ldap', 'LDAP: storing key '.$key.
+				' value '.print_r($value, true), \OCP\Util::DEBUG);
 			switch ($key) {
 				case 'ldapAgentPassword':
 					$value = base64_encode($value);
@@ -431,8 +442,9 @@ class Connection {
 					$config[$dbKey] = '';
 				}
 				continue;
-			} else if((strpos($classKey, 'ldapBase') !== false)
-					|| (strpos($classKey, 'ldapAttributes') !== false)) {
+			} else if((strpos($classKey, 'ldapBase') !== false
+						|| strpos($classKey, 'ldapAttributes') !== false)
+						&& is_array($this->config[$classKey])) {
 				$config[$dbKey] = implode("\n", $this->config[$classKey]);
 				continue;
 			}
@@ -551,7 +563,7 @@ class Connection {
 	 * @returns an associative array with the default values. Keys are correspond
 	 * to config-value entries in the database table
 	 */
-	public function getDefaults() {
+	static public function getDefaults() {
 		return array(
 			'ldap_host'                         => '',
 			'ldap_port'                         => '389',
@@ -603,7 +615,7 @@ class Connection {
 			return false;
 		}
 		if(!$this->ldapConnectionRes) {
-			if(!function_exists('ldap_connect')) {
+			if(!$this->ldap->areLDAPFunctionsAvailable()) {
 				$phpLDAPinstalled = false;
 				\OCP\Util::writeLog('user_ldap',
 					'function ldap_connect is not available. Make sure that the PHP ldap module is installed.',
@@ -623,7 +635,8 @@ class Connection {
 			if(!$this->config['ldapOverrideMainServer'] && !$this->getFromCache('overrideMainServer')) {
 				$this->doConnect($this->config['ldapHost'], $this->config['ldapPort']);
 				$bindStatus = $this->bind();
-				$error = is_resource($this->ldapConnectionRes) ? ldap_errno($this->ldapConnectionRes) : -1;
+				$error = $this->ldap->isResource($this->ldapConnectionRes) ?
+							$this->ldap->errno($this->ldapConnectionRes) : -1;
 			} else {
 				$bindStatus = false;
 				$error = null;
@@ -653,11 +666,11 @@ class Connection {
 			//ldap_connect ignores port paramater when URLs are passed
 			$host .= ':' . $port;
 		}
-		$this->ldapConnectionRes = ldap_connect($host, $port);
-		if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) {
-			if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) {
+		$this->ldapConnectionRes = $this->ldap->connect($host, $port);
+		if($this->ldap->setOption($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) {
+			if($this->ldap->setOption($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) {
 				if($this->config['ldapTLS']) {
-					ldap_start_tls($this->ldapConnectionRes);
+					$this->ldap->startTls($this->ldapConnectionRes);
 				}
 			}
 		}
@@ -678,13 +691,15 @@ class Connection {
 		$getConnectionResourceAttempt = true;
 		$cr = $this->getConnectionResource();
 		$getConnectionResourceAttempt = false;
-		if(!is_resource($cr)) {
+		if(!$this->ldap->isResource($cr)) {
 			return false;
 		}
-		$ldapLogin = @ldap_bind($cr, $this->config['ldapAgentName'], $this->config['ldapAgentPassword']);
+		$ldapLogin = @$this->ldap->bind($cr,
+										$this->config['ldapAgentName'],
+										$this->config['ldapAgentPassword']);
 		if(!$ldapLogin) {
 			\OCP\Util::writeLog('user_ldap',
-				'Bind failed: ' . ldap_errno($cr) . ': ' . ldap_error($cr),
+				'Bind failed: ' . $this->ldap->errno($cr) . ': ' . $this->ldap->error($cr),
 				\OCP\Util::ERROR);
 			$this->ldapConnectionRes = null;
 			return false;
diff --git a/apps/user_ldap/lib/ildapwrapper.php b/apps/user_ldap/lib/ildapwrapper.php
new file mode 100644
index 0000000000000000000000000000000000000000..9e6bd56ef2a0920087e259030b02ce756bafdc4f
--- /dev/null
+++ b/apps/user_ldap/lib/ildapwrapper.php
@@ -0,0 +1,180 @@
+<?php
+
+/**
+ * ownCloud – LDAP Wrapper Interface
+ *
+ * @author Arthur Schiwon
+ * @copyright 2013 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap\lib;
+
+interface ILDAPWrapper {
+
+	//LDAP functions in use
+
+	/**
+	 * @brief Bind to LDAP directory
+	 * @param $link LDAP link resource
+	 * @param $dn an RDN to log in with
+	 * @param $password the password
+	 * @return true on success, false otherwise
+	 *
+	 * with $dn and $password as null a anonymous bind is attempted.
+	 */
+	public function bind($link, $dn, $password);
+
+	/**
+	 * @brief connect to an LDAP server
+	 * @param $host The host to connect to
+	 * @param $port The port to connect to
+	 * @return a link resource on success, otherwise false
+	 */
+	public function connect($host, $port);
+
+	/**
+	 * @brief Send LDAP pagination control
+	 * @param $link LDAP link resource
+	 * @param $pagesize number of results per page
+	 * @param $isCritical Indicates whether the pagination is critical of not.
+	 * @param $cookie structure sent by LDAP server
+	 * @return true on success, false otherwise
+	 */
+	public function controlPagedResult($link, $pagesize, $isCritical, $cookie);
+
+	/**
+	 * @brief Retrieve the LDAP pagination cookie
+	 * @param $link LDAP link resource
+	 * @param $result LDAP result resource
+	 * @param $cookie structure sent by LDAP server
+	 * @return true on success, false otherwise
+	 *
+	 * Corresponds to ldap_control_paged_result_response
+	 */
+	public function controlPagedResultResponse($link, $result, &$cookie);
+
+	/**
+	 * @brief Return the LDAP error number of the last LDAP command
+	 * @param $link LDAP link resource
+	 * @return error message as string
+	 */
+	public function errno($link);
+
+	/**
+	 * @brief Return the LDAP error message of the last LDAP command
+	 * @param $link LDAP link resource
+	 * @return error code as integer
+	 */
+	public function error($link);
+
+	/**
+	 * @brief Return first result id
+	 * @param $link LDAP link resource
+	 * @param $result LDAP result resource
+	 * @return an LDAP search result resource
+	 * */
+	public function firstEntry($link, $result);
+
+	/**
+	 * @brief Get attributes from a search result entry
+	 * @param $link LDAP link resource
+	 * @param $result LDAP result resource
+	 * @return array containing the results, false on error
+	 * */
+	public function getAttributes($link, $result);
+
+	/**
+	 * @brief Get all result entries
+	 * @param $link LDAP link resource
+	 * @param $result LDAP result resource
+	 * @return array containing the results, false on error
+	 */
+	public function getEntries($link, $result);
+
+	/**
+	 * @brief Read an entry
+	 * @param $link LDAP link resource
+	 * @param $baseDN The DN of the entry to read from
+	 * @param $filter An LDAP filter
+	 * @param $attr array of the attributes to read
+	 * @return an LDAP search result resource
+	 */
+	public function read($link, $baseDN, $filter, $attr);
+
+	/**
+	 * @brief Search LDAP tree
+	 * @param $link LDAP link resource
+	 * @param $baseDN The DN of the entry to read from
+	 * @param $filter An LDAP filter
+	 * @param $attr array of the attributes to read
+	 * @return an LDAP search result resource, false on error
+	 */
+	public function search($link, $baseDN, $filter, $attr);
+
+	/**
+	 * @brief Sets the value of the specified option to be $value
+	 * @param $link LDAP link resource
+	 * @param $option a defined LDAP Server option
+	 * @param $value the new value for the option
+	 * @return true on success, false otherwise
+	 */
+	public function setOption($link, $option, $value);
+
+	/**
+	 * @brief establish Start TLS
+	 * @param $link LDAP link resource
+	 * @return true on success, false otherwise
+	 */
+	public function startTls($link);
+
+	/**
+	 * @brief Sort the result of a LDAP search
+	 * @param $link LDAP link resource
+	 * @param $result LDAP result resource
+	 * @param $sortfilter attribute to use a key in sort
+	 */
+	public function sort($link, $result, $sortfilter);
+
+	/**
+	 * @brief Unbind from LDAP directory
+	 * @param $link LDAP link resource
+	 * @return true on success, false otherwise
+	 */
+	public function unbind($link);
+
+	//additional required methods in owncloud
+
+	/**
+	 * @brief Checks whether the server supports LDAP
+	 * @return true if it the case, false otherwise
+	 * */
+	public function areLDAPFunctionsAvailable();
+
+	/**
+	 * @brief Checks whether PHP supports LDAP Paged Results
+	 * @return true if it the case, false otherwise
+	 * */
+	public function hasPagedResultSupport();
+
+	/**
+	 * @brief Checks whether the submitted parameter is a resource
+	 * @param $resource the resource variable to check
+	 * @return true if it is a resource, false otherwise
+	 */
+	public function isResource($resource);
+
+}
diff --git a/apps/user_ldap/lib/jobs.php b/apps/user_ldap/lib/jobs.php
index 6b7666d4ca1752ee869221fb41ff03ee9998832e..2f90da3bfb617e082e44e6d2a6db5707519cabe9 100644
--- a/apps/user_ldap/lib/jobs.php
+++ b/apps/user_ldap/lib/jobs.php
@@ -139,13 +139,14 @@ class Jobs extends \OC\BackgroundJob\TimedJob {
 			return self::$groupBE;
 		}
 		$configPrefixes = Helper::getServerConfigurationPrefixes(true);
-		if(count($configPrefixes) == 1) {
+		$ldapWrapper = new OCA\user_ldap\lib\LDAP();
+		if(count($configPrefixes) === 1) {
 			//avoid the proxy when there is only one LDAP server configured
-			$connector = new Connection($configPrefixes[0]);
-			self::$groupBE = new \OCA\user_ldap\GROUP_LDAP();
-			self::$groupBE->setConnector($connector);
+			$connector = new OCA\user_ldap\lib\Connection($ldapWrapper, $configPrefixes[0]);
+			$ldapAccess = new OCA\user_ldap\lib\Access($connector, $ldapWrapper);
+			self::$groupBE = new OCA\user_ldap\GROUP_LDAP($ldapAccess);
 		} else {
-			self::$groupBE = new \OCA\user_ldap\Group_Proxy($configPrefixes);
+			self::$groupBE = new \OCA\user_ldap\Group_Proxy($configPrefixes, $ldapWrapper);
 		}
 
 		return self::$groupBE;
diff --git a/apps/user_ldap/lib/ldap.php b/apps/user_ldap/lib/ldap.php
new file mode 100644
index 0000000000000000000000000000000000000000..b63e969912a129515f7f5977e3c1b613229f5c2e
--- /dev/null
+++ b/apps/user_ldap/lib/ldap.php
@@ -0,0 +1,167 @@
+<?php
+
+/**
+ * ownCloud – LDAP Wrapper
+ *
+ * @author Arthur Schiwon
+ * @copyright 2013 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap\lib;
+
+class LDAP implements ILDAPWrapper {
+	protected $curFunc = '';
+	protected $curArgs = array();
+
+	public function bind($link, $dn, $password) {
+		return $this->invokeLDAPMethod('bind', $link, $dn, $password);
+	}
+
+	public function connect($host, $port) {
+		return $this->invokeLDAPMethod('connect', $host, $port);
+	}
+
+	public function controlPagedResultResponse($link, $result, &$cookie) {
+		$this->preFunctionCall('ldap_control_paged_result_response',
+			array($link, $result, $cookie));
+		$result = ldap_control_paged_result_response($link, $result, $cookie);
+		$this->postFunctionCall();
+
+		return $result;
+	}
+
+	public function controlPagedResult($link, $pagesize, $isCritical, $cookie) {
+		return $this->invokeLDAPMethod('control_paged_result', $link, $pagesize,
+										$isCritical, $cookie);
+	}
+
+	public function errno($link) {
+		return $this->invokeLDAPMethod('errno', $link);
+	}
+
+	public function error($link) {
+		return $this->invokeLDAPMethod('error', $link);
+	}
+
+	public function firstEntry($link, $result) {
+		return $this->invokeLDAPMethod('first_entry', $link, $result);
+	}
+
+	public function getAttributes($link, $result) {
+		return $this->invokeLDAPMethod('get_attributes', $link, $result);
+	}
+
+	public function getEntries($link, $result) {
+		return $this->invokeLDAPMethod('get_entries', $link, $result);
+	}
+
+	public function read($link, $baseDN, $filter, $attr) {
+		return $this->invokeLDAPMethod('read', $link, $baseDN, $filter, $attr);
+	}
+
+	public function search($link, $baseDN, $filter, $attr) {
+		return $this->invokeLDAPMethod('search', $link, $baseDN,
+										$filter, $attr);
+	}
+
+	public function setOption($link, $option, $value) {
+		$this->invokeLDAPMethod('set_option', $link, $option, $value);
+	}
+
+	public function sort($link, $result, $sortfilter) {
+		return $this->invokeLDAPMethod('sort', $link, $result, $sortfilter);
+	}
+
+	public function startTls($link) {
+		return $this->invokeLDAPMethod('start_tls', $link);
+	}
+
+	public function unbind($link) {
+		return $this->invokeLDAPMethod('unbind', $link);
+	}
+
+	/**
+	 * @brief Checks whether the server supports LDAP
+	 * @return true if it the case, false otherwise
+	 * */
+	public function areLDAPFunctionsAvailable() {
+		return function_exists('ldap_connect');
+	}
+
+	/**
+	 * @brief Checks whether PHP supports LDAP Paged Results
+	 * @return true if it the case, false otherwise
+	 * */
+	public function hasPagedResultSupport() {
+		$hasSupport = function_exists('ldap_control_paged_result')
+			&& function_exists('ldap_control_paged_result_response');
+		return $hasSupport;
+	}
+
+	/**
+	 * @brief Checks whether the submitted parameter is a resource
+	 * @param $resource the resource variable to check
+	 * @return true if it is a resource, false otherwise
+	 */
+	public function isResource($resource) {
+		return is_resource($resource);
+	}
+
+	private function invokeLDAPMethod() {
+		$arguments = func_get_args();
+		$func = 'ldap_' . array_shift($arguments);
+		if(function_exists($func)) {
+			$this->preFunctionCall($func, $arguments);
+			$result = call_user_func_array($func, $arguments);
+			$this->postFunctionCall();
+			return $result;
+		}
+	}
+
+	private function preFunctionCall($functionName, $args) {
+		$this->curFunc = $functionName;
+		$this->curArgs = $args;
+	}
+
+	private function postFunctionCall() {
+		if($this->isResource($this->curArgs[0])) {
+			$errorCode = ldap_errno($this->curArgs[0]);
+			$errorMsg  = ldap_error($this->curArgs[0]);
+			if($errorCode !== 0) {
+				if($this->curFunc === 'ldap_sort' && $errorCode === -4) {
+					//You can safely ignore that decoding error.
+					//… says https://bugs.php.net/bug.php?id=18023
+				} else if($this->curFunc === 'ldap_get_entries'
+						  && $errorCode === -4) {
+				} else if ($errorCode === 32) {
+					//for now
+				} else if ($errorCode === 10) {
+					//referrals, we switch them off, but then there is AD :)
+				} else {
+					\OCP\Util::writeLog('user_ldap',
+										'LDAP error '.$errorMsg.' (' .
+											$errorCode.') after calling '.
+											$this->curFunc,
+										\OCP\Util::DEBUG);
+				}
+			}
+		}
+
+		$this->curFunc = '';
+		$this->curArgs = array();
+	}
+}
\ No newline at end of file
diff --git a/apps/user_ldap/lib/ldaputility.php b/apps/user_ldap/lib/ldaputility.php
new file mode 100644
index 0000000000000000000000000000000000000000..7fffd9c88d18c1efe5cdd97624738594b830bceb
--- /dev/null
+++ b/apps/user_ldap/lib/ldaputility.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * ownCloud – LDAP LDAPUtility
+ *
+ * @author Arthur Schiwon
+ * @copyright 2013 Arthur Schiwon blizzz@owncloud.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\user_ldap\lib;
+
+abstract class LDAPUtility {
+	protected $ldap;
+
+	/**
+	 * @brief constructor, make sure the subclasses call this one!
+	 * @param $ldapWrapper an instance of an ILDAPWrapper
+	 */
+	public function __construct(ILDAPWrapper $ldapWrapper) {
+		$this->ldap = $ldapWrapper;
+	}
+}
diff --git a/apps/user_ldap/lib/proxy.php b/apps/user_ldap/lib/proxy.php
index ae3e3be73611e355c3b16dfca5d0662e537424e3..c74b357bdd2fa77fdb564878fe31d9f6e6b4cac0 100644
--- a/apps/user_ldap/lib/proxy.php
+++ b/apps/user_ldap/lib/proxy.php
@@ -23,26 +23,27 @@
 
 namespace OCA\user_ldap\lib;
 
+use OCA\user_ldap\lib\Access;
+
 abstract class Proxy {
-	static private $connectors = array();
+	static private $accesses = array();
+	private $ldap = null;
 
-	public function __construct() {
+	public function __construct(ILDAPWrapper $ldap) {
+		$this->ldap = $ldap;
 		$this->cache = \OC_Cache::getGlobalCache();
 	}
 
-	private function addConnector($configPrefix) {
-		self::$connectors[$configPrefix] = new \OCA\user_ldap\lib\Connection($configPrefix);
+	private function addAccess($configPrefix) {
+		$connector = new Connection($this->ldap, $configPrefix);
+		self::$accesses[$configPrefix] = new Access($connector, $this->ldap);
 	}
 
-	protected function getConnector($configPrefix) {
-		if(!isset(self::$connectors[$configPrefix])) {
-			$this->addConnector($configPrefix);
+	protected function getAccess($configPrefix) {
+		if(!isset(self::$accesses[$configPrefix])) {
+			$this->addAccess($configPrefix);
 		}
-		return self::$connectors[$configPrefix];
-	}
-
-	protected function getConnectors() {
-		return self::$connectors;
+		return self::$accesses[$configPrefix];
 	}
 
 	protected function getUserCacheKey($uid) {
diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php
index 7169192a18e4a94e4eb3d087e594577aed43a8e5..b7070f23183b00b2c5cd73fe6d62b21ae7f2652f 100644
--- a/apps/user_ldap/settings.php
+++ b/apps/user_ldap/settings.php
@@ -49,14 +49,9 @@ $tmpl->assign('serverConfigurationPrefixes', $prefixes);
 $tmpl->assign('serverConfigurationHosts', $hosts);
 
 // assign default values
-if(!isset($ldap)) {
-	$ldap = new \OCA\user_ldap\lib\Connection();
-}
-$defaults = $ldap->getDefaults();
+$defaults = \OCA\user_ldap\lib\Connection::getDefaults();
 foreach($defaults as $key => $default) {
     $tmpl->assign($key.'_default', $default);
 }
 
-// $tmpl->assign();
-
 return $tmpl->fetchPage();
diff --git a/apps/user_ldap/tests/group_ldap.php b/apps/user_ldap/tests/group_ldap.php
deleted file mode 100644
index ae635597b71fcd15de2fa6b707e58fead4f3fc00..0000000000000000000000000000000000000000
--- a/apps/user_ldap/tests/group_ldap.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-/**
-* ownCloud
-*
-* @author Arthur Schiwon
-* @copyright 2012 Arthur Schiwon blizzz@owncloud.com
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
-*
-* You should have received a copy of the GNU Affero General Public
-* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*
-*/
-
-class Test_Group_Ldap extends PHPUnit_Framework_TestCase {
-	function setUp() {
-		OC_Group::clearBackends();
-	}
-
-	function testSingleBackend() {
-		OC_Group::useBackend(new OCA\user_ldap\GROUP_LDAP());
-		$group_ldap = new OCA\user_ldap\GROUP_LDAP();
-
-		$this->assertIsA(OC_Group::getGroups(), gettype(array()));
-		$this->assertIsA($group_ldap->getGroups(), gettype(array()));
-
-		$this->assertFalse(OC_Group::inGroup('john', 'dosers'), gettype(false));
-		$this->assertFalse($group_ldap->inGroup('john', 'dosers'), gettype(false));
-		//TODO: check also for expected true result. This backend won't be able to do any modifications, maybe use a dummy for this.
-
-		$this->assertIsA(OC_Group::getUserGroups('john doe'), gettype(array()));
-		$this->assertIsA($group_ldap->getUserGroups('john doe'), gettype(array()));
-
-		$this->assertIsA(OC_Group::usersInGroup('campers'), gettype(array()));
-		$this->assertIsA($group_ldap->usersInGroup('campers'), gettype(array()));
-	}
-
-}
diff --git a/apps/user_ldap/tests/user_ldap.php b/apps/user_ldap/tests/user_ldap.php
new file mode 100644
index 0000000000000000000000000000000000000000..6b9b8b3e18503162fcfc90638c5b09250df6b126
--- /dev/null
+++ b/apps/user_ldap/tests/user_ldap.php
@@ -0,0 +1,411 @@
+<?php
+/**
+* ownCloud
+*
+* @author Arthur Schiwon
+* @copyright 2013 Arthur Schiwon blizzz@owncloud.com
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+namespace OCA\user_ldap\tests;
+
+use \OCA\user_ldap\USER_LDAP as UserLDAP;
+use \OCA\user_ldap\lib\Access;
+use \OCA\user_ldap\lib\Connection;
+use \OCA\user_ldap\lib\ILDAPWrapper;
+
+class Test_User_Ldap_Direct extends \PHPUnit_Framework_TestCase {
+	protected $backend;
+
+	public function setUp() {
+		\OC_User::clearBackends();
+		\OC_Group::clearBackends();
+	}
+
+	private function getAccessMock() {
+		static $conMethods;
+		static $accMethods;
+
+		if(is_null($conMethods) || is_null($accMethods)) {
+			$conMethods = get_class_methods('\OCA\user_ldap\lib\Connection');
+			$accMethods = get_class_methods('\OCA\user_ldap\lib\Access');
+		}
+		$lw  = $this->getMock('\OCA\user_ldap\lib\ILDAPWrapper');
+		$connector = $this->getMock('\OCA\user_ldap\lib\Connection',
+									$conMethods,
+									array($lw, null, null));
+		$access = $this->getMock('\OCA\user_ldap\lib\Access',
+								 $accMethods,
+								 array($connector, $lw));
+
+		return $access;
+	}
+
+	private function prepareMockForUserExists(&$access) {
+		$access->expects($this->any())
+			   ->method('username2dn')
+			   ->will($this->returnCallback(function($uid) {
+					switch ($uid) {
+						case 'gunslinger':
+							return 'dnOfRoland';
+							break;
+						case 'formerUser':
+							return 'dnOfFormerUser';
+							break;
+						case 'newyorker':
+							return 'dnOfNewYorker';
+							break;
+						case 'ladyofshadows':
+							return 'dnOfLadyOfShadows';
+							break;
+						defautl:
+							return false;
+					}
+			   }));
+	}
+
+	/**
+	 * @brief Prepares the Access mock for checkPassword tests
+	 * @param $access mock of \OCA\user_ldap\lib\Access
+	 * @return void
+	 */
+	private function prepareAccessForCheckPassword(&$access) {
+		$access->connection->expects($this->any())
+			   ->method('__get')
+			   ->will($this->returnCallback(function($name) {
+					if($name === 'ldapLoginFilter') {
+						return '%uid';
+					}
+					return null;
+			   }));
+
+		$access->expects($this->any())
+			   ->method('fetchListOfUsers')
+			   ->will($this->returnCallback(function($filter) {
+					if($filter === 'roland') {
+						return array('dnOfRoland');
+					}
+					return array();
+			   }));
+
+		$access->expects($this->any())
+			   ->method('dn2username')
+			   ->with($this->equalTo('dnOfRoland'))
+			   ->will($this->returnValue('gunslinger'));
+
+		$access->expects($this->any())
+			   ->method('areCredentialsValid')
+			   ->will($this->returnCallback(function($dn, $pwd) {
+					if($pwd === 'dt19') {
+						return true;
+					}
+					return false;
+			   }));
+	}
+
+	public function testCheckPassword() {
+		$access = $this->getAccessMock();
+		$this->prepareAccessForCheckPassword($access);
+		$backend = new UserLDAP($access);
+		\OC_User::useBackend($backend);
+
+		$result = $backend->checkPassword('roland', 'dt19');
+		$this->assertEquals('gunslinger', $result);
+
+		$result = $backend->checkPassword('roland', 'wrong');
+		$this->assertFalse($result);
+
+		$result = $backend->checkPassword('mallory', 'evil');
+		$this->assertFalse($result);
+	}
+
+	public function testCheckPasswordPublicAPI() {
+		$access = $this->getAccessMock();
+		$this->prepareAccessForCheckPassword($access);
+		$backend = new UserLDAP($access);
+		\OC_User::useBackend($backend);
+
+		$result = \OCP\User::checkPassword('roland', 'dt19');
+		$this->assertEquals('gunslinger', $result);
+
+		$result = \OCP\User::checkPassword('roland', 'wrong');
+		$this->assertFalse($result);
+
+		$result = \OCP\User::checkPassword('mallory', 'evil');
+		$this->assertFalse($result);
+	}
+
+	/**
+	 * @brief Prepares the Access mock for getUsers tests
+	 * @param $access mock of \OCA\user_ldap\lib\Access
+	 * @return void
+	 */
+	private function prepareAccessForGetUsers(&$access) {
+		$access->expects($this->any())
+			   ->method('getFilterPartForUserSearch')
+			   ->will($this->returnCallback(function($search) {
+					return $search;
+			   }));
+
+		$access->expects($this->any())
+			   ->method('combineFilterWithAnd')
+			   ->will($this->returnCallback(function($param) {
+					return $param[1];
+			   }));
+
+		$access->expects($this->any())
+			   ->method('fetchListOfUsers')
+			   ->will($this->returnCallback(function($search, $a, $l, $o) {
+					$users = array('gunslinger', 'newyorker', 'ladyofshadows');
+					if(empty($search)) {
+						$result = $users;
+					} else {
+						$result = array();
+						foreach($users as $user) {
+							if(stripos($user,  $search) !== false) {
+								$result[] = $user;
+							}
+						}
+					}
+					if(!is_null($l) || !is_null($o)) {
+						$result = array_slice($result, $o, $l);
+					}
+					return $result;
+			   }));
+
+		$access->expects($this->any())
+			   ->method('ownCloudUserNames')
+			   ->will($this->returnArgument(0));
+	}
+
+	public function testGetUsers() {
+		$access = $this->getAccessMock();
+		$this->prepareAccessForGetUsers($access);
+		$backend = new UserLDAP($access);
+
+		$result = $backend->getUsers();
+		$this->assertEquals(3, count($result));
+
+		$result = $backend->getUsers('', 1, 2);
+		$this->assertEquals(1, count($result));
+
+		$result = $backend->getUsers('', 2, 1);
+		$this->assertEquals(2, count($result));
+
+		$result = $backend->getUsers('yo');
+		$this->assertEquals(2, count($result));
+
+		$result = $backend->getUsers('nix');
+		$this->assertEquals(0, count($result));
+	}
+
+	public function testGetUsersViaAPI() {
+		$access = $this->getAccessMock();
+		$this->prepareAccessForGetUsers($access);
+		$backend = new UserLDAP($access);
+		\OC_User::useBackend($backend);
+
+		$result = \OCP\User::getUsers();
+		$this->assertEquals(3, count($result));
+
+		$result = \OCP\User::getUsers('', 1, 2);
+		$this->assertEquals(1, count($result));
+
+		$result = \OCP\User::getUsers('', 2, 1);
+		$this->assertEquals(2, count($result));
+
+		$result = \OCP\User::getUsers('yo');
+		$this->assertEquals(2, count($result));
+
+		$result = \OCP\User::getUsers('nix');
+		$this->assertEquals(0, count($result));
+	}
+
+	public function testUserExists() {
+		$access = $this->getAccessMock();
+		$backend = new UserLDAP($access);
+		$this->prepareMockForUserExists($access);
+
+		$access->expects($this->any())
+			   ->method('readAttribute')
+			   ->will($this->returnCallback(function($dn) {
+					if($dn === 'dnOfRoland') {
+						return array();
+					}
+					return false;
+			   }));
+
+		//test for existing user
+		$result = $backend->userExists('gunslinger');
+		$this->assertTrue($result);
+
+		//test for deleted user
+		$result = $backend->userExists('formerUser');
+		$this->assertFalse($result);
+
+		//test for never-existing user
+		$result = $backend->userExists('mallory');
+		$this->assertFalse($result);
+	}
+
+	public function testUserExistsPublicAPI() {
+		$access = $this->getAccessMock();
+		$backend = new UserLDAP($access);
+		$this->prepareMockForUserExists($access);
+		\OC_User::useBackend($backend);
+
+		$access->expects($this->any())
+			   ->method('readAttribute')
+			   ->will($this->returnCallback(function($dn) {
+					if($dn === 'dnOfRoland') {
+						return array();
+					}
+					return false;
+			   }));
+
+		//test for existing user
+		$result = \OCP\User::userExists('gunslinger');
+		$this->assertTrue($result);
+
+		//test for deleted user
+		$result = \OCP\User::userExists('formerUser');
+		$this->assertFalse($result);
+
+		//test for never-existing user
+		$result = \OCP\User::userExists('mallory');
+		$this->assertFalse($result);
+	}
+
+	public function testDeleteUser() {
+		$access = $this->getAccessMock();
+		$backend = new UserLDAP($access);
+
+		//we do not support deleting users at all
+		$result = $backend->deleteUser('gunslinger');
+		$this->assertFalse($result);
+	}
+
+	public function testGetHome() {
+		$access = $this->getAccessMock();
+		$backend = new UserLDAP($access);
+		$this->prepareMockForUserExists($access);
+
+		$access->connection->expects($this->any())
+			   ->method('__get')
+			   ->will($this->returnCallback(function($name) {
+					if($name === 'homeFolderNamingRule') {
+						return 'attr:testAttribute';
+					}
+					return null;
+			   }));
+
+		$access->expects($this->any())
+			   ->method('readAttribute')
+			   ->will($this->returnCallback(function($dn, $attr) {
+					switch ($dn) {
+						case 'dnOfRoland':
+							if($attr === 'testAttribute') {
+								return array('/tmp/rolandshome/');
+							}
+							return array();
+							break;
+						case 'dnOfLadyOfShadows':
+							if($attr === 'testAttribute') {
+								return array('susannah/');
+							}
+							return array();
+							break;
+						default:
+							return false;
+				   }
+			   }));
+
+		//absolut path
+		$result = $backend->getHome('gunslinger');
+		$this->assertEquals('/tmp/rolandshome/', $result);
+
+		//datadir-relativ path
+		$result = $backend->getHome('ladyofshadows');
+		$datadir = \OCP\Config::getSystemValue('datadirectory',
+											   \OC::$SERVERROOT.'/data');
+		$this->assertEquals($datadir.'/susannah/', $result);
+
+		//no path at all – triggers OC default behaviour
+		$result = $backend->getHome('newyorker');
+		$this->assertFalse($result);
+	}
+
+	private function prepareAccessForGetDisplayName(&$access) {
+		$access->connection->expects($this->any())
+			   ->method('__get')
+			   ->will($this->returnCallback(function($name) {
+					if($name === 'ldapUserDisplayName') {
+						return 'displayname';
+					}
+					return null;
+			   }));
+
+		$access->expects($this->any())
+			   ->method('readAttribute')
+			   ->will($this->returnCallback(function($dn, $attr) {
+					switch ($dn) {
+						case 'dnOfRoland':
+							if($attr === 'displayname') {
+								return array('Roland Deschain');
+							}
+							return array();
+							break;
+
+						default:
+							return false;
+				   }
+			   }));
+	}
+
+	public function testGetDisplayName() {
+		$access = $this->getAccessMock();
+		$this->prepareAccessForGetDisplayName($access);
+		$backend = new UserLDAP($access);
+		$this->prepareMockForUserExists($access);
+
+		//with displayName
+		$result = $backend->getDisplayName('gunslinger');
+		$this->assertEquals('Roland Deschain', $result);
+
+		//empty displayname retrieved
+		$result = $backend->getDisplayName('newyorker');
+		$this->assertEquals(null, $result);
+	}
+
+	public function testGetDisplayNamePublicAPI() {
+		$access = $this->getAccessMock();
+		$this->prepareAccessForGetDisplayName($access);
+		$backend = new UserLDAP($access);
+		$this->prepareMockForUserExists($access);
+		\OC_User::useBackend($backend);
+
+		//with displayName
+		$result = \OCP\User::getDisplayName('gunslinger');
+		$this->assertEquals('Roland Deschain', $result);
+
+		//empty displayname retrieved
+		$result = \OCP\User::getDisplayName('newyorker');
+		$this->assertEquals('newyorker', $result);
+	}
+
+	//no test for getDisplayNames, because it just invokes getUsers and
+	//getDisplayName
+}
\ No newline at end of file
diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php
index 850ca0df99546d339203931242aefe0df324337a..6f52bbdf233b3c85b5ad0b9617073640f7cba9d5 100644
--- a/apps/user_ldap/user_ldap.php
+++ b/apps/user_ldap/user_ldap.php
@@ -25,37 +25,46 @@
 
 namespace OCA\user_ldap;
 
-class USER_LDAP extends lib\Access implements \OCP\UserInterface {
+use OCA\user_ldap\lib\ILDAPWrapper;
+use OCA\user_ldap\lib\BackendUtility;
+
+class USER_LDAP extends BackendUtility implements \OCP\UserInterface {
 
 	private function updateQuota($dn) {
 		$quota = null;
-		$quotaDefault = $this->connection->ldapQuotaDefault;
-		$quotaAttribute = $this->connection->ldapQuotaAttribute;
+		$quotaDefault = $this->access->connection->ldapQuotaDefault;
+		$quotaAttribute = $this->access->connection->ldapQuotaAttribute;
 		if(!empty($quotaDefault)) {
 			$quota = $quotaDefault;
 		}
 		if(!empty($quotaAttribute)) {
-			$aQuota = $this->readAttribute($dn, $quotaAttribute);
+			$aQuota = $this->access->readAttribute($dn, $quotaAttribute);
 
 			if($aQuota && (count($aQuota) > 0)) {
 				$quota = $aQuota[0];
 			}
 		}
 		if(!is_null($quota)) {
-			\OCP\Config::setUserValue($this->dn2username($dn), 'files', 'quota', \OCP\Util::computerFileSize($quota));
+			\OCP\Config::setUserValue(	$this->access->dn2username($dn),
+										'files',
+										'quota',
+										\OCP\Util::computerFileSize($quota));
 		}
 	}
 
 	private function updateEmail($dn) {
 		$email = null;
-		$emailAttribute = $this->connection->ldapEmailAttribute;
+		$emailAttribute = $this->access->connection->ldapEmailAttribute;
 		if(!empty($emailAttribute)) {
-			$aEmail = $this->readAttribute($dn, $emailAttribute);
+			$aEmail = $this->access->readAttribute($dn, $emailAttribute);
 			if($aEmail && (count($aEmail) > 0)) {
 				$email = $aEmail[0];
 			}
 			if(!is_null($email)) {
-				\OCP\Config::setUserValue($this->dn2username($dn), 'settings', 'email', $email);
+				\OCP\Config::setUserValue(	$this->access->dn2username($dn),
+											'settings',
+											'email',
+											$email);
 			}
 		}
 	}
@@ -70,15 +79,16 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
 	 */
 	public function checkPassword($uid, $password) {
 		//find out dn of the user name
-		$filter = \OCP\Util::mb_str_replace('%uid', $uid, $this->connection->ldapLoginFilter, 'UTF-8');
-		$ldap_users = $this->fetchListOfUsers($filter, 'dn');
+		$filter = \OCP\Util::mb_str_replace(
+			'%uid', $uid, $this->access->connection->ldapLoginFilter, 'UTF-8');
+		$ldap_users = $this->access->fetchListOfUsers($filter, 'dn');
 		if(count($ldap_users) < 1) {
 			return false;
 		}
 		$dn = $ldap_users[0];
 
 		//do we have a username for him/her?
-		$ocname = $this->dn2username($dn);
+		$ocname = $this->access->dn2username($dn);
 
 		if($ocname) {
 			//update some settings, if necessary
@@ -86,7 +96,7 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
 			$this->updateEmail($dn);
 
 			//are the credentials OK?
-			if(!$this->areCredentialsValid($dn, $password)) {
+			if(!$this->access->areCredentialsValid($dn, $password)) {
 				return false;
 			}
 
@@ -107,7 +117,7 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
 		$cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset;
 
 		//check if users are cached, if so return
-		$ldap_users = $this->connection->getFromCache($cachekey);
+		$ldap_users = $this->access->connection->getFromCache($cachekey);
 		if(!is_null($ldap_users)) {
 			return $ldap_users;
 		}
@@ -117,21 +127,23 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
 		if($limit <= 0) {
 			$limit = null;
 		}
-		$filter = $this->combineFilterWithAnd(array(
-			$this->connection->ldapUserFilter,
-			$this->getFilterPartForUserSearch($search)
+		$filter = $this->access->combineFilterWithAnd(array(
+			$this->access->connection->ldapUserFilter,
+			$this->access->getFilterPartForUserSearch($search)
 		));
 
 		\OCP\Util::writeLog('user_ldap',
 			'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter,
 			\OCP\Util::DEBUG);
 		//do the search and translate results to owncloud names
-		$ldap_users = $this->fetchListOfUsers($filter, array($this->connection->ldapUserDisplayName, 'dn'),
+		$ldap_users = $this->access->fetchListOfUsers(
+			$filter,
+			array($this->access->connection->ldapUserDisplayName, 'dn'),
 			$limit, $offset);
-		$ldap_users = $this->ownCloudUserNames($ldap_users);
+		$ldap_users = $this->access->ownCloudUserNames($ldap_users);
 		\OCP\Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', \OCP\Util::DEBUG);
 
-		$this->connection->writeToCache($cachekey, $ldap_users);
+		$this->access->connection->writeToCache($cachekey, $ldap_users);
 		return $ldap_users;
 	}
 
@@ -141,24 +153,25 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
 	 * @return boolean
 	 */
 	public function userExists($uid) {
-		if($this->connection->isCached('userExists'.$uid)) {
-			return $this->connection->getFromCache('userExists'.$uid);
+		if($this->access->connection->isCached('userExists'.$uid)) {
+			return $this->access->connection->getFromCache('userExists'.$uid);
 		}
-
 		//getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking.
-		$dn = $this->username2dn($uid);
+		$dn = $this->access->username2dn($uid);
 		if(!$dn) {
-			$this->connection->writeToCache('userExists'.$uid, false);
+			\OCP\Util::writeLog('user_ldap', 'No DN found for '.$uid.' on '.
+				$this->access->connection->ldapHost, \OCP\Util::DEBUG);
+			$this->access->connection->writeToCache('userExists'.$uid, false);
 			return false;
 		}
-
 		//check if user really still exists by reading its entry
-		if(!is_array($this->readAttribute($dn, ''))) {
-			$this->connection->writeToCache('userExists'.$uid, false);
+		if(!is_array($this->access->readAttribute($dn, ''))) {
+			\OCP\Util::writeLog('user_ldap', 'LDAP says no user '.$dn, \OCP\Util::DEBUG);
+			$this->access->connection->writeToCache('userExists'.$uid, false);
 			return false;
 		}
 
-		$this->connection->writeToCache('userExists'.$uid, true);
+		$this->access->connection->writeToCache('userExists'.$uid, true);
 		$this->updateQuota($dn);
 		return true;
 	}
@@ -186,12 +199,13 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
 		}
 
 		$cacheKey = 'getHome'.$uid;
-		if($this->connection->isCached($cacheKey)) {
-			return $this->connection->getFromCache($cacheKey);
+		if($this->access->connection->isCached($cacheKey)) {
+			return $this->access->connection->getFromCache($cacheKey);
 		}
-		if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) {
-			$attr = substr($this->connection->homeFolderNamingRule, strlen('attr:'));
-			$homedir = $this->readAttribute($this->username2dn($uid), $attr);
+		if(strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0) {
+			$attr = substr($this->access->connection->homeFolderNamingRule, strlen('attr:'));
+			$homedir = $this->access->readAttribute(
+						$this->access->username2dn($uid), $attr);
 			if($homedir && isset($homedir[0])) {
 				$path = $homedir[0];
 				//if attribute's value is an absolute path take this, otherwise append it to data dir
@@ -206,13 +220,13 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
 					$homedir = \OCP\Config::getSystemValue('datadirectory',
 						\OC::$SERVERROOT.'/data' ) . '/' . $homedir[0];
 				}
-				$this->connection->writeToCache($cacheKey, $homedir);
+				$this->access->connection->writeToCache($cacheKey, $homedir);
 				return $homedir;
 			}
 		}
 
 		//false will apply default behaviour as defined and done by OC_User
-		$this->connection->writeToCache($cacheKey, false);
+		$this->access->connection->writeToCache($cacheKey, false);
 		return false;
 	}
 
@@ -227,16 +241,16 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
 		}
 
 		$cacheKey = 'getDisplayName'.$uid;
-		if(!is_null($displayName = $this->connection->getFromCache($cacheKey))) {
+		if(!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
 			return $displayName;
 		}
 
-		$displayName = $this->readAttribute(
-			$this->username2dn($uid),
-			$this->connection->ldapUserDisplayName);
+		$displayName = $this->access->readAttribute(
+			$this->access->username2dn($uid),
+			$this->access->connection->ldapUserDisplayName);
 
 		if($displayName && (count($displayName) > 0)) {
-			$this->connection->writeToCache($cacheKey, $displayName[0]);
+			$this->access->connection->writeToCache($cacheKey, $displayName[0]);
 			return $displayName[0];
 		}
 
@@ -251,7 +265,7 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
 	 */
 	public function getDisplayNames($search = '', $limit = null, $offset = null) {
 		$cacheKey = 'getDisplayNames-'.$search.'-'.$limit.'-'.$offset;
-		if(!is_null($displayNames = $this->connection->getFromCache($cacheKey))) {
+		if(!is_null($displayNames = $this->access->connection->getFromCache($cacheKey))) {
 			return $displayNames;
 		}
 
@@ -260,7 +274,7 @@ class USER_LDAP extends lib\Access implements \OCP\UserInterface {
 		foreach ($users as $user) {
 			$displayNames[$user] = $this->getDisplayName($user);
 		}
-		$this->connection->writeToCache($cacheKey, $displayNames);
+		$this->access->connection->writeToCache($cacheKey, $displayNames);
 		return $displayNames;
 	}
 
diff --git a/apps/user_ldap/user_proxy.php b/apps/user_ldap/user_proxy.php
index 0722d8871a42ce06eb097b7a6f4fe403d200ad17..092fdbf7c782c55d8b2f1d4417e8ef7f397abc8e 100644
--- a/apps/user_ldap/user_proxy.php
+++ b/apps/user_ldap/user_proxy.php
@@ -23,6 +23,8 @@
 
 namespace OCA\user_ldap;
 
+use OCA\user_ldap\lib\ILDAPWrapper;
+
 class User_Proxy extends lib\Proxy implements \OCP\UserInterface {
 	private $backends = array();
 	private $refBackend = null;
@@ -31,12 +33,11 @@ class User_Proxy extends lib\Proxy implements \OCP\UserInterface {
 	 * @brief Constructor
 	 * @param $serverConfigPrefixes array containing the config Prefixes
 	 */
-	public function __construct($serverConfigPrefixes) {
-		parent::__construct();
+	public function __construct($serverConfigPrefixes, ILDAPWrapper $ldap) {
+		parent::__construct($ldap);
 		foreach($serverConfigPrefixes as $configPrefix) {
-		    $this->backends[$configPrefix] = new \OCA\user_ldap\USER_LDAP();
-		    $connector = $this->getConnector($configPrefix);
-			$this->backends[$configPrefix]->setConnector($connector);
+		    $this->backends[$configPrefix] =
+				new \OCA\user_ldap\USER_LDAP($this->getAccess($configPrefix));
 			if(is_null($this->refBackend)) {
 				$this->refBackend = &$this->backends[$configPrefix];
 			}
diff --git a/autotest.sh b/autotest.sh
index 83f184fa9c065fa12088d700536b4405d4b57182..3831e1812451577c1a020be83c90926743b2ba83 100755
--- a/autotest.sh
+++ b/autotest.sh
@@ -12,6 +12,16 @@ DATABASEUSER=oc_autotest$EXECUTOR_NUMBER
 ADMINLOGIN=admin$EXECUTOR_NUMBER
 BASEDIR=$PWD
 
+if ! [ -w config -a -w config/config.php ]; then
+	echo "Please enable write permissions on config and config/config.php" >&2
+	exit 1
+fi
+
+# Back up existing (dev) config if one exists
+if [ -f config/config.php ]; then
+	mv config/config.php config/config-autotest-backup.php
+fi
+
 # use tmpfs for datadir - should speedup unit test execution
 if [ -d /dev/shm ]; then
   DATADIR=/dev/shm/data-autotest$EXECUTOR_NUMBER
@@ -158,6 +168,13 @@ else
 	execute_tests $1 $2 $3
 fi
 
+cd $BASEDIR
+
+# Restore existing config
+if [ -f config/config-autotest-backup.php ]; then
+	mv config/config-autotest-backup.php config/config.php
+fi
+
 #
 # NOTES on mysql:
 #  - CREATE DATABASE oc_autotest;
diff --git a/core/ajax/share.php b/core/ajax/share.php
index 648f0a71bd44da7c93fbaccb88388c7db92aac99..1166ea3198a2d4688151516cbe56c50f4c481219 100644
--- a/core/ajax/share.php
+++ b/core/ajax/share.php
@@ -23,6 +23,8 @@ OC_JSON::checkLoggedIn();
 OCP\JSON::callCheck();
 OC_App::loadApps();
 
+$defaults = new \OCP\Defaults();
+
 if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSource'])) {
 	switch ($_POST['action']) {
 		case 'share':
@@ -33,7 +35,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
 					if ($shareType === OCP\Share::SHARE_TYPE_LINK && $shareWith == '') {
 						$shareWith = null;
 					}
-					
+
 					$token = OCP\Share::shareItem(
 						$_POST['itemType'],
 						$_POST['itemSource'],
@@ -41,7 +43,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
 						$shareWith,
 						$_POST['permissions']
 					);
-					
+
 					if (is_string($token)) {
 						OC_JSON::success(array('data' => array('token' => $token)));
 					} else {
@@ -81,6 +83,104 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
 				($return) ? OC_JSON::success() : OC_JSON::error();
 			}
 			break;
+	case 'informRecipients':
+
+			$l = OC_L10N::get('core');
+
+			$shareType = (int) $_POST['shareType'];
+			$itemType = $_POST['itemType'];
+			$itemSource = $_POST['itemSource'];
+			$recipient = $_POST['recipient'];
+			$ownerDisplayName = \OCP\User::getDisplayName();
+			$from = \OCP\Util::getDefaultEmailAddress('sharing-noreply');
+
+			$noMail = array();
+			$recipientList = array();
+
+			if($shareType === \OCP\Share::SHARE_TYPE_USER) {
+				$recipientList[] = $recipient;
+			} elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
+				$recipientList = \OC_Group::usersInGroup($recipient);
+			}
+
+			// don't send a mail to the user who shared the file
+			$recipientList = array_diff($recipientList, array(\OCP\User::getUser()));
+
+			// send mail to all recipients with an email address
+			foreach ($recipientList as $recipient) {
+				//get correct target folder name
+				$email = OC_Preferences::getValue($recipient, 'settings', 'email', '');
+
+				if ($email !== '') {
+					$displayName = \OCP\User::getDisplayName($recipient);
+					$items = \OCP\Share::getItemSharedWithUser($itemType, $itemSource, $recipient);
+					$filename = trim($items[0]['file_target'], '/');
+					$subject = (string)$l->t('%s shared »%s« with you', array($ownerDisplayName, $filename));
+					$expiration = null;
+					if (isset($items[0]['expiration'])) {
+						$date = new DateTime($items[0]['expiration']);
+						$expiration = $date->format('Y-m-d');
+					}
+
+					if ($itemType === 'folder') {
+						$foldername = "/Shared/" . $filename;
+					} else {
+						// if it is a file we can just link to the Shared folder,
+						// that's the place where the user will find the file
+						$foldername = "/Shared";
+					}
+
+					$link = \OCP\Util::linkToAbsolute('files', 'index.php', array("dir" => $foldername));
+
+					$content = new OC_Template("core", "mail", "");
+					$content->assign('link', $link);
+					$content->assign('user_displayname', $ownerDisplayName);
+					$content->assign('filename', $filename);
+					$content->assign('expiration', $expiration);
+					$text = $content->fetchPage();
+
+					$content = new OC_Template("core", "altmail", "");
+					$content->assign('link', $link);
+					$content->assign('user_displayname', $ownerDisplayName);
+					$content->assign('filename', $filename);
+					$content->assign('expiration', $expiration);
+					$alttext = $content->fetchPage();
+
+					$default_from = OCP\Util::getDefaultEmailAddress('sharing-noreply');
+					$from = OCP\Config::getUserValue(\OCP\User::getUser(), 'settings', 'email', $default_from);
+
+					// send it out now
+					try {
+						OCP\Util::sendMail($email, $displayName, $subject, $text, $from, $ownerDisplayName, 1, $alttext);
+					} catch (Exception $exception) {
+						$noMail[] = \OCP\User::getDisplayName($recipient);
+					}
+				}
+			}
+
+			\OCP\Share::setSendMailStatus($itemType, $itemSource, $shareType, true);
+
+			if (empty($noMail)) {
+				OCP\JSON::success();
+			} else {
+				OCP\JSON::error(array(
+					'data' => array(
+						'message' => $l->t("Couldn't send mail to following users: %s ",
+								implode(', ', $noMail)
+								)
+						)
+					));
+			}
+			break;
+		case 'informRecipientsDisabled':
+			$itemSource = $_POST['itemSource'];
+			$shareType = $_POST['shareType'];
+			$itemType = $_POST['itemType'];
+			$recipient = $_POST['recipient'];
+			\OCP\Share::setSendMailStatus($itemType, $itemSource, $shareType, false);
+			OCP\JSON::success();
+			break;
+
 		case 'email':
 			// read post variables
 			$user = OCP\USER::getUser();
@@ -213,10 +313,10 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
 					}
 				}
 				$count = 0;
-				
+
 				// enable l10n support
 				$l = OC_L10N::get('core');
-				
+
 				foreach ($groups as $group) {
 					if ($count < 15) {
 						if (!isset($_GET['itemShares'])
diff --git a/core/css/share.css b/core/css/share.css
index 2d6849b4bb1361b1978906716ce0974fd7d1c643..2a21dc6edf66d1143febbb1db50ce217a44f8a96 100644
--- a/core/css/share.css
+++ b/core/css/share.css
@@ -2,95 +2,97 @@
  This file is licensed under the Affero General Public License version 3 or later.
  See the COPYING-README file. */
 
- #dropdown {
- 	background:#eee;
- 	border-bottom-left-radius:1em;
- 	border-bottom-right-radius:1em;
- 	box-shadow:0 1px 1px #777;
- 	display:block;
- 	margin-right:7em;
- 	position:absolute;
- 	right:0;
- 	width:19em;
- 	z-index:500;
- 	padding:1em;
- }
-
- #shareWithList {
- 	list-style-type:none;
- 	padding:.5em;
- }
-
- #shareWithList li {
- 	padding-top:.1em;
- }
- 
- #shareWithList li:first-child {
-	 white-space:normal;
- }
-
- #shareWithList .cruds {
-	 margin-left:-10px;
- }
+#dropdown {
+	background:#eee;
+	border-bottom-left-radius: 5px;
+	border-bottom-right-radius: 5px;
+	box-shadow:0 1px 1px #777;
+	display:block;
+	margin-right:7em;
+	position:absolute;
+	right:0;
+	width:25em;
+	z-index:500;
+	padding:1em;
+}
+
+#shareWithList {
+	list-style-type:none;
+	padding:.5em;
+}
+
+#shareWithList li {
+	padding-top:.1em;
+}
+
+#shareWithList li:first-child {
+	white-space:normal;
+}
+
+#shareWithList .cruds {
+	margin-left:-10px;
+}
 
 #shareWithList .unshare img, #shareWithList .showCruds img {
 	vertical-align:text-bottom; /* properly align icons */
 }
 
- #dropdown label {
- 	font-weight:400;
- }
+#dropdown label {
+	font-weight:400;
+}
 
- #dropdown input[type="checkbox"] {
- 	margin:0 .2em 0 .5em;
- }
+#dropdown input[type="checkbox"] {
+	margin:0 .2em 0 .5em;
+}
 
- a.showCruds {
- 	display:inline;
- 	opacity:.5;
- }
+a.showCruds {
+	display:inline;
+	opacity:.5;
+}
 
- a.unshare {
- 	display:inline;
- 	float:right;
- 	opacity:.5;
- 	padding:.3em 0 0 .3em !important;
+a.unshare {
+	display:inline;
+	float:right;
+	opacity:.5;
+	padding:.3em 0 0 .3em !important;
 	margin-top:-5px;
- }
+}
 
- #link {
- 	border-top:1px solid #ddd;
- 	padding-top:.5em;
- }
+#link {
+	border-top:1px solid #ddd;
+	padding-top:.5em;
+}
 
 #dropdown input[type="text"],#dropdown input[type="password"] {
-    width:90%;
+	width:90%;
 }
 
 #dropdown form {
-    font-size: 100%;
-    margin-left: 0;
-    margin-right: 0;
+	font-size: 100%;
+	margin-left: 0;
+	margin-right: 0;
 }
 
 #linkText,#linkPass,#expiration {
- 	display:none;
- }
+	display:none;
+}
 
- #link #showPassword img {
- 	padding-left:.3em;
- 	width:12px;
- }
+#link #showPassword img {
+	padding-left:.3em;
+	width:12px;
+}
 
- .reshare,#link label,#expiration label {
- 	padding-left:.5em;
- }
+.reshare,#link label,#expiration label {
+	padding-left:.5em;
+}
 
- a.showCruds:hover,a.unshare:hover {
- 	opacity:1;
- }
+a.showCruds:hover,a.unshare:hover {
+	opacity:1;
+}
 
-.reshare { white-space:normal; } /* fix shared by text going out of box */
+.reshare { /* fix shared by text going out of box */
+	white-space:normal;
+}
 
 .ui-autocomplete { /* limit dropdown height to 4 1/2 entries */
 	max-height:103px;
diff --git a/core/css/styles.css b/core/css/styles.css
index dcdeda8a9c95d7aecf57db686a81bb23f23b8e4f..481fb6d5f39caec48e7c66bc890353eb3cd1bd65 100644
--- a/core/css/styles.css
+++ b/core/css/styles.css
@@ -19,9 +19,6 @@ body { background:#fefefe; font:normal .8em/1.6em "Helvetica Neue",Helvetica,Ari
 #body-user #header, #body-settings #header {
 	position:fixed; top:0; left:0; right:0; z-index:100; height:45px; line-height:2.5em;
 	background:#1d2d44 url('../img/noise.png') repeat;
-	-moz-box-shadow:0 0 10px rgba(0, 0, 0, .5);
-	-webkit-box-shadow:0 0 10px rgba(0, 0, 0, .5);
-	box-shadow:0 0 10px rgba(0, 0, 0, .5);
 }
 
 #body-login {
@@ -46,6 +43,10 @@ body { background:#fefefe; font:normal .8em/1.6em "Helvetica Neue",Helvetica,Ari
 	display: inline-block;
 }
 
+#header .avatardiv img {
+	opacity: 1;
+}
+
 /* INPUTS */
 input[type="text"], input[type="password"], input[type="search"], input[type="number"], input[type="email"], input[type="url"],
 textarea, select,
@@ -155,23 +156,37 @@ input[type="submit"].enabled { background:#66f866; border:1px solid #5e5; -moz-b
 
 /* CONTENT ------------------------------------------------------------------ */
 #controls {
+	-moz-box-sizing: border-box;
+	-webkit-box-sizing: border-box;
+	box-sizing: border-box;
 	position: fixed;
-	height: 36px;
+	height: 44px;
 	width: 100%;
-	padding: 0 75px 0 6px;
+	padding-right: 75px;
 	margin: 0;
 	background: #eee;
 	border-bottom: 1px solid #e7e7e7;
 	z-index: 50;
-	-moz-box-sizing: border-box; box-sizing: border-box;
-	-moz-box-shadow: 0 -3px 7px #000; -webkit-box-shadow: 0 -3px 7px #000; box-shadow: 0 -3px 7px #000;
 }
-#controls .button {
+#controls .button,
+#controls button,
+#controls input[type='submit'],
+#controls input[type='text'],
+#controls input[type='password'],
+#controls select {
+	-moz-box-sizing: border-box;
+	-webkit-box-sizing: border-box;
+	box-sizing: border-box;
 	display: inline-block;
+	height: 36px;
+	padding: 7px 10px
 }
 
 #content { position:relative; height:100%; width:100%; }
-#content .hascontrols { position: relative; top: 2.9em; }
+#content .hascontrols {
+	position: relative;
+	top: 45px;
+}
 #content-wrapper {
 	position:absolute; height:100%; width:100%; padding-top:3.5em; padding-left:80px;
 	-moz-box-sizing:border-box; box-sizing:border-box;
@@ -512,7 +527,6 @@ label.infield { cursor:text !important; top:1.05em; left:.85em; }
 	z-index: 75;
 	height: 100%;
 	background:#383c43 url('../img/noise.png') repeat;
-	-moz-box-shadow:0 0 7px #000; -webkit-box-shadow:0 0 7px #000; box-shadow:0 0 7px #000;
 	overflow:hidden; box-sizing:border-box; -moz-box-sizing:border-box;
 	/* prevent ugly selection effect on accidental selection */
 	-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;
@@ -751,15 +765,38 @@ span.ui-icon {float: left; margin: 3px 7px 30px 0;}
 .arrow.left { left:-13px; bottom:1.2em; -webkit-transform:rotate(270deg); -moz-transform:rotate(270deg); -o-transform:rotate(270deg); -ms-transform:rotate(270deg); transform:rotate(270deg); }
 .arrow.up { top:-8px; right:2em; }
 .arrow.down { -webkit-transform:rotate(180deg); -moz-transform:rotate(180deg); -o-transform:rotate(180deg); -ms-transform:rotate(180deg); transform:rotate(180deg); }
-.help-includes {overflow: hidden; width: 100%; height: 100%; -moz-box-sizing: border-box;	box-sizing: border-box;	padding-top: 2.8em; }
+.help-includes {
+	overflow: hidden;
+	width: 100%;
+	height: 100%;
+	-moz-box-sizing: border-box;
+	box-sizing: border-box;
+	padding-top: 44px;
+}
 .help-iframe {width: 100%; height: 100%; margin: 0;padding: 0; border: 0; overflow: auto;}
 
 
 /* ---- BREADCRUMB ---- */
-div.crumb { float:left; display:block; background:url('../img/breadcrumb.svg') no-repeat right 0; padding:.75em 1.5em 0 1em; height:2.9em;  -moz-box-sizing:border-box; box-sizing:border-box; }
-div.crumb:first-child { padding:10px 20px 10px 5px; }
-div.crumb.last { font-weight:bold; background:none; padding-right:10px; }
-div.crumb a{ padding: 0.9em 0 0.7em 0; }
+div.crumb {
+	float: left;
+	display: block;
+	background: url('../img/breadcrumb.svg') no-repeat right center;
+	height: 44px;
+}
+div.crumb a {
+	position: relative;
+	top: 12px;
+	padding: 14px 24px 14px 17px;
+	color: #555;
+}
+div.crumb:first-child a {
+	position: relative;
+	top: 13px;
+}
+div.crumb.last {
+	font-weight: bold;
+	margin-right: 10px;
+}
 
 /* some feedback for hover/tap on breadcrumbs */
 div.crumb:hover,
diff --git a/core/img/breadcrumb-start.png b/core/img/breadcrumb-start.png
deleted file mode 100644
index b0df5f44037637c322fb4eb867f90c18c6e8f613..0000000000000000000000000000000000000000
Binary files a/core/img/breadcrumb-start.png and /dev/null differ
diff --git a/core/img/breadcrumb-start.svg b/core/img/breadcrumb-start.svg
deleted file mode 100644
index 7f36231cdf8d370d8abc8ea1cb2ea50b21f0c657..0000000000000000000000000000000000000000
--- a/core/img/breadcrumb-start.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="36" width="11" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
- <g transform="translate(0 -1016.4)">
-  <path d="m0 0 11 18-11 18z" transform="translate(0 1016.4)" fill="#ddd"/>
- </g>
-</svg>
diff --git a/core/img/breadcrumb.png b/core/img/breadcrumb.png
index 84992be0d9350d7a94455cfe46dcea2fcc669e64..7e9593a36bf9a2fc123ee6237fdfc88c022df0a6 100644
Binary files a/core/img/breadcrumb.png and b/core/img/breadcrumb.png differ
diff --git a/core/img/breadcrumb.svg b/core/img/breadcrumb.svg
index 05a216e50a9eaec7a20be7274957dc6e8ae2f46f..f0b5c9218d50b5b05e9a7446519bad8743464440 100644
--- a/core/img/breadcrumb.svg
+++ b/core/img/breadcrumb.svg
@@ -1,6 +1,12 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="36" width="11" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
- <g transform="translate(0 -1016.4)">
-  <path d="m0.5 0 10 18-10 18 10-18z" transform="translate(0 1016.4)" stroke="#ddd" stroke-linecap="round" stroke-miterlimit="31.2" stroke-width="0.9" fill="#ddd"/>
- </g>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="44" width="14" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata>
+  <rdf:RDF>
+   <cc:Work rdf:about="">
+    <dc:format>image/svg+xml</dc:format>
+    <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+    <dc:title/>
+   </cc:Work>
+  </rdf:RDF>
+ </metadata>
+ <path d="M0.54879,0.047777,12.744,22,0.54879,43.951,12.744,22z" stroke="#d7d7d7" stroke-linecap="round" stroke-miterlimit="31.20000076000000178" stroke-width="1.09758711000000009" fill="#F00"/>
 </svg>
diff --git a/core/img/places/link.png b/core/img/places/link.png
new file mode 100644
index 0000000000000000000000000000000000000000..44b7e199a7267e9c1d87f54ebdbf064e7fac0b45
Binary files /dev/null and b/core/img/places/link.png differ
diff --git a/core/img/places/link.svg b/core/img/places/link.svg
new file mode 100644
index 0000000000000000000000000000000000000000..8784ebc1456b6050f176baa85d3432f1375458f1
--- /dev/null
+++ b/core/img/places/link.svg
@@ -0,0 +1,12 @@
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="32" width="32" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata>
+  <rdf:RDF>
+   <cc:Work rdf:about="">
+    <dc:format>image/svg+xml</dc:format>
+    <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+    <dc:title/>
+   </cc:Work>
+  </rdf:RDF>
+ </metadata>
+ <path fill="#333" d="M16,4c-6.6274,0-12,5.3726-12,12,0,6.627,5.3726,12,12,12,6.627,0,12-5.373,12-12,0-6.6274-5.373-12-12-12zm1.375,1.5313c2.059,0.0457,3.879,1.2826,5.719,2.0938l2.9691,4.1093-0.46971,1.7657,0.90686,0.56246-0.01543,2.0937c-0.02074,0.59892,0.0086,1.1986-0.0156,1.7969-0.28517,1.1355-0.94394,2.1713-1.5,3.2031-0.37695,0.18585,0.03437-1.2317-0.20313-1.6719,0.05486-1.0173-0.80743-0.97029-1.3903-0.40526-0.72172,0.42068-2.3074,0.54754-2.3589-0.59383-0.40972-1.3716-0.06-2.833,0.49886-4.1093l-0.921-1.125,0.327-2.891-1.469-1.4839,0.345-1.6252-1.719-0.9687c-0.339-0.2661-0.984-0.3713-1.125-0.7344,0.13954-0.00789,0.28457-0.018686,0.42189-0.0156zm-4.2187,0.015634c0.0539,0.00789,0.11999,0.045309,0.21874,0.125,0.57943,0.31834-0.14143,0.67954-0.31251,1.0157-0.92537,0.62589,0.28457,1.1385,0.68743,1.6406,0.64577-0.18549,1.2917-1.1086,2.2344-0.828,1.2058-0.37629,1.0137,1.0099,1.7031,1.625,0.08948,0.28954,1.5086,1.2317,0.65623,0.92177-0.702-0.54411-1.4827-0.50314-1.9845,0.28131-1.355,0.735-0.552-1.4144-1.202-1.9373-0.982-1.0957-0.57,0.8186-0.687,1.3907-0.639-0.0139-1.831-0.4913-2.485,0.2816l0.64046,1.0467,0.76577-1.1719c0.186-0.42411,0.41949,0.32966,0.62486,0.46886,0.24531,0.47297,1.4109,1.2744,0.53126,1.5-1.3039,0.72326-2.3295,1.8202-3.4375,2.7969-0.37371,0.78857-1.1366,0.6984-1.6094,0.0468-1.1438-0.70372-1.0589,1.1256-0.99994,1.8125l1.0013-0.626v1.0312c-0.028286,0.19509-0.00411,0.39806-0.0156,0.59383-0.70063,0.732-1.4069-1.0277-2.0157-1.422l-0.0468-2.5781c0.022114-0.72429-0.1308-1.4659,0.0156-2.1718,1.3779-1.4789,2.7775-3.0107,3.5935-4.891h1.3437c0.93909,0.45497,0.40406-1.0082,0.7812-0.95314zm-1.984,13.406c0.16303-0.01739,0.34848,0.01984,0.54688,0.12501,1.265,0.18106,2.2109,1.0987,3.2187,1.7969,0.80352,0.79632,2.5419,0.54134,2.7345,1.8907-0.29248,1.4636-1.7323,2.2495-3,2.7657-0.31646,0.17657-0.65657,0.31714-1.0157,0.37543-1.1753,0.29314-1.6834-0.912-1.9219-1.8137-0.53212-1.1143-1.8621-1.9577-1.6718-3.3274,0.0312-0.68057,0.40286-1.7373,1.1093-1.8125z"/>
+</svg>
diff --git a/core/js/avatar.js b/core/js/avatar.js
index 57e6daa0930251fba4fa314bf36864e5209da8f2..c54c40687689259fb0ee4e04d4e48094ebac2856 100644
--- a/core/js/avatar.js
+++ b/core/js/avatar.js
@@ -1,6 +1,6 @@
 $(document).ready(function(){
 	if (OC.currentUser) {
-		$('#header .avatardiv').avatar(OC.currentUser, 32);
+		$('#header .avatardiv').avatar(OC.currentUser, 32, undefined, true);
 		// Personal settings
 		$('#avatar .avatardiv').avatar(OC.currentUser, 128);
 	}
diff --git a/core/js/jquery.avatar.js b/core/js/jquery.avatar.js
index 88a4c25d1eecfc401f7a684d6228a086eb8df44a..0006810172668fb4bf9d34bce83707dc64155d37 100644
--- a/core/js/jquery.avatar.js
+++ b/core/js/jquery.avatar.js
@@ -15,7 +15,7 @@
  * You may use this on any <div></div>
  * Here I'm using <div class="avatardiv"></div> as an example.
  *
- * There are 4 ways to call this:
+ * There are 5 ways to call this:
  *
  * 1. $('.avatardiv').avatar('jdoe', 128);
  * This will make the div to jdoe's fitting avatar, with a size of 128px.
@@ -34,10 +34,15 @@
  * 4. $('.avatardiv').avatar('jdoe', 128, true);
  * This will behave like the first example, except it will also append random
  * hashes to the custom avatar images, to force image reloading in IE8.
+ *
+ * 5. $('.avatardiv').avatar('jdoe', 128, undefined, true);
+ * This will behave like the first example, but it will hide the avatardiv, if
+ * it will display the default placeholder. undefined is the ie8fix from
+ * example 4 and can be either true, or false/undefined, to be ignored.
  */
 
 (function ($) {
-	$.fn.avatar = function(user, size, ie8fix) {
+	$.fn.avatar = function(user, size, ie8fix, hidedefault) {
 		if (typeof(size) === 'undefined') {
 			if (this.height() > 0) {
 				size = this.height();
@@ -69,12 +74,17 @@
 			var url = OC.Router.generate('core_avatar_get', {user: user, size: size})+'?requesttoken='+oc_requesttoken;
 			$.get(url, function(result) {
 				if (typeof(result) === 'object') {
-					if (result.data && result.data.displayname) {
-						$div.placeholder(user, result.data.displayname);
+					if (!hidedefault) {
+						if (result.data && result.data.displayname) {
+							$div.placeholder(user, result.data.displayname);
+						} else {
+							$div.placeholder(user);
+						}
 					} else {
-						$div.placeholder(user);
+						$div.hide();
 					}
 				} else {
+					$div.show();
 					if (ie8fix === true) {
 						$div.html('<img src="'+url+'#'+Math.floor(Math.random()*1000)+'">');
 					} else {
diff --git a/core/js/share.js b/core/js/share.js
index 82f5da0baea0f5f610b9f2ea1c4c8e6080981a91..8d14520cd74650934b691495dcdbc04b3ebadd64 100644
--- a/core/js/share.js
+++ b/core/js/share.js
@@ -114,6 +114,7 @@ OC.Share={
 				data = false;
 			}
 		}});
+
 		return data;
 	},
 	share:function(itemType, itemSource, shareType, shareWith, permissions, callback) {
@@ -217,9 +218,9 @@ OC.Share={
 						OC.Share.showLink(share.token, share.share_with, itemSource);
 					} else {
 						if (share.collection) {
-							OC.Share.addShareWith(share.share_type, share.share_with, share.share_with_displayname, share.permissions, possiblePermissions, share.collection);
+							OC.Share.addShareWith(share.share_type, share.share_with, share.share_with_displayname, share.permissions, possiblePermissions, share.mail_send, share.collection);
 						} else {
-							OC.Share.addShareWith(share.share_type, share.share_with, share.share_with_displayname,  share.permissions, possiblePermissions, false);
+							OC.Share.addShareWith(share.share_type, share.share_with, share.share_with_displayname, share.permissions, possiblePermissions, share.mail_send, false);
 						}
 					}
 					if (share.expiration != null) {
@@ -301,7 +302,7 @@ OC.Share={
 			}
 		});
 	},
-	addShareWith:function(shareType, shareWith, shareWithDisplayName, permissions, possiblePermissions, collection) {
+	addShareWith:function(shareType, shareWith, shareWithDisplayName, permissions, possiblePermissions, mailSend, collection) {
 		if (!OC.Share.itemShares[shareType]) {
 			OC.Share.itemShares[shareType] = [];
 		}
@@ -343,6 +344,14 @@ OC.Share={
 			}else{
 				html += escapeHTML(shareWithDisplayName);
 			}
+			var mailNotificationEnabled = $('input:hidden[name=mailNotificationEnabled]').val();
+			if (mailNotificationEnabled === 'yes') {
+				var checked = '';
+				if (mailSend === '1') {
+					checked = 'checked';
+				}
+				html += '<input type="checkbox" name="mailNotification" class="mailNotification" ' + checked + ' />'+t('core', 'notify user by email')+'</label>';
+			}
 			if (possiblePermissions & OC.PERMISSION_CREATE || possiblePermissions & OC.PERMISSION_UPDATE || possiblePermissions & OC.PERMISSION_DELETE) {
 				if (editChecked == '') {
 					html += '<label style="display:none;">';
@@ -699,5 +708,27 @@ $(document).ready(function() {
 		}
 	});
 
+	$(document).on('click', '#dropdown input[name=mailNotification]', function() {
+		var li = $(this).parent();
+		var itemType = $('#dropdown').data('item-type');
+		var itemSource = $('#dropdown').data('item-source');
+		var action = '';
+		if (this.checked) {
+			action = 'informRecipients';
+		} else {
+			action = 'informRecipientsDisabled';
+		}
+
+		var shareType = $(li).data('share-type');
+		var shareWith = $(li).data('share-with');
+
+		$.post(OC.filePath('core', 'ajax', 'share.php'), {action: action, recipient: shareWith, shareType: shareType, itemSource: itemSource, itemType: itemType}, function(result) {
+			if (result.status !== 'success') {
+				OC.dialogs.alert(t('core', result.data.message), t('core', 'Warning'));
+			}
+		});
+
+});
+
 
 });
diff --git a/core/skeleton/welcome.txt b/core/skeleton/welcome.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c86eaf91bbea5395ffa76ee1c99ed4b2380be47b
--- /dev/null
+++ b/core/skeleton/welcome.txt
@@ -0,0 +1,5 @@
+Welcome to your ownCloud account!
+
+This is just an example file for developers and git users. 
+The packaged and released versions will come with better examples.
+
diff --git a/core/templates/altmail.php b/core/templates/altmail.php
index 2551473c6f0939364d95f3739201709281b08a41..00b67bee456e75783dc3fea8ccf9abf64eb04214 100644
--- a/core/templates/altmail.php
+++ b/core/templates/altmail.php
@@ -1,5 +1,9 @@
 <?php
-print_unescaped($l->t("Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\nCheers!", array($_['user_displayname'], $_['filename'], $_['link'])));
+print_unescaped($l->t("Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n", array($_['user_displayname'], $_['filename'], $_['link'])));
+if ( isset($_['expiration']) ) {
+	print_unescaped($l->t("The share will expire on %s.\n\n", array($_['expiration'])));
+}
+p($l->t("Cheers!"));
 ?>
 
 --
diff --git a/core/templates/mail.php b/core/templates/mail.php
index de72b136b135149a73b09bf04b7dbb2b55b12cc4..40092f5491f88aa3f19156e975bb86b5ddecd1fd 100644
--- a/core/templates/mail.php
+++ b/core/templates/mail.php
@@ -12,7 +12,11 @@
 <td bgcolor="#f8f8f8" width="20px">&nbsp;</td>
 <td bgcolor="#f8f8f8" style="font-weight:normal; font-size:0.8em; line-height:1.2em; font-family:verdana,'arial',sans;">
 <?php
-print_unescaped($l->t('Hey there,<br><br>just letting you know that %s shared »%s« with you.<br><a href="%s">View it!</a><br><br>Cheers!', array($_['user_displayname'], $_['filename'], $_['link'])));
+print_unescaped($l->t('Hey there,<br><br>just letting you know that %s shared »%s« with you.<br><a href="%s">View it!</a><br><br>', array($_['user_displayname'], $_['filename'], $_['link'])));
+if ( isset($_['expiration']) ) {
+	print_unescaped($l->t("The share will expire on %s.<br><br>", array($_['expiration'])));
+}
+p($l->t('Cheers!'));
 ?>
 </td>
 </tr>
@@ -22,7 +26,8 @@ print_unescaped($l->t('Hey there,<br><br>just letting you know that %s shared »
 <td bgcolor="#f8f8f8" 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 print_unescaped($theme->getBaseUrl()); ?>"><?php print_unescaped($theme->getBaseUrl());?></a></td>
+<br><a href="<?php print_unescaped($theme->getBaseUrl()); ?>"><?php print_unescaped($theme->getBaseUrl());?></a>
+</td>
 </tr>
 <tr>
 <td bgcolor="#f8f8f8" colspan="2">&nbsp;</td>
diff --git a/db_structure.xml b/db_structure.xml
index 86f9989e1c237e7c035c40157e01dddbd3ca7340..f9470dc86b359f3769a83df225b80190c0c50568 100644
--- a/db_structure.xml
+++ b/db_structure.xml
@@ -844,6 +844,14 @@
 				<length>32</length>
 			</field>
 
+			<field>
+				<name>mail_send</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<length>1</length>
+			</field>
+
 			<index>
 				<name>token_index</name>
 				<field>
diff --git a/lib/autoloader.php b/lib/autoloader.php
index 8b12e6bc4b7c54bb858c9b126dc2aa1603739d85..b5b58918372f563e844851255254b8abc617eeba 100644
--- a/lib/autoloader.php
+++ b/lib/autoloader.php
@@ -77,6 +77,7 @@ class Autoloader {
 			$paths[] = 'private/' . strtolower(str_replace('_', '/', substr($class, 3)) . '.php');
 		} elseif (strpos($class, 'OC\\') === 0) {
 			$paths[] = 'private/' . strtolower(str_replace('\\', '/', substr($class, 3)) . '.php');
+			$paths[] = strtolower(str_replace('\\', '/', substr($class, 3)) . '.php');
 		} elseif (strpos($class, 'OCP\\') === 0) {
 			$paths[] = 'public/' . strtolower(str_replace('\\', '/', substr($class, 4)) . '.php');
 		} elseif (strpos($class, 'OCA\\') === 0) {
diff --git a/lib/private/connector/sabre/node.php b/lib/private/connector/sabre/node.php
index 29b7f9e53a5ba8d569e6ce0762e28e47360ca6e6..e65ad7b8bef6d643f5c17bacbd814260203b784f 100644
--- a/lib/private/connector/sabre/node.php
+++ b/lib/private/connector/sabre/node.php
@@ -207,7 +207,14 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr
 			while( $row = $result->fetchRow()) {
 				$this->property_cache[$row['propertyname']] = $row['propertyvalue'];
 			}
-			$this->property_cache[self::GETETAG_PROPERTYNAME] = $this->getETagPropertyForPath($this->path);
+
+			// Don't call the static getETagPropertyForPath, its result is not cached
+			$this->getFileinfoCache();
+			if ($this->fileinfo_cache['etag']) {
+				$this->property_cache[self::GETETAG_PROPERTYNAME] = '"'.$this->fileinfo_cache['etag'].'"';
+			} else {
+				$this->property_cache[self::GETETAG_PROPERTYNAME] = null;
+			}
 		}
 
 		// if the array was empty, we need to return everything
diff --git a/lib/private/defaults.php b/lib/private/defaults.php
index 10813a3e8d88d3eba151bb95f06f671c110374d4..4951c6f50aebc90b75a13f1a39df29c4db012ac3 100644
--- a/lib/private/defaults.php
+++ b/lib/private/defaults.php
@@ -13,6 +13,7 @@ if (file_exists(OC::$SERVERROOT . '/themes/' . OC_Util::getTheme() . '/defaults.
 class OC_Defaults {
 
 	private $theme;
+	private $l;
 
 	private $defaultEntity;
 	private $defaultName;
@@ -24,7 +25,7 @@ class OC_Defaults {
 	private $defaultLogoClaim;
 
 	function __construct() {
-		$l = OC_L10N::get('core');
+		$this->l = OC_L10N::get('core');
 
 		$this->defaultEntity = "ownCloud"; /* e.g. company name, used for footers and copyright notices */
 		$this->defaultName = "ownCloud"; /* short name, used when referring to the software */
@@ -32,7 +33,7 @@ class OC_Defaults {
 		$this->defaultBaseUrl = "http://owncloud.org";
 		$this->defaultSyncClientUrl = " http://owncloud.org/sync-clients/";
 		$this->defaultDocBaseUrl = "http://doc.owncloud.org";
-		$this->defaultSlogan = $l->t("web services under your control");
+		$this->defaultSlogan = $this->l->t("web services under your control");
 		$this->defaultLogoClaim = "";
 
 		if (class_exists("OC_Theme")) {
diff --git a/lib/private/files/cache/upgrade.php b/lib/private/files/cache/upgrade.php
index cfb9a1173113fffd6f278acb780a05e87879001d..e3a46896cbfa49a18f26d0d2446346a170a61a9f 100644
--- a/lib/private/files/cache/upgrade.php
+++ b/lib/private/files/cache/upgrade.php
@@ -192,7 +192,15 @@ class Upgrade {
 	 */
 	static function needUpgrade($user) {
 		$cacheVersion = (int)\OCP\Config::getUserValue($user, 'files', 'cache_version', 4);
-		return $cacheVersion < 5;
+		if ($cacheVersion < 5) {
+			$legacy = new \OC\Files\Cache\Legacy($user);
+			if ($legacy->hasItems()) {
+				return true;
+			}
+			self::upgradeDone($user);
+		}
+
+		return false;
 	}
 
 	/**
diff --git a/lib/private/template/functions.php b/lib/private/template/functions.php
index 501f8081bff9969d2d3b4b4317c03176d92d0395..0aa2b27b96be5dde13d6663dd4d1b8a26d0e5e4d 100644
--- a/lib/private/template/functions.php
+++ b/lib/private/template/functions.php
@@ -85,22 +85,51 @@ function human_file_size( $bytes ) {
 	return OC_Helper::humanFileSize( $bytes );
 }
 
-function relative_modified_date($timestamp) {
+/**
+ * @brief Strips the timestamp of its time value
+ * @param int $timestamp UNIX timestamp to strip
+ * @return $timestamp without time value
+ */
+function strip_time($timestamp){
+	$date = new \DateTime("@{$timestamp}");
+	$date->setTime(0, 0, 0);
+	return intval($date->format('U'));
+}
+
+/**
+ * @brief Formats timestamp relatively to the current time using
+ * a human-friendly format like "x minutes ago" or "yesterday"
+ * @param int $timestamp timestamp to format
+ * @param int $fromTime timestamp to compare from, defaults to current time
+ * @param bool $dateOnly whether to strip time information
+ * @return formatted timestamp
+ */
+function relative_modified_date($timestamp, $fromTime = null, $dateOnly = false) {
 	$l=OC_L10N::get('lib');
-	$timediff = time() - $timestamp;
+	if (!isset($fromTime) || $fromTime === null){
+		$fromTime = time();
+	}
+	if ($dateOnly){
+		$fromTime = strip_time($fromTime);
+		$timestamp = strip_time($timestamp);
+	}
+	$timediff = $fromTime - $timestamp;
 	$diffminutes = round($timediff/60);
 	$diffhours = round($diffminutes/60);
 	$diffdays = round($diffhours/24);
 	$diffmonths = round($diffdays/31);
 
-	if($timediff < 60) { return $l->t('seconds ago'); }
-	else if($timediff < 3600) { return $l->n('%n minute ago', '%n minutes ago', $diffminutes); }
-	else if($timediff < 86400) { return $l->n('%n hour ago', '%n hours ago', $diffhours); }
-	else if((date('G')-$diffhours) > 0) { return $l->t('today'); }
-	else if((date('G')-$diffhours) > -24) { return $l->t('yesterday'); }
+	if(!$dateOnly && $timediff < 60) { return $l->t('seconds ago'); }
+	else if(!$dateOnly && $timediff < 3600) { return $l->n('%n minute ago', '%n minutes ago', $diffminutes); }
+	else if(!$dateOnly && $timediff < 86400) { return $l->n('%n hour ago', '%n hours ago', $diffhours); }
+	else if((date('G', $fromTime)-$diffhours) >= 0) { return $l->t('today'); }
+	else if((date('G', $fromTime)-$diffhours) >= -24) { return $l->t('yesterday'); }
+	// 86400 * 31 days = 2678400
 	else if($timediff < 2678400) { return $l->n('%n day go', '%n days ago', $diffdays); }
+	// 86400 * 60 days = 518400
 	else if($timediff < 5184000) { return $l->t('last month'); }
-	else if((date('n')-$diffmonths) > 0) { return $l->n('%n month ago', '%n months ago', $diffmonths); }
+	else if((date('n', $fromTime)-$diffmonths) > 0) { return $l->n('%n month ago', '%n months ago', $diffmonths); }
+	// 86400 * 365.25 days * 2 = 63113852
 	else if($timediff < 63113852) { return $l->t('last year'); }
 	else { return $l->t('years ago'); }
 }
diff --git a/lib/private/util.php b/lib/private/util.php
index 1cbb19eaec44ae70f4ef8a3506db79b20bc1d217..ae9aef69b4cd1fce4efb6a85e4571cdf46042cb0 100755
--- a/lib/private/util.php
+++ b/lib/private/util.php
@@ -68,6 +68,7 @@ class OC_Util {
 			$userDirectory = $userRoot . '/files';
 			if( !is_dir( $userDirectory )) {
 				mkdir( $userDirectory, 0755, true );
+				OC_Util::copySkeleton($userDirectory);
 			}
 			//jail the user into his "home" directory
 			\OC\Files\Filesystem::init($user, $userDir);
@@ -92,6 +93,35 @@ class OC_Util {
 		}
 	}
 
+	/**
+	 * @brief copies the user skeleton files into the fresh user home files
+	 * @param string $userDirectory
+	 */
+	public static function copySkeleton($userDirectory) {
+		OC_Util::copyr(\OC::$SERVERROOT.'/core/skeleton' , $userDirectory);
+	}
+
+	/**
+	 * @brief copies a directory recursively
+	 * @param string $source
+	 * @param string $target
+	 * @return void
+	 */
+	public static function copyr($source,$target) {
+		$dir = opendir($source);
+		@mkdir($target);
+		while(false !== ( $file = readdir($dir)) ) {
+			if ( !\OC\Files\Filesystem::isIgnoredDir($file) ) {
+				if ( is_dir($source . '/' . $file) ) {
+					OC_Util::copyr($source . '/' . $file , $target . '/' . $file);
+				} else {
+					copy($source . '/' . $file,$target . '/' . $file);
+				}
+			}
+		}
+		closedir($dir);
+	}
+
 	/**
 	 * @return void
 	 */
@@ -138,7 +168,7 @@ class OC_Util {
 		OC_Util::loadVersion();
 		return \OC::$server->getSession()->get('OC_Channel');
 	}
-        
+
 	/**
 	 * @description get the build number of the current installed of ownCloud.
 	 * @return string
diff --git a/lib/public/share.php b/lib/public/share.php
index 6c5783f1179014c8897514a5df4c3008d2d25171..e6a74117aa21b012cf9cb6ca33b6a388c30eb2ec 100644
--- a/lib/public/share.php
+++ b/lib/public/share.php
@@ -246,9 +246,9 @@ class Share {
 
 	/**
 	* @brief Get the item of item type shared with the current user
-	* @param string Item type
-	* @param string Item target
-	* @param int Format (optional) Format type must be defined by the backend
+	* @param string $itemType
+	* @param string $ItemTarget
+	* @param int $format (optional) Format type must be defined by the backend
 	* @return Return depends on format
 	*/
 	public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE,
@@ -257,6 +257,55 @@ class Share {
 			$parameters, 1, $includeCollections);
 	}
 
+	/**
+	 * @brief Get the item of item type shared with a given user by source
+	 * @param string $ItemType
+	 * @param string $ItemSource
+	 * @param string $user User user to whom the item was shared
+	 * @return array Return list of items with file_target, permissions and expiration
+	 */
+	public static function getItemSharedWithUser($itemType, $itemSource, $user) {
+
+		$shares = array();
+
+		// first check if there is a db entry for the specific user
+		$query = \OC_DB::prepare(
+				'SELECT `file_target`, `permissions`, `expiration`
+					FROM
+					`*PREFIX*share`
+					WHERE
+					`item_source` = ? AND `item_type` = ? AND `share_with` = ?'
+				);
+
+		$result = \OC_DB::executeAudited($query, array($itemSource, $itemType, $user));
+
+		while ($row = $result->fetchRow()) {
+			$shares[] = $row;
+		}
+
+		//if didn't found a result than let's look for a group share.
+		if(empty($shares)) {
+			$groups = \OC_Group::getUserGroups($user);
+
+			$query = \OC_DB::prepare(
+					'SELECT `file_target`, `permissions`, `expiration`
+						FROM
+						`*PREFIX*share`
+						WHERE
+						`item_source` = ? AND `item_type` = ? AND `share_with` in (?)'
+					);
+
+			$result = \OC_DB::executeAudited($query, array($itemSource, $itemType, implode(',', $groups)));
+
+			while ($row = $result->fetchRow()) {
+				$shares[] = $row;
+			}
+		}
+
+		return $shares;
+
+	}
+
 	/**
 	* @brief Get the item of item type shared with the current user by source
 	* @param string Item type
@@ -653,6 +702,29 @@ class Share {
 		}
 		return false;
 	}
+	/**
+	 * @brief sent status if users got informed by mail about share
+	 * @param string $itemType
+	 * @param string $itemSource
+	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
+	 * @param bool $status
+	 */
+	public static function setSendMailStatus($itemType, $itemSource, $shareType, $status) {
+		$status = $status ? 1 : 0;
+
+		$query = \OC_DB::prepare(
+				'UPDATE `*PREFIX*share`
+					SET `mail_send` = ?
+					WHERE `item_type` = ? AND `item_source` = ? AND `share_type` = ?');
+
+		$result = $query->execute(array($status, $itemType, $itemSource, $shareType));
+
+		if($result === false) {
+			\OC_Log::write('OCP\Share', 'Couldn\'t set send mail status', \OC_Log::ERROR);
+		}
+
+
+	}
 
 	/**
 	* @brief Set the permissions of an item for a specific user or group
@@ -983,19 +1055,19 @@ class Share {
 		if ($format == self::FORMAT_STATUSES) {
 			if ($itemType == 'file' || $itemType == 'folder') {
 				$select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`,'
-					.' `share_type`, `file_source`, `path`, `expiration`, `storage`';
+					.' `share_type`, `file_source`, `path`, `expiration`, `storage`, `mail_send`';
 			} else {
-				$select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `expiration`';
+				$select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `expiration`, `mail_send`';
 			}
 		} else {
 			if (isset($uidOwner)) {
 				if ($itemType == 'file' || $itemType == 'folder') {
 					$select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`,'
 						.' `share_type`, `share_with`, `file_source`, `path`, `permissions`, `stime`,'
-						.' `expiration`, `token`, `storage`';
+						.' `expiration`, `token`, `storage`, `mail_send`';
 				} else {
 					$select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `permissions`,'
-						.' `stime`, `file_source`, `expiration`, `token`';
+						.' `stime`, `file_source`, `expiration`, `token`, `mail_send`';
 				}
 			} else {
 				if ($fileDependent) {
@@ -1006,11 +1078,11 @@ class Share {
 						$select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `uid_owner`, '
 							.'`share_type`, `share_with`, `file_source`, `path`, `file_target`, '
 							.'`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
-							.'`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`';
+							.'`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `mail_send`';
 					} else {
 						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,
 							`*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,
-							`file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`, `storage`';
+							`file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`, `storage`, `mail_send`';
 					}
 				} else {
 					$select = '*';
diff --git a/lib/public/template.php b/lib/public/template.php
index 3b1a4ed4906763fae906314ec3fcc436c86e4771..a5c500b0e25a90b09270394017b74162d73c699c 100644
--- a/lib/public/template.php
+++ b/lib/public/template.php
@@ -90,8 +90,8 @@ function human_file_size( $bytes ) {
  * @param $timestamp unix timestamp
  * @returns human readable interpretation of the timestamp
  */
-function relative_modified_date($timestamp) {
-	return(\relative_modified_date($timestamp));
+function relative_modified_date($timestamp, $dateOnly = false) {
+	return(\relative_modified_date($timestamp, null, $dateOnly));
 }
 
 
diff --git a/settings/admin.php b/settings/admin.php
index dd36790907df7621733938c032479ce648cba290..120f15bec19b0d59249359051810cd1bfbdaaeb5 100755
--- a/settings/admin.php
+++ b/settings/admin.php
@@ -33,16 +33,17 @@ $tmpl->assign('shareAPIEnabled', OC_Appconfig::getValue('core', 'shareapi_enable
 
 // Check if connected using HTTPS
 if (OC_Request::serverProtocol() === 'https') {
-	$connectedHTTPS = true; 
+	$connectedHTTPS = true;
 } else {
 	$connectedHTTPS = false;
-} 
+}
 $tmpl->assign('isConnectedViaHTTPS', $connectedHTTPS);
 $tmpl->assign('enforceHTTPSEnabled', OC_Config::getValue( "forcessl", false));
 
 $tmpl->assign('allowLinks', OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes'));
 $tmpl->assign('allowPublicUpload', OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes'));
 $tmpl->assign('allowResharing', OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'));
+$tmpl->assign('allowMailNotification', OC_Appconfig::getValue('core', 'shareapi_allow_mail_notification', 'yes'));
 $tmpl->assign('sharePolicy', OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'));
 $tmpl->assign('forms', array());
 foreach($forms as $form) {
diff --git a/settings/css/settings.css b/settings/css/settings.css
index 57a43180a43a5e9b5f2e2d54ecb6add952ab24bf..0afdd9edbfaef15627d0512d24f5e1621a71c5f3 100644
--- a/settings/css/settings.css
+++ b/settings/css/settings.css
@@ -47,18 +47,37 @@ tr:hover>td.remove>a { float:right; }
 li.selected { background-color:#ddd; }
 table:not(.nostyle) { width:100%; }
 #rightcontent { padding-left: 1em; }
-div.quota { float:right; display:block; position:absolute; right:25em; top:-1px; }
+div.quota {
+	float: right;
+	display: block;
+	position: absolute;
+	right: 216px;
+	top: 0;
+}
 div.quota-select-wrapper { position: relative; }
 div.recoveryPassword { left:50em; display:block; position:absolute; top:-1px; }
 input#recoveryPassword {width:15em;}
 select.quota { position:absolute; left:0; top:0; width:10em; }
 select.quota-user { position:relative; left:0; top:0; width:10em; }
-div.quota>span { position:absolute; right:0; white-space:nowrap; top:.7em; color:#888; text-shadow:0 1px 0 #fff; }
+div.quota>span {
+	position: absolute;
+	right: 0;
+	white-space: nowrap;
+	top: 12px;
+	color: #888;
+	text-shadow: 0 1px 0 #fff;
+}
 select.quota.active { background: #fff; }
 
 /* positioning fixes */
-#newuser { position:relative; top:-3px; }
-#newuser .multiselect { top:1px; }
+#newuser .multiselect {
+	min-width: 150px !important;
+}
+#newuser .multiselect,
+#newusergroups + input[type='submit'] {
+	position: relative;
+	top: 1px;
+}
 #headerGroups, #headerSubAdmins, #headerQuota { padding-left:18px; }
 
 .ie8 table.hascontrols{border-collapse:collapse;width: 100%;}
diff --git a/settings/js/personal.js b/settings/js/personal.js
index a923b47573145e16d2c8dca45b1cef76734afc5a..3fdc2907c4604ed48bc4b965fd53176ed1544116 100644
--- a/settings/js/personal.js
+++ b/settings/js/personal.js
@@ -45,12 +45,16 @@ function changeDisplayName(){
     }
 }
 
-function updateAvatar () {
+function updateAvatar (hidedefault) {
 	$headerdiv = $('#header .avatardiv');
 	$displaydiv = $('#displayavatar .avatardiv');
 
-	$headerdiv.css({'background-color': ''});
-	$headerdiv.avatar(OC.currentUser, 32, true);
+	if(hidedefault) {
+		$headerdiv.hide();
+	} else {
+		$headerdiv.css({'background-color': ''});
+		$headerdiv.avatar(OC.currentUser, 32, true);
+	}
 	$displaydiv.css({'background-color': ''});
 	$displaydiv.avatar(OC.currentUser, 128, true);
 }
@@ -232,7 +236,7 @@ $(document).ready(function(){
 			type:	'DELETE',
 			url:	OC.Router.generate('core_avatar_delete'),
 			success: function(msg) {
-				updateAvatar();
+				updateAvatar(true);
 			}
 		});
 	});
diff --git a/settings/templates/admin.php b/settings/templates/admin.php
index e54586b80dfa15f4c91ff7a897aa433be745a942..72e93e78dac74c5d5a4b10bbbc1cd04cb40d6178 100644
--- a/settings/templates/admin.php
+++ b/settings/templates/admin.php
@@ -128,7 +128,7 @@ if (!$_['internetconnectionworking']) {
 			</td>
 		</tr>
 		<tr>
-			<td <?php if ($_['shareAPIEnabled'] === 'no') print_unescaped('style="display:none"');?>>
+			<td <?php if ($_['shareAPIEnabled'] === 'no') print_unescaped('class="hidden"');?>>
 				<input type="checkbox" name="shareapi_allow_links" id="allowLinks"
 					   value="1" <?php if ($_['allowLinks'] === 'yes') print_unescaped('checked="checked"'); ?> />
 				<label for="allowLinks"><?php p($l->t('Allow links'));?></label><br/>
@@ -137,7 +137,7 @@ if (!$_['internetconnectionworking']) {
 		</tr>
 		<?php if (!\OCP\App::isEnabled('files_encryption')) { ?>
 		<tr>
-			<td <?php if ($_['shareAPIEnabled'] == 'no') print_unescaped('style="display:none"');?>>
+			<td <?php if ($_['shareAPIEnabled'] == 'no') print_unescaped('class="hidden"');?>>
 				<input type="checkbox" name="shareapi_allow_public_upload" id="allowPublicUpload"
 				       value="1" <?php if ($_['allowPublicUpload'] == 'yes') print_unescaped('checked="checked"'); ?> />
 				<label for="allowPublicUpload"><?php p($l->t('Allow public uploads'));?></label><br/>
@@ -146,7 +146,7 @@ if (!$_['internetconnectionworking']) {
 		</tr>
 		<?php } ?>
 		<tr>
-			<td <?php if ($_['shareAPIEnabled'] === 'no') print_unescaped('style="display:none"');?>>
+			<td <?php if ($_['shareAPIEnabled'] === 'no') print_unescaped('class="hidden"');?>>
 				<input type="checkbox" name="shareapi_allow_resharing" id="allowResharing"
 					   value="1" <?php if ($_['allowResharing'] === 'yes') print_unescaped('checked="checked"'); ?> />
 				<label for="allowResharing"><?php p($l->t('Allow resharing'));?></label><br/>
@@ -154,7 +154,7 @@ if (!$_['internetconnectionworking']) {
 			</td>
 		</tr>
 		<tr>
-			<td <?php if ($_['shareAPIEnabled'] === 'no') print_unescaped('style="display:none"');?>>
+			<td <?php if ($_['shareAPIEnabled'] === 'no') print_unescaped('class="hidden"');?>>
 				<input type="radio" name="shareapi_share_policy" id="sharePolicyGlobal"
 					   value="global" <?php if ($_['sharePolicy'] === 'global') print_unescaped('checked="checked"'); ?> />
 				<label for="sharePolicyGlobal"><?php p($l->t('Allow users to share with anyone')); ?></label><br/>
@@ -163,6 +163,14 @@ if (!$_['internetconnectionworking']) {
 				<label for="sharePolicyGroupsOnly"><?php p($l->t('Allow users to only share with users in their groups'));?></label><br/>
 			</td>
 		</tr>
+		<tr>
+			<td <?php if ($_['shareAPIEnabled'] === 'no') print_unescaped('class="hidden"');?>>
+				<input type="checkbox" name="shareapi_allow_mail_notification" id="allowMailNotification"
+					   value="1" <?php if ($_['allowMailNotification'] === 'yes') print_unescaped('checked="checked"'); ?> />
+				<label for="allowMailNotification"><?php p($l->t('Allow mail notification'));?></label><br/>
+				<em><?php p($l->t('Allow user to send mail notification for shared files')); ?></em>
+			</td>
+		</tr>
 	</table>
 </fieldset>
 
@@ -223,7 +231,7 @@ endfor;?>
 			</td>
 			<td>
 				<?php if(is_int($entry->time)){
-					p(OC_Util::formatDate($entry->time)); 
+					p(OC_Util::formatDate($entry->time));
 				} else {
 					p($entry->time);
 				}?>
diff --git a/tests/enable_all.php b/tests/enable_all.php
index 111ed0e13572770ecbd170cf67bb498c973d4b33..43ee16a72f8bef36f430980d79e2d3ae337a7c40 100644
--- a/tests/enable_all.php
+++ b/tests/enable_all.php
@@ -8,6 +8,7 @@
 
 require_once __DIR__.'/../lib/base.php';
 
+OC_App::enable('files_sharing');
 OC_App::enable('files_encryption');
 OC_App::enable('calendar');
 OC_App::enable('contacts');
diff --git a/tests/lib/autoloader.php b/tests/lib/autoloader.php
index b182dc8747795fae5c777e901526f1d36f9fe7c9..314a8ebee8d4c9ca86d101f0658d4b2555db4acb 100644
--- a/tests/lib/autoloader.php
+++ b/tests/lib/autoloader.php
@@ -19,11 +19,11 @@ class AutoLoader extends \PHPUnit_Framework_TestCase {
 	}
 
 	public function testLeadingSlashOnClassName() {
-		$this->assertEquals(array('private/files/storage/local.php'), $this->loader->findClass('\OC\Files\Storage\Local'));
+		$this->assertEquals(array('private/files/storage/local.php', 'files/storage/local.php'), $this->loader->findClass('\OC\Files\Storage\Local'));
 	}
 
 	public function testNoLeadingSlashOnClassName() {
-		$this->assertEquals(array('private/files/storage/local.php'), $this->loader->findClass('OC\Files\Storage\Local'));
+		$this->assertEquals(array('private/files/storage/local.php', 'files/storage/local.php'), $this->loader->findClass('OC\Files\Storage\Local'));
 	}
 
 	public function testLegacyPath() {
@@ -54,7 +54,7 @@ class AutoLoader extends \PHPUnit_Framework_TestCase {
 	}
 
 	public function testLoadCoreNamespace() {
-		$this->assertEquals(array('private/foo/bar.php'), $this->loader->findClass('OC\Foo\Bar'));
+		$this->assertEquals(array('private/foo/bar.php', 'foo/bar.php'), $this->loader->findClass('OC\Foo\Bar'));
 	}
 
 	public function testLoadCore() {
diff --git a/tests/lib/template.php b/tests/lib/template.php
index fd12119da580aa5ed241a0784cdd4de389c27ae6..b4f1a4c40533f9b8cf1d587763ae83de7fe49388 100644
--- a/tests/lib/template.php
+++ b/tests/lib/template.php
@@ -46,7 +46,6 @@ class Test_TemplateFunctions extends PHPUnit_Framework_TestCase {
 		$this->assertEquals("This is a good string!", $result);
 	}
 
-
 	public function testPrintUnescaped() {
 		$htmlString = "<script>alert('xss');</script>";
 
@@ -66,5 +65,194 @@ class Test_TemplateFunctions extends PHPUnit_Framework_TestCase {
 		$this->assertEquals("This is a good string!", $result);
 	}
 
+	// ---------------------------------------------------------------------------
+	// Test relative_modified_date with dates only
+	// ---------------------------------------------------------------------------
+	public function testRelativeDateToday(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('today', $result);
+
+		// 2 hours ago is still today
+		$elementTime = $currentTime - 2 * 3600;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('today', $result);
+	}
+
+	public function testRelativeDateYesterday(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 24 * 3600;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('yesterday', $result);
+
+		// yesterday - 2 hours is still yesterday
+		$elementTime = $currentTime - 26 * 3600;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('yesterday', $result);
+	}
+
+	public function testRelativeDate2DaysAgo(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 48 * 3600;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('2 days ago', $result);
+
+		// 2 days ago minus 4 hours is still 2 days ago
+		$elementTime = $currentTime - 52 * 3600;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('2 days ago', $result);
+	}
+
+	public function testRelativeDateLastMonth(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 86400 * 31;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('last month', $result);
+
+		$elementTime = $currentTime - 86400 * 35;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('last month', $result);
+	}
+
+	public function testRelativeDateMonthsAgo(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 86400 * 60;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('2 months ago', $result);
+
+		$elementTime = $currentTime - 86400 * 65;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('2 months ago', $result);
+	}
+
+	public function testRelativeDateLastYear(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 86400 * 365;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('last year', $result);
+
+		$elementTime = $currentTime - 86400 * 450;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('last year', $result);
+	}
+
+	public function testRelativeDateYearsAgo(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 86400 * 365.25 * 2;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('years ago', $result);
+
+		$elementTime = $currentTime - 86400 * 365.25 * 3;
+		$result = (string)relative_modified_date($elementTime, $currentTime, true);
+
+		$this->assertEquals('years ago', $result);
+	}
 
+	// ---------------------------------------------------------------------------
+	// Test relative_modified_date with timestamps only (date + time value)
+	// ---------------------------------------------------------------------------
+
+	public function testRelativeTimeSecondsAgo(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 5;
+		$result = (string)relative_modified_date($elementTime, $currentTime, false);
+
+		$this->assertEquals('seconds ago', $result);
+	}
+
+	public function testRelativeTimeMinutesAgo(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 190;
+		$result = (string)relative_modified_date($elementTime, $currentTime, false);
+
+		$this->assertEquals('3 minutes ago', $result);
+	}
+
+	public function testRelativeTimeHoursAgo(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 7500;
+		$result = (string)relative_modified_date($elementTime, $currentTime, false);
+
+		$this->assertEquals('2 hours ago', $result);
+	}
+
+	public function testRelativeTime2DaysAgo(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 48 * 3600;
+		$result = (string)relative_modified_date($elementTime, $currentTime, false);
+
+		$this->assertEquals('2 days ago', $result);
+
+		// 2 days ago minus 4 hours is still 2 days ago
+		$elementTime = $currentTime - 52 * 3600;
+		$result = (string)relative_modified_date($elementTime, $currentTime, false);
+
+		$this->assertEquals('2 days ago', $result);
+	}
+
+	public function testRelativeTimeLastMonth(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 86400 * 31;
+		$result = (string)relative_modified_date($elementTime, $currentTime, false);
+
+		$this->assertEquals('last month', $result);
+
+		$elementTime = $currentTime - 86400 * 35;
+		$result = (string)relative_modified_date($elementTime, $currentTime, false);
+
+		$this->assertEquals('last month', $result);
+	}
+
+	public function testRelativeTimeMonthsAgo(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 86400 * 60;
+		$result = (string)relative_modified_date($elementTime, $currentTime, false);
+
+		$this->assertEquals('2 months ago', $result);
+
+		$elementTime = $currentTime - 86400 * 65;
+		$result = (string)relative_modified_date($elementTime, $currentTime, false);
+
+		$this->assertEquals('2 months ago', $result);
+	}
+
+	public function testRelativeTimeLastYear(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 86400 * 365;
+		$result = (string)relative_modified_date($elementTime, $currentTime, false);
+
+		$this->assertEquals('last year', $result);
+
+		$elementTime = $currentTime - 86400 * 450;
+		$result = (string)relative_modified_date($elementTime, $currentTime, false);
+
+		$this->assertEquals('last year', $result);
+	}
+
+	public function testRelativeTimeYearsAgo(){
+		$currentTime = 1380703592;
+		$elementTime = $currentTime - 86400 * 365.25 * 2;
+		$result = (string)relative_modified_date($elementTime, $currentTime, false);
+
+		$this->assertEquals('years ago', $result);
+
+		$elementTime = $currentTime - 86400 * 365.25 * 3;
+		$result = (string)relative_modified_date($elementTime, $currentTime, false);
+
+		$this->assertEquals('years ago', $result);
+	}
 }
diff --git a/version.php b/version.php
index eb2e9a4a68ba0305af3a915f7e968d87bf5aa253..b4396d1d2d8ae5f4c9e4398f46684e655c28123b 100644
--- a/version.php
+++ b/version.php
@@ -1,7 +1,7 @@
 <?php
 
 // We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel when updating major/minor version number.
-$OC_Version=array(5, 80, 8, 0);
+$OC_Version=array(5, 80, 8, 1);
 
 // The human radable string
 $OC_VersionString='6.0 pre alpha';