Blame view

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