Blame view

sources/3rdparty/sabre/dav/lib/Sabre/CalDAV/CalendarQueryParser.php 8.49 KB
03e52840d   Kload   Init
1
  <?php
6d9380f96   Cédric Dupont   Update sources OC...
2
  namespace Sabre\CalDAV;
03e52840d   Kload   Init
3
4
5
6
7
8
9
10
  use Sabre\VObject;
  
  /**
   * Parses the calendar-query report request body.
   *
   * Whoever designed this format, and the CalDAV equivalent even more so,
   * has no feel for design.
   *
6d9380f96   Cédric Dupont   Update sources OC...
11
12
13
   * @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
   * @author Evert Pot (http://evertpot.com/)
   * @license http://sabre.io/license/ Modified BSD License
03e52840d   Kload   Init
14
   */
6d9380f96   Cédric Dupont   Update sources OC...
15
  class CalendarQueryParser {
03e52840d   Kload   Init
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  
      /**
       * List of requested properties the client wanted
       *
       * @var array
       */
      public $requestedProperties;
  
      /**
       * List of property/component filters.
       *
       * @var array
       */
      public $filters;
  
      /**
6d9380f96   Cédric Dupont   Update sources OC...
32
33
       * This property will contain null if CALDAV:expand was not specified,
       * otherwise it will contain an array with 2 elements (start, end). Each
03e52840d   Kload   Init
34
35
       * contain a DateTime object.
       *
6d9380f96   Cédric Dupont   Update sources OC...
36
37
       * If expand is specified, recurring calendar objects are to be expanded
       * into their individual components, and only the components that fall
03e52840d   Kload   Init
38
39
40
       * within the specified time-range are to be returned.
       *
       * For more details, see rfc4791, section 9.6.5.
6d9380f96   Cédric Dupont   Update sources OC...
41
42
       *
       * @var null|array
03e52840d   Kload   Init
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
       */
      public $expand;
  
      /**
       * DOM Document
       *
       * @var DOMDocument
       */
      protected $dom;
  
      /**
       * DOM XPath object
       *
       * @var DOMXPath
       */
      protected $xpath;
  
      /**
       * Creates the parser
       *
6d9380f96   Cédric Dupont   Update sources OC...
63
       * @param \DOMDocument $dom
03e52840d   Kload   Init
64
       */
6d9380f96   Cédric Dupont   Update sources OC...
65
      public function __construct(\DOMDocument $dom) {
03e52840d   Kload   Init
66
67
  
          $this->dom = $dom;
6d9380f96   Cédric Dupont   Update sources OC...
68
69
          $this->xpath = new \DOMXPath($dom);
          $this->xpath->registerNameSpace('cal',Plugin::NS_CALDAV);
03e52840d   Kload   Init
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
          $this->xpath->registerNameSpace('dav','urn:DAV');
  
      }
  
      /**
       * Parses the request.
       *
       * @return void
       */
      public function parse() {
  
          $filterNode = null;
  
          $filter = $this->xpath->query('/cal:calendar-query/cal:filter');
          if ($filter->length !== 1) {
6d9380f96   Cédric Dupont   Update sources OC...
85
              throw new \Sabre\DAV\Exception\BadRequest('Only one filter element is allowed');
03e52840d   Kload   Init
86
87
88
89
          }
  
          $compFilters = $this->parseCompFilters($filter->item(0));
          if (count($compFilters)!==1) {
6d9380f96   Cédric Dupont   Update sources OC...
90
              throw new \Sabre\DAV\Exception\BadRequest('There must be exactly 1 top-level comp-filter.');
03e52840d   Kload   Init
91
92
93
          }
  
          $this->filters = $compFilters[0];
6d9380f96   Cédric Dupont   Update sources OC...
94
          $this->requestedProperties = array_keys(\Sabre\DAV\XMLUtil::parseProperties($this->dom->firstChild));
03e52840d   Kload   Init
95
96
97
98
99
  
          $expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand');
          if ($expand->length>0) {
              $this->expand = $this->parseExpand($expand->item(0));
          }
6d9380f96   Cédric Dupont   Update sources OC...
100

03e52840d   Kload   Init
101
102
103
104
105
106
  
      }
  
      /**
       * Parses all the 'comp-filter' elements from a node
       *
6d9380f96   Cédric Dupont   Update sources OC...
107
       * @param \DOMElement $parentNode
03e52840d   Kload   Init
108
109
       * @return array
       */
6d9380f96   Cédric Dupont   Update sources OC...
110
      protected function parseCompFilters(\DOMElement $parentNode) {
03e52840d   Kload   Init
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
  
          $compFilterNodes = $this->xpath->query('cal:comp-filter', $parentNode);
          $result = array();
  
          for($ii=0; $ii < $compFilterNodes->length; $ii++) {
  
              $compFilterNode = $compFilterNodes->item($ii);
  
              $compFilter = array();
              $compFilter['name'] = $compFilterNode->getAttribute('name');
              $compFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $compFilterNode)->length>0;
              $compFilter['comp-filters'] = $this->parseCompFilters($compFilterNode);
              $compFilter['prop-filters'] = $this->parsePropFilters($compFilterNode);
              $compFilter['time-range'] = $this->parseTimeRange($compFilterNode);
  
              if ($compFilter['time-range'] && !in_array($compFilter['name'],array(
                  'VEVENT',
                  'VTODO',
                  'VJOURNAL',
                  'VFREEBUSY',
                  'VALARM',
              ))) {
6d9380f96   Cédric Dupont   Update sources OC...
133
                  throw new \Sabre\DAV\Exception\BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component');
03e52840d   Kload   Init
134
135
136
137
138
139
140
141
142
143
144
145
146
              };
  
              $result[] = $compFilter;
  
          }
  
          return $result;
  
      }
  
      /**
       * Parses all the prop-filter elements from a node
       *
6d9380f96   Cédric Dupont   Update sources OC...
147
       * @param \DOMElement $parentNode
03e52840d   Kload   Init
148
149
       * @return array
       */
6d9380f96   Cédric Dupont   Update sources OC...
150
      protected function parsePropFilters(\DOMElement $parentNode) {
03e52840d   Kload   Init
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  
          $propFilterNodes = $this->xpath->query('cal:prop-filter', $parentNode);
          $result = array();
  
          for ($ii=0; $ii < $propFilterNodes->length; $ii++) {
  
              $propFilterNode = $propFilterNodes->item($ii);
              $propFilter = array();
              $propFilter['name'] = $propFilterNode->getAttribute('name');
              $propFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $propFilterNode)->length>0;
              $propFilter['param-filters'] = $this->parseParamFilters($propFilterNode);
              $propFilter['text-match'] = $this->parseTextMatch($propFilterNode);
              $propFilter['time-range'] = $this->parseTimeRange($propFilterNode);
  
              $result[] = $propFilter;
  
          }
  
          return $result;
  
      }
  
      /**
       * Parses the param-filter element
       *
6d9380f96   Cédric Dupont   Update sources OC...
176
       * @param \DOMElement $parentNode
03e52840d   Kload   Init
177
178
       * @return array
       */
6d9380f96   Cédric Dupont   Update sources OC...
179
      protected function parseParamFilters(\DOMElement $parentNode) {
03e52840d   Kload   Init
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
  
          $paramFilterNodes = $this->xpath->query('cal:param-filter', $parentNode);
          $result = array();
  
          for($ii=0;$ii<$paramFilterNodes->length;$ii++) {
  
              $paramFilterNode = $paramFilterNodes->item($ii);
              $paramFilter = array();
              $paramFilter['name'] = $paramFilterNode->getAttribute('name');
              $paramFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $paramFilterNode)->length>0;
              $paramFilter['text-match'] = $this->parseTextMatch($paramFilterNode);
  
              $result[] = $paramFilter;
  
          }
  
          return $result;
  
      }
  
      /**
       * Parses the text-match element
       *
6d9380f96   Cédric Dupont   Update sources OC...
203
       * @param \DOMElement $parentNode
03e52840d   Kload   Init
204
205
       * @return array|null
       */
6d9380f96   Cédric Dupont   Update sources OC...
206
      protected function parseTextMatch(\DOMElement $parentNode) {
03e52840d   Kload   Init
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
  
          $textMatchNodes = $this->xpath->query('cal:text-match', $parentNode);
  
          if ($textMatchNodes->length === 0)
              return null;
  
          $textMatchNode = $textMatchNodes->item(0);
          $negateCondition = $textMatchNode->getAttribute('negate-condition');
          $negateCondition = $negateCondition==='yes';
          $collation = $textMatchNode->getAttribute('collation');
          if (!$collation) $collation = 'i;ascii-casemap';
  
          return array(
              'negate-condition' => $negateCondition,
              'collation' => $collation,
              'value' => $textMatchNode->nodeValue
          );
  
      }
  
      /**
       * Parses the time-range element
       *
6d9380f96   Cédric Dupont   Update sources OC...
230
       * @param \DOMElement $parentNode
03e52840d   Kload   Init
231
232
       * @return array|null
       */
6d9380f96   Cédric Dupont   Update sources OC...
233
      protected function parseTimeRange(\DOMElement $parentNode) {
03e52840d   Kload   Init
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
  
          $timeRangeNodes = $this->xpath->query('cal:time-range', $parentNode);
          if ($timeRangeNodes->length === 0) {
              return null;
          }
  
          $timeRangeNode = $timeRangeNodes->item(0);
  
          if ($start = $timeRangeNode->getAttribute('start')) {
              $start = VObject\DateTimeParser::parseDateTime($start);
          } else {
              $start = null;
          }
          if ($end = $timeRangeNode->getAttribute('end')) {
              $end = VObject\DateTimeParser::parseDateTime($end);
          } else {
              $end = null;
          }
  
          if (!is_null($start) && !is_null($end) && $end <= $start) {
6d9380f96   Cédric Dupont   Update sources OC...
254
              throw new \Sabre\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the time-range filter');
03e52840d   Kload   Init
255
256
257
258
259
260
261
262
263
264
265
          }
  
          return array(
              'start' => $start,
              'end' => $end,
          );
  
      }
  
      /**
       * Parses the CALDAV:expand element
6d9380f96   Cédric Dupont   Update sources OC...
266
267
       *
       * @param \DOMElement $parentNode
03e52840d   Kload   Init
268
269
       * @return void
       */
6d9380f96   Cédric Dupont   Update sources OC...
270
      protected function parseExpand(\DOMElement $parentNode) {
03e52840d   Kload   Init
271
272
273
  
          $start = $parentNode->getAttribute('start');
          if(!$start) {
6d9380f96   Cédric Dupont   Update sources OC...
274
275
              throw new \Sabre\DAV\Exception\BadRequest('The "start" attribute is required for the CALDAV:expand element');
          }
03e52840d   Kload   Init
276
277
278
279
          $start = VObject\DateTimeParser::parseDateTime($start);
  
          $end = $parentNode->getAttribute('end');
          if(!$end) {
6d9380f96   Cédric Dupont   Update sources OC...
280
281
              throw new \Sabre\DAV\Exception\BadRequest('The "end" attribute is required for the CALDAV:expand element');
          }
03e52840d   Kload   Init
282
          $end = VObject\DateTimeParser::parseDateTime($end);
6d9380f96   Cédric Dupont   Update sources OC...
283

03e52840d   Kload   Init
284
          if ($end <= $start) {
6d9380f96   Cédric Dupont   Update sources OC...
285
              throw new \Sabre\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the expand element.');
03e52840d   Kload   Init
286
287
288
289
290
291
292
293
294
295
          }
  
          return array(
              'start' => $start,
              'end' => $end,
          );
  
      }
  
  }