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