dom = $dom; $this->xpath = new \DOMXPath($dom); $this->xpath->registerNameSpace('card',Plugin::NS_CARDDAV); } /** * Parses the request. * * @return void */ public function parse() { $filterNode = null; $limit = $this->xpath->evaluate('number(/card:addressbook-query/card:limit/card:nresults)'); if (is_nan($limit)) $limit = null; $filter = $this->xpath->query('/card:addressbook-query/card:filter'); // According to the CardDAV spec there needs to be exactly 1 filter // element. However, KDE 4.8.2 contains a bug that will encode 0 filter // elements, so this is a workaround for that. // // See: https://bugs.kde.org/show_bug.cgi?id=300047 if ($filter->length === 0) { $test = null; $filter = null; } elseif ($filter->length === 1) { $filter = $filter->item(0); $test = $this->xpath->evaluate('string(@test)', $filter); } else { throw new DAV\Exception\BadRequest('Only one filter element is allowed'); } if (!$test) $test = self::TEST_ANYOF; if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) { throw new DAV\Exception\BadRequest('The test attribute must either hold "anyof" or "allof"'); } $propFilters = array(); $propFilterNodes = $this->xpath->query('card:prop-filter', $filter); for($ii=0; $ii < $propFilterNodes->length; $ii++) { $propFilters[] = $this->parsePropFilterNode($propFilterNodes->item($ii)); } $this->filters = $propFilters; $this->limit = $limit; $this->requestedProperties = array_keys(DAV\XMLUtil::parseProperties($this->dom->firstChild)); $this->test = $test; } /** * Parses the prop-filter xml element * * @param \DOMElement $propFilterNode * @return array */ protected function parsePropFilterNode(\DOMElement $propFilterNode) { $propFilter = array(); $propFilter['name'] = $propFilterNode->getAttribute('name'); $propFilter['test'] = $propFilterNode->getAttribute('test'); if (!$propFilter['test']) $propFilter['test'] = 'anyof'; $propFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $propFilterNode)->length>0; $paramFilterNodes = $this->xpath->query('card:param-filter', $propFilterNode); $propFilter['param-filters'] = array(); for($ii=0;$ii<$paramFilterNodes->length;$ii++) { $propFilter['param-filters'][] = $this->parseParamFilterNode($paramFilterNodes->item($ii)); } $propFilter['text-matches'] = array(); $textMatchNodes = $this->xpath->query('card:text-match', $propFilterNode); for($ii=0;$ii<$textMatchNodes->length;$ii++) { $propFilter['text-matches'][] = $this->parseTextMatchNode($textMatchNodes->item($ii)); } return $propFilter; } /** * Parses the param-filter element * * @param \DOMElement $paramFilterNode * @return array */ public function parseParamFilterNode(\DOMElement $paramFilterNode) { $paramFilter = array(); $paramFilter['name'] = $paramFilterNode->getAttribute('name'); $paramFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $paramFilterNode)->length>0; $paramFilter['text-match'] = null; $textMatch = $this->xpath->query('card:text-match', $paramFilterNode); if ($textMatch->length>0) { $paramFilter['text-match'] = $this->parseTextMatchNode($textMatch->item(0)); } return $paramFilter; } /** * Text match * * @param \DOMElement $textMatchNode * @return array */ public function parseTextMatchNode(\DOMElement $textMatchNode) { $matchType = $textMatchNode->getAttribute('match-type'); if (!$matchType) $matchType = 'contains'; if (!in_array($matchType, array('contains', 'equals', 'starts-with', 'ends-with'))) { throw new DAV\Exception\BadRequest('Unknown match-type: ' . $matchType); } $negateCondition = $textMatchNode->getAttribute('negate-condition'); $negateCondition = $negateCondition==='yes'; $collation = $textMatchNode->getAttribute('collation'); if (!$collation) $collation = 'i;unicode-casemap'; return array( 'negate-condition' => $negateCondition, 'collation' => $collation, 'match-type' => $matchType, 'value' => $textMatchNode->nodeValue ); } }