PageSourceSearch

https://cloud.zambello.it/owncloud/core/vendor/davclient.js/lib/client.js?v=7371f8fbd38fb04840db67d138f1720b

js zambello.it collected 2026-10-09 00:34:25 UTC 12,826 bytes, 455 lines download raw bytes

1/*
2 * vim: expandtab shiftwidth=4 softtabstop=4
3 */
4
5/* global dav */
6if (typeof dav == 'undefined') { dav = {}; };
7
8dav._XML_CHAR_MAP = {
9    '<': '&lt;',
10    '>': '&gt;',
11    '&': '&amp;',
12    '"': '&quot;',
13    "'": '&apos;'
14};
15
16dav._escapeXml = function(s) {
17    return s.replace(/[<>&"']/g, function (ch) {
18        return dav._XML_CHAR_MAP[ch];
19    });
20};
21
22dav.Client = function(options) {
23    var i;
24    for(i in options) {
25        this[i] = options[i];
26    }
27
28};
29
30dav.Client.prototype = {
31
32    baseUrl : null,
33
34    userName : null,
35
36    password : null,
37
38
39    xmlNamespaces : {
40        'DAV:' : 'd'
41    },
42
43    /**
44     * Generates a propFind request.
45     *
46     * @param {string} url Url to do the propfind request on
47     * @param {Array} properties List of properties to retrieve.
48     * @param {Object} [headers] headers
49     * @return {Promise}
50     */
51    propFind : function(url, properties, depth, headers) {
52
53        if(typeof depth == "undefined") {
54            depth = 0;
55        }
56
57        headers = headers || {};
58
59        headers['Depth'] = depth;
60        headers['Content-Type'] = 'application/xml; charset=utf-8';
61
62        var body =
63            '<?xml version="1.0"?>\n' +
64            '<d:propfind ';
65        var namespace;
66        for (namespace in this.xmlNamespaces) {
67            body += ' xmlns:' + this.xmlNamespaces[namespace] + '="' + namespace + '"';
68        }
69        body += '>\n' +
70            '  <d:prop>\n';
71
72        for(var ii in properties) {
73
74            var property = this.parseClarkNotation(properties[ii]);
75            if (this.xmlNamespaces[property.namespace]) {
76                body+='    <' + this.xmlNamespaces[property.namespace] + ':' + property.name + ' />\n';
77            } else {
78                body+='    <x:' + property.name + ' xmlns:x="' + property.namespace + '" />\n';
79            }
80
81        }
82        body+='  </d:prop>\n';
83        body+='</d:propfind>';
84
85        return this.request('PROPFIND', url, headers, body).then(
86            function(result) {
87
88                if (depth===0) {
89                    return {
90                        status: result.status,
91                        body: result.body[0],
92                        xhr: result.xhr
93                    };
94                } else {
95                    return {
96                        status: result.status,
97                        body: result.body,
98                        xhr: result.xhr
99                    };
100                }
101
102            }.bind(this)
103        );
104
105    },
106
107    /**
108     * Renders a "d:set" block for the given properties.
109     *
110     * @param {Object.<String,String>} properties
111     * @return {String} XML "<d:set>" block
112     */
113    _renderPropSet: function(properties) {
114        var body = '  <d:set>\n' +
115            '   <d:prop>\n';
116
117        for(var ii in properties) {
118            var property = this.parseClarkNotation(ii);
119            var propName;
120            var propValue = properties[ii];
121            if (this.xmlNamespaces[property.namespace]) {
122                propName = this.xmlNamespaces[property.namespace] + ':' + property.name;
123            } else {
124                propName = 'x:' + property.name + ' xmlns:x="' + property.namespace + '"';
125            }
126
127            // FIXME: hard-coded for now until we allow properties to
128            // specify whether to be escaped or not
129            if (propName !== 'd:resourcetype') {
130                propValue = dav._escapeXml(propValue);
131            }
132            body += '      <' + propName + '>' + propValue + '</' + propName + '>\n';
133        }
134        body +='    </d:prop>\n';
135        body +='  </d:set>\n';
136        return body;
137    },
138
139    /**
140     * Generates a propPatch request.
141     *
142     * @param {string} url Url to do the proppatch request on
143     * @param {Object.<String,String>} properties List of properties to store.
144     * @param {Object} [headers] headers
145     * @return {Promise}
146     */
147    propPatch : function(url, properties, headers) {
148        headers = headers || {};
149
150        headers['Content-Type'] = 'application/xml; charset=utf-8';
151
152        var body =
153            '<?xml version="1.0"?>\n' +
154            '<d:propertyupdate ';
155        var namespace;
156        for (namespace in this.xmlNamespaces) {
157            body += ' xmlns:' + this.xmlNamespaces[namespace] + '="' + namespace + '"';
158        }
159        body += '>\n' + this._renderPropSet(properties);
160        body += '</d:propertyupdate>';
161
162        return this.request('PROPPATCH', url, headers, body).then(
163            function(result) {
164                return {
165                    status: result.status,
166                    body: result.body,
167                    xhr: result.xhr
168                };
169            }.bind(this)
170        );
171
172    },
173
174    /**
175     * Generates a MKCOL request.
176     * If attributes are given, it will use an extended MKCOL request.
177     *
178     * @param {string} url Url to do the proppatch request on
179     * @param {Object.<String,String>} [properties] list of properties to store.
180     * @param {Object} [headers] headers
181     * @return {Promise}
182     */
183    mkcol : function(url, properties, headers) {
184        var body = '';
185        headers = headers || {};
186        headers['Content-Type'] = 'application/xml; charset=utf-8';
187
188        if (properties) {
189            body =
190                '<?xml version="1.0"?>\n' +
191                '<d:mkcol';
192            var namespace;
193            for (namespace in this.xmlNamespaces) {
194                body += ' xmlns:' + this.xmlNamespaces[namespace] + '="' + namespace + '"';
195            }
196            body += '>\n' + this._renderPropSet(properties);
197            body +='</d:mkcol>';
198        }
199
200        return this.request('MKCOL', url, headers, body).then(
201            function(result) {
202                return {
203                    status: result.status,
204                    body: result.body,
205                    xhr: result.xhr
206                };
207            }.bind(this)
208        );
209
210    },
211
212    /**
213     * Performs a HTTP request, and returns a Promise
214     *
215     * @param {string} method HTTP method
216     * @param {string} url Relative or absolute url
217     * @param {Object} headers HTTP headers as an object.
218     * @param {string} body HTTP request body.
219     * @return {Promise}
220     */
221    request : function(method, url, headers, body) {
222
223        var self = this;
224        var xhr = this.xhrProvider();
225        headers = headers || {};
226        
227        if (this.userName) {
228            headers['Authorization'] = 'Basic ' + btoa(this.userName + ':' + this.password);
229            // xhr.open(method, this.resolveUrl(url), true, this.userName, this.password);
230        }
231        xhr.open(method, this.resolveUrl(url), true);
232        var ii;
233        for(ii in headers) {
234            xhr.setRequestHeader(ii, headers[ii]);
235        }
236
237        // Work around for edge
238        if (body === undefined) {
239            xhr.send();
240        } else {
241            xhr.send(body);
242        }
243
244        return new Promise(function(fulfill, reject) {
245
246            xhr.onreadystatechange = function() {
247
248                if (xhr.readyState !== 4) {
249                    return;
250                }
251
252                var resultBody = xhr.response;
253                if (xhr.status === 207) {
254                    resultBody = self.parseMultiStatus(xhr.response);
255                }
256
257                fulfill({
258                    body: resultBody,
259                    status: xhr.status,
260                    xhr: xhr
261                });
262
263            };
264
265            xhr.ontimeout = function() {
266
267                reject(new Error('Timeout exceeded'));
268
269            };
270
271        });
272
273    },
274
275    /**
276     * Returns an XMLHttpRequest object.
277     *
278     * This is in its own method, so it can be easily overridden.
279     *
280     * @return {XMLHttpRequest}
281     */
282    xhrProvider : function() {
283
284        return new XMLHttpRequest();
285
286    },
287
288    /**
289     * Parses a property node.
290     *
291     * Either returns a string if the node only contains text, or returns an
292     * array of non-text subnodes.
293     *
294     * @param {Object} propNode node to parse
295     * @return {string|Array} text content as string or array of subnodes, excluding text nodes
296     */
297    _parsePropNode: function(propNode) {
298        var content = null;
299        if (propNode.childNodes && propNode.childNodes.length > 0) {
300            var subNodes = [];
301            // filter out text nodes
302            for (var j = 0; j < propNode.childNodes.length; j++) {
303                var node = propNode.childNodes[j];
304                if (node.nodeType === 1) {
305                    subNodes.push(node);
306                }
307            }
308            if (subNodes.length) {
309                content = subNodes;
310            }
311        }
312
313        return content || propNode.textContent || propNode.text || '';
314    },
315
316    /**
317     * Parses a multi-status response body.
318     *
319     * @param {string} xmlBody
320     * @param {Array}
321     */
322    parseMultiStatus : function(xmlBody) {
323
324        var parser = new DOMParser();
325        var doc = parser.parseFromString(xmlBody, "application/xml");
326
327        var resolver = function(foo) {
328            var ii;
329            for(ii in this.xmlNamespaces) {
330                if (this.xmlNamespaces[ii] === foo) {
331                    return ii;
332                }
333            }
334        }.bind(this);
335
336        var responseIterator = doc.evaluate('/d:multistatus/d:response', doc, resolver, XPathResult.ANY_TYPE, null);
337
338        var result = [];
339        var responseNode = responseIterator.iterateNext();
340
341        while(responseNode) {
342
343            var response = {
344                href : null,
345                propStat : []
346            };
347
348            response.href = doc.evaluate('string(d:href)', responseNode, resolver, XPathResult.ANY_TYPE, null).stringValue;
349
350            var propStatIterator = doc.evaluate('d:propstat', responseNode, resolver, XPathResult.ANY_TYPE, null);
351            var propStatNode = propStatIterator.iterateNext();
352
353            while(propStatNode) {
354                var propStat = {
355                    status : doc.evaluate('string(d:status)', propStatNode, resolver, XPathResult.ANY_TYPE, null).stringValue,
356                    properties : {},
357                };
358
359                var propIterator = doc.evaluate('d:prop/*', propStatNode, resolver, XPathResult.ANY_TYPE, null);
360
361                var propNode = propIterator.iterateNext();
362                while(propNode) {
363                    var content = this._parsePropNode(propNode);
364                    propStat.properties['{' + propNode.namespaceURI + '}' + propNode.localName] = content;
365                    propNode = propIterator.iterateNext();
366
367                }
368                response.propStat.push(propStat);
369                propStatNode = propStatIterator.iterateNext();
370
371
372            }
373
374            result.push(response);
375            responseNode = responseIterator.iterateNext();
376
377        }
378
379        return result;
380
381    },
382
383    /**
384     * Takes a relative url, and maps it to an absolute url, using the baseUrl
385     *
386     * @param {string} url
387     * @return {string}
388     */
389    resolveUrl : function(url) {
390
391        // Note: this is rudamentary.. not sure yet if it handles every case.
392        if (/^https?:\/\//i.test(url)) {
393            // absolute
394            return url;
395        }
396
397        var baseParts = this.parseUrl(this.baseUrl);
398        if (url.charAt('/')) {
399            // Url starts with a slash
400            return baseParts.root + url;
401        }
402
403        // Url does not start with a slash, we need grab the base url right up until the last slash.
404        var newUrl = baseParts.root + '/';
405        if (baseParts.path.lastIndexOf('/')!==-1) {
406            newUrl = newUrl = baseParts.path.subString(0, baseParts.path.lastIndexOf('/')) + '/';
407        }
408        newUrl+=url;
409        return url;
410
411    },
412
413    /**
414     * Parses a url and returns its individual components.
415     *
416     * @param {String} url
417     * @return {Object}
418     */
419    parseUrl : function(url) {
420
421         var parts = url.match(/^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/);
422         var result = {
423             url : parts[0],
424             scheme : parts[1],
425             host : parts[3],
426             port : parts[4],
427             path : parts[5],
428             query : parts[6],
429             fragment : parts[7],
430         };
431         result.root =
432            result.scheme + '://' +
433            result.host +
434            (result.port ? ':' + result.port : '');
435
436         return result;
437
438    },
439
440    parseClarkNotation : function(propertyName) {
441
442        var result = propertyName.match(/^{([^}]+)}(.*)$/);
443        if (!result) {
444            return;
445        }
446
447        return {
448            name : result[2],
449            namespace : result[1]
450        };
451
452    }
453
454};
455

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.