diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml
index 38878ec27f246a98f37912c9b0af76ff1fb88e0a..8f378f5e18d727f73adde84d4e3b0df694d493a5 100644
--- a/apps/dav/appinfo/info.xml
+++ b/apps/dav/appinfo/info.xml
@@ -5,7 +5,7 @@
 	<description>ownCloud WebDAV endpoint</description>
 	<licence>AGPL</licence>
 	<author>owncloud.org</author>
-	<version>0.1</version>
+	<version>0.1.1</version>
 	<requiremin>9.0</requiremin>
 	<shipped>true</shipped>
 	<standalone/>
@@ -16,6 +16,7 @@
 	<remote>
 		<files>appinfo/v1/webdav.php</files>
 		<webdav>appinfo/v1/webdav.php</webdav>
+		<dav>appinfo/v2/remote.php</dav>
 	</remote>
 	<public>
 		<webdav>appinfo/v1/publicwebdav.php</webdav>
diff --git a/apps/dav/appinfo/v2/remote.php b/apps/dav/appinfo/v2/remote.php
new file mode 100644
index 0000000000000000000000000000000000000000..02457bd3ccc7e034cc1f380b0d985329a4a91e5c
--- /dev/null
+++ b/apps/dav/appinfo/v2/remote.php
@@ -0,0 +1,11 @@
+<?php
+
+// no php execution timeout for webdav
+set_time_limit(0);
+
+// Turn off output buffering to prevent memory problems
+\OC_Util::obEnd();
+
+$request = \OC::$server->getRequest();
+$server = new \OCA\DAV\Server($request, $baseuri);
+$server->exec();
diff --git a/apps/dav/lib/files/custompropertiesbackend.php b/apps/dav/lib/files/custompropertiesbackend.php
new file mode 100644
index 0000000000000000000000000000000000000000..83776997a52952aefc97aaf9d1ac187937722ccf
--- /dev/null
+++ b/apps/dav/lib/files/custompropertiesbackend.php
@@ -0,0 +1,271 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\DAV\Files;
+
+use OCP\IDBConnection;
+use OCP\IUser;
+use Sabre\DAV\PropertyStorage\Backend\BackendInterface;
+use Sabre\DAV\PropFind;
+use Sabre\DAV\PropPatch;
+use Sabre\DAV\Tree;
+
+class CustomPropertiesBackend implements BackendInterface {
+
+	/**
+	 * Ignored properties
+	 *
+	 * @var array
+	 */
+	private $ignoredProperties = array(
+		'{DAV:}getcontentlength',
+		'{DAV:}getcontenttype',
+		'{DAV:}getetag',
+		'{DAV:}quota-used-bytes',
+		'{DAV:}quota-available-bytes',
+		'{DAV:}quota-available-bytes',
+		'{http://owncloud.org/ns}permissions',
+		'{http://owncloud.org/ns}downloadURL',
+		'{http://owncloud.org/ns}dDC',
+		'{http://owncloud.org/ns}size',
+	);
+
+	/**
+	 * @var Tree
+	 */
+	private $tree;
+
+	/**
+	 * @var IDBConnection
+	 */
+	private $connection;
+
+	/**
+	 * @var IUser
+	 */
+	private $user;
+
+	/**
+	 * Properties cache
+	 *
+	 * @var array
+	 */
+	private $cache = [];
+
+	/**
+	 * @param Tree $tree node tree
+	 * @param IDBConnection $connection database connection
+	 * @param IUser $user owner of the tree and properties
+	 */
+	public function __construct(
+		Tree $tree,
+		IDBConnection $connection,
+		IUser $user) {
+		$this->tree = $tree;
+		$this->connection = $connection;
+		$this->user = $user->getUID();
+	}
+
+	/**
+	 * Fetches properties for a path.
+	 *
+	 * @param string $path
+	 * @param PropFind $propFind
+	 * @return void
+	 */
+	public function propFind($path, PropFind $propFind) {
+
+		$requestedProps = $propFind->get404Properties();
+
+		// these might appear
+		$requestedProps = array_diff(
+			$requestedProps,
+			$this->ignoredProperties
+		);
+
+		if (empty($requestedProps)) {
+			return;
+		}
+
+		$props = $this->getProperties($path, $requestedProps);
+		foreach ($props as $propName => $propValue) {
+			$propFind->set($propName, $propValue);
+		}
+	}
+
+	/**
+	 * Updates properties for a path
+	 *
+	 * @param string $path
+	 * @param PropPatch $propPatch
+	 *
+	 * @return void
+	 */
+	public function propPatch($path, PropPatch $propPatch) {
+		$propPatch->handleRemaining(function($changedProps) use ($path) {
+			return $this->updateProperties($path, $changedProps);
+		});
+	}
+
+	/**
+	 * This method is called after a node is deleted.
+	 *
+	 * @param string $path path of node for which to delete properties
+	 */
+	public function delete($path) {
+		$statement = $this->connection->prepare(
+			'DELETE FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?'
+		);
+		$statement->execute(array($this->user, $path));
+		$statement->closeCursor();
+
+		unset($this->cache[$path]);
+	}
+
+	/**
+	 * This method is called after a successful MOVE
+	 *
+	 * @param string $source
+	 * @param string $destination
+	 *
+	 * @return void
+	 */
+	public function move($source, $destination) {
+		$statement = $this->connection->prepare(
+			'UPDATE `*PREFIX*properties` SET `propertypath` = ?' .
+			' WHERE `userid` = ? AND `propertypath` = ?'
+		);
+		$statement->execute(array($destination, $this->user, $source));
+		$statement->closeCursor();
+	}
+
+	/**
+	 * Returns a list of properties for this nodes.;
+	 * @param string $path
+	 * @param array $requestedProperties requested properties or empty array for "all"
+	 * @return array
+	 * @note The properties list is a list of propertynames the client
+	 * requested, encoded as xmlnamespace#tagName, for example:
+	 * http://www.example.org/namespace#author If the array is empty, all
+	 * properties should be returned
+	 */
+	private function getProperties($path, array $requestedProperties) {
+		if (isset($this->cache[$path])) {
+			return $this->cache[$path];
+		}
+
+		// TODO: chunking if more than 1000 properties
+		$sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?';
+
+		$whereValues = array($this->user, $path);
+		$whereTypes = array(null, null);
+
+		if (!empty($requestedProperties)) {
+			// request only a subset
+			$sql .= ' AND `propertyname` in (?)';
+			$whereValues[] = $requestedProperties;
+			$whereTypes[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
+		}
+
+		$result = $this->connection->executeQuery(
+			$sql,
+			$whereValues,
+			$whereTypes
+		);
+
+		$props = [];
+		while ($row = $result->fetch()) {
+			$props[$row['propertyname']] = $row['propertyvalue'];
+		}
+
+		$result->closeCursor();
+
+		$this->cache[$path] = $props;
+		return $props;
+	}
+
+	/**
+	 * Update properties
+	 *
+	 * @param string $path node for which to update properties
+	 * @param array $properties array of properties to update
+	 *
+	 * @return bool
+	 */
+	private function updateProperties($path, $properties) {
+
+		$deleteStatement = 'DELETE FROM `*PREFIX*properties`' .
+			' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
+
+		$insertStatement = 'INSERT INTO `*PREFIX*properties`' .
+			' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)';
+
+		$updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' .
+			' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
+
+		// TODO: use "insert or update" strategy ?
+		$existing = $this->getProperties($path, array());
+		$this->connection->beginTransaction();
+		foreach ($properties as $propertyName => $propertyValue) {
+			// If it was null, we need to delete the property
+			if (is_null($propertyValue)) {
+				if (array_key_exists($propertyName, $existing)) {
+					$this->connection->executeUpdate($deleteStatement,
+						array(
+							$this->user,
+							$path,
+							$propertyName
+						)
+					);
+				}
+			} else {
+				if (!array_key_exists($propertyName, $existing)) {
+					$this->connection->executeUpdate($insertStatement,
+						array(
+							$this->user,
+							$path,
+							$propertyName,
+							$propertyValue
+						)
+					);
+				} else {
+					$this->connection->executeUpdate($updateStatement,
+						array(
+							$propertyValue,
+							$this->user,
+							$path,
+							$propertyName
+						)
+					);
+				}
+			}
+		}
+
+		$this->connection->commit();
+		unset($this->cache[$path]);
+
+		return true;
+	}
+
+}
diff --git a/apps/dav/lib/files/fileshome.php b/apps/dav/lib/files/fileshome.php
new file mode 100644
index 0000000000000000000000000000000000000000..5e145a2b002c027eabf91e9609dea26d9af1bded
--- /dev/null
+++ b/apps/dav/lib/files/fileshome.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace OCA\DAV\Files;
+
+use OCA\DAV\Connector\Sabre\Directory;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\ICollection;
+use Sabre\DAV\SimpleCollection;
+use Sabre\HTTP\URLUtil;
+
+class FilesHome implements ICollection {
+
+	/**
+	 * FilesHome constructor.
+	 *
+	 * @param array $principalInfo
+	 */
+	public function __construct($principalInfo) {
+		$this->principalInfo = $principalInfo;
+	}
+
+	function createFile($name, $data = null) {
+		return $this->impl()->createFile($name, $data);
+	}
+
+	function createDirectory($name) {
+		$this->impl()->createDirectory($name);
+	}
+
+	function getChild($name) {
+		return $this->impl()->getChild($name);
+	}
+
+	function getChildren() {
+		return $this->impl()->getChildren();
+	}
+
+	function childExists($name) {
+		return $this->impl()->childExists($name);
+	}
+
+	function delete() {
+		$this->impl()->delete();
+	}
+
+	function getName() {
+		list(,$name) = URLUtil::splitPath($this->principalInfo['uri']);
+		return $name;
+	}
+
+	function setName($name) {
+		throw new Forbidden('Permission denied to rename this folder');
+	}
+
+	/**
+	 * Returns the last modification time, as a unix timestamp
+	 *
+	 * @return int
+	 */
+	function getLastModified() {
+		return $this->impl()->getLastModified();
+	}
+
+	/**
+	 * @return Directory
+	 */
+	private function impl() {
+		//
+		// TODO: we need to mount filesystem of the give user
+		//
+		$user = \OC::$server->getUserSession()->getUser();
+		if ($this->getName() !== $user->getUID()) {
+			return new SimpleCollection($this->getName());
+		}
+		$view = \OC\Files\Filesystem::getView();
+		$rootInfo = $view->getFileInfo('');
+		$impl = new Directory($view, $rootInfo);
+		return $impl;
+	}
+}
diff --git a/apps/dav/lib/files/rootcollection.php b/apps/dav/lib/files/rootcollection.php
new file mode 100644
index 0000000000000000000000000000000000000000..bbe3c784a53eadc363735511e8e008f06431b6f1
--- /dev/null
+++ b/apps/dav/lib/files/rootcollection.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace OCA\DAV\Files;
+
+use Sabre\DAVACL\AbstractPrincipalCollection;
+use Sabre\DAVACL\IPrincipal;
+
+class RootCollection extends AbstractPrincipalCollection {
+
+	/**
+	 * This method returns a node for a principal.
+	 *
+	 * The passed array contains principal information, and is guaranteed to
+	 * at least contain a uri item. Other properties may or may not be
+	 * supplied by the authentication backend.
+	 *
+	 * @param array $principalInfo
+	 * @return IPrincipal
+	 */
+	function getChildForPrincipal(array $principalInfo) {
+		return new FilesHome($principalInfo);
+	}
+
+	function getName() {
+		return 'files';
+	}
+
+}
diff --git a/apps/dav/lib/rootcollection.php b/apps/dav/lib/rootcollection.php
new file mode 100644
index 0000000000000000000000000000000000000000..62ec3219caaf2e041c0f88e9b04b2eb0e0a87de0
--- /dev/null
+++ b/apps/dav/lib/rootcollection.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace OCA\DAV;
+
+use OCA\DAV\Connector\Sabre\Principal;
+use Sabre\CalDAV\Principal\Collection;
+use Sabre\DAV\SimpleCollection;
+
+class RootCollection extends SimpleCollection {
+
+	public function __construct() {
+		$principalBackend = new Principal(
+			\OC::$server->getConfig(),
+			\OC::$server->getUserManager()
+		);
+		$principalCollection = new Collection($principalBackend);
+		$principalCollection->disableListing = true;
+		$filesCollection = new Files\RootCollection($principalBackend);
+		$filesCollection->disableListing = true;
+
+		$children = [
+			$principalCollection,
+			$filesCollection,
+		];
+
+		parent::__construct('root', $children);
+	}
+
+}
diff --git a/apps/dav/lib/server.php b/apps/dav/lib/server.php
new file mode 100644
index 0000000000000000000000000000000000000000..fa572eb30d1467ca453f6f3a239277328e9c1f3d
--- /dev/null
+++ b/apps/dav/lib/server.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace OCA\DAV;
+
+use OCA\DAV\Connector\Sabre\Auth;
+use OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin;
+use OCA\DAV\Files\CustomPropertiesBackend;
+use OCP\IRequest;
+use Sabre\DAV\Auth\Plugin;
+use Sabre\HTTP\Util;
+
+class Server {
+
+	/** @var IRequest */
+	private $request;
+
+	public function __construct(IRequest $request, $baseUri) {
+		$this->request = $request;
+		$this->baseUri = $baseUri;
+		$root = new RootCollection();
+		$this->server = new \OCA\DAV\Connector\Sabre\Server($root);
+
+		// Backends
+		$authBackend = new Auth();
+
+		// Set URL explicitly due to reverse-proxy situations
+		$this->server->httpRequest->setUrl($this->request->getRequestUri());
+		$this->server->setBaseUri($this->baseUri);
+
+		$this->server->addPlugin(new BlockLegacyClientPlugin(\OC::$server->getConfig()));
+		$this->server->addPlugin(new Plugin($authBackend, 'ownCloud'));
+
+		// wait with registering these until auth is handled and the filesystem is setup
+		$this->server->on('beforeMethod', function () {
+			// custom properties plugin must be the last one
+			$user = \OC::$server->getUserSession()->getUser();
+			if (!is_null($user)) {
+				$this->server->addPlugin(
+					new \Sabre\DAV\PropertyStorage\Plugin(
+						new CustomPropertiesBackend(
+							$this->server->tree,
+							\OC::$server->getDatabaseConnection(),
+							\OC::$server->getUserSession()->getUser()
+						)
+					)
+				);
+			}
+		});
+	}
+
+	public function exec() {
+		$this->server->exec();
+	}
+}