move control socket handling to separate file
[mizunara.git] / libidav / methods.c
1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3  *
4  * Copyright 2018 Olaf Wintermann. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *   1. Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above copyright
13  *      notice, this list of conditions and the following disclaimer in the
14  *      documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "utils.h"
34 #include "methods.h"
35 #include "crypto.h"
36 #include "session.h"
37 #include "xml.h"
38
39 #include <ucx/utils.h>
40
41 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
42
43
44 int dav_buffer_seek(UcxBuffer *b, curl_off_t offset, int origin) {
45     return ucx_buffer_seek(b, offset, origin) == 0 ? 0:CURL_SEEKFUNC_CANTSEEK;
46 }
47
48 /* ----------------------------- PROPFIND ----------------------------- */
49
50 CURLcode do_propfind_request(
51         DavSession *sn,
52         UcxBuffer *request,
53         UcxBuffer *response)
54 {
55     CURL *handle = sn->handle;
56     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPFIND");
57     
58     // always try to get information about possible children
59     int depth = 1;
60     
61     int maxretry = 2;
62     
63     struct curl_slist *headers = NULL;
64     CURLcode ret = 0;
65     
66     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 
67     curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read);
68     curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, dav_buffer_seek);
69     curl_easy_setopt(handle, CURLOPT_READDATA, request); 
70     curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size);
71     
72     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
73     curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
74     UcxMap *respheaders = ucx_map_new(32);
75     util_capture_header(handle, respheaders);
76     
77     for(int i=0;i<maxretry;i++) {
78         if (depth == 1) {
79             headers = curl_slist_append(headers, "Depth: 1");
80         } else if (depth == -1) {
81             headers = curl_slist_append(headers, "Depth: infinity");
82         } else {
83             headers = curl_slist_append(headers, "Depth: 0");
84         }
85         headers = curl_slist_append(headers, "Content-Type: text/xml");
86         curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
87
88         // reset buffers and perform request
89         request->pos = 0;
90         response->size = response->pos = 0;
91         ret = dav_session_curl_perform_buf(sn, request, response, NULL);
92         curl_slist_free_all(headers);
93         headers = NULL;
94         
95         /*
96          * Handle two cases:
97          * 1. We communicate with IIS and get a X-MSDAVEXT_Error: 589831
98          *    => try with depth 0 next time, it's not a collection
99          * 2. Other cases
100          *    => the server handled our request and we can stop requesting
101          */
102         char *msdavexterror;
103         msdavexterror = ucx_map_cstr_get(respheaders, "x-msdavext_error");
104         int iishack =  depth == 1 &&
105             msdavexterror && !strncmp(msdavexterror, "589831;", 7);
106         
107         if(iishack) {
108             depth = 0;
109         } else {
110             break;
111         }
112     }
113     
114     // deactivate header capturing and free captured map
115     util_capture_header(handle, NULL);
116     ucx_map_free_content(respheaders, free);
117     ucx_map_free(respheaders);
118        
119     return ret;
120 }
121
122 UcxBuffer* create_allprop_propfind_request(void) {
123     UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOFREE);
124     sstr_t s;
125     
126     s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
127     ucx_buffer_write(s.ptr, 1, s.length, buf);
128     
129     s = S("<D:propfind xmlns:D=\"DAV:\">\n");
130     ucx_buffer_write(s.ptr, 1, s.length, buf);
131     
132     s = S("<D:allprop/></D:propfind>\n");
133     ucx_buffer_write(s.ptr, 1, s.length, buf);
134     
135     return buf;
136 }
137
138 UcxBuffer* create_cryptoprop_propfind_request(void) {
139     UcxBuffer *buf = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOFREE);
140     scstr_t s;
141     
142     s = SC("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
143     ucx_buffer_write(s.ptr, 1, s.length, buf);
144     
145     s = SC("<D:propfind xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
146     ucx_buffer_write(s.ptr, 1, s.length, buf);
147     
148     s = SC("<D:prop><idav:crypto-prop/></D:prop></D:propfind>\n");
149     ucx_buffer_write(s.ptr, 1, s.length, buf);
150     
151     return buf;
152 }
153
154 UcxBuffer* create_propfind_request(DavSession *sn, UcxList *properties, char *rootelm, DavBool nocrypt) {
155     UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
156     sstr_t s;
157     
158     int add_crypto_name = 1;
159     int add_crypto_key = 1;
160     int add_crypto_hash = 1;
161     char *crypto_ns = "idav";
162     UcxMap *namespaces = ucx_map_new(8);
163     UCX_FOREACH(elm, properties) {
164         DavProperty *p = elm->data;
165         if(strcmp(p->ns->name, "DAV:")) {
166             ucx_map_cstr_put(namespaces, p->ns->prefix, p->ns);
167         }
168         
169         // if the properties list contains the idav properties crypto-name
170         // and crypto-key, mark them as existent 
171         if(!strcmp(p->ns->name, DAV_NS)) {
172             if(!strcmp(p->name, "crypto-name")) {
173                 add_crypto_name = 0;
174                 crypto_ns = p->ns->prefix;
175             } else if(!strcmp(p->name, "crypto-key")) {
176                 add_crypto_key = 0;
177                 crypto_ns = p->ns->prefix;
178             } else if(!strcmp(p->name, "crypto-hash")) {
179                 add_crypto_hash = 0;
180                 crypto_ns = p->ns->prefix;
181             }
182         }
183     }
184     
185     DavNamespace idav_ns;
186     if(add_crypto_name && add_crypto_key && DAV_CRYPTO(sn) && !nocrypt) {
187         idav_ns.prefix = "idav";
188         idav_ns.name = DAV_NS;
189         ucx_map_cstr_put(namespaces, "idav", &idav_ns);
190     }
191     
192     s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
193     ucx_buffer_write(s.ptr, 1, s.length, buf);
194     
195     // write root element and namespaces
196     ucx_bprintf(buf, "<D:%s xmlns:D=\"DAV:\"", rootelm);
197     
198     UcxMapIterator mapi = ucx_map_iterator(namespaces);
199     UcxKey key;
200     DavNamespace *ns;
201     UCX_MAP_FOREACH(key, ns, mapi) {
202         s = S(" xmlns:");
203         ucx_buffer_write(s.ptr, 1, s.length, buf);
204         s = sstr(ns->prefix);
205         ucx_buffer_write(s.ptr, 1, s.length, buf);
206         s = S("=\"");
207         ucx_buffer_write(s.ptr, 1, s.length, buf);
208         s = sstr(ns->name);
209         ucx_buffer_write(s.ptr, 1, s.length, buf);
210         s = S("\"");
211         ucx_buffer_write(s.ptr, 1, s.length, buf);
212     }
213     s = S(">\n");
214     ucx_buffer_write(s.ptr, 1, s.length, buf);
215     
216     // default properties
217     s = S("<D:prop>\n");
218     ucx_buffer_write(s.ptr, 1, s.length, buf);
219     
220     s = S("<D:creationdate />\n<D:getlastmodified />\n");
221     ucx_buffer_write(s.ptr, 1, s.length, buf);
222     
223     s = S("<D:getcontentlength />\n<D:getcontenttype />\n");
224     ucx_buffer_write(s.ptr, 1, s.length, buf);
225     
226     s = S("<D:resourcetype />\n");
227     ucx_buffer_write(s.ptr, 1, s.length, buf);
228     
229     // crypto properties
230     if(DAV_CRYPTO(sn) && !nocrypt) {
231         if(add_crypto_name) {
232             ucx_buffer_putc(buf, '<');
233             ucx_buffer_puts(buf, crypto_ns);
234             s = S(":crypto-name />\n");
235             ucx_buffer_write(s.ptr, 1, s.length, buf);
236         }
237         if(add_crypto_key) {
238             ucx_buffer_putc(buf, '<');
239             ucx_buffer_puts(buf, crypto_ns);
240             s = S(":crypto-key />\n");
241             ucx_buffer_write(s.ptr, 1, s.length, buf);
242         }
243         if(add_crypto_hash) {
244             ucx_buffer_putc(buf, '<');
245             ucx_buffer_puts(buf, crypto_ns);
246             s = S(":crypto-hash />\n");
247             ucx_buffer_write(s.ptr, 1, s.length, buf);
248         }
249     }
250     
251     // extra properties
252     UCX_FOREACH(elm, properties) {
253         DavProperty *prop = elm->data;
254         s = S("<");
255         ucx_buffer_write(s.ptr, 1, s.length, buf);
256         s = sstr(prop->ns->prefix);
257         ucx_buffer_write(s.ptr, 1, s.length, buf);
258         s = S(":");
259         ucx_buffer_write(s.ptr, 1, s.length, buf);
260         s = sstr(prop->name);
261         ucx_buffer_write(s.ptr, 1, s.length, buf);
262         s = S(" />\n");
263         ucx_buffer_write(s.ptr, 1, s.length, buf);
264     }
265     
266     // end
267     ucx_bprintf(buf, "</D:prop>\n</D:%s>\n", rootelm);
268     
269     ucx_map_free(namespaces);
270     return buf;
271 }
272
273 UcxBuffer* create_basic_propfind_request(void) {
274     UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
275     sstr_t s;
276     
277     s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
278     ucx_buffer_write(s.ptr, 1, s.length, buf);
279     
280     s = S("<D:propfind xmlns:D=\"DAV:\" xmlns:i=\"");
281     ucx_buffer_write(s.ptr, 1, s.length, buf);  
282     s = S(DAV_NS);
283     ucx_buffer_write(s.ptr, 1, s.length, buf);
284     s = S("\" >\n");
285     ucx_buffer_write(s.ptr, 1, s.length, buf);
286     
287     // properties
288     s = S("<D:prop>\n");
289     ucx_buffer_write(s.ptr, 1, s.length, buf);
290     s = S("<D:resourcetype />\n");
291     ucx_buffer_write(s.ptr, 1, s.length, buf);
292     s = S("<i:crypto-key />\n");
293     ucx_buffer_write(s.ptr, 1, s.length, buf);
294     s = S("<i:crypto-name />\n");
295     ucx_buffer_write(s.ptr, 1, s.length, buf);
296     s = S("<i:crypto-hash />\n");
297     ucx_buffer_write(s.ptr, 1, s.length, buf);
298     s = S("</D:prop>\n");
299     ucx_buffer_write(s.ptr, 1, s.length, buf);
300     
301     // end
302     s = S("</D:propfind>\n");
303     ucx_buffer_write(s.ptr, 1, s.length, buf);
304     
305     return buf;
306 }
307
308 PropfindParser* create_propfind_parser(UcxBuffer *response, char *url) {
309     PropfindParser *parser = malloc(sizeof(PropfindParser));
310     if(!parser) {
311         return NULL;
312     }
313     parser->document = xmlReadMemory(response->space, response->pos, url, NULL, 0);
314     parser->current = NULL;
315     if(parser->document) {
316         xmlNode *xml_root = xmlDocGetRootElement(parser->document);
317         if(xml_root) {
318             xmlNode *node = xml_root->children;
319             while(node) {
320                 // find first response tag
321                 if(node->type == XML_ELEMENT_NODE) {
322                     if(xstreq(node->name, "response")) {
323                         parser->current = node;
324                         break;
325                     }
326                 }
327                 node = node->next;
328             }
329             return parser;
330         } else {
331             xmlFreeDoc(parser->document);
332         }
333     }
334     free(parser);
335     return NULL;
336 }
337
338 void destroy_propfind_parser(PropfindParser *parser) {
339     if(parser->document) {
340         xmlFreeDoc(parser->document);
341     }
342     free(parser);
343 }
344
345 int get_propfind_response(PropfindParser *parser, ResponseTag *result) {
346     if(parser->current == NULL) {
347         return 0;
348     }
349     
350     char *href = NULL;
351     int iscollection = 0;
352     UcxList *properties = NULL; // xmlNode list
353     char *crypto_name = NULL; // name set by crypto-name property
354     char *crypto_key = NULL;
355     
356     result->properties = NULL;
357     
358     xmlNode *node = parser->current->children;
359     while(node) {
360         if(node->type == XML_ELEMENT_NODE) {
361             if(xstreq(node->name, "href")) {
362                 xmlNode *href_node = node->children;
363                 if(href_node->type != XML_TEXT_NODE) {
364                     // error
365                     return -1;
366                 }
367                 href = (char*)href_node->content;
368             } else if(xstreq(node->name, "propstat")) {
369                 xmlNode *n = node->children;
370                 xmlNode *prop_node = NULL;
371                 int ok = 0;
372                 // get the status code
373                 while(n) {
374                     if(n->type == XML_ELEMENT_NODE) {
375                         if(xstreq(n->name, "prop")) {
376                             prop_node = n;
377                         } else if(xstreq(n->name, "status")) {
378                             xmlNode *status_node = n->children;
379                             if(status_node->type != XML_TEXT_NODE) {
380                                 // error
381                                 return -1;
382                             }
383                             sstr_t status_str = sstr((char*)status_node->content);
384                             if(status_str.length < 13) {
385                                 // error
386                                 return -1;
387                             }
388                             status_str = sstrsubsl(status_str, 9, 3);
389                             if(!sstrcmp(status_str, S("200"))) {
390                                 ok = 1;
391                             }
392                         }
393                     }    
394                     n = n->next;
395                 }
396                 // if status is ok, get all properties
397                 if(ok) {
398                     n = prop_node->children;
399                     while(n) {
400                         if(n->type == XML_ELEMENT_NODE) {
401                             properties = ucx_list_append(properties, n);
402                             if(xstreq(n->name, "resourcetype")) {
403                                 if(parse_resource_type(n)) {
404                                     iscollection = TRUE;
405                                 }
406                             } else if(xstreq(n->ns->href, DAV_NS)) {
407                                 if(xstreq(n->name, "crypto-name")) {
408                                     crypto_name = util_xml_get_text(n);
409                                 } else if(xstreq(n->name, "crypto-key")) {
410                                     crypto_key = util_xml_get_text(n);
411                                 }
412                             }
413                         }
414                         n = n->next;
415                     }
416                 }
417             }
418         }
419         node = node->next;
420     }
421     
422     result->href = util_url_path(href);
423     result->iscollection = iscollection;
424     result->properties = properties;
425     result->crypto_name = crypto_name;
426     result->crypto_key = crypto_key;
427     
428     // find next response tag
429     xmlNode *next = parser->current->next;
430     while(next) {
431         if(next->type == XML_ELEMENT_NODE) {
432             if(xstreq(next->name, "response")) {
433                 break;
434             }
435         }
436         next = next->next;
437     }
438     parser->current = next;
439     
440     return 1;
441 }
442
443 void cleanup_response(ResponseTag *result) {
444     if(result) {
445         ucx_list_free(result->properties);
446     }
447 }
448
449 int hrefeq(DavSession *sn, char *href1, char *href2) {
450     sstr_t href_s = sstr(util_url_decode(sn, href1));
451     sstr_t href_r = sstr(util_url_decode(sn, href2));
452     int ret = 0;
453     if(!sstrcmp(href_s, href_r)) {
454         ret = 1;
455     } else if(href_s.length == href_r.length + 1) {
456         if(href_s.ptr[href_s.length-1] == '/') {
457             href_s.length--;
458             if(!sstrcmp(href_s, href_r)) {
459                 ret = 1;
460             }
461         }
462     } else if(href_r.length == href_s.length + 1) {
463         if(href_r.ptr[href_r.length-1] == '/') {
464             href_r.length--;
465             if(!sstrcmp(href_s, href_r)) {
466                 ret = 1;
467             }
468         }
469     } 
470
471     free(href_s.ptr);
472     free(href_r.ptr);
473     
474     return ret;
475 }
476
477
478 DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response) {
479     char *url = NULL;
480     curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &url);
481     if(!root) {
482         printf("methods.c: TODO: remove\n");
483         root = dav_resource_new_href(sn, util_url_path(url)); // TODO: remove
484     }
485     
486     //printf("%.*s\n\n", response->size, response->space);
487     xmlDoc *doc = xmlReadMemory(response->space, response->size, url, NULL, 0);
488     if(!doc) {
489         // TODO: free stuff
490         sn->error = DAV_ERROR;
491         return NULL;
492     }
493     
494     xmlNode *xml_root = xmlDocGetRootElement(doc);
495     xmlNode *node = xml_root->children;
496     while(node) {
497         if(node->type == XML_ELEMENT_NODE) {
498             if(xstreq(node->name, "response")) {
499                 parse_response_tag(root, node);
500             }
501         }
502         node = node->next;
503     }
504     xmlFreeDoc(doc);
505     
506     return root;
507 }
508
509 DavResource* response2resource(DavSession *sn, ResponseTag *response, char *parent_path) {
510     // create resource
511     char *name = NULL;
512     DavKey *key = NULL;
513     if(DAV_DECRYPT_NAME(sn) && response->crypto_name && (key = dav_context_get_key(sn->context, response->crypto_key))) {
514         if(!response->crypto_key) {
515             sn->error = DAV_ERROR;
516             dav_session_set_errstr(sn, "Missing crypto-key property");
517             return NULL;
518         }
519         name = util_decrypt_str_k(sn, response->crypto_name, key);
520         if(!name) {
521             sn->error = DAV_ERROR;
522             dav_session_set_errstr(sn, "Cannot decrypt resource name");
523             return NULL;
524         }
525     } else {
526         sstr_t resname = sstr(util_resource_name(response->href));
527         int nlen = 0;
528         char *uname = curl_easy_unescape(
529                 sn->handle,
530                 resname.ptr,
531                 resname.length,
532                 &nlen);
533         name = dav_session_strdup(sn, uname);
534         curl_free(uname);
535     }
536
537     char *href = dav_session_strdup(sn, response->href);
538     DavResource *res = NULL;
539     if(parent_path) {
540         res = dav_resource_new_full(sn, parent_path, name, href);
541     } else {
542         res = dav_resource_new_href(sn, href);
543     }
544     dav_session_free(sn, name);
545     
546     add_properties(res, response); 
547     return res;
548 }
549
550 void add_properties(DavResource *res, ResponseTag *response) {
551     res->iscollection = response->iscollection;
552     
553     int decrypt_props = DAV_ENCRYPT_PROPERTIES(res->session);
554     xmlNode *crypto_prop = NULL;
555     char *crypto_key = NULL;
556     
557     // add properties
558     UCX_FOREACH(elm, response->properties) {
559         xmlNode *prop = elm->data;
560         resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children);
561         
562         if (decrypt_props &&
563             prop->children &&
564             prop->children->type == XML_TEXT_NODE &&
565             xstreq(prop->ns->href, DAV_NS))
566         {
567             if(xstreq(prop->name, "crypto-prop")) {
568                 crypto_prop = prop;
569             } else if(xstreq(prop->name, "crypto-key")) {
570                 crypto_key = util_xml_get_text(prop);
571             }
572         }
573     }
574     
575     if(crypto_prop && crypto_key) {
576         char *crypto_prop_content = util_xml_get_text(crypto_prop);
577         DavKey *key = dav_context_get_key(res->session->context, crypto_key);
578         if(crypto_prop_content) {
579             UcxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content);
580             resource_set_crypto_properties(res, cprops);
581         }
582     }
583     
584     set_davprops(res);
585 }
586
587 int parse_response_tag(DavResource *resource, xmlNode *node) {
588     DavSession *sn = resource->session;
589     
590     //DavResource *res = resource;
591     DavResource *res = NULL;
592     char *href = NULL;
593     UcxList *properties = NULL; // xmlNode list
594     char *crypto_name = NULL; // name set by crypto-name property
595     char *crypto_key = NULL;
596     
597     int iscollection = 0; // TODO: remove
598     
599     node = node->children;
600     while(node) {
601         if(node->type == XML_ELEMENT_NODE) {
602             if(xstreq(node->name, "href")) {
603                 xmlNode *href_node = node->children;
604                 if(href_node->type != XML_TEXT_NODE) {
605                     // error
606                     sn->error = DAV_ERROR;
607                     return 1;
608                 }
609                 //char *href = (char*)href_node->content;
610                 href = util_url_path((char*)href_node->content);
611                 
612                 char *href_s = util_url_decode(resource->session, href);
613                 char *href_r = util_url_decode(resource->session, resource->href);
614                 
615                 if(hrefeq(sn, href_s, href_r)) {
616                     res = resource;
617                 }   
618                 
619                 free(href_s);
620                 free(href_r);
621             } else if(xstreq(node->name, "propstat")) {
622                 xmlNode *n = node->children;
623                 xmlNode *prop_node = NULL;
624                 int ok = 0;
625                 // get the status code
626                 while(n) {
627                     if(n->type == XML_ELEMENT_NODE) {
628                         if(xstreq(n->name, "prop")) {
629                             prop_node = n;
630                         } else if(xstreq(n->name, "status")) {
631                             xmlNode *status_node = n->children;
632                             if(status_node->type != XML_TEXT_NODE) {
633                                 sn->error = DAV_ERROR;
634                                 return 1;
635                             }
636                             sstr_t status_str = sstr((char*)status_node->content);
637                             if(status_str.length < 13) {
638                                 sn->error = DAV_ERROR;
639                                 return 1;
640                             }
641                             status_str = sstrsubsl(status_str, 9, 3);
642                             if(!sstrcmp(status_str, S("200"))) {
643                                 ok = 1;
644                             }
645                         }
646                     }    
647                     n = n->next;
648                 }
649                 // if status is ok, get all properties
650                 if(ok) {
651                     n = prop_node->children;
652                     while(n) {
653                         if(n->type == XML_ELEMENT_NODE) {
654                             properties = ucx_list_append(properties, n);
655                             if(xstreq(n->name, "resourcetype")) {
656                                 if(parse_resource_type(n)) {
657                                     iscollection = TRUE;
658                                 }
659                             } else if(xstreq(n->ns->href, DAV_NS)) {
660                                 if(xstreq(n->name, "crypto-name")) {
661                                     crypto_name = util_xml_get_text(n);
662                                 } else if(xstreq(n->name, "crypto-key")) {
663                                     crypto_key = util_xml_get_text(n);
664                                 }
665                             }
666                         }
667                         n = n->next;
668                     }
669                 }
670             }
671         }
672         
673         node = node->next;
674     }
675     
676     if(!res) {
677         // create new resource object
678         char *name = NULL;
679         if(DAV_DECRYPT_NAME(sn) && crypto_name) {
680             if(!crypto_key) {
681                 sn->error = DAV_ERROR;
682                 dav_session_set_errstr(sn, "Missing crypto-key property");
683                 return -1;
684             }
685             name = util_decrypt_str(sn, crypto_name, crypto_key);
686             if(!name) {
687                 sn->error = DAV_ERROR;
688                 dav_session_set_errstr(sn, "Cannot decrypt resource name");
689                 return -1;
690             }
691         } else {
692             sstr_t resname = sstr(util_resource_name(href));
693             int nlen = 0;
694             char *uname = curl_easy_unescape(
695                     sn->handle,
696                     resname.ptr,
697                     resname.length,
698                     &nlen);
699             name = dav_session_strdup(sn, uname);
700             curl_free(uname);
701         }
702         
703         href = dav_session_strdup(sn, href);
704         res = dav_resource_new_full(sn, resource->path, name, href);
705         
706         dav_session_free(sn, name);
707     }
708     res->iscollection = iscollection;
709     
710     // add properties
711     int decrypt_props = DAV_ENCRYPT_PROPERTIES(res->session);
712     xmlNode *crypto_prop = NULL;
713     
714     UCX_FOREACH(elm, properties) {
715         xmlNode *prop = elm->data;
716         resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children);
717         
718         if (decrypt_props &&
719             prop->children &&
720             prop->children->type == XML_TEXT_NODE &&
721             xstreq(prop->ns->href, DAV_NS))
722         {
723             if(xstreq(prop->name, "crypto-prop")) {
724                 crypto_prop = prop;
725             }
726         }
727     }
728     ucx_list_free(properties);
729     
730     if(crypto_prop && crypto_key) {
731         char *crypto_prop_content = util_xml_get_text(crypto_prop);
732         DavKey *key = dav_context_get_key(res->session->context, crypto_key);
733         if(crypto_prop_content && key) {
734             UcxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content);
735             resource_set_crypto_properties(res, cprops);
736         }
737     }
738     
739     
740     set_davprops(res);
741     if(res != resource) {
742         resource_add_child(resource, res);
743     }
744     
745     return 0;
746 }
747
748 void set_davprops(DavResource *res) {
749     char *cl = dav_get_string_property_ns(res, "DAV:", "getcontentlength");
750     char *ct = dav_get_string_property_ns(res, "DAV:", "getcontenttype");
751     char *cd = dav_get_string_property_ns(res, "DAV:", "creationdate");
752     char *lm = dav_get_string_property_ns(res, "DAV:", "getlastmodified");
753     
754     res->contenttype = ct;
755     if(cl) {
756         char *end = NULL;
757         res->contentlength = strtoull(cl, &end, 0);
758     }
759     res->creationdate = util_parse_creationdate(cd);
760     res->lastmodified = util_parse_lastmodified(lm);
761 }
762
763 int parse_resource_type(xmlNode *node) {
764     int collection = FALSE;
765     xmlNode *c = node->children;
766     while(c) {
767         if(c->type == XML_ELEMENT_NODE) {
768             if(xstreq(c->ns->href, "DAV:") && xstreq(c->name, "collection")) {
769                 collection = TRUE;
770                 break;
771             }
772         }
773         c = c->next;
774     }
775     return collection;
776 }
777
778
779 /* ----------------------------- PROPPATCH ----------------------------- */
780
781 CURLcode do_proppatch_request(
782         DavSession *sn,
783         char *lock,
784         UcxBuffer *request,
785         UcxBuffer *response)
786 {       
787     CURL *handle = sn->handle;
788     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPPATCH");
789     
790     struct curl_slist *headers = NULL;
791     headers = curl_slist_append(headers, "Content-Type: text/xml"); 
792     if(lock) {
793         char *url = NULL;
794         curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
795         char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
796         headers = curl_slist_append(headers, ltheader);
797         free(ltheader);
798         curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
799     }
800     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
801     
802     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 
803     curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read);
804     curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, dav_buffer_seek);
805     curl_easy_setopt(handle, CURLOPT_READDATA, request); 
806     curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size);
807     
808     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
809     curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
810     
811     ucx_buffer_seek(request, 0, SEEK_SET);
812     CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL);
813     curl_slist_free_all(headers);
814     
815     //printf("proppatch: \n%.*s\n", request->size, request->space);
816     
817     return ret;
818 }
819
820 UcxBuffer* create_proppatch_request(DavResourceData *data) {
821     UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
822     scstr_t s;
823     
824     UcxMap *namespaces = ucx_map_new(8);
825     char prefix[8];
826     int pfxnum = 0;
827     UCX_FOREACH(elm, data->set) {
828         DavProperty *p = elm->data;
829         if(strcmp(p->ns->name, "DAV:")) {
830             snprintf(prefix, 8, "x%d", pfxnum++);
831             ucx_map_cstr_put(namespaces, p->ns->name, strdup(prefix));
832         }
833     }
834     UCX_FOREACH(elm, data->remove) {
835         DavProperty *p = elm->data;
836         if(strcmp(p->ns->name, "DAV:")) {
837             snprintf(prefix, 8, "x%d", pfxnum++);
838             ucx_map_cstr_put(namespaces, p->ns->name, strdup(prefix));
839         }
840     }
841     
842     s = SC("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
843     ucx_buffer_write(s.ptr, 1, s.length, buf);
844     
845     // write root element and namespaces
846     s = SC("<D:propertyupdate xmlns:D=\"DAV:\"");
847     ucx_buffer_write(s.ptr, 1, s.length, buf);
848     UcxMapIterator mapi = ucx_map_iterator(namespaces);
849     UcxKey key;
850     char *pfxval;
851     UCX_MAP_FOREACH(key, pfxval, mapi) {
852         s = SC(" xmlns:");
853         ucx_buffer_write(s.ptr, 1, s.length, buf);
854         s = scstr(pfxval);
855         ucx_buffer_write(s.ptr, 1, s.length, buf);
856         s = SC("=\"");
857         ucx_buffer_write(s.ptr, 1, s.length, buf);
858         s = scstrn(key.data, key.len);
859         ucx_buffer_write(s.ptr, 1, s.length, buf);
860         s = SC("\"");
861         ucx_buffer_write(s.ptr, 1, s.length, buf);
862     }
863     s = SC(">\n");
864     ucx_buffer_write(s.ptr, 1, s.length, buf);
865     
866     if(data->set) {
867         s = SC("<D:set>\n<D:prop>\n");
868         ucx_buffer_write(s.ptr, 1, s.length, buf);
869         UCX_FOREACH(elm, data->set) {
870             DavProperty *property = elm->data;
871             char *prefix = ucx_map_cstr_get(namespaces, property->ns->name);
872             if(!prefix) {
873                 prefix = "D";
874             }
875             
876             // begin tag
877             s = SC("<");
878             ucx_buffer_write(s.ptr, 1, s.length, buf);
879             s = scstr(prefix);
880             ucx_buffer_write(s.ptr, 1, s.length, buf);
881             s = SC(":");
882             ucx_buffer_write(s.ptr, 1, s.length, buf);
883             s = scstr(property->name);
884             ucx_buffer_write(s.ptr, 1, s.length, buf);
885             s = SC(">");
886             ucx_buffer_write(s.ptr, 1, s.length, buf);
887             
888             // content
889             DavXmlNode *content = property->value;
890             if(content->type == DAV_XML_TEXT && !content->next) {
891                 ucx_buffer_write(content->content, 1, content->contentlength, buf);
892             } else {
893                 dav_print_node(buf, (write_func)ucx_buffer_write, namespaces, content);
894             }
895             
896             // end tag
897             s = SC("</");
898             ucx_buffer_write(s.ptr, 1, s.length, buf);
899             s = scstr(prefix);
900             ucx_buffer_write(s.ptr, 1, s.length, buf);
901             s = SC(":");
902             ucx_buffer_write(s.ptr, 1, s.length, buf);
903             s = scstr(property->name);
904             ucx_buffer_write(s.ptr, 1, s.length, buf);
905             s = SC(">\n");
906             ucx_buffer_write(s.ptr, 1, s.length, buf);
907         }
908         s = SC("</D:prop>\n</D:set>\n");
909         ucx_buffer_write(s.ptr, 1, s.length, buf);
910     }
911     if(data->remove) {
912         s = SC("<D:remove>\n<D:prop>\n");
913         ucx_buffer_write(s.ptr, 1, s.length, buf);
914         UCX_FOREACH(elm, data->remove) {
915             DavProperty *property = elm->data;
916             char *prefix = ucx_map_cstr_get(namespaces, property->ns->name);
917             
918             s = SC("<");
919             ucx_buffer_write(s.ptr, 1, s.length, buf);
920             s = scstr(prefix);
921             ucx_buffer_write(s.ptr, 1, s.length, buf);
922             s = SC(":");
923             ucx_buffer_write(s.ptr, 1, s.length, buf);
924             s = scstr(property->name);
925             ucx_buffer_write(s.ptr, 1, s.length, buf);
926             s = SC(" />\n");
927             ucx_buffer_write(s.ptr, 1, s.length, buf);
928         }
929         s = SC("</D:prop>\n</D:remove>\n");
930         ucx_buffer_write(s.ptr, 1, s.length, buf);
931     }
932     
933     s = SC("</D:propertyupdate>\n");
934     ucx_buffer_write(s.ptr, 1, s.length, buf);
935     
936     // cleanup namespace map
937     ucx_map_free_content(namespaces, free);
938     ucx_map_free(namespaces);
939     
940     return buf;
941 }
942
943 UcxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash) {
944     UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
945     sstr_t s;
946     
947     s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
948     ucx_buffer_write(s.ptr, 1, s.length, buf);
949     
950     s = S("<D:propertyupdate xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
951     ucx_buffer_write(s.ptr, 1, s.length, buf);
952     
953     s = S("<D:set>\n<D:prop>\n");
954     ucx_buffer_write(s.ptr, 1, s.length, buf);
955     
956     if(DAV_ENCRYPT_NAME(sn)) {
957         s = S("<idav:crypto-name>");
958         ucx_buffer_write(s.ptr, 1, s.length, buf);
959         char *crname = aes_encrypt(name, strlen(name), key);
960         ucx_buffer_puts(buf, crname);
961         free(crname);
962         s = S("</idav:crypto-name>\n");
963         ucx_buffer_write(s.ptr, 1, s.length, buf);
964     }
965     
966     s = S("<idav:crypto-key>");
967     ucx_buffer_write(s.ptr, 1, s.length, buf);
968     ucx_buffer_puts(buf, key->name);
969     s = S("</idav:crypto-key>\n");
970     ucx_buffer_write(s.ptr, 1, s.length, buf);
971     
972     if(hash) {
973         s = S("<idav:crypto-hash>");
974         ucx_buffer_write(s.ptr, 1, s.length, buf);
975         ucx_buffer_puts(buf, hash);
976         s = S("</idav:crypto-hash>\n");
977         ucx_buffer_write(s.ptr, 1, s.length, buf);
978     }
979     
980     s = S("</D:prop>\n</D:set>\n</D:propertyupdate>\n");
981     ucx_buffer_write(s.ptr, 1, s.length, buf);
982     
983     return buf;
984 }
985
986 /* ----------------------------- PUT ----------------------------- */
987
988 static size_t dummy_write(void *buf, size_t s, size_t n, void *data) {
989     //fwrite(buf, s, n, stdout);
990     return s*n;
991 }
992
993 CURLcode do_put_request(DavSession *sn, char *lock, DavBool create, void *data, dav_read_func read_func, dav_seek_func seek_func, size_t length) {
994     CURL *handle = sn->handle;
995     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL);
996     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
997     
998     // clear headers
999     struct curl_slist *headers = NULL;
1000     if(lock) {
1001         char *url = NULL;
1002         curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
1003         char *ltheader = NULL;
1004         if(create) {
1005             url = util_parent_path(url);
1006             ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
1007             free(url);
1008         } else {
1009             ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
1010         }
1011         headers = curl_slist_append(headers, ltheader);
1012         free(ltheader);
1013         curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
1014     }
1015     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
1016     
1017     UcxBuffer *buf = NULL;
1018     if(!read_func) {
1019         buf = ucx_buffer_new(data, length, 0);
1020         buf->size = length;
1021         data = buf;
1022         read_func = (dav_read_func)ucx_buffer_read;
1023         curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length);
1024     } else if(length == 0) {
1025         headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
1026         curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)1);
1027         curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
1028     } else {
1029         curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length);
1030     }
1031     
1032     curl_easy_setopt(handle, CURLOPT_READFUNCTION, read_func);
1033     curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, seek_func);
1034     curl_easy_setopt(handle, CURLOPT_READDATA, data);
1035     
1036     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
1037     curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
1038     
1039     CURLcode ret = dav_session_curl_perform(sn, NULL);
1040     curl_slist_free_all(headers);
1041     if(buf) {
1042         ucx_buffer_free(buf);
1043     }
1044     
1045     return ret;
1046 }
1047
1048 CURLcode do_delete_request(DavSession *sn, char *lock, UcxBuffer *response) { 
1049     CURL *handle = sn->handle;
1050     struct curl_slist *headers = NULL;
1051     if(lock) {
1052         char *url = NULL;
1053         curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
1054         char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
1055         headers = curl_slist_append(headers, ltheader);
1056         free(ltheader);
1057         curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
1058     } else {
1059         curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL);
1060     }
1061     
1062     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "DELETE");
1063     curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
1064     
1065     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
1066     curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
1067     
1068     CURLcode ret = dav_session_curl_perform(sn, NULL);
1069     curl_slist_free_all(headers);
1070     return ret;
1071 }
1072
1073 CURLcode do_mkcol_request(DavSession *sn, char *lock) {
1074     CURL *handle = sn->handle;
1075     struct curl_slist *headers = NULL;
1076     if(lock) {
1077         char *url = NULL;
1078         curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
1079         url = util_parent_path(url);
1080         char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
1081         free(url);
1082         headers = curl_slist_append(headers, ltheader);
1083         free(ltheader);
1084         curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
1085     } else {
1086         curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL);
1087     }
1088     
1089     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MKCOL");
1090     curl_easy_setopt(handle, CURLOPT_PUT, 0L);  
1091     curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
1092     
1093     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
1094     curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
1095     
1096     CURLcode ret = dav_session_curl_perform(sn, NULL);
1097     curl_slist_free_all(headers);
1098     return ret;
1099 }
1100
1101
1102 CURLcode do_head_request(DavSession *sn) {
1103     CURL *handle = sn->handle;
1104     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "HEAD");
1105     curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
1106     curl_easy_setopt(handle, CURLOPT_NOBODY, 1L);
1107     
1108     // clear headers
1109     struct curl_slist *headers = NULL;
1110     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
1111     
1112     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
1113     curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
1114     
1115     CURLcode ret = dav_session_curl_perform(sn, NULL);
1116     curl_easy_setopt(handle, CURLOPT_NOBODY, 0L);
1117     return ret;
1118 }
1119
1120
1121 CURLcode do_copy_move_request(DavSession *sn, char *dest, char *lock, DavBool copy, DavBool override) { 
1122     CURL *handle = sn->handle;
1123     if(copy) {
1124         curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "COPY");
1125     } else {
1126         curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MOVE");
1127     }
1128     curl_easy_setopt(handle, CURLOPT_PUT, 0L);  
1129     curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
1130     
1131     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
1132     curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
1133     
1134     struct curl_slist *headers = NULL;
1135     if(lock) {
1136         char *url = NULL;
1137         curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
1138         char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
1139         headers = curl_slist_append(headers, ltheader);
1140         free(ltheader);
1141     }
1142     //sstr_t deststr = ucx_sprintf("Destination: %s", dest);
1143     sstr_t deststr = sstrcat(2, S("Destination: "), sstr(dest));
1144     headers = curl_slist_append(headers, deststr.ptr);
1145     if(override) {
1146         headers = curl_slist_append(headers, "Overwrite: T");
1147     } else {
1148         headers = curl_slist_append(headers, "Overwrite: F");
1149     }
1150     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
1151     
1152     CURLcode ret = dav_session_curl_perform(sn, NULL);
1153     free(deststr.ptr);
1154     curl_slist_free_all(headers);
1155     headers = NULL;
1156     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
1157     return ret;
1158 }
1159
1160
1161 UcxBuffer* create_lock_request(void) {
1162     UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
1163     sstr_t s;
1164     
1165     s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
1166     ucx_buffer_write(s.ptr, 1, s.length, buf);
1167     
1168     s = S("<D:lockinfo xmlns:D=\"DAV:\">\n"
1169           "<D:lockscope><D:exclusive/></D:lockscope>\n"
1170           "<D:locktype><D:write/></D:locktype>\n"
1171           "<D:owner><D:href>http://davutils.org/libidav/</D:href></D:owner>\n");
1172     ucx_buffer_write(s.ptr, 1, s.length, buf);
1173     
1174     s = S("</D:lockinfo>\n");
1175     ucx_buffer_write(s.ptr, 1, s.length, buf);
1176     
1177     return buf;
1178 }
1179
1180 int parse_lock_response(DavSession *sn, UcxBuffer *response, LockDiscovery *lock) {
1181     lock->locktoken = NULL;
1182     lock->timeout = NULL;
1183     
1184     xmlDoc *doc = xmlReadMemory(response->space, response->size, NULL, NULL, 0);
1185     if(!doc) {
1186         sn->error = DAV_ERROR;
1187         return -1;
1188     }
1189     
1190     char *timeout = NULL;
1191     char *locktoken = NULL;
1192     
1193     int ret = -1;
1194     xmlNode *xml_root = xmlDocGetRootElement(doc);
1195     DavBool lockdiscovery = 0;
1196     if(xml_root) {
1197         xmlNode *node = xml_root->children;
1198         while(node) {
1199             if(node->type == XML_ELEMENT_NODE) {
1200                 if(xstreq(node->name, "lockdiscovery")) {
1201                     node = node->children;
1202                     lockdiscovery = 1;
1203                     continue;
1204                 }
1205                 
1206                 if(xstreq(node->name, "activelock")) {
1207                     node = node->children;
1208                     continue;
1209                 }
1210                 
1211                 if(lockdiscovery) {
1212                     if(xstreq(node->name, "timeout")) {
1213                         timeout = util_xml_get_text(node);
1214                     } else if(xstreq(node->name, "locktoken")) {
1215                         xmlNode *n = node->children;
1216                         while(n) {
1217                             if(xstreq(n->name, "href")) {
1218                                 locktoken = util_xml_get_text(n);
1219                                 break;
1220                             }
1221                             n = n->next;
1222                         }
1223                     }
1224                 }
1225             }
1226             node = node->next;
1227         }
1228     }
1229     
1230     if(timeout && locktoken) {
1231         lock->timeout = strdup(timeout);
1232         lock->locktoken = strdup(locktoken);
1233         ret = 0;
1234     }
1235     
1236     xmlFreeDoc(doc);
1237     return ret;
1238 }
1239
1240 CURLcode do_lock_request(DavSession *sn, UcxBuffer *request, UcxBuffer *response, time_t timeout) { 
1241     CURL *handle = sn->handle;
1242     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "LOCK");  
1243     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
1244     request->pos = 0;
1245     
1246     // clear headers    
1247     struct curl_slist *headers = NULL;
1248     
1249     if(timeout != 0) {
1250         sstr_t thdr;
1251         if(timeout < 0) {
1252             thdr = ucx_sprintf("%s", "Timeout: Infinite");
1253         } else {
1254             thdr = ucx_sprintf("Timeout: Second-%u", (unsigned int)timeout);
1255         }
1256         headers = curl_slist_append(headers, thdr.ptr);
1257         free(thdr.ptr);
1258     }
1259     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
1260     
1261     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 
1262     curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read);
1263     curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, dav_buffer_seek);
1264     curl_easy_setopt(handle, CURLOPT_READDATA, request); 
1265     curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size);
1266     
1267     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
1268     curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
1269     
1270     CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL);
1271     
1272     if(headers) {
1273         curl_slist_free_all(headers);
1274     }
1275     
1276     return ret;
1277 }
1278
1279 CURLcode do_unlock_request(DavSession *sn, char *locktoken) {   
1280     CURL *handle = sn->handle;
1281     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "UNLOCK");
1282     curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
1283     
1284     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
1285     curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
1286     
1287     // set lock-token header
1288     sstr_t ltheader = ucx_sprintf("Lock-Token: <%s>", locktoken);
1289     struct curl_slist *headers = curl_slist_append(NULL, ltheader.ptr);
1290     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
1291     
1292     CURLcode ret = dav_session_curl_perform(sn, NULL);
1293     curl_slist_free_all(headers);
1294     free(ltheader.ptr);
1295     
1296     return ret;
1297 }
1298
1299 CURLcode do_simple_request(DavSession *sn, char *method, char *locktoken) {
1300     CURL *handle = sn->handle;
1301     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, method);
1302     curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
1303     
1304     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
1305     curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
1306     
1307     // set lock-token header
1308     sstr_t ltheader;
1309     struct curl_slist *headers = NULL;
1310     if(locktoken) {
1311         ltheader = ucx_sprintf("Lock-Token: <%s>", locktoken);
1312         headers = curl_slist_append(NULL, ltheader.ptr);
1313     }
1314     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
1315     
1316     CURLcode ret = dav_session_curl_perform(sn, NULL);
1317     if(locktoken) {
1318         curl_slist_free_all(headers);
1319         free(ltheader.ptr);
1320     }
1321     
1322     return ret;
1323 }
1324
1325
1326 CURLcode do_report_request(DavSession *sn, UcxBuffer *request, UcxBuffer *response) {
1327     CURL *handle = sn->handle;
1328     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "REPORT");
1329     
1330     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 
1331     curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read);
1332     curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, dav_buffer_seek);
1333     curl_easy_setopt(handle, CURLOPT_READDATA, request); 
1334     curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size);
1335     
1336     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
1337     curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
1338     
1339     struct curl_slist *headers = NULL;
1340     headers = curl_slist_append(headers, "Content-Type: text/xml");
1341     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
1342     
1343     request->pos = 0;
1344     response->size = response->pos = 0;
1345     CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL);
1346     
1347     curl_slist_free_all(headers);
1348     
1349     return ret;
1350 }