summaryrefslogtreecommitdiffstats
path: root/3dparty/Sabre/CardDAV
diff options
context:
space:
mode:
authorJakob Sack <kde@jakobsack.de>2011-07-20 15:53:34 +0200
committerJakob Sack <kde@jakobsack.de>2011-07-20 15:53:34 +0200
commitbf1ca75710a99a96ba39790e9db79bb0a0f950b4 (patch)
treea1905c91590944e4558a6d2c5bd856be70a70f0e /3dparty/Sabre/CardDAV
parent6230001a3c80c081001c46197cc95403cc73622f (diff)
downloadnextcloud-server-bf1ca75710a99a96ba39790e9db79bb0a0f950b4.tar.gz
nextcloud-server-bf1ca75710a99a96ba39790e9db79bb0a0f950b4.zip
Integration of SabreDAV
Diffstat (limited to '3dparty/Sabre/CardDAV')
-rw-r--r--3dparty/Sabre/CardDAV/AddressBook.php225
-rw-r--r--3dparty/Sabre/CardDAV/AddressBookQueryParser.php211
-rw-r--r--3dparty/Sabre/CardDAV/AddressBookRoot.php72
-rw-r--r--3dparty/Sabre/CardDAV/Backend/Abstract.php123
-rw-r--r--3dparty/Sabre/CardDAV/Backend/PDO.php265
-rw-r--r--3dparty/Sabre/CardDAV/Card.php151
-rw-r--r--3dparty/Sabre/CardDAV/IAddressBook.php18
-rw-r--r--3dparty/Sabre/CardDAV/ICard.php20
-rw-r--r--3dparty/Sabre/CardDAV/IDirectory.php21
-rw-r--r--3dparty/Sabre/CardDAV/Plugin.php465
-rw-r--r--3dparty/Sabre/CardDAV/UserAddressBooks.php171
-rw-r--r--3dparty/Sabre/CardDAV/Version.php28
12 files changed, 1770 insertions, 0 deletions
diff --git a/3dparty/Sabre/CardDAV/AddressBook.php b/3dparty/Sabre/CardDAV/AddressBook.php
new file mode 100644
index 00000000000..a4d2d0c4783
--- /dev/null
+++ b/3dparty/Sabre/CardDAV/AddressBook.php
@@ -0,0 +1,225 @@
+<?php
+
+/**
+ * UserAddressBook class
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * The AddressBook class represents a CardDAV addressbook, owned by a specific user
+ *
+ * The AddressBook can contain multiple vcards
+ */
+class Sabre_CardDAV_AddressBook extends Sabre_DAV_Directory implements Sabre_CardDAV_IAddressBook, Sabre_DAV_IProperties {
+
+ /**
+ * This is an array with addressbook information
+ *
+ * @var array
+ */
+ private $addressBookInfo;
+
+ /**
+ * CardDAV backend
+ *
+ * @var Sabre_CardDAV_Backend_Abstract
+ */
+ private $carddavBackend;
+
+ /**
+ * Constructor
+ *
+ * @param Sabre_CardDAV_Backend_Abstract $carddavBackend
+ * @param array $addressBookInfo
+ * @return void
+ */
+ public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend,array $addressBookInfo) {
+
+ $this->carddavBackend = $carddavBackend;
+ $this->addressBookInfo = $addressBookInfo;
+
+ }
+
+ /**
+ * Returns the name of the addressbook
+ *
+ * @return string
+ */
+ public function getName() {
+
+ return $this->addressBookInfo['uri'];
+
+ }
+
+ /**
+ * Returns a card
+ *
+ * @param string $name
+ * @return Sabre_DAV_Card
+ */
+ public function getChild($name) {
+
+ $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'],$name);
+ if (!$obj) throw new Sabre_DAV_Exception_FileNotFound('Card not found');
+ return new Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj);
+
+ }
+
+ /**
+ * Returns the full list of cards
+ *
+ * @return array
+ */
+ public function getChildren() {
+
+ $objs = $this->carddavBackend->getCards($this->addressBookInfo['id']);
+ $children = array();
+ foreach($objs as $obj) {
+ $children[] = new Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj);
+ }
+ return $children;
+
+ }
+
+ /**
+ * Creates a new directory
+ *
+ * We actually block this, as subdirectories are not allowed in addressbooks.
+ *
+ * @param string $name
+ * @return void
+ */
+ public function createDirectory($name) {
+
+ throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in addressbooks is not allowed');
+
+ }
+
+ /**
+ * Creates a new file
+ *
+ * The contents of the new file must be a valid VCARD
+ *
+ * @param string $name
+ * @param resource $vcardData
+ * @return void
+ */
+ public function createFile($name,$vcardData = null) {
+
+ $vcardData = stream_get_contents($vcardData);
+
+ $this->carddavBackend->createCard($this->addressBookInfo['id'],$name,$vcardData);
+
+ }
+
+ /**
+ * Deletes the entire addressbook.
+ *
+ * @return void
+ */
+ public function delete() {
+
+ $this->carddavBackend->deleteAddressBook($this->addressBookInfo['id']);
+
+ }
+
+ /**
+ * Renames the addressbook. Note that most calendars use the
+ * {DAV:}displayname to display a name to display a name.
+ *
+ * @param string $newName
+ * @return void
+ */
+ public function setName($newName) {
+
+ throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming addressbooks is not yet supported');
+
+ }
+
+ /**
+ * Returns the last modification date as a unix timestamp.
+ *
+ * @return void
+ */
+ public function getLastModified() {
+
+ return null;
+
+ }
+
+ /**
+ * Updates properties on this node,
+ *
+ * The properties array uses the propertyName in clark-notation as key,
+ * and the array value for the property value. In the case a property
+ * should be deleted, the property value will be null.
+ *
+ * This method must be atomic. If one property cannot be changed, the
+ * entire operation must fail.
+ *
+ * If the operation was successful, true can be returned.
+ * If the operation failed, false can be returned.
+ *
+ * Deletion of a non-existant property is always succesful.
+ *
+ * Lastly, it is optional to return detailed information about any
+ * failures. In this case an array should be returned with the following
+ * structure:
+ *
+ * array(
+ * 403 => array(
+ * '{DAV:}displayname' => null,
+ * ),
+ * 424 => array(
+ * '{DAV:}owner' => null,
+ * )
+ * )
+ *
+ * In this example it was forbidden to update {DAV:}displayname.
+ * (403 Forbidden), which in turn also caused {DAV:}owner to fail
+ * (424 Failed Dependency) because the request needs to be atomic.
+ *
+ * @param array $mutations
+ * @return bool|array
+ */
+ public function updateProperties($mutations) {
+
+ return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $mutations);
+
+ }
+
+ /**
+ * Returns a list of properties for this nodes.
+ *
+ * The properties list is a list of propertynames the client requested,
+ * encoded in clark-notation {xmlnamespace}tagname
+ *
+ * If the array is empty, it means 'all properties' were requested.
+ *
+ * @param array $properties
+ * @return void
+ */
+ public function getProperties($properties) {
+
+ $response = array();
+ foreach($properties as $propertyName) {
+
+ if (isset($this->addressBookInfo[$propertyName])) {
+
+ $response[$propertyName] = $this->addressBookInfo[$propertyName];
+
+ }
+
+ }
+
+ return $response;
+
+ }
+
+
+}
diff --git a/3dparty/Sabre/CardDAV/AddressBookQueryParser.php b/3dparty/Sabre/CardDAV/AddressBookQueryParser.php
new file mode 100644
index 00000000000..08adc3b8157
--- /dev/null
+++ b/3dparty/Sabre/CardDAV/AddressBookQueryParser.php
@@ -0,0 +1,211 @@
+<?php
+
+/**
+ * Parses the addressbook-query report request body.
+ *
+ * Whoever designed this format, and the CalDAV equavalent even more so,
+ * has no feel for design.
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_AddressBookQueryParser {
+
+ const TEST_ANYOF = 'anyof';
+ const TEST_ALLOF = 'allof';
+
+ /**
+ * List of requested properties the client wanted
+ *
+ * @var array
+ */
+ public $requestedProperties;
+
+ /**
+ * The number of results the client wants
+ *
+ * null means it wasn't specified, which in most cases means 'all results'.
+ *
+ * @var int|null
+ */
+ public $limit;
+
+ /**
+ * List of property filters.
+ *
+ * @var array
+ */
+ public $filters;
+
+ /**
+ * Either TEST_ANYOF or TEST_ALLOF
+ *
+ * @var string
+ */
+ public $test;
+
+ /**
+ * DOM Document
+ *
+ * @var DOMDocument
+ */
+ protected $dom;
+
+ /**
+ * DOM XPath object
+ *
+ * @var DOMXPath
+ */
+ protected $xpath;
+
+ /**
+ * Creates the parser
+ *
+ * @param DOMNode $dom
+ * @return void
+ */
+ public function __construct(DOMDocument $dom) {
+
+ $this->dom = $dom;
+
+ $this->xpath = new DOMXPath($dom);
+ $this->xpath->registerNameSpace('card',Sabre_CardDAV_Plugin::NS_CARDDAV);
+
+ }
+
+ /**
+ * Parses the request.
+ *
+ * @param DOMNode $dom
+ * @return void
+ */
+ public function parse() {
+
+ $filterNode = null;
+
+ $limit = $this->xpath->evaluate('number(/card:addressbook-query/card:limit/card:nresults)');
+ if (is_nan($limit)) $limit = null;
+
+ $filter = $this->xpath->query('/card:addressbook-query/card:filter');
+ if ($filter->length !== 1) {
+ throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
+ }
+
+ $filter = $filter->item(0);
+ $test = $this->xpath->evaluate('string(@test)', $filter);
+ if (!$test) $test = self::TEST_ANYOF;
+ if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) {
+ throw new Sabre_DAV_Exception_BadRequest('The test attribute must either hold "anyof" or "allof"');
+ }
+
+ $propFilters = array();
+
+ $propFilterNodes = $this->xpath->query('card:prop-filter', $filter);
+ for($ii=0; $ii < $propFilterNodes->length; $ii++) {
+
+ $propFilters[] = $this->parsePropFilterNode($propFilterNodes->item($ii));
+
+
+ }
+
+ $this->filters = $propFilters;
+ $this->limit = $limit;
+ $this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild));
+ $this->test = $test;
+
+ }
+
+ /**
+ * Parses the prop-filter xml element
+ *
+ * @param DOMElement $propFilterNode
+ * @return array
+ */
+ protected function parsePropFilterNode(DOMElement $propFilterNode) {
+
+ $propFilter = array();
+ $propFilter['name'] = $propFilterNode->getAttribute('name');
+ $propFilter['test'] = $propFilterNode->getAttribute('test');
+ if (!$propFilter['test']) $propFilter['test'] = 'anyof';
+
+ $propFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $propFilterNode)->length>0;
+
+ $paramFilterNodes = $this->xpath->query('card:param-filter', $propFilterNode);
+
+ $propFilter['param-filters'] = array();
+
+
+ for($ii=0;$ii<$paramFilterNodes->length;$ii++) {
+
+ $propFilter['param-filters'][] = $this->parseParamFilterNode($paramFilterNodes->item($ii));
+
+ }
+ $propFilter['text-matches'] = array();
+ $textMatchNodes = $this->xpath->query('card:text-match', $propFilterNode);
+
+ for($ii=0;$ii<$textMatchNodes->length;$ii++) {
+
+ $propFilter['text-matches'][] = $this->parseTextMatchNode($textMatchNodes->item($ii));
+
+ }
+
+ return $propFilter;
+
+ }
+
+ /**
+ * Parses the param-filter element
+ *
+ * @param DOMElement $paramFilterNode
+ * @return array
+ */
+ public function parseParamFilterNode(DOMElement $paramFilterNode) {
+
+ $paramFilter = array();
+ $paramFilter['name'] = $paramFilterNode->getAttribute('name');
+ $paramFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $paramFilterNode)->length>0;
+ $paramFilter['text-match'] = null;
+
+ $textMatch = $this->xpath->query('card:text-match', $paramFilterNode);
+ if ($textMatch->length>0) {
+ $paramFilter['text-match'] = $this->parseTextMatchNode($textMatch->item(0));
+ }
+
+ return $paramFilter;
+
+ }
+
+ /**
+ * Text match
+ *
+ * @param DOMElement $textMatchNode
+ * @return void
+ */
+ public function parseTextMatchNode(DOMElement $textMatchNode) {
+
+ $matchType = $textMatchNode->getAttribute('match-type');
+ if (!$matchType) $matchType = 'contains';
+
+ if (!in_array($matchType, array('contains', 'equals', 'starts-with', 'ends-with'))) {
+ throw new Sabre_DAV_Exception_BadRequest('Unknown match-type: ' . $matchType);
+ }
+
+ $negateCondition = $textMatchNode->getAttribute('negate-condition');
+ $negateCondition = $negateCondition==='yes';
+ $collation = $textMatchNode->getAttribute('collation');
+ if (!$collation) $collation = 'i;unicode-casemap';
+
+ return array(
+ 'negate-condition' => $negateCondition,
+ 'collation' => $collation,
+ 'match-type' => $matchType,
+ 'value' => $textMatchNode->nodeValue
+ );
+
+
+ }
+
+}
diff --git a/3dparty/Sabre/CardDAV/AddressBookRoot.php b/3dparty/Sabre/CardDAV/AddressBookRoot.php
new file mode 100644
index 00000000000..88c8ed2e061
--- /dev/null
+++ b/3dparty/Sabre/CardDAV/AddressBookRoot.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * AddressBook rootnode
+ *
+ * This object lists a collection of users, which can contain addressbooks.
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_AddressBookRoot extends Sabre_DAVACL_AbstractPrincipalCollection {
+
+ /**
+ * Principal Backend
+ *
+ * @var Sabre_DAVACL_IPrincipalBackend
+ */
+ protected $principalBackend;
+
+ /**
+ * CardDAV backend
+ *
+ * @var Sabre_CardDAV_Backend_Abstract
+ */
+ protected $carddavBackend;
+
+ /**
+ * Constructor
+ *
+ * This constructor needs both a principal and a carddav backend.
+ *
+ * @param Sabre_DAVACL_IPrincipalBackend $principalBackend
+ * @param Sabre_CardDAV_Backend_Abstract $carddavBackend
+ */
+ public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CardDAV_Backend_Abstract $carddavBackend) {
+
+ $this->carddavBackend = $carddavBackend;
+ parent::__construct($principalBackend);
+
+ }
+
+ /**
+ * Returns the name of the node
+ *
+ * @return string
+ */
+ public function getName() {
+
+ return Sabre_CardDAV_Plugin::ADDRESSBOOK_ROOT;
+
+ }
+
+ /**
+ * 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 $principal
+ * @return Sabre_DAV_INode
+ */
+ public function getChildForPrincipal(array $principal) {
+
+ return new Sabre_CardDAV_UserAddressBooks($this->carddavBackend, $principal['uri']);
+
+ }
+
+}
diff --git a/3dparty/Sabre/CardDAV/Backend/Abstract.php b/3dparty/Sabre/CardDAV/Backend/Abstract.php
new file mode 100644
index 00000000000..f6d10291ca6
--- /dev/null
+++ b/3dparty/Sabre/CardDAV/Backend/Abstract.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * Abstract Backend class
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * This class serves as a base-class for addressbook backends
+ *
+ * Note that there are references to 'addressBookId' scattered throughout the
+ * class. The value of the addressBookId is completely up to you, it can be any
+ * arbitrary value you can use as an unique identifier.
+ */
+abstract class Sabre_CardDAV_Backend_Abstract {
+
+ /**
+ * Returns the list of addressbooks for a specific user.
+ *
+ * Every addressbook should have the following properties:
+ * id - an arbitrary unique id
+ * uri - the 'basename' part of the url
+ * principaluri - Same as the passed parameter
+ *
+ * Any additional clark-notation property may be passed besides this. Some
+ * common ones are :
+ * {DAV:}displayname
+ * {urn:ietf:params:xml:ns:carddav}addressbook-description
+ * {http://calendarserver.org/ns/}getctag
+ *
+ * @param string $principalUri
+ * @return array
+ */
+ public abstract function getAddressBooksForUser($principalUri);
+
+ /**
+ * Updates an addressbook's properties
+ *
+ * See Sabre_DAV_IProperties for a description of the mutations array, as
+ * well as the return value.
+ *
+ * @param mixed $addressBookId
+ * @param array $mutations
+ * @see Sabre_DAV_IProperties::updateProperties
+ * @return bool|array
+ */
+ public abstract function updateAddressBook($addressBookId, array $mutations);
+
+ /**
+ * Creates a new address book
+ *
+ * @param string $principalUri
+ * @param string $url Just the 'basename' of the url.
+ * @param array $properties
+ * @return void
+ */
+ abstract public function createAddressBook($principalUri, $url, array $properties);
+
+ /**
+ * Deletes an entire addressbook and all its contents
+ *
+ * @param mixed $addressBookId
+ * @return void
+ */
+ abstract public function deleteAddressBook($addressBookId);
+
+ /**
+ * Returns all cards for a specific addressbook id.
+ *
+ * This method should return the following properties for each card:
+ * * carddata - raw vcard data
+ * * uri - Some unique url
+ * * lastmodified - A unix timestamp
+
+ * @param mixed $addressbookId
+ * @return array
+ */
+ public abstract function getCards($addressbookId);
+
+ /**
+ * Returns a specfic card
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @return void
+ */
+ public abstract function getCard($addressBookId, $cardUri);
+
+ /**
+ * Creates a new card
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @param string $cardData
+ * @return bool
+ */
+ abstract public function createCard($addressBookId, $cardUri, $cardData);
+
+ /**
+ * Updates a card
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @param string $cardData
+ * @return bool
+ */
+ abstract public function updateCard($addressBookId, $cardUri, $cardData);
+
+ /**
+ * Deletes a card
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @return bool
+ */
+ abstract public function deleteCard($addressBookId, $cardUri);
+
+}
diff --git a/3dparty/Sabre/CardDAV/Backend/PDO.php b/3dparty/Sabre/CardDAV/Backend/PDO.php
new file mode 100644
index 00000000000..e7cd4ecd4df
--- /dev/null
+++ b/3dparty/Sabre/CardDAV/Backend/PDO.php
@@ -0,0 +1,265 @@
+<?php
+
+/**
+ * PDO CardDAV backend
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * This CardDAV backend uses PDO to store addressbooks
+ */
+class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract {
+
+ /**
+ * PDO connection
+ *
+ * @var PDO
+ */
+ protected $pdo;
+
+ /**
+ * Sets up the object
+ *
+ * @param PDO $pdo
+ */
+ public function __construct(PDO $pdo) {
+
+ $this->pdo = $pdo;
+
+ }
+
+ /**
+ * Returns the list of addressbooks for a specific user.
+ *
+ * @param string $principalUri
+ * @return array
+ */
+ public function getAddressBooksForUser($principalUri) {
+
+ $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, ctag FROM addressbooks WHERE principaluri = ?');
+ $result = $stmt->execute(array($principalUri));
+
+ $addressBooks = array();
+
+ foreach($stmt->fetchAll() as $row) {
+
+ $addressBooks[] = array(
+ 'id' => $row['id'],
+ 'uri' => $row['uri'],
+ 'principaluri' => $row['principaluri'],
+ '{DAV:}displayname' => $row['displayname'],
+ '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
+ '{http://calendarserver.org/ns/}getctag' => $row['ctag'],
+ );
+
+ }
+
+ return $addressBooks;
+
+ }
+
+
+ /**
+ * Updates an addressbook's properties
+ *
+ * See Sabre_DAV_IProperties for a description of the mutations array, as
+ * well as the return value.
+ *
+ * @param mixed $addressBookId
+ * @param array $mutations
+ * @see Sabre_DAV_IProperties::updateProperties
+ * @return bool|array
+ */
+ public function updateAddressBook($addressBookId, array $mutations) {
+
+ $updates = array();
+
+ foreach($mutations as $property=>$newValue) {
+
+ switch($property) {
+ case '{DAV:}displayname' :
+ $updates['displayname'] = $newValue;
+ break;
+ case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
+ $updates['description'] = $newValue;
+ break;
+ default :
+ // If any unsupported values were being updated, we must
+ // let the entire request fail.
+ return false;
+ }
+
+ }
+
+ // No values are being updated?
+ if (!$updates) {
+ return false;
+ }
+
+ $query = 'UPDATE addressbooks SET ctag = ctag + 1 ';
+ foreach($updates as $key=>$value) {
+ $query.=', `' . $key . '` = :' . $key . ' ';
+ }
+ $query.=' WHERE id = :addressbookid';
+
+ $stmt = $this->pdo->prepare($query);
+ $updates['addressbookid'] = $addressBookId;
+
+ $stmt->execute($updates);
+
+ return true;
+
+ }
+
+ /**
+ * Creates a new address book
+ *
+ * @param string $principalUri
+ * @param string $url Just the 'basename' of the url.
+ * @param array $properties
+ * @return void
+ */
+ public function createAddressBook($principalUri, $url, array $properties) {
+
+ $values = array(
+ 'displayname' => null,
+ 'description' => null,
+ 'principaluri' => $principalUri,
+ 'uri' => $url,
+ );
+
+ foreach($properties as $property=>$newValue) {
+
+ switch($property) {
+ case '{DAV:}displayname' :
+ $values['displayname'] = $newValue;
+ break;
+ case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
+ $values['description'] = $newValue;
+ break;
+ default :
+ throw new Sabre_DAV_Exception_BadRequest('Unknown property: ' . $property);
+ }
+
+ }
+
+ $query = 'INSERT INTO addressbooks (uri, displayname, description, principaluri, ctag) VALUES (:uri, :displayname, :description, :principaluri, 1)';
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute($values);
+
+ }
+
+ /**
+ * Deletes an entire addressbook and all its contents
+ *
+ * @param int $addressBookId
+ * @return void
+ */
+ public function deleteAddressBook($addressBookId) {
+
+ $stmt = $this->pdo->prepare('DELETE FROM cards WHERE addressbookid = ?');
+ $stmt->execute(array($addressBookId));
+
+ $stmt = $this->pdo->prepare('DELETE FROM addressbooks WHERE id = ?');
+ $stmt->execute(array($addressBookId));
+
+ }
+
+ /**
+ * Returns all cards for a specific addressbook id.
+ *
+ * @param mixed $addressbookId
+ * @return array
+ */
+ public function getCards($addressbookId) {
+
+ $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM cards WHERE addressbookid = ?');
+ $stmt->execute(array($addressbookId));
+
+ return $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+
+ }
+ /**
+ * Returns a specfic card
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @return array
+ */
+ public function getCard($addressBookId, $cardUri) {
+
+ $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM cards WHERE addressbookid = ? AND uri = ? LIMIT 1');
+ $stmt->execute(array($addressBookId, $cardUri));
+
+ $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ return (count($result)>0?$result[0]:false);
+
+ }
+
+ /**
+ * Creates a new card
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @param string $cardData
+ * @return bool
+ */
+ public function createCard($addressBookId, $cardUri, $cardData) {
+
+ $stmt = $this->pdo->prepare('INSERT INTO cards (carddata, uri, lastmodified, addressbookid) VALUES (?, ?, ?, ?)');
+
+ $result = $stmt->execute(array($cardData, $cardUri, time(), $addressBookId));
+
+ $stmt2 = $this->pdo->prepare('UPDATE addressbooks SET ctag = ctag + 1 WHERE id = ?');
+ $stmt2->execute(array($addressBookId));
+
+ return $result;
+
+ }
+
+ /**
+ * Updates a card
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @param string $cardData
+ * @return bool
+ */
+ public function updateCard($addressBookId, $cardUri, $cardData) {
+
+ $stmt = $this->pdo->prepare('UPDATE cards SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?');
+ $result = $stmt->execute(array($cardData, time(), $cardUri, $addressBookId));
+
+ $stmt2 = $this->pdo->prepare('UPDATE addressbooks SET ctag = ctag + 1 WHERE id = ?');
+ $stmt2->execute(array($addressBookId));
+
+ return $stmt->rowCount()===1;
+
+ }
+
+ /**
+ * Deletes a card
+ *
+ * @param mixed $addressBookId
+ * @param string $cardUri
+ * @return bool
+ */
+ public function deleteCard($addressBookId, $cardUri) {
+
+ $stmt = $this->pdo->prepare('DELETE FROM cards WHERE addressbookid = ? AND uri = ?');
+ $stmt->execute(array($addressBookId, $cardUri));
+
+ $stmt2 = $this->pdo->prepare('UPDATE addressbooks SET ctag = ctag + 1 WHERE id = ?');
+ $stmt2->execute(array($addressBookId));
+
+ return $stmt->rowCount()===1;
+
+ }
+}
diff --git a/3dparty/Sabre/CardDAV/Card.php b/3dparty/Sabre/CardDAV/Card.php
new file mode 100644
index 00000000000..a12e6d3914b
--- /dev/null
+++ b/3dparty/Sabre/CardDAV/Card.php
@@ -0,0 +1,151 @@
+<?php
+
+/**
+ * Card class
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ *
+ /
+/**
+ * The Card object represents a single Card from an addressbook
+ */
+class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard {
+
+ /**
+ * CardDAV backend
+ *
+ * @var Sabre_CardDAV_Backend_Abstract
+ */
+ private $carddavBackend;
+
+ /**
+ * Array with information about this Card
+ *
+ * @var array
+ */
+ private $cardData;
+
+ /**
+ * Array with information about the containing addressbook
+ *
+ * @var array
+ */
+ private $addressBookInfo;
+
+ /**
+ * Constructor
+ *
+ * @param Sabre_CardDAV_Backend_Abstract $carddavBackend
+ * @param array $addressBookInfo
+ * @param array $cardData
+ */
+ public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend,array $addressBookInfo,array $cardData) {
+
+ $this->carddavBackend = $carddavBackend;
+ $this->addressBookInfo = $addressBookInfo;
+ $this->cardData = $cardData;
+
+ }
+
+ /**
+ * Returns the uri for this object
+ *
+ * @return string
+ */
+ public function getName() {
+
+ return $this->cardData['uri'];
+
+ }
+
+ /**
+ * Returns the VCard-formatted object
+ *
+ * @return string
+ */
+ public function get() {
+
+ $cardData = $this->cardData['carddata'];
+ $s = fopen('php://temp','r+');
+ fwrite($s, $cardData);
+ rewind($s);
+ return $s;
+
+ }
+
+ /**
+ * Updates the VCard-formatted object
+ *
+ * @param string $cardData
+ * @return void
+ */
+ public function put($cardData) {
+
+ if (is_resource($cardData))
+ $cardData = stream_get_contents($cardData);
+
+ $this->carddavBackend->updateCard($this->addressBookInfo['id'],$this->cardData['uri'],$cardData);
+ $this->cardData['carddata'] = $cardData;
+
+ }
+
+ /**
+ * Deletes the card
+ *
+ * @return void
+ */
+ public function delete() {
+
+ $this->carddavBackend->deleteCard($this->addressBookInfo['id'],$this->cardData['uri']);
+
+ }
+
+ /**
+ * Returns the mime content-type
+ *
+ * @return string
+ */
+ public function getContentType() {
+
+ return 'text/x-vcard';
+
+ }
+
+ /**
+ * Returns an ETag for this object
+ *
+ * @return string
+ */
+ public function getETag() {
+
+ return md5($this->cardData['carddata']);
+
+ }
+
+ /**
+ * Returns the last modification date as a unix timestamp
+ *
+ * @return time
+ */
+ public function getLastModified() {
+
+ return isset($this->cardData['lastmodified'])?$this->cardData['lastmodified']:null;
+
+ }
+
+ /**
+ * Returns the size of this object in bytes
+ *
+ * @return int
+ */
+ public function getSize() {
+
+ return strlen($this->cardData['carddata']);
+
+ }
+}
+
diff --git a/3dparty/Sabre/CardDAV/IAddressBook.php b/3dparty/Sabre/CardDAV/IAddressBook.php
new file mode 100644
index 00000000000..a0dffb30aea
--- /dev/null
+++ b/3dparty/Sabre/CardDAV/IAddressBook.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * AddressBook interface
+ *
+ * Implement this interface to allow a node to be recognized as an addressbook.
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_CardDAV_IAddressBook extends Sabre_DAV_ICollection {
+
+
+
+}
diff --git a/3dparty/Sabre/CardDAV/ICard.php b/3dparty/Sabre/CardDAV/ICard.php
new file mode 100644
index 00000000000..8f9bb097b56
--- /dev/null
+++ b/3dparty/Sabre/CardDAV/ICard.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * Card interface
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ *
+ /
+/**
+ * Extend the ICard interface to allow your custom nodes to be picked up as
+ * 'Cards'.
+ */
+interface Sabre_CardDAV_ICard extends Sabre_DAV_IFile {
+
+}
+
diff --git a/3dparty/Sabre/CardDAV/IDirectory.php b/3dparty/Sabre/CardDAV/IDirectory.php
new file mode 100644
index 00000000000..e0d0797d285
--- /dev/null
+++ b/3dparty/Sabre/CardDAV/IDirectory.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * IDirectory interface
+ *
+ * Implement this interface to have an addressbook marked as a 'directory'. A
+ * directory is an (often) global addressbook.
+ *
+ * A full description can be found in the IETF draft:
+ * - draft-daboo-carddav-directory-gateway
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_CardDAV_IDirectory extends Sabre_CardDAV_IAddressBook {
+
+
+}
diff --git a/3dparty/Sabre/CardDAV/Plugin.php b/3dparty/Sabre/CardDAV/Plugin.php
new file mode 100644
index 00000000000..16fadc526e4
--- /dev/null
+++ b/3dparty/Sabre/CardDAV/Plugin.php
@@ -0,0 +1,465 @@
+<?php
+
+/**
+ * CardDAV plugin
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+
+/**
+ * The CardDAV plugin adds CardDAV functionality to the WebDAV server
+ */
+class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
+
+ /**
+ * Url to the addressbooks
+ */
+ const ADDRESSBOOK_ROOT = 'addressbooks';
+
+ /**
+ * xml namespace for CardDAV elements
+ */
+ const NS_CARDDAV = 'urn:ietf:params:xml:ns:carddav';
+
+ /**
+ * Add urls to this property to have them automatically exposed as
+ * 'directories' to the user.
+ *
+ * @var array
+ */
+ public $directories = array();
+
+ /**
+ * Server class
+ *
+ * @var Sabre_DAV_Server
+ */
+ protected $server;
+
+ /**
+ * Initializes the plugin
+ *
+ * @param Sabre_DAV_Server $server
+ * @return void
+ */
+ public function initialize(Sabre_DAV_Server $server) {
+
+ /* Events */
+ $server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties'));
+ $server->subscribeEvent('report', array($this,'report'));
+
+ /* Namespaces */
+ $server->xmlNamespaces[self::NS_CARDDAV] = 'card';
+
+ /* Mapping Interfaces to {DAV:}resourcetype values */
+ $server->resourceTypeMapping['Sabre_CardDAV_IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook';
+ $server->resourceTypeMapping['Sabre_CardDAV_IDirectory'] = '{' . self::NS_CARDDAV . '}directory';
+
+ /* Adding properties that may never be changed */
+ $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-address-data';
+ $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}max-resource-size';
+
+
+ $this->server = $server;
+
+ }
+
+ /**
+ * Returns a list of supported features.
+ *
+ * This is used in the DAV: header in the OPTIONS and PROPFIND requests.
+ *
+ * @return array
+ */
+ public function getFeatures() {
+
+ return array('addressbook');
+
+ }
+
+ /**
+ * Returns a list of reports this plugin supports.
+ *
+ * This will be used in the {DAV:}supported-report-set property.
+ * Note that you still need to subscribe to the 'report' event to actually
+ * implement them
+ *
+ * @param string $uri
+ * @return array
+ */
+ public function getSupportedReportSet($uri) {
+
+ $node = $this->server->tree->getNodeForPath($uri);
+ if ($node instanceof Sabre_CardDAV_AddressBook || $node instanceof Sabre_CardDAV_ICard) {
+ return array(
+ '{' . self::NS_CARDDAV . '}addressbook-multiget',
+ );
+ }
+ return array();
+
+ }
+
+
+ /**
+ * Adds all CardDAV-specific properties
+ *
+ * @param string $path
+ * @param Sabre_DAV_INode $node
+ * @param array $requestedProperties
+ * @param array $returnedProperties
+ * @return void
+ */
+ public function beforeGetProperties($path, Sabre_DAV_INode $node, array &$requestedProperties, array &$returnedProperties) {
+
+ if ($node instanceof Sabre_DAVACL_IPrincipal) {
+
+ // calendar-home-set property
+ $addHome = '{' . self::NS_CARDDAV . '}addressbook-home-set';
+ if (in_array($addHome,$requestedProperties)) {
+ $principalId = $node->getName();
+ $addressbookHomePath = self::ADDRESSBOOK_ROOT . '/' . $principalId . '/';
+ unset($requestedProperties[array_search($addHome, $requestedProperties)]);
+ $returnedProperties[200][$addHome] = new Sabre_DAV_Property_Href($addressbookHomePath);
+ }
+
+ $directories = '{' . self::NS_CARDDAV . '}directory-gateway';
+ if ($this->directories && in_array($directories, $requestedProperties)) {
+ unset($requestedProperties[array_search($directories, $requestedProperties)]);
+ $returnedProperties[200][$directories] = new Sabre_DAV_Property_HrefList($this->directories);
+ }
+
+ }
+
+ if ($node instanceof Sabre_CardDAV_ICard) {
+
+ // The address-data property is not supposed to be a 'real'
+ // property, but in large chunks of the spec it does act as such.
+ // Therefore we simply expose it as a property.
+ $addressDataProp = '{' . self::NS_CARDDAV . '}address-data';
+ if (in_array($addressDataProp, $requestedProperties)) {
+ unset($requestedProperties[$addressDataProp]);
+ $val = $node->get();
+ if (is_resource($val))
+ $val = stream_get_contents($val);
+
+ // Taking out \r to not screw up the xml output
+ $returnedProperties[200][$addressDataProp] = str_replace("\r","", $val);
+
+ }
+ }
+
+ }
+
+ /**
+ * This functions handles REPORT requests specific to CardDAV
+ *
+ * @param string $reportName
+ * @param DOMNode $dom
+ * @return bool
+ */
+ public function report($reportName,$dom) {
+
+ switch($reportName) {
+ case '{'.self::NS_CARDDAV.'}addressbook-multiget' :
+ $this->addressbookMultiGetReport($dom);
+ return false;
+ case '{'.self::NS_CARDDAV.'}addressbook-query' :
+ $this->addressBookQueryReport($dom);
+ return false;
+ default :
+ return;
+
+ }
+
+
+ }
+
+ /**
+ * This function handles the addressbook-multiget REPORT.
+ *
+ * This report is used by the client to fetch the content of a series
+ * of urls. Effectively avoiding a lot of redundant requests.
+ *
+ * @param DOMNode $dom
+ * @return void
+ */
+ public function addressbookMultiGetReport($dom) {
+
+ $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
+
+ $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href');
+ $propertyList = array();
+
+ foreach($hrefElems as $elem) {
+
+ $uri = $this->server->calculateUri($elem->nodeValue);
+ list($propertyList[]) = $this->server->getPropertiesForPath($uri,$properties);
+
+ }
+
+ $this->server->httpResponse->sendStatus(207);
+ $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+ $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList));
+
+ }
+
+ /**
+ * This function handles the addressbook-query REPORT
+ *
+ * This report is used by the client to filter an addressbook based on a
+ * complex query.
+ *
+ * @param DOMNode $dom
+ * @return void
+ */
+ protected function addressbookQueryReport($dom) {
+
+ $query = new Sabre_CardDAV_AddressBookQueryParser($dom);
+ $query->parse();
+
+ $depth = $this->server->getHTTPDepth(0);
+
+ if ($depth==0) {
+ $candidateNodes = array(
+ $this->server->tree->getNodeForPath($this->server->getRequestUri())
+ );
+ } else {
+ $candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri());
+ }
+
+ $validNodes = array();
+ foreach($candidateNodes as $node) {
+
+ if (!$node instanceof Sabre_CardDAV_ICard)
+ continue;
+
+ $blob = $node->get();
+ if (is_resource($blob)) {
+ $blob = stream_get_contents($blob);
+ }
+
+ if (!$this->validateFilters($blob, $query->filters, $query->test)) {
+ continue;
+ }
+
+ $validNodes[] = $node;
+
+ if ($query->limit && $query->limit <= count($validNodes)) {
+ // We hit the maximum number of items, we can stop now.
+ break;
+ }
+
+ }
+
+ $result = array();
+ foreach($validNodes as $validNode) {
+ if ($depth==0) {
+ $href = $this->server->getRequestUri();
+ } else {
+ $href = $this->server->getRequestUri() . '/' . $validNode->getName();
+ }
+
+ list($result[]) = $this->server->getPropertiesForPath($href, $query->requestedProperties, 0);
+
+ }
+
+ $this->server->httpResponse->sendStatus(207);
+ $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+ $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result));
+
+ }
+
+ /**
+ * Validates if a vcard makes it throught a list of filters.
+ *
+ * @param string $vcardData
+ * @param array $filters
+ * @param string $test anyof or allof (which means OR or AND)
+ * @return bool
+ */
+ public function validateFilters($vcardData, array $filters, $test) {
+
+ $vcard = Sabre_VObject_Reader::read($vcardData);
+
+ $success = true;
+
+ foreach($filters as $filter) {
+
+ $isDefined = isset($vcard->{$filter['name']});
+ if ($filter['is-not-defined']) {
+ if ($isDefined) {
+ $success = false;
+ } else {
+ $success = true;
+ }
+ } elseif ((!$filter['param-filters'] && !$filter['text-matches']) || !$isDefined) {
+
+ // We only need to check for existence
+ $success = $isDefined;
+
+ } else {
+
+ $vProperties = $vcard->select($filter['name']);
+
+ $results = array();
+ if ($filter['param-filters']) {
+ $results[] = $this->validateParamFilters($vProperties, $filter['param-filters'], $filter['test']);
+ }
+ if ($filter['text-matches']) {
+ $texts = array();
+ foreach($vProperties as $vProperty)
+ $texts[] = $vProperty->value;
+
+ $results[] = $this->validateTextMatches($texts, $filter['text-matches'], $filter['test']);
+ }
+
+ if (count($results)===1) {
+ $success = $results[0];
+ } else {
+ if ($filter['test'] === 'anyof') {
+ $success = $results[0] || $results[1];
+ } else {
+ $success = $results[0] && $results[1];
+ }
+ }
+
+ } // else
+
+ // There are two conditions where we can already determine wether
+ // or not this filter succeeds.
+ if ($test==='anyof' && $success) {
+ return true;
+ }
+ if ($test==='allof' && !$success) {
+ return false;
+ }
+
+ } // foreach
+
+ // If we got all the way here, it means we haven't been able to
+ // determine early if the test failed or not.
+ //
+ // This implies for 'anyof' that the test failed, and for 'allof' that
+ // we succeeded. Sounds weird, but makes sense.
+ return $test==='allof';
+
+ }
+
+ /**
+ * Validates if a param-filter can be applied to a specific property.
+ *
+ * @todo currently we're only validating the first parameter of the passed
+ * property. Any subsequence parameters with the same name are
+ * ignored.
+ * @param Sabre_VObject_Property $vProperty
+ * @param array $filters
+ * @param string $test
+ * @return bool
+ */
+ protected function validateParamFilters(array $vProperties, array $filters, $test) {
+
+ $success = false;
+ foreach($filters as $filter) {
+
+ $isDefined = false;
+ foreach($vProperties as $vProperty) {
+ $isDefined = isset($vProperty[$filter['name']]);
+ if ($isDefined) break;
+ }
+
+ if ($filter['is-not-defined']) {
+ if ($isDefined) {
+ $success = false;
+ } else {
+ $success = true;
+ }
+
+ // If there's no text-match, we can just check for existence
+ } elseif (!$filter['text-match'] || !$isDefined) {
+
+ $success = $isDefined;
+
+ } else {
+
+ $texts = array();
+ $success = false;
+ foreach($vProperties as $vProperty) {
+ // If we got all the way here, we'll need to validate the
+ // text-match filter.
+ $success = Sabre_DAV_StringUtil::textMatch($vProperty[$filter['name']]->value, $filter['text-match']['value'], $filter['text-match']['collation'], $filter['text-match']['matchType']);
+ if ($success) break;
+ }
+ if ($filter['text-match']['negate-condition']) {
+ $success = !$success;
+ }
+
+ } // else
+
+ // There are two conditions where we can already determine wether
+ // or not this filter succeeds.
+ if ($test==='anyof' && $success) {
+ return true;
+ }
+ if ($test==='allof' && !$success) {
+ return false;
+ }
+
+ }
+
+ // If we got all the way here, it means we haven't been able to
+ // determine early if the test failed or not.
+ //
+ // This implies for 'anyof' that the test failed, and for 'allof' that
+ // we succeeded. Sounds weird, but makes sense.
+ return $test==='allof';
+
+ }
+
+ /**
+ * Validates if a text-filter can be applied to a specific property.
+ *
+ * @param array $texts
+ * @param array $filters
+ * @param string $test
+ * @return bool
+ */
+ protected function validateTextMatches(array $texts, array $filters, $test) {
+
+ foreach($filters as $filter) {
+
+ $success = false;
+ foreach($texts as $haystack) {
+ $success = Sabre_DAV_StringUtil::textMatch($haystack, $filter['value'], $filter['collation'], $filter['matchType']);
+
+ // Breaking on the first match
+ if ($success) break;
+ }
+ if ($filter['negate-condition']) {
+ $success = !$success;
+ }
+
+ if ($success && $test==='anyof')
+ return true;
+
+ if (!$success && $test=='allof')
+ return false;
+
+
+ }
+
+ // If we got all the way here, it means we haven't been able to
+ // determine early if the test failed or not.
+ //
+ // This implies for 'anyof' that the test failed, and for 'allof' that
+ // we succeeded. Sounds weird, but makes sense.
+ return $test==='allof';
+
+ }
+
+
+}
diff --git a/3dparty/Sabre/CardDAV/UserAddressBooks.php b/3dparty/Sabre/CardDAV/UserAddressBooks.php
new file mode 100644
index 00000000000..186bf016a1e
--- /dev/null
+++ b/3dparty/Sabre/CardDAV/UserAddressBooks.php
@@ -0,0 +1,171 @@
+<?php
+
+/**
+ * UserAddressBooks class
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * The UserAddressBooks collection contains a list of addressbooks associated with a user
+ */
+class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Directory implements Sabre_DAV_IExtendedCollection {
+
+ /**
+ * Principal uri
+ *
+ * @var array
+ */
+ protected $principalUri;
+
+ /**
+ * carddavBackend
+ *
+ * @var Sabre_CardDAV_Backend_Abstract
+ */
+ protected $carddavBackend;
+
+ /**
+ * Constructor
+ *
+ * @param Sabre_CardDAV_Backend_Abstract $carddavBackend
+ * @param string $principalUri
+ */
+ public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend, $principalUri) {
+
+ $this->carddavBackend = $carddavBackend;
+ $this->principalUri = $principalUri;
+
+ }
+
+ /**
+ * Returns the name of this object
+ *
+ * @return string
+ */
+ public function getName() {
+
+ list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalUri);
+ return $name;
+
+ }
+
+ /**
+ * Updates the name of this object
+ *
+ * @param string $name
+ * @return void
+ */
+ public function setName($name) {
+
+ throw new Sabre_DAV_Exception_MethodNotAllowed();
+
+ }
+
+ /**
+ * Deletes this object
+ *
+ * @return void
+ */
+ public function delete() {
+
+ throw new Sabre_DAV_Exception_MethodNotAllowed();
+
+ }
+
+ /**
+ * Returns the last modification date
+ *
+ * @return int
+ */
+ public function getLastModified() {
+
+ return null;
+
+ }
+
+ /**
+ * Creates a new file under this object.
+ *
+ * This is currently not allowed
+ *
+ * @param string $filename
+ * @param resource $data
+ * @return void
+ */
+ public function createFile($filename, $data=null) {
+
+ throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported');
+
+ }
+
+ /**
+ * Creates a new directory under this object.
+ *
+ * This is currently not allowed.
+ *
+ * @param string $filename
+ * @return void
+ */
+ public function createDirectory($filename) {
+
+ throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported');
+
+ }
+
+ /**
+ * Returns a single calendar, by name
+ *
+ * @param string $name
+ * @todo needs optimizing
+ * @return Sabre_CardDAV_AddressBook
+ */
+ public function getChild($name) {
+
+ foreach($this->getChildren() as $child) {
+ if ($name==$child->getName())
+ return $child;
+
+ }
+ throw new Sabre_DAV_Exception_FileNotFound('Addressbook with name \'' . $name . '\' could not be found');
+
+ }
+
+ /**
+ * Returns a list of addressbooks
+ *
+ * @return array
+ */
+ public function getChildren() {
+
+ $addressbooks = $this->carddavBackend->getAddressbooksForUser($this->principalUri);
+ $objs = array();
+ foreach($addressbooks as $addressbook) {
+ $objs[] = new Sabre_CardDAV_AddressBook($this->carddavBackend, $addressbook);
+ }
+ return $objs;
+
+ }
+
+ /**
+ * Creates a new addressbook
+ *
+ * @param string $name
+ * @param array $resourceType
+ * @param array $properties
+ * @return void
+ */
+ public function createExtendedCollection($name, array $resourceType, array $properties) {
+
+ if (!in_array('{'.Sabre_CardDAV_Plugin::NS_CARDDAV.'}addressbook',$resourceType) || count($resourceType)!==2) {
+ throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection');
+ }
+ $this->carddavBackend->createAddressBook($this->principalUri, $name, $properties);
+
+ }
+
+}
diff --git a/3dparty/Sabre/CardDAV/Version.php b/3dparty/Sabre/CardDAV/Version.php
new file mode 100644
index 00000000000..8961027fc89
--- /dev/null
+++ b/3dparty/Sabre/CardDAV/Version.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * Version Class
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * This class contains the Sabre_CardDAV version information
+ */
+class Sabre_CardDAV_Version {
+
+ /**
+ * Full version number
+ */
+ const VERSION = '0.2';
+
+ /**
+ * Stability : alpha, beta, stable
+ */
+ const STABILITY = 'alpha';
+
+}