1<?php
2
3namespace Sabre\CardDAV\Xml\Request;
4
5use Sabre\Xml\Reader;
6use Sabre\Xml\XmlDeserializable;
7use Sabre\DAV\Exception\BadRequest;
8use Sabre\CardDAV\Plugin;
9
10/**
11 * AddressBookQueryReport request parser.
12 *
13 * This class parses the {urn:ietf:params:xml:ns:carddav}addressbook-query
14 * REPORT, as defined in:
15 *
16 * http://tools.ietf.org/html/rfc6352#section-8.6
17 *
18 * @copyright Copyright (C) 2007-2013 Rooftop Solutions. All rights reserved.
19 * @author Evert Pot (http://www.rooftopsolutions.nl/)
20 * @license http://sabre.io/license/ Modified BSD License
21 */
22class AddressBookQueryReport implements XmlDeserializable {
23
24    /**
25     * An array with requested properties.
26     *
27     * @var array
28     */
29    public $properties;
30
31    /**
32     * List of property/component filters.
33     *
34     * This is an array with filters. Every item is a property filter. Every
35     * property filter has the following keys:
36     *   * name - name of the component to filter on
37     *   * test - anyof or allof
38     *   * is-not-defined - Test for non-existence
39     *   * param-filters - A list of parameter filters on the property
40     *   * text-matches - A list of text values the filter needs to match
41     *
42     * Each param-filter has the following keys:
43     *   * name - name of the parameter
44     *   * is-not-defined - Test for non-existence
45     *   * text-match - Match the parameter value
46     *
47     * Each text-match in property filters, and the single text-match in
48     * param-filters have the following keys:
49     *
50     *   * value - value to match
51     *   * match-type - contains, starts-with, ends-with, equals
52     *   * negate-condition - Do the opposite match
53     *   * collation - Usually i;unicode-casemap
54     *
55     * @var array
56     */
57    public $filters;
58
59    /**
60     * The number of results the client wants
61     *
62     * null means it wasn't specified, which in most cases means 'all results'.
63     *
64     * @var int|null
65     */
66    public $limit;
67
68    /**
69     * Either 'anyof' or 'allof'
70     *
71     * @var string
72     */
73    public $test;
74
75    /**
76     * The mimetype of the content that should be returend. Usually
77     * text/vcard.
78     *
79     * @var string
80     */
81    public $contentType = null;
82
83    /**
84     * The version of vcard data that should be returned. Usually 3.0,
85     * referring to vCard 3.0.
86     *
87     * @var string
88     */
89    public $version = null;
90
91
92    /**
93     * The deserialize method is called during xml parsing.
94     *
95     * This method is called statictly, this is because in theory this method
96     * may be used as a type of constructor, or factory method.
97     *
98     * Often you want to return an instance of the current class, but you are
99     * free to return other data as well.
100     *
101     * You are responsible for advancing the reader to the next element. Not
102     * doing anything will result in a never-ending loop.
103     *
104     * If you just want to skip parsing for this element altogether, you can
105     * just call $reader->next();
106     *
107     * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
108     * the next element.
109     *
110     * @param Reader $reader
111     * @return mixed
112     */
113    static function xmlDeserialize(Reader $reader) {
114
115        $elems = (array)$reader->parseInnerTree([
116            '{urn:ietf:params:xml:ns:carddav}prop-filter'  => 'Sabre\\CardDAV\\Xml\\Filter\\PropFilter',
117            '{urn:ietf:params:xml:ns:carddav}param-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\ParamFilter',
118            '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData',
119            '{DAV:}prop'                                   => 'Sabre\\Xml\\Element\\KeyValue',
120        ]);
121
122        $newProps = [
123            'filters'    => null,
124            'properties' => [],
125            'test'       => 'anyof',
126            'limit'      => null,
127        ];
128
129        if (!is_array($elems)) $elems = [];
130
131        foreach ($elems as $elem) {
132
133            switch ($elem['name']) {
134
135                case '{DAV:}prop' :
136                    $newProps['properties'] = array_keys($elem['value']);
137                    if (isset($elem['value']['{' . Plugin::NS_CARDDAV . '}address-data'])) {
138                        $newProps += $elem['value']['{' . Plugin::NS_CARDDAV . '}address-data'];
139                    }
140                    break;
141                case '{' . Plugin::NS_CARDDAV . '}filter' :
142
143                    if (!is_null($newProps['filters'])) {
144                        throw new BadRequest('You can only include 1 {' . Plugin::NS_CARDDAV . '}filter element');
145                    }
146                    if (isset($elem['attributes']['test'])) {
147                        $newProps['test'] = $elem['attributes']['test'];
148                        if ($newProps['test'] !== 'allof' && $newProps['test'] !== 'anyof') {
149                            throw new BadRequest('The "test" attribute must be one of "allof" or "anyof"');
150                        }
151                    }
152
153                    $newProps['filters'] = [];
154                    foreach ((array)$elem['value'] as $subElem) {
155                        if ($subElem['name'] === '{' . Plugin::NS_CARDDAV . '}prop-filter') {
156                            $newProps['filters'][] = $subElem['value'];
157                        }
158                    }
159                    break;
160                case '{' . Plugin::NS_CARDDAV . '}limit' :
161                    foreach ($elem['value'] as $child) {
162                        if ($child['name'] === '{' . Plugin::NS_CARDDAV . '}nresults') {
163                            $newProps['limit'] = (int)$child['value'];
164                        }
165                    }
166                    break;
167
168            }
169
170        }
171
172        if (is_null($newProps['filters'])) {
173            /*
174             * We are supposed to throw this error, but KDE sometimes does not
175             * include the filter element, and we need to treat it as if no
176             * filters are supplied
177             */
178            //throw new BadRequest('The {' . Plugin::NS_CARDDAV . '}filter element is required for this request');
179            $newProps['filters'] = [];
180
181        }
182
183        $obj = new self();
184        foreach ($newProps as $key => $value) {
185            $obj->$key = $value;
186        }
187
188        return $obj;
189
190    }
191
192}
193