xref: /plugin/davcal/vendor/sabre/dav/lib/DAV/Client.php (revision a1a3b6794e0e143a4a8b51d3185ce2d339be61ab)
1*a1a3b679SAndreas Boehler<?php
2*a1a3b679SAndreas Boehler
3*a1a3b679SAndreas Boehlernamespace Sabre\DAV;
4*a1a3b679SAndreas Boehler
5*a1a3b679SAndreas Boehleruse Sabre\HTTP;
6*a1a3b679SAndreas Boehler
7*a1a3b679SAndreas Boehler/**
8*a1a3b679SAndreas Boehler * SabreDAV DAV client
9*a1a3b679SAndreas Boehler *
10*a1a3b679SAndreas Boehler * This client wraps around Curl to provide a convenient API to a WebDAV
11*a1a3b679SAndreas Boehler * server.
12*a1a3b679SAndreas Boehler *
13*a1a3b679SAndreas Boehler * NOTE: This class is experimental, it's api will likely change in the future.
14*a1a3b679SAndreas Boehler *
15*a1a3b679SAndreas Boehler * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/).
16*a1a3b679SAndreas Boehler * @author Evert Pot (http://evertpot.com/)
17*a1a3b679SAndreas Boehler * @license http://sabre.io/license/ Modified BSD License
18*a1a3b679SAndreas Boehler */
19*a1a3b679SAndreas Boehlerclass Client extends HTTP\Client {
20*a1a3b679SAndreas Boehler
21*a1a3b679SAndreas Boehler    /**
22*a1a3b679SAndreas Boehler     * The xml service.
23*a1a3b679SAndreas Boehler     *
24*a1a3b679SAndreas Boehler     * Uset this service to configure the property and namespace maps.
25*a1a3b679SAndreas Boehler     *
26*a1a3b679SAndreas Boehler     * @var mixed
27*a1a3b679SAndreas Boehler     */
28*a1a3b679SAndreas Boehler    public $xml;
29*a1a3b679SAndreas Boehler
30*a1a3b679SAndreas Boehler    /**
31*a1a3b679SAndreas Boehler     * The elementMap
32*a1a3b679SAndreas Boehler     *
33*a1a3b679SAndreas Boehler     * This property is linked via reference to $this->xml->elementMap.
34*a1a3b679SAndreas Boehler     * It's deprecated as of version 3.0.0, and should no longer be used.
35*a1a3b679SAndreas Boehler     *
36*a1a3b679SAndreas Boehler     * @deprecated
37*a1a3b679SAndreas Boehler     * @var array
38*a1a3b679SAndreas Boehler     */
39*a1a3b679SAndreas Boehler    public $propertyMap = [];
40*a1a3b679SAndreas Boehler
41*a1a3b679SAndreas Boehler    /**
42*a1a3b679SAndreas Boehler     * Base URI
43*a1a3b679SAndreas Boehler     *
44*a1a3b679SAndreas Boehler     * This URI will be used to resolve relative urls.
45*a1a3b679SAndreas Boehler     *
46*a1a3b679SAndreas Boehler     * @var string
47*a1a3b679SAndreas Boehler     */
48*a1a3b679SAndreas Boehler    protected $baseUri;
49*a1a3b679SAndreas Boehler
50*a1a3b679SAndreas Boehler    /**
51*a1a3b679SAndreas Boehler     * Basic authentication
52*a1a3b679SAndreas Boehler     */
53*a1a3b679SAndreas Boehler    const AUTH_BASIC = 1;
54*a1a3b679SAndreas Boehler
55*a1a3b679SAndreas Boehler    /**
56*a1a3b679SAndreas Boehler     * Digest authentication
57*a1a3b679SAndreas Boehler     */
58*a1a3b679SAndreas Boehler    const AUTH_DIGEST = 2;
59*a1a3b679SAndreas Boehler
60*a1a3b679SAndreas Boehler    /**
61*a1a3b679SAndreas Boehler     * NTLM authentication
62*a1a3b679SAndreas Boehler     */
63*a1a3b679SAndreas Boehler    const AUTH_NTLM = 4;
64*a1a3b679SAndreas Boehler
65*a1a3b679SAndreas Boehler    /**
66*a1a3b679SAndreas Boehler     * Identity encoding, which basically does not nothing.
67*a1a3b679SAndreas Boehler     */
68*a1a3b679SAndreas Boehler    const ENCODING_IDENTITY = 1;
69*a1a3b679SAndreas Boehler
70*a1a3b679SAndreas Boehler    /**
71*a1a3b679SAndreas Boehler     * Deflate encoding
72*a1a3b679SAndreas Boehler     */
73*a1a3b679SAndreas Boehler    const ENCODING_DEFLATE = 2;
74*a1a3b679SAndreas Boehler
75*a1a3b679SAndreas Boehler    /**
76*a1a3b679SAndreas Boehler     * Gzip encoding
77*a1a3b679SAndreas Boehler     */
78*a1a3b679SAndreas Boehler    const ENCODING_GZIP = 4;
79*a1a3b679SAndreas Boehler
80*a1a3b679SAndreas Boehler    /**
81*a1a3b679SAndreas Boehler     * Sends all encoding headers.
82*a1a3b679SAndreas Boehler     */
83*a1a3b679SAndreas Boehler    const ENCODING_ALL = 7;
84*a1a3b679SAndreas Boehler
85*a1a3b679SAndreas Boehler    /**
86*a1a3b679SAndreas Boehler     * Content-encoding
87*a1a3b679SAndreas Boehler     *
88*a1a3b679SAndreas Boehler     * @var int
89*a1a3b679SAndreas Boehler     */
90*a1a3b679SAndreas Boehler    protected $encoding = self::ENCODING_IDENTITY;
91*a1a3b679SAndreas Boehler
92*a1a3b679SAndreas Boehler    /**
93*a1a3b679SAndreas Boehler     * Constructor
94*a1a3b679SAndreas Boehler     *
95*a1a3b679SAndreas Boehler     * Settings are provided through the 'settings' argument. The following
96*a1a3b679SAndreas Boehler     * settings are supported:
97*a1a3b679SAndreas Boehler     *
98*a1a3b679SAndreas Boehler     *   * baseUri
99*a1a3b679SAndreas Boehler     *   * userName (optional)
100*a1a3b679SAndreas Boehler     *   * password (optional)
101*a1a3b679SAndreas Boehler     *   * proxy (optional)
102*a1a3b679SAndreas Boehler     *   * authType (optional)
103*a1a3b679SAndreas Boehler     *   * encoding (optional)
104*a1a3b679SAndreas Boehler     *
105*a1a3b679SAndreas Boehler     *  authType must be a bitmap, using self::AUTH_BASIC, self::AUTH_DIGEST
106*a1a3b679SAndreas Boehler     *  and self::AUTH_NTLM. If you know which authentication method will be
107*a1a3b679SAndreas Boehler     *  used, it's recommended to set it, as it will save a great deal of
108*a1a3b679SAndreas Boehler     *  requests to 'discover' this information.
109*a1a3b679SAndreas Boehler     *
110*a1a3b679SAndreas Boehler     *  Encoding is a bitmap with one of the ENCODING constants.
111*a1a3b679SAndreas Boehler     *
112*a1a3b679SAndreas Boehler     * @param array $settings
113*a1a3b679SAndreas Boehler     */
114*a1a3b679SAndreas Boehler    function __construct(array $settings) {
115*a1a3b679SAndreas Boehler
116*a1a3b679SAndreas Boehler        if (!isset($settings['baseUri'])) {
117*a1a3b679SAndreas Boehler            throw new \InvalidArgumentException('A baseUri must be provided');
118*a1a3b679SAndreas Boehler        }
119*a1a3b679SAndreas Boehler
120*a1a3b679SAndreas Boehler        parent::__construct();
121*a1a3b679SAndreas Boehler
122*a1a3b679SAndreas Boehler        $this->baseUri = $settings['baseUri'];
123*a1a3b679SAndreas Boehler
124*a1a3b679SAndreas Boehler        if (isset($settings['proxy'])) {
125*a1a3b679SAndreas Boehler            $this->addCurlSetting(CURLOPT_PROXY, $settings['proxy']);
126*a1a3b679SAndreas Boehler        }
127*a1a3b679SAndreas Boehler
128*a1a3b679SAndreas Boehler        if (isset($settings['userName'])) {
129*a1a3b679SAndreas Boehler            $userName = $settings['userName'];
130*a1a3b679SAndreas Boehler            $password = isset($settings['password']) ? $settings['password'] : '';
131*a1a3b679SAndreas Boehler
132*a1a3b679SAndreas Boehler            if (isset($settings['authType'])) {
133*a1a3b679SAndreas Boehler                $curlType = 0;
134*a1a3b679SAndreas Boehler                if ($settings['authType'] & self::AUTH_BASIC) {
135*a1a3b679SAndreas Boehler                    $curlType |= CURLAUTH_BASIC;
136*a1a3b679SAndreas Boehler                }
137*a1a3b679SAndreas Boehler                if ($settings['authType'] & self::AUTH_DIGEST) {
138*a1a3b679SAndreas Boehler                    $curlType |= CURLAUTH_DIGEST;
139*a1a3b679SAndreas Boehler                }
140*a1a3b679SAndreas Boehler                if ($settings['authType'] & self::AUTH_NTLM) {
141*a1a3b679SAndreas Boehler                    $curlType |= CURLAUTH_NTLM;
142*a1a3b679SAndreas Boehler                }
143*a1a3b679SAndreas Boehler            } else {
144*a1a3b679SAndreas Boehler                $curlType = CURLAUTH_BASIC | CURLAUTH_DIGEST;
145*a1a3b679SAndreas Boehler            }
146*a1a3b679SAndreas Boehler
147*a1a3b679SAndreas Boehler            $this->addCurlSetting(CURLOPT_HTTPAUTH, $curlType);
148*a1a3b679SAndreas Boehler            $this->addCurlSetting(CURLOPT_USERPWD, $userName . ':' . $password);
149*a1a3b679SAndreas Boehler
150*a1a3b679SAndreas Boehler        }
151*a1a3b679SAndreas Boehler
152*a1a3b679SAndreas Boehler        if (isset($settings['encoding'])) {
153*a1a3b679SAndreas Boehler            $encoding = $settings['encoding'];
154*a1a3b679SAndreas Boehler
155*a1a3b679SAndreas Boehler            $encodings = [];
156*a1a3b679SAndreas Boehler            if ($encoding & self::ENCODING_IDENTITY) {
157*a1a3b679SAndreas Boehler                $encodings[] = 'identity';
158*a1a3b679SAndreas Boehler            }
159*a1a3b679SAndreas Boehler            if ($encoding & self::ENCODING_DEFLATE) {
160*a1a3b679SAndreas Boehler                $encodings[] = 'deflate';
161*a1a3b679SAndreas Boehler            }
162*a1a3b679SAndreas Boehler            if ($encoding & self::ENCODING_GZIP) {
163*a1a3b679SAndreas Boehler                $encodings[] = 'gzip';
164*a1a3b679SAndreas Boehler            }
165*a1a3b679SAndreas Boehler            $this->addCurlSetting(CURLOPT_ENCODING, implode(',', $encodings));
166*a1a3b679SAndreas Boehler        }
167*a1a3b679SAndreas Boehler
168*a1a3b679SAndreas Boehler        $this->xml = new Xml\Service();
169*a1a3b679SAndreas Boehler        // BC
170*a1a3b679SAndreas Boehler        $this->propertyMap = & $this->xml->elementMap;
171*a1a3b679SAndreas Boehler
172*a1a3b679SAndreas Boehler    }
173*a1a3b679SAndreas Boehler
174*a1a3b679SAndreas Boehler    /**
175*a1a3b679SAndreas Boehler     * Does a PROPFIND request
176*a1a3b679SAndreas Boehler     *
177*a1a3b679SAndreas Boehler     * The list of requested properties must be specified as an array, in clark
178*a1a3b679SAndreas Boehler     * notation.
179*a1a3b679SAndreas Boehler     *
180*a1a3b679SAndreas Boehler     * The returned array will contain a list of filenames as keys, and
181*a1a3b679SAndreas Boehler     * properties as values.
182*a1a3b679SAndreas Boehler     *
183*a1a3b679SAndreas Boehler     * The properties array will contain the list of properties. Only properties
184*a1a3b679SAndreas Boehler     * that are actually returned from the server (without error) will be
185*a1a3b679SAndreas Boehler     * returned, anything else is discarded.
186*a1a3b679SAndreas Boehler     *
187*a1a3b679SAndreas Boehler     * Depth should be either 0 or 1. A depth of 1 will cause a request to be
188*a1a3b679SAndreas Boehler     * made to the server to also return all child resources.
189*a1a3b679SAndreas Boehler     *
190*a1a3b679SAndreas Boehler     * @param string $url
191*a1a3b679SAndreas Boehler     * @param array $properties
192*a1a3b679SAndreas Boehler     * @param int $depth
193*a1a3b679SAndreas Boehler     * @return array
194*a1a3b679SAndreas Boehler     */
195*a1a3b679SAndreas Boehler    function propFind($url, array $properties, $depth = 0) {
196*a1a3b679SAndreas Boehler
197*a1a3b679SAndreas Boehler        $dom = new \DOMDocument('1.0', 'UTF-8');
198*a1a3b679SAndreas Boehler        $dom->formatOutput = true;
199*a1a3b679SAndreas Boehler        $root = $dom->createElementNS('DAV:', 'd:propfind');
200*a1a3b679SAndreas Boehler        $prop = $dom->createElement('d:prop');
201*a1a3b679SAndreas Boehler
202*a1a3b679SAndreas Boehler        foreach ($properties as $property) {
203*a1a3b679SAndreas Boehler
204*a1a3b679SAndreas Boehler            list(
205*a1a3b679SAndreas Boehler                $namespace,
206*a1a3b679SAndreas Boehler                $elementName
207*a1a3b679SAndreas Boehler            ) = \Sabre\Xml\Service::parseClarkNotation($property);
208*a1a3b679SAndreas Boehler
209*a1a3b679SAndreas Boehler            if ($namespace === 'DAV:') {
210*a1a3b679SAndreas Boehler                $element = $dom->createElement('d:' . $elementName);
211*a1a3b679SAndreas Boehler            } else {
212*a1a3b679SAndreas Boehler                $element = $dom->createElementNS($namespace, 'x:' . $elementName);
213*a1a3b679SAndreas Boehler            }
214*a1a3b679SAndreas Boehler
215*a1a3b679SAndreas Boehler            $prop->appendChild($element);
216*a1a3b679SAndreas Boehler        }
217*a1a3b679SAndreas Boehler
218*a1a3b679SAndreas Boehler        $dom->appendChild($root)->appendChild($prop);
219*a1a3b679SAndreas Boehler        $body = $dom->saveXML();
220*a1a3b679SAndreas Boehler
221*a1a3b679SAndreas Boehler        $url = $this->getAbsoluteUrl($url);
222*a1a3b679SAndreas Boehler
223*a1a3b679SAndreas Boehler        $request = new HTTP\Request('PROPFIND', $url, [
224*a1a3b679SAndreas Boehler            'Depth'        => $depth,
225*a1a3b679SAndreas Boehler            'Content-Type' => 'application/xml'
226*a1a3b679SAndreas Boehler        ], $body);
227*a1a3b679SAndreas Boehler
228*a1a3b679SAndreas Boehler        $response = $this->send($request);
229*a1a3b679SAndreas Boehler
230*a1a3b679SAndreas Boehler        if ((int)$response->getStatus() >= 400) {
231*a1a3b679SAndreas Boehler            throw new Exception('HTTP error: ' . $response->getStatus());
232*a1a3b679SAndreas Boehler        }
233*a1a3b679SAndreas Boehler
234*a1a3b679SAndreas Boehler        $result = $this->parseMultiStatus($response->getBodyAsString());
235*a1a3b679SAndreas Boehler
236*a1a3b679SAndreas Boehler        // If depth was 0, we only return the top item
237*a1a3b679SAndreas Boehler        if ($depth === 0) {
238*a1a3b679SAndreas Boehler            reset($result);
239*a1a3b679SAndreas Boehler            $result = current($result);
240*a1a3b679SAndreas Boehler            return isset($result[200]) ? $result[200] : [];
241*a1a3b679SAndreas Boehler        }
242*a1a3b679SAndreas Boehler
243*a1a3b679SAndreas Boehler        $newResult = [];
244*a1a3b679SAndreas Boehler        foreach ($result as $href => $statusList) {
245*a1a3b679SAndreas Boehler
246*a1a3b679SAndreas Boehler            $newResult[$href] = isset($statusList[200]) ? $statusList[200] : [];
247*a1a3b679SAndreas Boehler
248*a1a3b679SAndreas Boehler        }
249*a1a3b679SAndreas Boehler
250*a1a3b679SAndreas Boehler        return $newResult;
251*a1a3b679SAndreas Boehler
252*a1a3b679SAndreas Boehler    }
253*a1a3b679SAndreas Boehler
254*a1a3b679SAndreas Boehler    /**
255*a1a3b679SAndreas Boehler     * Updates a list of properties on the server
256*a1a3b679SAndreas Boehler     *
257*a1a3b679SAndreas Boehler     * The list of properties must have clark-notation properties for the keys,
258*a1a3b679SAndreas Boehler     * and the actual (string) value for the value. If the value is null, an
259*a1a3b679SAndreas Boehler     * attempt is made to delete the property.
260*a1a3b679SAndreas Boehler     *
261*a1a3b679SAndreas Boehler     * @param string $url
262*a1a3b679SAndreas Boehler     * @param array $properties
263*a1a3b679SAndreas Boehler     * @return void
264*a1a3b679SAndreas Boehler     */
265*a1a3b679SAndreas Boehler    function propPatch($url, array $properties) {
266*a1a3b679SAndreas Boehler
267*a1a3b679SAndreas Boehler        $propPatch = new Xml\Request\PropPatch();
268*a1a3b679SAndreas Boehler        $propPatch->properties = $properties;
269*a1a3b679SAndreas Boehler        $xml = $this->xml->write(
270*a1a3b679SAndreas Boehler            '{DAV:}propertyupdate',
271*a1a3b679SAndreas Boehler            $propPatch
272*a1a3b679SAndreas Boehler        );
273*a1a3b679SAndreas Boehler
274*a1a3b679SAndreas Boehler        $url = $this->getAbsoluteUrl($url);
275*a1a3b679SAndreas Boehler        $request = new HTTP\Request('PROPPATCH', $url, [
276*a1a3b679SAndreas Boehler            'Content-Type' => 'application/xml',
277*a1a3b679SAndreas Boehler        ], $xml);
278*a1a3b679SAndreas Boehler        $this->send($request);
279*a1a3b679SAndreas Boehler    }
280*a1a3b679SAndreas Boehler
281*a1a3b679SAndreas Boehler    /**
282*a1a3b679SAndreas Boehler     * Performs an HTTP options request
283*a1a3b679SAndreas Boehler     *
284*a1a3b679SAndreas Boehler     * This method returns all the features from the 'DAV:' header as an array.
285*a1a3b679SAndreas Boehler     * If there was no DAV header, or no contents this method will return an
286*a1a3b679SAndreas Boehler     * empty array.
287*a1a3b679SAndreas Boehler     *
288*a1a3b679SAndreas Boehler     * @return array
289*a1a3b679SAndreas Boehler     */
290*a1a3b679SAndreas Boehler    function options() {
291*a1a3b679SAndreas Boehler
292*a1a3b679SAndreas Boehler        $request = new HTTP\Request('OPTIONS', $this->getAbsoluteUrl(''));
293*a1a3b679SAndreas Boehler        $response = $this->send($request);
294*a1a3b679SAndreas Boehler
295*a1a3b679SAndreas Boehler        $dav = $response->getHeader('Dav');
296*a1a3b679SAndreas Boehler        if (!$dav) {
297*a1a3b679SAndreas Boehler            return [];
298*a1a3b679SAndreas Boehler        }
299*a1a3b679SAndreas Boehler
300*a1a3b679SAndreas Boehler        $features = explode(',', $dav);
301*a1a3b679SAndreas Boehler        foreach ($features as &$v) {
302*a1a3b679SAndreas Boehler            $v = trim($v);
303*a1a3b679SAndreas Boehler        }
304*a1a3b679SAndreas Boehler        return $features;
305*a1a3b679SAndreas Boehler
306*a1a3b679SAndreas Boehler    }
307*a1a3b679SAndreas Boehler
308*a1a3b679SAndreas Boehler    /**
309*a1a3b679SAndreas Boehler     * Performs an actual HTTP request, and returns the result.
310*a1a3b679SAndreas Boehler     *
311*a1a3b679SAndreas Boehler     * If the specified url is relative, it will be expanded based on the base
312*a1a3b679SAndreas Boehler     * url.
313*a1a3b679SAndreas Boehler     *
314*a1a3b679SAndreas Boehler     * The returned array contains 3 keys:
315*a1a3b679SAndreas Boehler     *   * body - the response body
316*a1a3b679SAndreas Boehler     *   * httpCode - a HTTP code (200, 404, etc)
317*a1a3b679SAndreas Boehler     *   * headers - a list of response http headers. The header names have
318*a1a3b679SAndreas Boehler     *     been lowercased.
319*a1a3b679SAndreas Boehler     *
320*a1a3b679SAndreas Boehler     * For large uploads, it's highly recommended to specify body as a stream
321*a1a3b679SAndreas Boehler     * resource. You can easily do this by simply passing the result of
322*a1a3b679SAndreas Boehler     * fopen(..., 'r').
323*a1a3b679SAndreas Boehler     *
324*a1a3b679SAndreas Boehler     * This method will throw an exception if an HTTP error was received. Any
325*a1a3b679SAndreas Boehler     * HTTP status code above 399 is considered an error.
326*a1a3b679SAndreas Boehler     *
327*a1a3b679SAndreas Boehler     * Note that it is no longer recommended to use this method, use the send()
328*a1a3b679SAndreas Boehler     * method instead.
329*a1a3b679SAndreas Boehler     *
330*a1a3b679SAndreas Boehler     * @param string $method
331*a1a3b679SAndreas Boehler     * @param string $url
332*a1a3b679SAndreas Boehler     * @param string|resource|null $body
333*a1a3b679SAndreas Boehler     * @param array $headers
334*a1a3b679SAndreas Boehler     * @throws ClientException, in case a curl error occurred.
335*a1a3b679SAndreas Boehler     * @return array
336*a1a3b679SAndreas Boehler     */
337*a1a3b679SAndreas Boehler    function request($method, $url = '', $body = null, array $headers = []) {
338*a1a3b679SAndreas Boehler
339*a1a3b679SAndreas Boehler        $url = $this->getAbsoluteUrl($url);
340*a1a3b679SAndreas Boehler
341*a1a3b679SAndreas Boehler        $response = $this->send(new HTTP\Request($method, $url, $headers, $body));
342*a1a3b679SAndreas Boehler        return [
343*a1a3b679SAndreas Boehler            'body'       => $response->getBodyAsString(),
344*a1a3b679SAndreas Boehler            'statusCode' => (int)$response->getStatus(),
345*a1a3b679SAndreas Boehler            'headers'    => array_change_key_case($response->getHeaders()),
346*a1a3b679SAndreas Boehler        ];
347*a1a3b679SAndreas Boehler
348*a1a3b679SAndreas Boehler    }
349*a1a3b679SAndreas Boehler
350*a1a3b679SAndreas Boehler    /**
351*a1a3b679SAndreas Boehler     * Returns the full url based on the given url (which may be relative). All
352*a1a3b679SAndreas Boehler     * urls are expanded based on the base url as given by the server.
353*a1a3b679SAndreas Boehler     *
354*a1a3b679SAndreas Boehler     * @param string $url
355*a1a3b679SAndreas Boehler     * @return string
356*a1a3b679SAndreas Boehler     */
357*a1a3b679SAndreas Boehler    function getAbsoluteUrl($url) {
358*a1a3b679SAndreas Boehler
359*a1a3b679SAndreas Boehler        // If the url starts with http:// or https://, the url is already absolute.
360*a1a3b679SAndreas Boehler        if (preg_match('/^http(s?):\/\//', $url)) {
361*a1a3b679SAndreas Boehler            return $url;
362*a1a3b679SAndreas Boehler        }
363*a1a3b679SAndreas Boehler
364*a1a3b679SAndreas Boehler        // If the url starts with a slash, we must calculate the url based off
365*a1a3b679SAndreas Boehler        // the root of the base url.
366*a1a3b679SAndreas Boehler        if (strpos($url, '/') === 0) {
367*a1a3b679SAndreas Boehler            $parts = parse_url($this->baseUri);
368*a1a3b679SAndreas Boehler            return $parts['scheme'] . '://' . $parts['host'] . (isset($parts['port']) ? ':' . $parts['port'] : '') . $url;
369*a1a3b679SAndreas Boehler        }
370*a1a3b679SAndreas Boehler
371*a1a3b679SAndreas Boehler        // Otherwise...
372*a1a3b679SAndreas Boehler        return $this->baseUri . $url;
373*a1a3b679SAndreas Boehler
374*a1a3b679SAndreas Boehler    }
375*a1a3b679SAndreas Boehler
376*a1a3b679SAndreas Boehler    /**
377*a1a3b679SAndreas Boehler     * Parses a WebDAV multistatus response body
378*a1a3b679SAndreas Boehler     *
379*a1a3b679SAndreas Boehler     * This method returns an array with the following structure
380*a1a3b679SAndreas Boehler     *
381*a1a3b679SAndreas Boehler     * [
382*a1a3b679SAndreas Boehler     *   'url/to/resource' => [
383*a1a3b679SAndreas Boehler     *     '200' => [
384*a1a3b679SAndreas Boehler     *        '{DAV:}property1' => 'value1',
385*a1a3b679SAndreas Boehler     *        '{DAV:}property2' => 'value2',
386*a1a3b679SAndreas Boehler     *     ],
387*a1a3b679SAndreas Boehler     *     '404' => [
388*a1a3b679SAndreas Boehler     *        '{DAV:}property1' => null,
389*a1a3b679SAndreas Boehler     *        '{DAV:}property2' => null,
390*a1a3b679SAndreas Boehler     *     ],
391*a1a3b679SAndreas Boehler     *   ],
392*a1a3b679SAndreas Boehler     *   'url/to/resource2' => [
393*a1a3b679SAndreas Boehler     *      .. etc ..
394*a1a3b679SAndreas Boehler     *   ]
395*a1a3b679SAndreas Boehler     * ]
396*a1a3b679SAndreas Boehler     *
397*a1a3b679SAndreas Boehler     *
398*a1a3b679SAndreas Boehler     * @param string $body xml body
399*a1a3b679SAndreas Boehler     * @return array
400*a1a3b679SAndreas Boehler     */
401*a1a3b679SAndreas Boehler    function parseMultiStatus($body) {
402*a1a3b679SAndreas Boehler
403*a1a3b679SAndreas Boehler        $multistatus = $this->xml->expect('{DAV:}multistatus', $body);
404*a1a3b679SAndreas Boehler
405*a1a3b679SAndreas Boehler        $result = [];
406*a1a3b679SAndreas Boehler
407*a1a3b679SAndreas Boehler        foreach ($multistatus->getResponses() as $response) {
408*a1a3b679SAndreas Boehler
409*a1a3b679SAndreas Boehler            $result[$response->getHref()] = $response->getResponseProperties();
410*a1a3b679SAndreas Boehler
411*a1a3b679SAndreas Boehler        }
412*a1a3b679SAndreas Boehler
413*a1a3b679SAndreas Boehler        return $result;
414*a1a3b679SAndreas Boehler
415*a1a3b679SAndreas Boehler    }
416*a1a3b679SAndreas Boehler
417*a1a3b679SAndreas Boehler}
418