]> git.mxchange.org Git - friendica-addons.git/blob - dav/SabreDAV/lib/Sabre/DAV/XMLUtil.php
Heavily refactored, including multiple calendars per user and recurring events. Not...
[friendica-addons.git] / dav / SabreDAV / lib / Sabre / DAV / XMLUtil.php
1 <?php
2
3 /**
4  * XML utilities for WebDAV
5  *
6  * @package Sabre
7  * @subpackage DAV
8  * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
9  * @author Evert Pot (http://www.rooftopsolutions.nl/)
10  * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
11  */
12 class Sabre_DAV_XMLUtil {
13
14     /**
15      * Returns the 'clark notation' for an element.
16      *
17      * For example, and element encoded as:
18      * <b:myelem xmlns:b="http://www.example.org/" />
19      * will be returned as:
20      * {http://www.example.org}myelem
21      *
22      * This format is used throughout the SabreDAV sourcecode.
23      * Elements encoded with the urn:DAV namespace will
24      * be returned as if they were in the DAV: namespace. This is to avoid
25      * compatibility problems.
26      *
27      * This function will return null if a nodetype other than an Element is passed.
28      *
29      * @param DOMNode $dom
30      * @return string
31      */
32     static function toClarkNotation(DOMNode $dom) {
33
34         if ($dom->nodeType !== XML_ELEMENT_NODE) return null;
35
36         // Mapping back to the real namespace, in case it was dav
37         if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI;
38
39         // Mapping to clark notation
40         return '{' . $ns . '}' . $dom->localName;
41
42     }
43
44     /**
45      * Parses a clark-notation string, and returns the namespace and element
46      * name components.
47      *
48      * If the string was invalid, it will throw an InvalidArgumentException.
49      *
50      * @param string $str
51      * @throws InvalidArgumentException
52      * @return array
53      */
54     static function parseClarkNotation($str) {
55
56         if (!preg_match('/^{([^}]*)}(.*)$/',$str,$matches)) {
57             throw new InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string');
58         }
59
60         return array(
61             $matches[1],
62             $matches[2]
63         );
64
65     }
66
67     /**
68      * This method takes an XML document (as string) and converts all instances of the
69      * DAV: namespace to urn:DAV
70      *
71      * This is unfortunately needed, because the DAV: namespace violates the xml namespaces
72      * spec, and causes the DOM to throw errors
73      *
74      * @param string $xmlDocument
75      * @return array|string|null
76      */
77     static function convertDAVNamespace($xmlDocument) {
78
79         // This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV:
80         // namespace is actually a violation of the XML namespaces specification, and will cause errors
81         return preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/","xmlns\\1=\\2urn:DAV\\2",$xmlDocument);
82
83     }
84
85     /**
86      * This method provides a generic way to load a DOMDocument for WebDAV use.
87      *
88      * This method throws a Sabre_DAV_Exception_BadRequest exception for any xml errors.
89      * It does not preserve whitespace, and it converts the DAV: namespace to urn:DAV.
90      *
91      * @param string $xml
92      * @throws Sabre_DAV_Exception_BadRequest
93      * @return DOMDocument
94      */
95     static function loadDOMDocument($xml) {
96
97         if (empty($xml))
98             throw new Sabre_DAV_Exception_BadRequest('Empty XML document sent');
99
100         // The BitKinex client sends xml documents as UTF-16. PHP 5.3.1 (and presumably lower)
101         // does not support this, so we must intercept this and convert to UTF-8.
102         if (substr($xml,0,12) === "\x3c\x00\x3f\x00\x78\x00\x6d\x00\x6c\x00\x20\x00") {
103
104             // Note: the preceeding byte sequence is "<?xml" encoded as UTF_16, without the BOM.
105             $xml = iconv('UTF-16LE','UTF-8',$xml);
106
107             // Because the xml header might specify the encoding, we must also change this.
108             // This regex looks for the string encoding="UTF-16" and replaces it with
109             // encoding="UTF-8".
110             $xml = preg_replace('|<\?xml([^>]*)encoding="UTF-16"([^>]*)>|u','<?xml\1encoding="UTF-8"\2>',$xml);
111
112         }
113
114         // Retaining old error setting
115         $oldErrorSetting =  libxml_use_internal_errors(true);
116
117         // Clearing any previous errors
118         libxml_clear_errors();
119
120         $dom = new DOMDocument();
121         $dom->loadXML(self::convertDAVNamespace($xml),LIBXML_NOWARNING | LIBXML_NOERROR);
122
123         // We don't generally care about any whitespace
124         $dom->preserveWhiteSpace = false;
125
126         if ($error = libxml_get_last_error()) {
127             libxml_clear_errors();
128             throw new Sabre_DAV_Exception_BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')');
129         }
130
131         // Restoring old mechanism for error handling
132         if ($oldErrorSetting===false) libxml_use_internal_errors(false);
133
134         return $dom;
135
136     }
137
138     /**
139      * Parses all WebDAV properties out of a DOM Element
140      *
141      * Generally WebDAV properties are enclosed in {DAV:}prop elements. This
142      * method helps by going through all these and pulling out the actual
143      * propertynames, making them array keys and making the property values,
144      * well.. the array values.
145      *
146      * If no value was given (self-closing element) null will be used as the
147      * value. This is used in for example PROPFIND requests.
148      *
149      * Complex values are supported through the propertyMap argument. The
150      * propertyMap should have the clark-notation properties as it's keys, and
151      * classnames as values.
152      *
153      * When any of these properties are found, the unserialize() method will be
154      * (statically) called. The result of this method is used as the value.
155      *
156      * @param DOMElement $parentNode
157      * @param array $propertyMap
158      * @return array
159      */
160     static function parseProperties(DOMElement $parentNode, array $propertyMap = array()) {
161
162         $propList = array();
163         foreach($parentNode->childNodes as $propNode) {
164
165             if (Sabre_DAV_XMLUtil::toClarkNotation($propNode)!=='{DAV:}prop') continue;
166
167             foreach($propNode->childNodes as $propNodeData) {
168
169                 /* If there are no elements in here, we actually get 1 text node, this special case is dedicated to netdrive */
170                 if ($propNodeData->nodeType != XML_ELEMENT_NODE) continue;
171
172                 $propertyName = Sabre_DAV_XMLUtil::toClarkNotation($propNodeData);
173                 if (isset($propertyMap[$propertyName])) {
174                     $propList[$propertyName] = call_user_func(array($propertyMap[$propertyName],'unserialize'),$propNodeData);
175                 } else {
176                     $propList[$propertyName] = $propNodeData->textContent;
177                 }
178             }
179
180
181         }
182         return $propList;
183
184     }
185
186 }