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