summaryrefslogtreecommitdiffstats
path: root/apps/dav/lib
diff options
context:
space:
mode:
Diffstat (limited to 'apps/dav/lib')
-rw-r--r--apps/dav/lib/carddav/addressbook.php36
-rw-r--r--apps/dav/lib/carddav/addressbookroot.php12
-rw-r--r--apps/dav/lib/carddav/card.php39
-rw-r--r--apps/dav/lib/carddav/carddavbackend.php6
-rw-r--r--apps/dav/lib/carddav/converter.php158
-rw-r--r--apps/dav/lib/carddav/plugin.php47
-rw-r--r--apps/dav/lib/carddav/useraddressbooks.php36
-rw-r--r--apps/dav/lib/dav/systemprincipalbackend.php183
-rw-r--r--apps/dav/lib/rootcollection.php24
-rw-r--r--apps/dav/lib/server.php2
10 files changed, 526 insertions, 17 deletions
diff --git a/apps/dav/lib/carddav/addressbook.php b/apps/dav/lib/carddav/addressbook.php
index eff1ad321e5..507657e9682 100644
--- a/apps/dav/lib/carddav/addressbook.php
+++ b/apps/dav/lib/carddav/addressbook.php
@@ -3,6 +3,7 @@
namespace OCA\DAV\CardDAV;
use OCA\DAV\CardDAV\Sharing\IShareableAddressBook;
+use Sabre\DAV\Exception\NotFound;
class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddressBook {
@@ -51,4 +52,39 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddres
$carddavBackend = $this->carddavBackend;
$carddavBackend->getShares($this->getName());
}
+
+ function getACL() {
+ $acl = parent::getACL();
+ if ($this->getOwner() === 'principals/system/system') {
+ $acl[] = [
+ 'privilege' => '{DAV:}read',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ];
+ }
+
+ return $acl;
+ }
+
+ function getChildACL() {
+ $acl = parent::getChildACL();
+ if ($this->getOwner() === 'principals/system/system') {
+ $acl[] = [
+ 'privilege' => '{DAV:}read',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ];
+ }
+
+ return $acl;
+ }
+
+ function getChild($name) {
+ $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'], $name);
+ if (!$obj) {
+ throw new NotFound('Card not found');
+ }
+ return new Card($this->carddavBackend, $this->addressBookInfo, $obj);
+ }
+
}
diff --git a/apps/dav/lib/carddav/addressbookroot.php b/apps/dav/lib/carddav/addressbookroot.php
index ee99ac8d798..8c78d024556 100644
--- a/apps/dav/lib/carddav/addressbookroot.php
+++ b/apps/dav/lib/carddav/addressbookroot.php
@@ -20,4 +20,14 @@ class AddressBookRoot extends \Sabre\CardDAV\AddressBookRoot {
}
-} \ No newline at end of file
+ function getName() {
+
+ // Grabbing all the components of the principal path.
+ $parts = explode('/', $this->principalPrefix);
+
+ // We are only interested in the second part.
+ return $parts[1];
+
+ }
+
+}
diff --git a/apps/dav/lib/carddav/card.php b/apps/dav/lib/carddav/card.php
new file mode 100644
index 00000000000..cea0b1e41c7
--- /dev/null
+++ b/apps/dav/lib/carddav/card.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\CardDAV;
+
+class Card extends \Sabre\CardDAV\Card {
+
+ function getACL() {
+ $acl = parent::getACL();
+ if ($this->getOwner() === 'principals/system/system') {
+ $acl[] = [
+ 'privilege' => '{DAV:}read',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ];
+ }
+
+ return $acl;
+ }
+
+}
diff --git a/apps/dav/lib/carddav/carddavbackend.php b/apps/dav/lib/carddav/carddavbackend.php
index daa31725fa1..29b056672b4 100644
--- a/apps/dav/lib/carddav/carddavbackend.php
+++ b/apps/dav/lib/carddav/carddavbackend.php
@@ -108,7 +108,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
return $addressBooks;
}
- private function getAddressBooksByUri($addressBookUri) {
+ public function getAddressBooksByUri($addressBookUri) {
$query = $this->db->getQueryBuilder();
$result = $query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken'])
->from('addressbooks')
@@ -117,10 +117,10 @@ class CardDavBackend implements BackendInterface, SyncSupport {
->execute();
$row = $result->fetch();
- if (is_null($row)) {
+ $result->closeCursor();
+ if ($row === false) {
return null;
}
- $result->closeCursor();
return [
'id' => $row['id'],
diff --git a/apps/dav/lib/carddav/converter.php b/apps/dav/lib/carddav/converter.php
new file mode 100644
index 00000000000..56b73eba4c0
--- /dev/null
+++ b/apps/dav/lib/carddav/converter.php
@@ -0,0 +1,158 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\CardDAV;
+
+use OCP\IImage;
+use OCP\IUser;
+use Sabre\VObject\Component\VCard;
+use Sabre\VObject\Property\Text;
+
+class Converter {
+
+ /**
+ * @param IUser $user
+ * @return VCard
+ */
+ public function createCardFromUser(IUser $user) {
+
+ $uid = $user->getUID();
+ $displayName = $user->getDisplayName();
+ $displayName = empty($displayName ) ? $uid : $displayName;
+ $emailAddress = $user->getEMailAddress();
+ $cloudId = $user->getCloudId();
+ $image = $user->getAvatarImage(-1);
+
+ $vCard = new VCard();
+ $vCard->add(new Text($vCard, 'UID', $uid));
+ if (!empty($displayName)) {
+ $vCard->add(new Text($vCard, 'FN', $displayName));
+ $vCard->add(new Text($vCard, 'N', $this->splitFullName($displayName)));
+ }
+ if (!empty($emailAddress)) {
+ $vCard->add(new Text($vCard, 'EMAIL', $emailAddress, ['TYPE' => 'OTHER']));
+ }
+ if (!empty($cloudId)) {
+ $vCard->add(new Text($vCard, 'CLOUD', $cloudId));
+ }
+ if ($image) {
+ $vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]);
+ }
+ $vCard->validate();
+
+ return $vCard;
+ }
+
+ /**
+ * @param VCard $vCard
+ * @param IUser $user
+ * @return bool
+ */
+ public function updateCard(VCard $vCard, IUser $user) {
+ $uid = $user->getUID();
+ $displayName = $user->getDisplayName();
+ $displayName = empty($displayName ) ? $uid : $displayName;
+ $emailAddress = $user->getEMailAddress();
+ $cloudId = $user->getCloudId();
+ $image = $user->getAvatarImage(-1);
+
+ $updated = false;
+ if($this->propertyNeedsUpdate($vCard, 'FN', $displayName)) {
+ $vCard->FN = new Text($vCard, 'FN', $displayName);
+ unset($vCard->N);
+ $vCard->add(new Text($vCard, 'N', $this->splitFullName($displayName)));
+ $updated = true;
+ }
+ if($this->propertyNeedsUpdate($vCard, 'EMAIL', $emailAddress)) {
+ $vCard->EMAIL = new Text($vCard, 'EMAIL', $emailAddress);
+ $updated = true;
+ }
+ if($this->propertyNeedsUpdate($vCard, 'CLOUD', $cloudId)) {
+ $vCard->CLOUD = new Text($vCard, 'CLOUD', $cloudId);
+ $updated = true;
+ }
+
+ if($this->propertyNeedsUpdate($vCard, 'PHOTO', $image)) {
+ $vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]);
+ $updated = true;
+ }
+
+ if (empty($emailAddress) && !is_null($vCard->EMAIL)) {
+ unset($vCard->EMAIL);
+ $updated = true;
+ }
+ if (empty($cloudId) && !is_null($vCard->CLOUD)) {
+ unset($vCard->CLOUD);
+ $updated = true;
+ }
+ if (empty($image) && !is_null($vCard->PHOTO)) {
+ unset($vCard->PHOTO);
+ $updated = true;
+ }
+
+ return $updated;
+ }
+
+ /**
+ * @param VCard $vCard
+ * @param string $name
+ * @param string|IImage $newValue
+ * @return bool
+ */
+ private function propertyNeedsUpdate(VCard $vCard, $name, $newValue) {
+ if (is_null($newValue)) {
+ return false;
+ }
+ $value = $vCard->__get($name);
+ if (!is_null($value)) {
+ $value = $value->getValue();
+ $newValue = $newValue instanceof IImage ? $newValue->data() : $newValue;
+
+ return $value !== $newValue;
+ }
+ return true;
+ }
+
+ /**
+ * @param string $fullName
+ * @return string[]
+ */
+ public function splitFullName($fullName) {
+ // Very basic western style parsing. I'm not gonna implement
+ // https://github.com/android/platform_packages_providers_contactsprovider/blob/master/src/com/android/providers/contacts/NameSplitter.java ;)
+
+ $elements = explode(' ', $fullName);
+ $result = ['', '', '', '', ''];
+ if (count($elements) > 2) {
+ $result[0] = implode(' ', array_slice($elements, count($elements)-1));
+ $result[1] = $elements[0];
+ $result[2] = implode(' ', array_slice($elements, 1, count($elements)-2));
+ } elseif (count($elements) === 2) {
+ $result[0] = $elements[1];
+ $result[1] = $elements[0];
+ } else {
+ $result[0] = $elements[0];
+ }
+
+ return $result;
+ }
+
+}
diff --git a/apps/dav/lib/carddav/plugin.php b/apps/dav/lib/carddav/plugin.php
new file mode 100644
index 00000000000..f2b3dcbfda9
--- /dev/null
+++ b/apps/dav/lib/carddav/plugin.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\CardDAV;
+
+use Sabre\HTTP\URLUtil;
+
+class Plugin extends \Sabre\CardDAV\Plugin {
+
+ /**
+ * Returns the addressbook home for a given principal
+ *
+ * @param string $principal
+ * @return string
+ */
+ protected function getAddressbookHomeForPrincipal($principal) {
+
+ if (strrpos($principal, 'principals/users', -strlen($principal)) !== FALSE) {
+ list(, $principalId) = URLUtil::splitPath($principal);
+ return self::ADDRESSBOOK_ROOT . '/users/' . $principalId;
+ }
+ if (strrpos($principal, 'principals/system', -strlen($principal)) !== FALSE) {
+ list(, $principalId) = URLUtil::splitPath($principal);
+ return self::ADDRESSBOOK_ROOT . '/system/' . $principalId;
+ }
+
+ throw new \LogicException('This is not supposed to happen');
+ }
+}
diff --git a/apps/dav/lib/carddav/useraddressbooks.php b/apps/dav/lib/carddav/useraddressbooks.php
index 5f618a0ece3..093cee0e1b2 100644
--- a/apps/dav/lib/carddav/useraddressbooks.php
+++ b/apps/dav/lib/carddav/useraddressbooks.php
@@ -11,13 +11,39 @@ class UserAddressBooks extends \Sabre\CardDAV\AddressBookHome {
*/
function getChildren() {
- $addressbooks = $this->carddavBackend->getAddressBooksForUser($this->principalUri);
- $objs = [];
- foreach($addressbooks as $addressbook) {
- $objs[] = new AddressBook($this->carddavBackend, $addressbook);
+ $addressBooks = $this->carddavBackend->getAddressBooksForUser($this->principalUri);
+ $objects = [];
+ foreach($addressBooks as $addressBook) {
+ $objects[] = new AddressBook($this->carddavBackend, $addressBook);
}
- return $objs;
+ return $objects;
}
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ $acl = parent::getACL();
+ if ($this->principalUri === 'principals/system/system') {
+ $acl[] = [
+ 'privilege' => '{DAV:}read',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ];
+ }
+
+ return $acl;
+ }
+
}
diff --git a/apps/dav/lib/dav/systemprincipalbackend.php b/apps/dav/lib/dav/systemprincipalbackend.php
new file mode 100644
index 00000000000..2c2049ace60
--- /dev/null
+++ b/apps/dav/lib/dav/systemprincipalbackend.php
@@ -0,0 +1,183 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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\DAV;
+
+use Sabre\DAVACL\PrincipalBackend\AbstractBackend;
+use Sabre\HTTP\URLUtil;
+
+class SystemPrincipalBackend extends AbstractBackend {
+
+ /**
+ * 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
+ * {http://sabredav.org/ns}email-address - This is a custom SabreDAV
+ * field that's actually injected in a number of other properties. If
+ * you have an email address, use this property.
+ *
+ * @param string $prefixPath
+ * @return array
+ */
+ function getPrincipalsByPrefix($prefixPath) {
+ $principals = [];
+
+ if ($prefixPath === 'principals/system') {
+ $principals[] = [
+ 'uri' => 'principals/system/system',
+ '{DAV:}displayname' => 'system',
+ ];
+ }
+
+ 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
+ */
+ function getPrincipalByPath($path) {
+
+ $elements = explode('/', $path);
+ if ($elements[0] !== 'principals') {
+ return null;
+ }
+ if ($elements[1] === 'system') {
+ $principal = [
+ 'uri' => 'principals/system/system',
+ '{DAV:}displayname' => 'system',
+ ];
+ return $principal;
+ }
+
+ return null;
+ }
+
+ /**
+ * Updates one ore more webdav properties on a principal.
+ *
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
+ *
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
+ *
+ * Read the PropPatch documentation for more info and examples.
+ *
+ * @param string $path
+ * @param \Sabre\DAV\PropPatch $propPatch
+ * @return void
+ */
+ function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) {
+ }
+
+ /**
+ * This method is used to search for principals matching a set of
+ * properties.
+ *
+ * This search is specifically used by RFC3744's principal-property-search
+ * REPORT.
+ *
+ * The actual search should be a unicode-non-case-sensitive search. The
+ * keys in searchProperties are the WebDAV property names, while the values
+ * are the property values to search on.
+ *
+ * By default, if multiple properties are submitted to this method, the
+ * various properties should be combined with 'AND'. If $test is set to
+ * 'anyof', it should be combined using 'OR'.
+ *
+ * This method should simply return an array with full principal uri's.
+ *
+ * If somebody attempted to search on a property the backend does not
+ * support, you should simply return 0 results.
+ *
+ * You can also just return 0 results if you choose to not support
+ * searching at all, but keep in mind that this may stop certain features
+ * from working.
+ *
+ * @param string $prefixPath
+ * @param array $searchProperties
+ * @param string $test
+ * @return array
+ */
+ function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
+ return [];
+ }
+
+ /**
+ * Returns the list of members for a group-principal
+ *
+ * @param string $principal
+ * @return array
+ */
+ 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
+ */
+ function getGroupMembership($principal) {
+ list($prefix, $name) = URLUtil::splitPath($principal);
+
+ if ($prefix === 'principals/system') {
+ $principal = $this->getPrincipalByPath($principal);
+ if (!$principal) {
+ throw new \Sabre\DAV\Exception('Principal not found');
+ }
+
+ return [];
+ }
+ return [];
+ }
+
+ /**
+ * 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
+ * @return void
+ */
+ function setGroupMemberSet($principal, array $members) {
+ throw new \Sabre\DAV\Exception('Setting members of the group is not supported yet');
+ }
+}
diff --git a/apps/dav/lib/rootcollection.php b/apps/dav/lib/rootcollection.php
index 3e349fa31c9..c1635c9cde5 100644
--- a/apps/dav/lib/rootcollection.php
+++ b/apps/dav/lib/rootcollection.php
@@ -6,6 +6,7 @@ use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CardDAV\AddressBookRoot;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\Connector\Sabre\Principal;
+use OCA\DAV\DAV\SystemPrincipalBackend;
use Sabre\CalDAV\CalendarRoot;
use Sabre\CalDAV\Principal\Collection;
use Sabre\DAV\SimpleCollection;
@@ -23,24 +24,33 @@ class RootCollection extends SimpleCollection {
$disableListing = !$config->getSystemValue('debug', false);
// setup the first level of the dav tree
- $principalCollection = new Collection($principalBackend, 'principals/users');
- $principalCollection->disableListing = $disableListing;
+ $userPrincipals = new Collection($principalBackend, 'principals/users');
+ $userPrincipals->disableListing = $disableListing;
+ $systemPrincipals = new Collection(new SystemPrincipalBackend(), 'principals/system');
+ $systemPrincipals->disableListing = $disableListing;
$filesCollection = new Files\RootCollection($principalBackend, 'principals/users');
$filesCollection->disableListing = $disableListing;
$caldavBackend = new CalDavBackend($db);
$calendarRoot = new CalendarRoot($principalBackend, $caldavBackend, 'principals/users');
$calendarRoot->disableListing = $disableListing;
- $cardDavBackend = new CardDavBackend(\OC::$server->getDatabaseConnection(), $principalBackend);
+ $usersCardDavBackend = new CardDavBackend($db, $principalBackend);
+ $usersAddressBookRoot = new AddressBookRoot($principalBackend, $usersCardDavBackend, 'principals/users');
+ $usersAddressBookRoot->disableListing = $disableListing;
- $addressBookRoot = new AddressBookRoot($principalBackend, $cardDavBackend, 'principals/users');
- $addressBookRoot->disableListing = $disableListing;
+ $systemCardDavBackend = new CardDavBackend($db, $principalBackend);
+ $systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, 'principals/system');
+ $systemAddressBookRoot->disableListing = $disableListing;
$children = [
- new SimpleCollection('principals', [$principalCollection]),
+ new SimpleCollection('principals', [
+ $userPrincipals,
+ $systemPrincipals]),
$filesCollection,
$calendarRoot,
- $addressBookRoot,
+ new SimpleCollection('addressbooks', [
+ $usersAddressBookRoot,
+ $systemAddressBookRoot]),
];
parent::__construct('root', $children);
diff --git a/apps/dav/lib/server.php b/apps/dav/lib/server.php
index ffdb917085e..a031f2c442b 100644
--- a/apps/dav/lib/server.php
+++ b/apps/dav/lib/server.php
@@ -58,7 +58,7 @@ class Server {
$this->server->addPlugin(new CardDAV\Sharing\Plugin($authBackend, \OC::$server->getRequest()));
// addressbook plugins
- $this->server->addPlugin(new \Sabre\CardDAV\Plugin());
+ $this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin());
// Finder on OS X requires Class 2 WebDAV support (locking), since we do
// not provide locking we emulate it using a fake locking plugin.