|
|
|
|
@ -26,7 +26,6 @@ namespace OCA\DAV\CardDAV; |
|
|
|
|
|
|
|
|
|
use OCA\DAV\Connector\Sabre\Principal; |
|
|
|
|
use OCP\IDBConnection; |
|
|
|
|
use OCP\ILogger; |
|
|
|
|
use Sabre\CardDAV\Backend\BackendInterface; |
|
|
|
|
use Sabre\CardDAV\Backend\SyncSupport; |
|
|
|
|
use Sabre\CardDAV\Plugin; |
|
|
|
|
@ -53,6 +52,10 @@ class CardDavBackend implements BackendInterface, SyncSupport { |
|
|
|
|
'BDAY', 'UID', 'N', 'FN', 'TITLE', 'ROLE', 'NOTE', 'NICKNAME', |
|
|
|
|
'ORG', 'CATEGORIES', 'EMAIL', 'TEL', 'IMPP', 'ADR', 'URL', 'GEO', 'CLOUD'); |
|
|
|
|
|
|
|
|
|
const ACCESS_OWNER = 1; |
|
|
|
|
const ACCESS_READ_WRITE = 2; |
|
|
|
|
const ACCESS_READ = 3; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* CardDavBackend constructor. |
|
|
|
|
* |
|
|
|
|
@ -65,7 +68,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Returns the list of addressbooks for a specific user. |
|
|
|
|
* Returns the list of address books for a specific user. |
|
|
|
|
* |
|
|
|
|
* Every addressbook should have the following properties: |
|
|
|
|
* id - an arbitrary unique id |
|
|
|
|
@ -105,28 +108,30 @@ class CardDavBackend implements BackendInterface, SyncSupport { |
|
|
|
|
$result->closeCursor(); |
|
|
|
|
|
|
|
|
|
// query for shared calendars |
|
|
|
|
$principals = $this->principalBackend->getGroupMembership($principalUri); |
|
|
|
|
$principals[]= $principalUri; |
|
|
|
|
|
|
|
|
|
$query = $this->db->getQueryBuilder(); |
|
|
|
|
$query2 = $this->db->getQueryBuilder(); |
|
|
|
|
$query2->select(['resourceid']) |
|
|
|
|
->from('dav_shares') |
|
|
|
|
->where($query2->expr()->eq('principaluri', $query2->createParameter('principaluri'))) |
|
|
|
|
->andWhere($query2->expr()->eq('type', $query2->createParameter('type'))); |
|
|
|
|
$result = $query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken']) |
|
|
|
|
->from('addressbooks') |
|
|
|
|
->where($query->expr()->in('id', $query->createFunction($query2->getSQL()))) |
|
|
|
|
$result = $query->select(['a.id', 'a.uri', 'a.displayname', 'a.principaluri', 'a.description', 'a.synctoken', 's.uri', 's.access']) |
|
|
|
|
->from('dav_shares', 's') |
|
|
|
|
->join('s', 'addressbooks', 'a', $query->expr()->eq('s.resourceid', 'a.id')) |
|
|
|
|
->where($query->expr()->in('s.principaluri', $query->createParameter('principaluri'))) |
|
|
|
|
->andWhere($query->expr()->eq('s.type', $query->createParameter('type'))) |
|
|
|
|
->setParameter('type', 'addressbook') |
|
|
|
|
->setParameter('principaluri', $principalUri) |
|
|
|
|
->setParameter('principaluri', $principals, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY) |
|
|
|
|
->execute(); |
|
|
|
|
|
|
|
|
|
while($row = $result->fetch()) { |
|
|
|
|
$addressBooks[] = [ |
|
|
|
|
'id' => $row['id'], |
|
|
|
|
'uri' => $row['uri'], |
|
|
|
|
'principaluri' => $row['principaluri'], |
|
|
|
|
'principaluri' => $principalUri, |
|
|
|
|
'{DAV:}displayname' => $row['displayname'], |
|
|
|
|
'{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], |
|
|
|
|
'{http://calendarserver.org/ns/}getctag' => $row['synctoken'], |
|
|
|
|
'{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', |
|
|
|
|
'{' . \OCA\DAV\CardDAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $row['principaluri'], |
|
|
|
|
'{' . \OCA\DAV\CardDAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => $row['access'] === self::ACCESS_READ, |
|
|
|
|
]; |
|
|
|
|
} |
|
|
|
|
$result->closeCursor(); |
|
|
|
|
@ -134,11 +139,43 @@ class CardDavBackend implements BackendInterface, SyncSupport { |
|
|
|
|
return $addressBooks; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function getAddressBooksByUri($addressBookUri) { |
|
|
|
|
/** |
|
|
|
|
* @param int $addressBookId |
|
|
|
|
*/ |
|
|
|
|
public function getAddressBookById($addressBookId) { |
|
|
|
|
$query = $this->db->getQueryBuilder(); |
|
|
|
|
$result = $query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken']) |
|
|
|
|
->from('addressbooks') |
|
|
|
|
->where($query->expr()->eq('id', $query->createNamedParameter($addressBookId))) |
|
|
|
|
->execute(); |
|
|
|
|
|
|
|
|
|
$row = $result->fetch(); |
|
|
|
|
$result->closeCursor(); |
|
|
|
|
if ($row === false) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return [ |
|
|
|
|
'id' => $row['id'], |
|
|
|
|
'uri' => $row['uri'], |
|
|
|
|
'principaluri' => $row['principaluri'], |
|
|
|
|
'{DAV:}displayname' => $row['displayname'], |
|
|
|
|
'{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'], |
|
|
|
|
'{http://calendarserver.org/ns/}getctag' => $row['synctoken'], |
|
|
|
|
'{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0', |
|
|
|
|
]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @param $addressBookUri |
|
|
|
|
* @return array|null |
|
|
|
|
*/ |
|
|
|
|
public function getAddressBooksByUri($principal, $addressBookUri) { |
|
|
|
|
$query = $this->db->getQueryBuilder(); |
|
|
|
|
$result = $query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken']) |
|
|
|
|
->from('addressbooks') |
|
|
|
|
->where($query->expr()->eq('uri', $query->createNamedParameter($addressBookUri))) |
|
|
|
|
->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($principal))) |
|
|
|
|
->setMaxResults(1) |
|
|
|
|
->execute(); |
|
|
|
|
|
|
|
|
|
@ -217,6 +254,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { |
|
|
|
|
* @param string $principalUri |
|
|
|
|
* @param string $url Just the 'basename' of the url. |
|
|
|
|
* @param array $properties |
|
|
|
|
* @return int |
|
|
|
|
* @throws BadRequest |
|
|
|
|
*/ |
|
|
|
|
function createAddressBook($principalUri, $url, array $properties) { |
|
|
|
|
@ -260,6 +298,8 @@ class CardDavBackend implements BackendInterface, SyncSupport { |
|
|
|
|
]) |
|
|
|
|
->setParameters($values) |
|
|
|
|
->execute(); |
|
|
|
|
|
|
|
|
|
return $query->getLastInsertId(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
@ -663,16 +703,16 @@ class CardDavBackend implements BackendInterface, SyncSupport { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @param string $path |
|
|
|
|
* @param AddressBook $book |
|
|
|
|
* @param string[] $add |
|
|
|
|
* @param string[] $remove |
|
|
|
|
*/ |
|
|
|
|
public function updateShares($path, $add, $remove) { |
|
|
|
|
public function updateShares($book, $add, $remove) { |
|
|
|
|
foreach($add as $element) { |
|
|
|
|
$this->shareWith($path, $element); |
|
|
|
|
$this->shareWith($book, $element); |
|
|
|
|
} |
|
|
|
|
foreach($remove as $element) { |
|
|
|
|
$this->unshare($path, $element); |
|
|
|
|
$this->unshare($book->getBookId(), $element); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -758,10 +798,10 @@ class CardDavBackend implements BackendInterface, SyncSupport { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @param string $addressBookUri |
|
|
|
|
* @param AddressBook $addressBook |
|
|
|
|
* @param string $element |
|
|
|
|
*/ |
|
|
|
|
private function shareWith($addressBookUri, $element) { |
|
|
|
|
private function shareWith($addressBook, $element) { |
|
|
|
|
$user = $element['href']; |
|
|
|
|
$parts = explode(':', $user, 2); |
|
|
|
|
if ($parts[0] !== 'principal') { |
|
|
|
|
@ -772,31 +812,31 @@ class CardDavBackend implements BackendInterface, SyncSupport { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$addressBook = $this->getAddressBooksByUri($addressBookUri); |
|
|
|
|
if (is_null($addressBook)) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// remove the share if it already exists |
|
|
|
|
$this->unshare($addressBookUri, $element['href']); |
|
|
|
|
$this->unshare($addressBook->getBookId(), $element['href']); |
|
|
|
|
$access = self::ACCESS_READ; |
|
|
|
|
if (isset($element['readOnly'])) { |
|
|
|
|
$access = $element['readOnly'] ? self::ACCESS_READ : self::ACCESS_READ_WRITE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$newUri = sha1($addressBook->getName() . $addressBook->getOwner()); |
|
|
|
|
$query = $this->db->getQueryBuilder(); |
|
|
|
|
$query->insert('dav_shares') |
|
|
|
|
->values([ |
|
|
|
|
'principaluri' => $query->createNamedParameter($parts[1]), |
|
|
|
|
'uri' => $query->createNamedParameter($addressBookUri), |
|
|
|
|
'uri' => $query->createNamedParameter($newUri), |
|
|
|
|
'type' => $query->createNamedParameter('addressbook'), |
|
|
|
|
'access' => $query->createNamedParameter(0), |
|
|
|
|
'resourceid' => $query->createNamedParameter($addressBook['id']) |
|
|
|
|
'access' => $query->createNamedParameter($access), |
|
|
|
|
'resourceid' => $query->createNamedParameter($addressBook->getBookId()) |
|
|
|
|
]); |
|
|
|
|
$query->execute(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @param string $addressBookUri |
|
|
|
|
* @param int $addressBookId |
|
|
|
|
* @param string $element |
|
|
|
|
*/ |
|
|
|
|
private function unshare($addressBookUri, $element) { |
|
|
|
|
private function unshare($addressBookId, $element) { |
|
|
|
|
$parts = explode(':', $element, 2); |
|
|
|
|
if ($parts[0] !== 'principal') { |
|
|
|
|
return; |
|
|
|
|
@ -806,14 +846,9 @@ class CardDavBackend implements BackendInterface, SyncSupport { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$addressBook = $this->getAddressBooksByUri($addressBookUri); |
|
|
|
|
if (is_null($addressBook)) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
$query = $this->db->getQueryBuilder(); |
|
|
|
|
$query->delete('dav_shares') |
|
|
|
|
->where($query->expr()->eq('resourceid', $query->createNamedParameter($addressBook['id']))) |
|
|
|
|
->where($query->expr()->eq('resourceid', $query->createNamedParameter($addressBookId))) |
|
|
|
|
->andWhere($query->expr()->eq('type', $query->createNamedParameter('addressbook'))) |
|
|
|
|
->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($parts[1]))) |
|
|
|
|
; |
|
|
|
|
@ -832,11 +867,11 @@ class CardDavBackend implements BackendInterface, SyncSupport { |
|
|
|
|
* |
|
|
|
|
* @return array |
|
|
|
|
*/ |
|
|
|
|
public function getShares($addressBookUri) { |
|
|
|
|
public function getShares($addressBookId) { |
|
|
|
|
$query = $this->db->getQueryBuilder(); |
|
|
|
|
$result = $query->select(['principaluri', 'access']) |
|
|
|
|
->from('dav_shares') |
|
|
|
|
->where($query->expr()->eq('uri', $query->createNamedParameter($addressBookUri))) |
|
|
|
|
->where($query->expr()->eq('resourceid', $query->createNamedParameter($addressBookId))) |
|
|
|
|
->andWhere($query->expr()->eq('type', $query->createNamedParameter('addressbook'))) |
|
|
|
|
->execute(); |
|
|
|
|
|
|
|
|
|
@ -847,7 +882,8 @@ class CardDavBackend implements BackendInterface, SyncSupport { |
|
|
|
|
'href' => "principal:${p['uri']}", |
|
|
|
|
'commonName' => isset($p['{DAV:}displayname']) ? $p['{DAV:}displayname'] : '', |
|
|
|
|
'status' => 1, |
|
|
|
|
'readOnly' => ($row['access'] === 1) |
|
|
|
|
'readOnly' => ($row['access'] === self::ACCESS_READ), |
|
|
|
|
'{'.\OCA\DAV\CardDAV\Sharing\Plugin::NS_OWNCLOUD.'}principal' => $p['uri'] |
|
|
|
|
]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -942,4 +978,30 @@ class CardDavBackend implements BackendInterface, SyncSupport { |
|
|
|
|
|
|
|
|
|
return (int)$cardIds['id']; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* For shared address books the sharee is set in the ACL of the address book |
|
|
|
|
* @param $addressBookId |
|
|
|
|
* @param $acl |
|
|
|
|
* @return array |
|
|
|
|
*/ |
|
|
|
|
public function applyShareAcl($addressBookId, $acl) { |
|
|
|
|
|
|
|
|
|
$shares = $this->getShares($addressBookId); |
|
|
|
|
foreach ($shares as $share) { |
|
|
|
|
$acl[] = [ |
|
|
|
|
'privilege' => '{DAV:}read', |
|
|
|
|
'principal' => $share['{' . \OCA\DAV\CardDAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'], |
|
|
|
|
'protected' => true, |
|
|
|
|
]; |
|
|
|
|
if (!$share['readOnly']) { |
|
|
|
|
$acl[] = [ |
|
|
|
|
'privilege' => '{DAV:}write', |
|
|
|
|
'principal' => $share['{' . \OCA\DAV\CardDAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'], |
|
|
|
|
'protected' => true, |
|
|
|
|
]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return $acl; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|