]> git.mxchange.org Git - friendica-addons.git/blob - dav/SabreDAV/lib/Sabre/CardDAV/AddressBookQueryParser.php
46bb8ff18dd48847ae94bec334d605a96b021962
[friendica-addons.git] / dav / SabreDAV / lib / Sabre / CardDAV / AddressBookQueryParser.php
1 <?php
2
3 /**
4  * Parses the addressbook-query report request body.
5  *
6  * Whoever designed this format, and the CalDAV equivalent even more so,
7  * has no feel for design.
8  *
9  * @package Sabre
10  * @subpackage CardDAV
11  * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
12  * @author Evert Pot (http://www.rooftopsolutions.nl/)
13  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
14  */
15 class Sabre_CardDAV_AddressBookQueryParser {
16
17     const TEST_ANYOF = 'anyof';
18     const TEST_ALLOF = 'allof';
19
20     /**
21      * List of requested properties the client wanted
22      *
23      * @var array
24      */
25     public $requestedProperties;
26
27     /**
28      * The number of results the client wants
29      *
30      * null means it wasn't specified, which in most cases means 'all results'.
31      *
32      * @var int|null
33      */
34     public $limit;
35
36     /**
37      * List of property filters.
38      *
39      * @var array
40      */
41     public $filters;
42
43     /**
44      * Either TEST_ANYOF or TEST_ALLOF
45      *
46      * @var string
47      */
48     public $test;
49
50     /**
51      * DOM Document
52      *
53      * @var DOMDocument
54      */
55     protected $dom;
56
57     /**
58      * DOM XPath object
59      *
60      * @var DOMXPath
61      */
62     protected $xpath;
63
64     /**
65      * Creates the parser
66      *
67      * @param DOMDocument $dom
68      */
69     public function __construct(DOMDocument $dom) {
70
71         $this->dom = $dom;
72
73         $this->xpath = new DOMXPath($dom);
74         $this->xpath->registerNameSpace('card',Sabre_CardDAV_Plugin::NS_CARDDAV);
75
76     }
77
78     /**
79      * Parses the request.
80      *
81      * @return void
82      */
83     public function parse() {
84
85         $filterNode = null;
86
87         $limit = $this->xpath->evaluate('number(/card:addressbook-query/card:limit/card:nresults)');
88         if (is_nan($limit)) $limit = null;
89
90         $filter = $this->xpath->query('/card:addressbook-query/card:filter');
91
92         // According to the CardDAV spec there needs to be exactly 1 filter
93         // element. However, KDE 4.8.2 contains a bug that will encode 0 filter
94         // elements, so this is a workaround for that.
95         //
96         // See: https://bugs.kde.org/show_bug.cgi?id=300047
97         if ($filter->length === 0) {
98             $test = null;
99             $filter = null;
100         } elseif ($filter->length === 1) {
101             $filter = $filter->item(0);
102             $test = $this->xpath->evaluate('string(@test)', $filter);
103         } else {
104             throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
105         }
106
107         if (!$test) $test = self::TEST_ANYOF;
108         if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) {
109             throw new Sabre_DAV_Exception_BadRequest('The test attribute must either hold "anyof" or "allof"');
110         }
111
112         $propFilters = array();
113
114         $propFilterNodes = $this->xpath->query('card:prop-filter', $filter);
115         for($ii=0; $ii < $propFilterNodes->length; $ii++) {
116
117             $propFilters[] = $this->parsePropFilterNode($propFilterNodes->item($ii));
118
119
120         }
121
122         $this->filters = $propFilters;
123         $this->limit = $limit;
124         $this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild));
125         $this->test = $test;
126
127     }
128
129     /**
130      * Parses the prop-filter xml element
131      *
132      * @param DOMElement $propFilterNode
133      * @return array
134      */
135     protected function parsePropFilterNode(DOMElement $propFilterNode) {
136
137         $propFilter = array();
138         $propFilter['name'] = $propFilterNode->getAttribute('name');
139         $propFilter['test'] = $propFilterNode->getAttribute('test');
140         if (!$propFilter['test']) $propFilter['test'] = 'anyof';
141
142         $propFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $propFilterNode)->length>0;
143
144         $paramFilterNodes = $this->xpath->query('card:param-filter', $propFilterNode);
145
146         $propFilter['param-filters'] = array();
147
148
149         for($ii=0;$ii<$paramFilterNodes->length;$ii++) {
150
151             $propFilter['param-filters'][] = $this->parseParamFilterNode($paramFilterNodes->item($ii));
152
153         }
154         $propFilter['text-matches'] = array();
155         $textMatchNodes = $this->xpath->query('card:text-match', $propFilterNode);
156
157         for($ii=0;$ii<$textMatchNodes->length;$ii++) {
158
159             $propFilter['text-matches'][] = $this->parseTextMatchNode($textMatchNodes->item($ii));
160
161         }
162
163         return $propFilter;
164
165     }
166
167     /**
168      * Parses the param-filter element
169      *
170      * @param DOMElement $paramFilterNode
171      * @return array
172      */
173     public function parseParamFilterNode(DOMElement $paramFilterNode) {
174
175         $paramFilter = array();
176         $paramFilter['name'] = $paramFilterNode->getAttribute('name');
177         $paramFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $paramFilterNode)->length>0;
178         $paramFilter['text-match'] = null;
179
180         $textMatch = $this->xpath->query('card:text-match', $paramFilterNode);
181         if ($textMatch->length>0) {
182             $paramFilter['text-match'] = $this->parseTextMatchNode($textMatch->item(0));
183         }
184
185         return $paramFilter;
186
187     }
188
189     /**
190      * Text match
191      *
192      * @param DOMElement $textMatchNode
193      * @return array
194      */
195     public function parseTextMatchNode(DOMElement $textMatchNode) {
196
197         $matchType = $textMatchNode->getAttribute('match-type');
198         if (!$matchType) $matchType = 'contains';
199
200         if (!in_array($matchType, array('contains', 'equals', 'starts-with', 'ends-with'))) {
201             throw new Sabre_DAV_Exception_BadRequest('Unknown match-type: ' . $matchType);
202         }
203
204         $negateCondition = $textMatchNode->getAttribute('negate-condition');
205         $negateCondition = $negateCondition==='yes';
206         $collation = $textMatchNode->getAttribute('collation');
207         if (!$collation) $collation = 'i;unicode-casemap';
208
209         return array(
210             'negate-condition' => $negateCondition,
211             'collation' => $collation,
212             'match-type' => $matchType,
213             'value' => $textMatchNode->nodeValue
214         );
215
216
217     }
218
219 }