66 $server->on(
'propFind', [$this,
'propFindEarly']);
67 $server->on(
'propFind', [$this,
'propFindLate'], 150);
68 $server->on(
'report', [$this,
'report']);
69 $server->on(
'onHTMLActionsPanel', [$this,
'htmlActionsPanel']);
70 $server->on(
'beforeWriteContent', [$this,
'beforeWriteContent']);
71 $server->on(
'beforeCreateFile', [$this,
'beforeCreateFile']);
72 $server->on(
'afterMethod:GET', [$this,
'httpAfterGet']);
74 $server->xml->namespaceMap[self::NS_CARDDAV] =
'card';
76 $server->xml->elementMap[
'{' . self::NS_CARDDAV .
'}addressbook-query'] =
'Sabre\\CardDAV\\Xml\\Request\\AddressBookQueryReport';
77 $server->xml->elementMap[
'{' . self::NS_CARDDAV .
'}addressbook-multiget'] =
'Sabre\\CardDAV\\Xml\\Request\\AddressBookMultiGetReport';
80 $server->resourceTypeMapping[
'Sabre\\CardDAV\\IAddressBook'] =
'{' . self::NS_CARDDAV .
'}addressbook';
81 $server->resourceTypeMapping[
'Sabre\\CardDAV\\IDirectory'] =
'{' . self::NS_CARDDAV .
'}directory';
84 $server->protectedProperties[] =
'{' . self::NS_CARDDAV .
'}supported-address-data';
85 $server->protectedProperties[] =
'{' . self::NS_CARDDAV .
'}max-resource-size';
86 $server->protectedProperties[] =
'{' . self::NS_CARDDAV .
'}addressbook-home-set';
87 $server->protectedProperties[] =
'{' . self::NS_CARDDAV .
'}supported-collation-set';
89 $server->xml->elementMap[
'{http://calendarserver.org/ns/}me-card'] =
'Sabre\\DAV\\Xml\\Property\\Href';
104 return [
'addressbook'];
120 $node = $this->server->tree->getNodeForPath($uri);
123 '{' . self::NS_CARDDAV .
'}addressbook-multiget',
124 '{' . self::NS_CARDDAV .
'}addressbook-query',
141 $ns =
'{' . self::NS_CARDDAV .
'}';
145 $propFind->handle($ns .
'max-resource-size', $this->maxResourceSize);
146 $propFind->handle($ns .
'supported-address-data',
function() {
149 $propFind->handle($ns .
'supported-collation-set',
function() {
156 $path = $propFind->getPath();
158 $propFind->handle(
'{' . self::NS_CARDDAV .
'}addressbook-home-set',
function() use (
$path) {
159 return new LocalHref($this->getAddressBookHomeForPrincipal(
$path) .
'/');
162 if ($this->directories) $propFind->handle(
'{' . self::NS_CARDDAV .
'}directory-gateway',
function() {
163 return new LocalHref($this->directories);
168 if ($node instanceof
ICard) {
173 $propFind->handle(
'{' . self::NS_CARDDAV .
'}address-data',
function() use ($node) {
175 if (is_resource($val))
176 $val = stream_get_contents($val);
196 switch ($reportName) {
197 case '{' . self::NS_CARDDAV .
'}addressbook-multiget' :
198 $this->server->transactionType =
'report-addressbook-multiget';
201 case '{' . self::NS_CARDDAV .
'}addressbook-query' :
202 $this->server->transactionType =
'report-addressbook-query';
203 $this->addressBookQueryReport($dom);
222 return self::ADDRESSBOOK_ROOT .
'/' . $principalId;
250 [$this->server,
'calculateUri'],
253 foreach ($this->server->getPropertiesForMultiplePaths(
$paths, $report->properties) as $props) {
255 if (isset($props[
'200'][
'{' . self::NS_CARDDAV .
'}address-data'])) {
257 $props[
'200'][
'{' . self::NS_CARDDAV .
'}address-data'] = $this->
convertVCard(
258 $props[200][
'{' . self::NS_CARDDAV .
'}address-data'],
263 $propertyList[] = $props;
267 $prefer = $this->server->getHTTPPrefer();
269 $this->server->httpResponse->setStatus(207);
270 $this->server->httpResponse->setHeader(
'Content-Type',
'application/xml; charset=utf-8');
271 $this->server->httpResponse->setHeader(
'Vary',
'Brief,Prefer');
272 $this->server->httpResponse->setBody($this->server->generateMultiStatus($propertyList, $prefer[
'return'] ===
'minimal'));
291 if (!$node instanceof
ICard)
333 if (is_resource(
$data)) {
343 if (substr(
$data, 0, 1) ===
'[') {
356 throw new DAV\Exception\UnsupportedMediaType(
'This resource only supports valid vCard or jCard data. Parse error: ' . $e->getMessage());
360 if (
$vobj->name !==
'VCARD') {
361 throw new DAV\Exception\UnsupportedMediaType(
'This collection can only support vcard objects.');
365 $prefer = $this->server->getHTTPPrefer();
367 if ($prefer[
'handling'] !==
'strict') {
374 $warningMessage = null;
380 if ($message[
'level'] > $highestLevel) {
382 $highestLevel = $message[
'level'];
383 $warningMessage = $message[
'message'];
386 switch ($message[
'level']) {
397 throw new DAV\Exception\UnsupportedMediaType(
'Validation error in vCard: ' . $message[
'message']);
402 if ($warningMessage) {
403 $this->server->httpResponse->setHeader(
405 'vCard validation warning: ' . $warningMessage
410 if (!$modified && strcmp(
$data, $before) !== 0) {
432 $depth = $this->server->getHTTPDepth(0);
436 $this->server->tree->getNodeForPath($this->server->getRequestUri())
438 if (!$candidateNodes[0] instanceof
ICard) {
439 throw new ReportNotSupported(
'The addressbook-query report is not supported on this url with Depth: 0');
442 $candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri());
446 if ($report->version) {
455 foreach ($candidateNodes as $node) {
457 if (!$node instanceof
ICard)
460 $blob = $node->get();
461 if (is_resource($blob)) {
462 $blob = stream_get_contents($blob);
469 $validNodes[] = $node;
471 if ($report->limit && $report->limit <= count($validNodes)) {
479 foreach ($validNodes as $validNode) {
482 $href = $this->server->getRequestUri();
484 $href = $this->server->getRequestUri() .
'/' . $validNode->getName();
487 list($props) = $this->server->getPropertiesForPath($href, $report->properties, 0);
489 if (isset($props[200][
'{' . self::NS_CARDDAV .
'}address-data'])) {
491 $props[200][
'{' . self::NS_CARDDAV .
'}address-data'] = $this->
convertVCard(
492 $props[200][
'{' . self::NS_CARDDAV .
'}address-data'],
494 $report->addressDataProperties
502 $prefer = $this->server->getHTTPPrefer();
504 $this->server->httpResponse->setStatus(207);
505 $this->server->httpResponse->setHeader(
'Content-Type',
'application/xml; charset=utf-8');
506 $this->server->httpResponse->setHeader(
'Vary',
'Brief,Prefer');
507 $this->server->httpResponse->setBody($this->server->generateMultiStatus(
$result, $prefer[
'return'] ===
'minimal'));
522 if (!$filters)
return true;
525 foreach ($filters as $filter) {
527 $isDefined = isset($vcard->{$filter[
'name']});
528 if ($filter[
'is-not-defined']) {
534 } elseif ((!$filter[
'param-filters'] && !$filter[
'text-matches']) || !$isDefined) {
541 $vProperties = $vcard->select($filter[
'name']);
544 if ($filter[
'param-filters']) {
547 if ($filter[
'text-matches']) {
549 foreach ($vProperties as $vProperty)
550 $texts[] = $vProperty->getValue();
558 if ($filter[
'test'] ===
'anyof') {
595 return $test ===
'allof';
612 foreach ($filters as $filter) {
615 foreach ($vProperties as $vProperty) {
616 $isDefined = isset($vProperty[$filter[
'name']]);
617 if ($isDefined)
break;
620 if ($filter[
'is-not-defined']) {
628 } elseif (!$filter[
'text-match'] || !$isDefined) {
635 foreach ($vProperties as $vProperty) {
638 $success =
DAV\StringUtil::textMatch($vProperty[$filter[
'name']]->getValue(), $filter[
'text-match'][
'value'], $filter[
'text-match'][
'collation'], $filter[
'text-match'][
'match-type']);
641 if ($filter[
'text-match'][
'negate-condition']) {
663 return $test ===
'allof';
677 foreach ($filters as $filter) {
680 foreach ($texts as $haystack) {
686 if ($filter[
'negate-condition']) {
704 return $test ===
'allof';
724 if (strpos($this->server->httpRequest->getHeader(
'User-Agent'),
'Thunderbird') ===
false) {
729 if ($part ===
'text/x-vcard' || $part ===
'text/vcard') {
730 $propFind->set(
'{DAV:}getcontenttype',
'text/x-vcard');
749 $output .=
'<tr><td colspan="2"><form method="post" action=""> 750 <h3>Create new address book</h3> 751 <input type="hidden" name="sabreAction" value="mkcol" /> 752 <input type="hidden" name="resourceType" value="{DAV:}collection,{' . self::NS_CARDDAV .
'}addressbook" /> 753 <label>Name (uri):</label> <input type="text" name="name" /><br /> 754 <label>Display name:</label> <input type="text" name="{DAV:}displayname" /><br /> 755 <input type="submit" value="create" /> 774 if (strpos($response->
getHeader(
'Content-Type'),
'text/vcard') ===
false) {
786 $response->
setHeader(
'Content-Type', $mimeType .
'; charset=utf-8');
787 $response->
setHeader(
'Content-Length', strlen($newBody));
816 'text/vcard; version=4.0',
818 'text/vcard; version=3.0',
820 'application/vcard+json',
828 case 'text/x-vcard' :
830 case 'text/vcard; version=3.0' :
831 $mimeType =
'text/vcard';
833 case 'text/vcard; version=4.0' :
835 case 'application/vcard+json' :
854 if (is_resource(
$data)) {
858 if (!empty($propertiesFilter)) {
859 $propertiesFilter = array_merge([
'UID',
'VERSION',
'FN'], $propertiesFilter);
860 $keys = array_unique(array_map(
function($child) {
934 'description' =>
'Adds support for CardDAV (rfc6352)',
935 'link' =>
'http://sabre.io/dav/carddav/',
validateParamFilters(array $vProperties, array $filters, $test)
Validates if a param-filter can be applied to a specific property.
This interface represents a HTTP response.
The RequestInterface represents a HTTP request.
validateTextMatches(array $texts, array $filters, $test)
Validates if a text-filter can be applied to a specific property.
The baseclass for all server plugins.
setBody($body)
Updates the body resource with a new stream.
beforeCreateFile($path, &$data, DAV\ICollection $parentNode, &$modified)
This method is triggered before a new file is created.
foreach($paths as $path) $request
static negotiate($acceptHeaderValue, array $availableOptions)
Deprecated! Use negotiateContentType.
const NS_CARDDAV
xml namespace for CardDAV elements
negotiateVCard($input, &$mimeType=null)
This helper function performs the content-type negotiation for vcards.
getPluginName()
Returns a plugin name.
httpAfterGet(RequestInterface $request, ResponseInterface $response)
This event is triggered after GET requests.
This class holds all the information about a PROPFIND request.
getPluginInfo()
Returns a bunch of meta-data about the plugin.
propFindLate(DAV\PropFind $propFind, DAV\INode $node)
This event is triggered when fetching properties.
propFindEarly(DAV\PropFind $propFind, DAV\INode $node)
Adds all CardDAV-specific properties.
The ICollection Interface.
getSupportedReportSet($uri)
Returns a list of reports this plugin supports.
Supported-address-data property.
getAddressbookHomeForPrincipal($principal)
Returns the addressbook home for a given principal.
initialize(DAV\Server $server)
Initializes the plugin.
catch(Exception $e) $message
addressbookMultiGetReport($report)
This function handles the addressbook-multiget REPORT.
static textMatch($haystack, $needle, $collation, $matchType='contains')
Checks if a needle occurs in a haystack ;)
beforeWriteContent($path, DAV\IFile $node, &$data, &$modified)
This method is triggered before a file gets updated with new content.
$maxResourceSize
The default PDO storage uses a MySQL MEDIUMBLOB for iCalendar data, which can hold up to 2^24 = 16777...
validateFilters($vcardData, array $filters, $test)
Validates if a vcard makes it throught a list of filters.
supported-collation-set property
getHeader($name)
Returns a specific HTTP header, based on it's name.
This interface represents a file in the directory tree.
The INode interface is the base interface, and the parent class of both ICollection and IFile...
const ADDRESSBOOK_ROOT
Url to the addressbooks.
static readJson($data, $options=0)
Parses a jCard or jCal object, and returns the top component.
const REPAIR
The following constants are used by the validate() method.
static read($data, $options=0, $charset='UTF-8')
Parses a vCard or iCalendar object, and returns the top component.
validateVCard(&$data, &$modified)
Checks if the submitted iCalendar data is in fact, valid.
Exception thrown by Reader if an invalid object was attempted to be parsed.
if($path[strlen($path) - 1]==='/') if(is_dir($path)) if(!file_exists($path)) if(preg_match('#\.php$#D', mb_strtolower($path, 'UTF-8'))) $contentType
convertVCard($data, $target, array $propertiesFilter=null)
Converts a vcard blob to a different version, or jcard.
getFeatures()
Returns a list of supported features.
report($reportName, $dom, $path)
This functions handles REPORT requests specific to CardDAV.
static splitPath($path)
Returns the 'dirname' and 'basename' for a path.
setHeader($name, $value)
Updates a HTTP header.
const PROFILE_CARDDAV
If this option is set, the validator will operate on the vcards on the assumption that the vcards nee...
getBody()
Returns the message body, as it's internal representation.
htmlActionsPanel(DAV\INode $node, &$output)
This method is used to generate HTML output for the Sabre.
addressbookQueryReport($report)
This function handles the addressbook-query REPORT.