diff --git a/appinfo/info.xml b/appinfo/info.xml
index 7ad9168b..7830c947 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -14,7 +14,7 @@
appinfo/remote.php
-
+
168708
diff --git a/appinfo/remote.php b/appinfo/remote.php
index eb840e0c..a344fb08 100644
--- a/appinfo/remote.php
+++ b/appinfo/remote.php
@@ -37,8 +37,11 @@ $RUNTIME_APPTYPES = array('authentication');
OC_App::loadApps($RUNTIME_APPTYPES);
// Backends
-$authBackend = new \OC\Connector\Sabre\Auth();
-$principalBackend = new \OC\Connector\Sabre\Principal(
+$authBackend = new \OCA\Contacts\Sabre\Auth(
+ \OC::$server->getSession(),
+ \OC::$server->getUserSession()
+);
+$principalBackend = new \OCA\Contacts\Sabre\Principal(
\OC::$server->getConfig(),
\OC::$server->getUserManager()
);
@@ -68,13 +71,13 @@ $server = new \Sabre\DAV\Server($nodes);
$server->httpRequest->setUrl(\OC::$server->getRequest()->getRequestUri());
$server->setBaseUri($baseuri);
// Add plugins
-$server->addPlugin(new \OC\Connector\Sabre\MaintenancePlugin());
+$server->addPlugin(new \OCA\Contacts\Sabre\MaintenancePlugin());
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, 'ownCloud'));
$server->addPlugin(new OCA\Contacts\CardDAV\Plugin());
$server->addPlugin(new \Sabre\DAVACL\Plugin());
$server->addPlugin(new \Sabre\CardDAV\VCFExportPlugin());
-$server->addPlugin(new \OC\Connector\Sabre\ExceptionLoggerPlugin('carddav', \OC::$server->getLogger()));
-$server->addPlugin(new \OC\Connector\Sabre\AppEnabledPlugin(
+$server->addPlugin(new \OCA\Contacts\Sabre\ExceptionLoggerPlugin('carddav', \OC::$server->getLogger()));
+$server->addPlugin(new \OCA\Contacts\Sabre\AppEnabledPlugin(
'contacts',
OC::$server->getAppManager()
));
diff --git a/lib/sabre/appenabledplugin.php b/lib/sabre/appenabledplugin.php
new file mode 100644
index 00000000..e01aac7d
--- /dev/null
+++ b/lib/sabre/appenabledplugin.php
@@ -0,0 +1,89 @@
+
+ * @author Robin Appelman
+ * @author Vincent Petry
+ *
+ * @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
+ *
+ */
+
+namespace OCA\Contacts\Sabre;
+
+use OCP\App\IAppManager;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\ServerPlugin;
+
+/**
+ * Plugin to check if an app is enabled for the current user
+ */
+class AppEnabledPlugin extends ServerPlugin {
+
+ /**
+ * Reference to main server object
+ *
+ * @var \Sabre\DAV\Server
+ */
+ private $server;
+
+ /**
+ * @var string
+ */
+ private $app;
+
+ /**
+ * @var \OCP\App\IAppManager
+ */
+ private $appManager;
+
+ /**
+ * @param string $app
+ * @param \OCP\App\IAppManager $appManager
+ */
+ public function __construct($app, IAppManager $appManager) {
+ $this->app = $app;
+ $this->appManager = $appManager;
+ }
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by \Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param \Sabre\DAV\Server $server
+ * @return void
+ */
+ public function initialize(\Sabre\DAV\Server $server) {
+
+ $this->server = $server;
+ $this->server->on('beforeMethod', array($this, 'checkAppEnabled'), 30);
+ }
+
+ /**
+ * This method is called before any HTTP after auth and checks if the user has access to the app
+ *
+ * @throws \Sabre\DAV\Exception\Forbidden
+ * @return bool
+ */
+ public function checkAppEnabled() {
+ if (!$this->appManager->isEnabledForUser($this->app)) {
+ throw new Forbidden();
+ }
+ }
+}
diff --git a/lib/sabre/auth.php b/lib/sabre/auth.php
new file mode 100644
index 00000000..da160af4
--- /dev/null
+++ b/lib/sabre/auth.php
@@ -0,0 +1,169 @@
+
+ * @author Bart Visscher
+ * @author Christian Seiler
+ * @author Jakob Sack
+ * @author Lukas Reschke
+ * @author Markus Goetz
+ * @author Michael Gapczynski
+ * @author Morris Jobke
+ * @author Thomas Müller
+ * @author Vincent Petry
+ *
+ * @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
+ *
+ */
+namespace OCA\Contacts\Sabre;
+
+use Exception;
+use OCP\ISession;
+use OCP\IUserSession;
+use Sabre\DAV\Auth\Backend\AbstractBasic;
+use Sabre\DAV\Exception\NotAuthenticated;
+use Sabre\DAV\Exception\ServiceUnavailable;
+
+class Auth extends AbstractBasic {
+ const DAV_AUTHENTICATED = 'AUTHENTICATED_TO_DAV_BACKEND';
+
+ /** @var ISession */
+ private $session;
+ /** @var IUserSession */
+ private $userSession;
+
+ /**
+ * @param ISession $session
+ * @param IUserSession $userSession
+ */
+ public function __construct(ISession $session,
+ IUserSession $userSession) {
+ $this->session = $session;
+ $this->userSession = $userSession;
+ }
+
+ /**
+ * Whether the user has initially authenticated via DAV
+ *
+ * This is required for WebDAV clients that resent the cookies even when the
+ * account was changed.
+ *
+ * @see https://github.com/owncloud/core/issues/13245
+ *
+ * @param string $username
+ * @return bool
+ */
+ protected function isDavAuthenticated($username) {
+ return !is_null($this->session->get(self::DAV_AUTHENTICATED)) &&
+ $this->session->get(self::DAV_AUTHENTICATED) === $username;
+ }
+
+ /**
+ * Validates a username and password
+ *
+ * This method should return true or false depending on if login
+ * succeeded.
+ *
+ * @param string $username
+ * @param string $password
+ * @return bool
+ */
+ protected function validateUserPass($username, $password) {
+ if ($this->userSession->isLoggedIn() &&
+ $this->isDavAuthenticated($this->userSession->getUser()->getUID())
+ ) {
+ \OC_Util::setupFS($this->userSession->getUser()->getUID());
+ $this->session->close();
+ return true;
+ } else {
+ \OC_Util::setUpFS(); //login hooks may need early access to the filesystem
+ if($this->userSession->login($username, $password)) {
+ \OC_Util::setUpFS($this->userSession->getUser()->getUID());
+ $this->session->set(self::DAV_AUTHENTICATED, $this->userSession->getUser()->getUID());
+ $this->session->close();
+ return true;
+ } else {
+ $this->session->close();
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Returns information about the currently logged in username.
+ *
+ * If nobody is currently logged in, this method should return null.
+ *
+ * @return string|null
+ */
+ public function getCurrentUser() {
+ $user = $this->userSession->getUser() ? $this->userSession->getUser()->getUID() : null;
+ if($user !== null && $this->isDavAuthenticated($user)) {
+ return $user;
+ }
+
+ if($user !== null && is_null($this->session->get(self::DAV_AUTHENTICATED))) {
+ return $user;
+ }
+
+ return null;
+ }
+
+ /**
+ * Override function here. We want to cache authentication cookies
+ * in the syncing client to avoid HTTP-401 roundtrips.
+ * If the sync client supplies the cookies, then OC_User::isLoggedIn()
+ * will return true and we can see this WebDAV request as already authenticated,
+ * even if there are no HTTP Basic Auth headers.
+ * In other case, just fallback to the parent implementation.
+ *
+ * @param \Sabre\DAV\Server $server
+ * @param string $realm
+ * @return bool
+ * @throws ServiceUnavailable
+ * @throws NotAuthenticated
+ */
+ public function authenticate(\Sabre\DAV\Server $server, $realm) {
+ try {
+ $result = $this->auth($server, $realm);
+ return $result;
+ } catch (NotAuthenticated $e) {
+ throw $e;
+ } catch (Exception $e) {
+ $class = get_class($e);
+ $msg = $e->getMessage();
+ throw new ServiceUnavailable("$class: $msg");
+ }
+ }
+
+ /**
+ * @param \Sabre\DAV\Server $server
+ * @param $realm
+ * @return bool
+ */
+ private function auth(\Sabre\DAV\Server $server, $realm) {
+ if (\OC_User::handleApacheAuth() ||
+ ($this->userSession->isLoggedIn() && is_null($this->session->get(self::DAV_AUTHENTICATED)))
+ ) {
+ $user = $this->userSession->getUser()->getUID();
+ \OC_Util::setupFS($user);
+ $this->currentUser = $user;
+ $this->session->close();
+ return true;
+ }
+
+ return parent::authenticate($server, $realm);
+ }
+}
diff --git a/lib/sabre/exceptionloggerplugin.php b/lib/sabre/exceptionloggerplugin.php
new file mode 100644
index 00000000..95cecefc
--- /dev/null
+++ b/lib/sabre/exceptionloggerplugin.php
@@ -0,0 +1,107 @@
+
+ * @author Robin Appelman
+ * @author Thomas Müller
+ * @author Vincent Petry
+ *
+ * @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
+ *
+ */
+
+namespace OCA\Contacts\Sabre;
+
+use OCP\ILogger;
+use Sabre\DAV\Exception;
+use Sabre\HTTP\Response;
+
+class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin {
+ protected $nonFatalExceptions = array(
+ 'Sabre\DAV\Exception\NotAuthenticated' => true,
+ // the sync client uses this to find out whether files exist,
+ // so it is not always an error, log it as debug
+ 'Sabre\DAV\Exception\NotFound' => true,
+ // this one mostly happens when the same file is uploaded at
+ // exactly the same time from two clients, only one client
+ // wins, the second one gets "Precondition failed"
+ 'Sabre\DAV\Exception\PreconditionFailed' => true,
+ // forbidden can be expected when trying to upload to
+ // read-only folders for example
+ 'Sabre\DAV\Exception\Forbidden' => true,
+ );
+
+ /** @var string */
+ private $appName;
+
+ /** @var ILogger */
+ private $logger;
+
+ /**
+ * @param string $loggerAppName app name to use when logging
+ * @param ILogger $logger
+ */
+ public function __construct($loggerAppName, $logger) {
+ $this->appName = $loggerAppName;
+ $this->logger = $logger;
+ }
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by \Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param \Sabre\DAV\Server $server
+ * @return void
+ */
+ public function initialize(\Sabre\DAV\Server $server) {
+
+ $server->on('exception', array($this, 'logException'), 10);
+ }
+
+ /**
+ * Log exception
+ *
+ */
+ public function logException(\Exception $ex) {
+ $exceptionClass = get_class($ex);
+ $level = \OCP\Util::FATAL;
+ if (isset($this->nonFatalExceptions[$exceptionClass])) {
+ $level = \OCP\Util::DEBUG;
+ }
+
+ $message = $ex->getMessage();
+ if ($ex instanceof Exception) {
+ if (empty($message)) {
+ $response = new Response($ex->getHTTPCode());
+ $message = $response->getStatusText();
+ }
+ $message = "HTTP/1.1 {$ex->getHTTPCode()} $message";
+ }
+
+ $exception = [
+ 'Message' => $message,
+ 'Exception' => $exceptionClass,
+ 'Code' => $ex->getCode(),
+ 'Trace' => $ex->getTraceAsString(),
+ 'File' => $ex->getFile(),
+ 'Line' => $ex->getLine(),
+ ];
+ $this->logger->log($level, 'Exception: ' . json_encode($exception), ['app' => $this->appName]);
+ }
+}
diff --git a/lib/sabre/maintenanceplugin.php b/lib/sabre/maintenanceplugin.php
new file mode 100644
index 00000000..1a5e396f
--- /dev/null
+++ b/lib/sabre/maintenanceplugin.php
@@ -0,0 +1,92 @@
+
+ * @author Joas Schilling
+ * @author Morris Jobke
+ * @author Robin Appelman
+ * @author Thomas Müller
+ * @author Vincent Petry
+ *
+ * @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
+ *
+ */
+
+namespace OCA\Contacts\Sabre;
+
+use OCP\IConfig;
+use Sabre\DAV\Exception\ServiceUnavailable;
+use Sabre\DAV\ServerPlugin;
+
+class MaintenancePlugin extends ServerPlugin {
+
+ /** @var IConfig */
+ private $config;
+
+ /**
+ * Reference to main server object
+ *
+ * @var \Sabre\DAV\Server
+ */
+ private $server;
+
+ /**
+ * @param IConfig $config
+ */
+ public function __construct(IConfig $config = null) {
+ $this->config = $config;
+ if (is_null($config)) {
+ $this->config = \OC::$server->getConfig();
+ }
+ }
+
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by \Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param \Sabre\DAV\Server $server
+ * @return void
+ */
+ public function initialize(\Sabre\DAV\Server $server) {
+ $this->server = $server;
+ $this->server->on('beforeMethod', array($this, 'checkMaintenanceMode'), 1);
+ }
+
+ /**
+ * This method is called before any HTTP method and returns http status code 503
+ * in case the system is in maintenance mode.
+ *
+ * @throws ServiceUnavailable
+ * @return bool
+ */
+ public function checkMaintenanceMode() {
+ if ($this->config->getSystemValue('singleuser', false)) {
+ throw new ServiceUnavailable('System in single user mode.');
+ }
+ if ($this->config->getSystemValue('maintenance', false)) {
+ throw new ServiceUnavailable('System in maintenance mode.');
+ }
+ if (\OC::checkUpgrade(false)) {
+ throw new ServiceUnavailable('Upgrade needed');
+ }
+
+ return true;
+ }
+}
diff --git a/lib/sabre/principal.php b/lib/sabre/principal.php
new file mode 100644
index 00000000..f918f4bf
--- /dev/null
+++ b/lib/sabre/principal.php
@@ -0,0 +1,205 @@
+
+ * @author Felix Moeller
+ * @author Jakob Sack
+ * @author Jörn Friedrich Dreyer
+ * @author Lukas Reschke
+ * @author Morris Jobke
+ * @author Sebastian Döll
+ * @author Thomas Müller
+ * @author Thomas Tanghus
+ * @author Vincent Petry
+ *
+ * @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
+ *
+ */
+
+namespace OCA\Contacts\Sabre;
+
+use OCP\IUserManager;
+use OCP\IConfig;
+use \Sabre\DAV\PropPatch;
+
+class Principal implements \Sabre\DAVACL\PrincipalBackend\BackendInterface {
+ /** @var IConfig */
+ private $config;
+ /** @var IUserManager */
+ private $userManager;
+
+ /**
+ * @param IConfig $config
+ * @param IUserManager $userManager
+ */
+ public function __construct(IConfig $config,
+ IUserManager $userManager) {
+ $this->config = $config;
+ $this->userManager = $userManager;
+ }
+
+ /**
+ * Returns a list of principals based on a prefix.
+ *
+ * This prefix will often contain something like 'principals'. You are only
+ * expected to return principals that are in this base path.
+ *
+ * You are expected to return at least a 'uri' for every user, you can
+ * return any additional properties if you wish so. Common properties are:
+ * {DAV:}displayname
+ *
+ * @param string $prefixPath
+ * @return string[]
+ */
+ public function getPrincipalsByPrefix($prefixPath) {
+ $principals = [];
+
+ if ($prefixPath === 'principals') {
+ foreach($this->userManager->search('') as $user) {
+
+ $principal = [
+ 'uri' => 'principals/' . $user->getUID(),
+ '{DAV:}displayname' => $user->getUID(),
+ ];
+
+ $email = $this->config->getUserValue($user->getUID(), 'settings', 'email');
+ if(!empty($email)) {
+ $principal['{http://sabredav.org/ns}email-address'] = $email;
+ }
+
+ $principals[] = $principal;
+ }
+ }
+
+ return $principals;
+ }
+
+ /**
+ * Returns a specific principal, specified by it's path.
+ * The returned structure should be the exact same as from
+ * getPrincipalsByPrefix.
+ *
+ * @param string $path
+ * @return array
+ */
+ public function getPrincipalByPath($path) {
+ list($prefix, $name) = explode('/', $path);
+ $user = $this->userManager->get($name);
+
+ if ($prefix === 'principals' && !is_null($user)) {
+ $principal = [
+ 'uri' => 'principals/' . $user->getUID(),
+ '{DAV:}displayname' => $user->getUID(),
+ ];
+
+ $email = $this->config->getUserValue($user->getUID(), 'settings', 'email');
+ if($email) {
+ $principal['{http://sabredav.org/ns}email-address'] = $email;
+ }
+
+ return $principal;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the list of members for a group-principal
+ *
+ * @param string $principal
+ * @return string[]
+ * @throws \Sabre\DAV\Exception
+ */
+ public function getGroupMemberSet($principal) {
+ // TODO: for now the group principal has only one member, the user itself
+ $principal = $this->getPrincipalByPath($principal);
+ if (!$principal) {
+ throw new \Sabre\DAV\Exception('Principal not found');
+ }
+
+ return [$principal['uri']];
+ }
+
+ /**
+ * Returns the list of groups a principal is a member of
+ *
+ * @param string $principal
+ * @return array
+ * @throws \Sabre\DAV\Exception
+ */
+ public function getGroupMembership($principal) {
+ list($prefix, $name) = \Sabre\HTTP\URLUtil::splitPath($principal);
+
+ $group_membership = array();
+ if ($prefix === 'principals') {
+ $principal = $this->getPrincipalByPath($principal);
+ if (!$principal) {
+ throw new \Sabre\DAV\Exception('Principal not found');
+ }
+
+ // TODO: for now the user principal has only its own groups
+ return array(
+ 'principals/'.$name.'/calendar-proxy-read',
+ 'principals/'.$name.'/calendar-proxy-write',
+ // The addressbook groups are not supported in Sabre,
+ // see http://groups.google.com/group/sabredav-discuss/browse_thread/thread/ef2fa9759d55f8c#msg_5720afc11602e753
+ //'principals/'.$name.'/addressbook-proxy-read',
+ //'principals/'.$name.'/addressbook-proxy-write',
+ );
+ }
+ return $group_membership;
+ }
+
+ /**
+ * Updates the list of group members for a group principal.
+ *
+ * The principals should be passed as a list of uri's.
+ *
+ * @param string $principal
+ * @param array $members
+ * @throws \Sabre\DAV\Exception
+ */
+ public function setGroupMemberSet($principal, array $members) {
+ throw new \Sabre\DAV\Exception('Setting members of the group is not supported yet');
+ }
+
+ /**
+ * @param string $path
+ * @param PropPatch $propPatch
+ * @return int
+ */
+ function updatePrincipal($path, PropPatch $propPatch) {
+ return 0;
+ }
+
+ /**
+ * @param string $prefixPath
+ * @param array $searchProperties
+ * @param string $test
+ * @return array
+ */
+ function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
+ return [];
+ }
+
+ /**
+ * @param string $uri
+ * @param string $principalPrefix
+ * @return string
+ */
+ function findByUri($uri, $principalPrefix) {
+ return '';
+ }
+}