move control socket handling to separate file
[mizunara.git] / libidav / resource.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 #include <stdbool.h>
33 #include <libxml/tree.h>
34
35 #include "utils.h"
36 #include "session.h"
37 #include "methods.h"
38 #include "crypto.h"
39 #include "ucx/buffer.h"
40 #include "ucx/utils.h"
41
42 #include "resource.h"
43 #include "xml.h"
44 #include "davqlexec.h"
45
46 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
47
48 DavResource* dav_resource_new(DavSession *sn, char *path) {
49     //char *href = util_url_path(url);
50     //DavResource *res = dav_resource_new_href(sn, href);
51     char *parent = util_parent_path(path);
52     char *name = util_resource_name(path); 
53     char *href = dav_session_create_plain_href(sn, path);
54     
55     DavResource *res = dav_resource_new_full(sn, parent, name, href);
56     free(parent);
57     return res;
58 }
59
60 DavResource* dav_resource_new_child(DavSession *sn, DavResource *parent, char *name) {
61     char *path = util_concat_path(parent->path, name);
62     char *href = dav_session_create_plain_href(sn, path);
63     DavResource *res = dav_resource_new_full(sn, parent->path, name, href);
64     free(path);
65     return res;
66 }
67
68
69 DavResource* dav_resource_new_href(DavSession *sn, char *href) {  
70     DavResource *res = ucx_mempool_calloc(sn->mp, 1, sizeof(DavResource));
71     res->session = sn;
72     
73     // set name, path and href
74     resource_set_info(res, href);
75     
76     // initialize resource data
77     res->data = resource_data_new(sn);
78     
79     return res;
80 }
81
82 DavResource* dav_resource_new_full(DavSession *sn, char *parent_path, char *name, char *href) {
83     sstr_t n = sstr(name);
84     // the name must not contain path separators
85     if(n.length > 0 && href) {
86         for(int i=0;i<n.length-1;i++) {
87             char c = n.ptr[i];
88             if(c == '/' || c == '\\') {
89                 n = sstr(util_resource_name(href));
90                 break;
91             }
92         }
93     }
94     // remove trailing '/'
95     if(n.length > 0 && n.ptr[n.length-1] == '/') {
96         n.length--;
97     }
98     
99     DavResource *res = ucx_mempool_calloc(sn->mp, 1, sizeof(DavResource));
100     res->session = sn;
101     
102     // set name, path and href
103     res->name = sstrdup_a(sn->mp->allocator, n).ptr;
104     
105     char *path = util_concat_path(parent_path, name); 
106     res->path = dav_session_strdup(sn, path);
107     
108     res->href = href;
109     
110     // initialize resource data
111     res->data = resource_data_new(sn);
112     
113     // cache href/path
114     if(href) {
115         dav_session_cache_path(sn, sstr(path), sstr(href));
116     }
117     free(path);
118     
119     return res;
120 }
121
122 void resource_free_properties(DavSession *sn, UcxMap *properties) {
123     if(!properties) return;
124     
125     UcxMapIterator i = ucx_map_iterator(properties);
126     DavProperty *property;
127     UCX_MAP_FOREACH(key, property, i) {
128         // TODO: free everything
129         dav_session_free(sn, property);
130     }
131     ucx_map_free(properties);
132 }
133
134 void dav_resource_free(DavResource *res) {
135     DavSession *sn = res->session;
136     
137     dav_session_free(sn, res->name);
138     dav_session_free(sn, res->path);
139     if(res->href) {
140         dav_session_free(sn, res->href);
141     }
142     
143     DavResourceData *data = res->data;
144     resource_free_properties(sn, data->properties);
145     resource_free_properties(sn, data->crypto_properties);
146     
147     UCX_FOREACH(elm, data->set) {
148         DavProperty *p = elm->data;
149         dav_session_free(sn, p->ns->name);
150         if(p->ns->prefix) {
151             dav_session_free(sn, p->ns->prefix);
152         }
153         dav_session_free(sn, p->ns);
154         
155         dav_session_free(sn, p->name);
156         dav_session_free(sn, p->value);
157         dav_session_free(sn, p);
158     }
159     
160     UCX_FOREACH(elm, data->remove) {
161         DavProperty *p = elm->data;
162         dav_session_free(sn, p->ns->name);
163         if(p->ns->prefix) {
164             dav_session_free(sn, p->ns->prefix);
165         }
166         dav_session_free(sn, p->ns);
167         
168         dav_session_free(sn, p->name);
169         dav_session_free(sn, p);
170     }
171     
172     if(!data->read && data->content) {
173         dav_session_free(sn, data->content);
174     }
175     dav_session_free(sn, data);
176     
177     dav_session_free(sn, res);
178 }
179
180 void dav_resource_free_all(DavResource *res) {
181     DavResource *child = res->children;
182     dav_resource_free(res);
183     while(child) {
184         DavResource *next = child->next;
185         dav_resource_free_all(child);
186         child = next;
187     }
188 }
189
190 void resource_set_href(DavResource *res, sstr_t href) {
191     res->href = sstrdup_a(res->session->mp->allocator, href).ptr;
192 }
193
194 void resource_set_info(DavResource *res, char *href_str) {
195     char *url_str = NULL;
196     curl_easy_getinfo(res->session->handle, CURLINFO_EFFECTIVE_URL, &url_str);
197     sstr_t name = sstr(util_resource_name(href_str));
198     sstr_t href = sstr(href_str);
199     
200     sstr_t base_href = sstr(util_url_path(res->session->base_url));
201     sstr_t path = sstrsubs(href, base_href.length - 1);
202     
203     UcxAllocator *a = res->session->mp->allocator;
204     CURL *handle = res->session->handle;
205     
206     int nlen = 0;
207     char *uname = curl_easy_unescape(handle, name.ptr, name.length , &nlen);
208     int plen = 0;
209     char *upath = curl_easy_unescape(handle, path.ptr, path.length, &plen); 
210     
211     res->name = sstrdup_a(a, sstrn(uname, nlen)).ptr;
212     res->href = sstrdup_a(a, href).ptr;
213     res->path = sstrdup_a(a, sstrn(upath, plen)).ptr;
214     
215     curl_free(uname);
216     curl_free(upath);
217 }
218
219 DavResourceData* resource_data_new(DavSession *sn) {
220     DavResourceData *data = ucx_mempool_malloc(
221             sn->mp,
222             sizeof(DavResourceData));
223     if(!data) {
224         return NULL;
225     }
226     data->properties = ucx_map_new_a(sn->mp->allocator, 32);
227     data->crypto_properties = NULL;
228     data->set = NULL;
229     data->remove = NULL;
230     data->crypto_set = NULL;
231     data->crypto_remove = NULL;
232     data->read = NULL;
233     data->content = NULL;
234     data->seek = NULL;
235     data->length = 0;
236     return data;
237 }
238
239 char* dav_resource_get_href(DavResource *resource) {
240     if(!resource->href) {
241         resource->href = dav_session_get_href(
242                 resource->session,
243                 resource->path);
244     }
245     return resource->href;
246 }
247
248 void resource_add_prop(DavResource *res, const char *ns, const char *name, DavXmlNode *val) {
249     DavSession *sn = res->session;
250     
251     DavNamespace *namespace = dav_session_malloc(sn, sizeof(DavNamespace));
252     namespace->prefix = NULL;
253     namespace->name = dav_session_strdup(sn, ns);
254     
255     DavProperty *prop = dav_session_malloc(sn, sizeof(DavProperty));
256     prop->name = dav_session_strdup(sn, name);
257     prop->ns = namespace;
258     prop->value = val;
259     
260     sstr_t key = dav_property_key(ns, name);
261     ucx_map_sstr_put(((DavResourceData*)res->data)->properties, key, prop);
262     free(key.ptr);
263 }
264
265 void resource_add_property(DavResource *res, const char *ns, const char *name, xmlNode *val) {
266     if(!val) {
267         return;
268     }
269     
270     resource_add_prop(res, ns, name, dav_convert_xml(res->session, val));
271 }
272
273 void resource_add_string_property(DavResource *res, char *ns, char *name, char *val) {
274     if(!val) {
275         return;
276     }
277     
278     resource_add_prop(res, ns, name, dav_text_node(res->session, val));
279 }
280
281 void resource_set_crypto_properties(DavResource *res, UcxMap *cprops) {
282     DavResourceData *data = res->data;
283     resource_free_properties(res->session, data->crypto_properties);
284     data->crypto_properties = cprops;
285 }
286
287 DavXmlNode* resource_get_property(DavResource *res, const char *ns, const char *name) {
288     sstr_t keystr = dav_property_key(ns, name);
289     UcxKey key = ucx_key(keystr.ptr, keystr.length);
290     DavXmlNode *ret = resource_get_property_k(res, key);
291     free(keystr.ptr);
292     
293     return ret;
294 }
295
296 DavXmlNode* resource_get_encrypted_property(DavResource *res, const char *ns, const char *name) {
297     sstr_t keystr = dav_property_key(ns, name);
298     UcxKey key = ucx_key(keystr.ptr, keystr.length);
299     DavXmlNode *ret = resource_get_encrypted_property_k(res, key);
300     free(keystr.ptr);
301     
302     return ret;
303 }
304
305 DavXmlNode* resource_get_property_k(DavResource *res, UcxKey key) {
306     DavResourceData *data = (DavResourceData*)res->data;
307     DavProperty *property = ucx_map_get(data->properties, key);
308     
309     return property ? property->value : NULL;
310 }
311
312 DavXmlNode* resource_get_encrypted_property_k(DavResource *res, UcxKey key) {
313     DavResourceData *data = (DavResourceData*)res->data;
314     DavProperty *property = ucx_map_get(data->crypto_properties, key);
315     
316     return property ? property->value : NULL;
317 }
318
319 sstr_t dav_property_key(const char *ns, const char *name) {
320     return dav_property_key_a(ucx_default_allocator(), ns, name);
321 }
322
323 sstr_t dav_property_key_a(UcxAllocator *a, const char *ns, const char *name) {
324     scstr_t ns_str = scstr(ns);
325     scstr_t name_str = scstr(name);
326     
327     return sstrcat_a(a, 4, ns_str, S("\0"), name_str, S("\0"));
328 }
329
330
331
332
333 void resource_add_child(DavResource *parent, DavResource *child) {
334     child->next = NULL;
335     if(parent->children) {
336         DavResource *last = parent->children;
337         while(last->next) {
338             last = last->next;
339         }
340         last->next = child;
341         child->prev = last;
342     } else {
343         child->prev = NULL;
344         parent->children = child;
345     }
346     child->parent = parent;
347 }
348
349 static int resource_cmp(DavResource *res1, DavResource *res2, DavOrderCriterion *cr) {
350     if(!(res1 && res2)) {
351         return 0;
352     }
353     
354     int ret;
355     if(cr->type == 0) {
356         switch(cr->column.resprop) {
357             case DAVQL_RES_NAME: {
358                 ret = strcmp(res1->name, res2->name);
359                 break;
360             }
361             case DAVQL_RES_PATH: {
362                 ret = strcmp(res1->path, res2->path);
363                 break;
364             }
365             case DAVQL_RES_HREF: {
366                 ret = strcmp(res1->href, res2->href);
367                 break;
368             }
369             case DAVQL_RES_CONTENTLENGTH: {
370                 int c = res1->contentlength == res2->contentlength;
371                 ret = c ? 0 : (res1->contentlength < res2->contentlength?-1:1);
372                 break;
373             }
374             case DAVQL_RES_CONTENTTYPE: {
375                 ret = strcmp(res1->contenttype, res2->contenttype);
376                 break;
377             }
378             case DAVQL_RES_CREATIONDATE: {
379                 int c = res1->creationdate == res2->creationdate;
380                 ret = c ? 0 : (res1->creationdate < res2->creationdate?-1:1);
381                 break;
382             }
383             case DAVQL_RES_LASTMODIFIED: {
384                 int c = res1->lastmodified == res2->lastmodified;
385                 ret = c ? 0 : (res1->lastmodified < res2->lastmodified?-1:1);
386                 break;
387             }
388             case DAVQL_RES_ISCOLLECTION: {
389                 int c = res1->iscollection == res2->iscollection;
390                 ret = c ? 0 : (res1->iscollection < res2->iscollection?-1:1);
391                 break;
392             }
393             default: ret = 0;
394         }
395     } else if(cr->type == 1) {
396         DavXmlNode *xvalue1 = resource_get_property_k(res1, cr->column.property);
397         DavXmlNode *xvalue2 = resource_get_property_k(res2, cr->column.property);
398         char *value1 = dav_xml_getstring(xvalue1);
399         char *value2 = dav_xml_getstring(xvalue2);
400         if(!value1) {
401             ret = value2 ? -1 : 0;
402         } else if(!value2) {
403             ret = value1 ? 1 : 0;
404         } else {
405             ret = strcmp(value1, value2);
406         }
407     } else {
408         return 0;
409     }
410     
411     return cr->descending ? -ret : ret;
412 }
413
414 void resource_add_ordered_child(DavResource *parent, DavResource *child, UcxList *ordercr) {
415     if(!ordercr) {
416         resource_add_child(parent, child);
417         return;
418     }
419     
420     child->parent = parent;
421     
422     if(!parent->children) {
423         child->next = NULL;
424         child->prev = NULL;
425         parent->children = child;
426     } else {
427         DavResource *resource = parent->children;
428         while(resource) {
429             int r = 0;
430             UCX_FOREACH(elm, ordercr) {
431                 DavOrderCriterion *cr = elm->data;
432                 r = resource_cmp(child, resource, cr);
433                 if(r != 0) {
434                     break;
435                 }
436             }
437             
438             if(r < 0) {
439                 // insert child before resource
440                 child->prev = resource->prev;
441                 child->next = resource;
442                 if(resource->prev) {
443                     resource->prev->next = child;
444                 } else {
445                     parent->children = child;
446                 }
447                 resource->prev = child;
448                 break;
449             } if(!resource->next) {
450                 // append child
451                 child->prev = resource;
452                 child->next = NULL;
453                 resource->next = child;
454                 break;
455             } else {
456                 resource = resource->next;
457             }
458         }
459     }
460 }
461
462 char* dav_get_string_property(DavResource *res, char *name) {
463     char *pns;
464     char *pname;
465     dav_get_property_namespace_str(res->session->context, name, &pns, &pname);
466     if(!pns || !pname) {
467         return NULL;
468     }
469     return dav_get_string_property_ns(res, pns, pname);
470 }
471
472 char* dav_get_string_property_ns(DavResource *res, char *ns, char *name) {
473     DavXmlNode *prop = dav_get_property_ns(res, ns, name);
474     if(!prop) {
475         return NULL;
476     }
477     return dav_xml_getstring(prop);
478 }
479
480 DavXmlNode* dav_get_property(DavResource *res, char *name) {
481     char *pns;
482     char *pname;
483     dav_get_property_namespace_str(res->session->context, name, &pns, &pname);
484     if(!pns || !pname) {
485         return NULL;
486     }
487     return dav_get_property_ns(res, pns, pname);
488 }
489
490 static DavXmlNode* get_property_ns(DavResource *res, DavBool encrypted, const char *ns, const char *name) {
491     if(!ns || !name) {
492         return NULL;
493     }
494     
495     DavResourceData *data = res->data;
496     
497     DavXmlNode *property = NULL;
498     UcxList *remove_list = NULL;
499     UcxList *set_list = NULL;
500     
501     if(encrypted) {
502         // check if crypto_properties because it will only be created
503         // if the resource has encrypted properties
504         if(!data->crypto_properties) {
505             return NULL;
506         }
507         property = resource_get_encrypted_property(res, ns, name);
508         remove_list = data->crypto_remove;
509         set_list = data->crypto_set;
510     } else {
511         property = resource_get_property(res, ns, name);
512         remove_list = data->remove;
513         set_list = data->set;
514     }
515     
516     // resource_get_property only returns persistent properties
517     // check the remove and set list
518     if(property) {
519         // if the property is in the remove list, we return NULL
520         UCX_FOREACH(elm, remove_list) {
521             DavProperty *p = elm->data;
522             if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) {
523                 return NULL;
524             }
525         }
526     }
527     // the set list contains property updates
528     // we return an updated property if possible
529     UCX_FOREACH(elm, set_list) {
530         DavProperty *p = elm->data;
531         if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) {
532             return p->value; // TODO: fix
533         }
534     }
535     // no property update
536     
537     return property;
538 }
539
540 DavXmlNode* dav_get_property_ns(DavResource *res, const char *ns, const char *name) {
541     DavXmlNode *property_value = get_property_ns(res, FALSE, ns, name);
542     
543     if(!property_value && DAV_DECRYPT_PROPERTIES(res->session)) {
544         property_value = get_property_ns(res, TRUE, ns, name);
545     }
546     
547     return property_value;
548 }
549
550 DavXmlNode* dav_get_encrypted_property_ns(DavResource *res, const char *ns, const char *name) {
551     return get_property_ns(res, TRUE, ns, name);
552 }
553
554 static DavProperty* createprop(DavSession *sn, const char *ns, const char *name) {
555     DavProperty *property = dav_session_malloc(sn, sizeof(DavProperty));
556     property->name = dav_session_strdup(sn, name);
557     property->value = NULL;
558     
559     DavNamespace *namespace = dav_session_malloc(sn, sizeof(DavNamespace));
560     namespace->prefix = NULL;
561     namespace->name = dav_session_strdup(sn, ns);
562     
563     property->ns = namespace;
564     
565     return property;
566 }
567
568 void dav_set_string_property(DavResource *res, char *name, char *value) {
569     char *pns;
570     char *pname;
571     dav_get_property_namespace_str(res->session->context, name, &pns, &pname);
572     dav_set_string_property_ns(res, pns, pname, value);
573 }
574
575 void dav_set_string_property_ns(DavResource *res, char *ns, char *name, char *value) {
576     DavSession *sn = res->session;
577     UcxAllocator *a = res->session->mp->allocator;
578     DavResourceData *data = res->data;
579     
580     DavProperty *property = createprop(res->session, ns, name);
581     property->value = dav_text_node(res->session, value);
582     
583     if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) {
584         data->crypto_set = ucx_list_append_a(a, data->crypto_set, property);
585     } else {
586         data->set = ucx_list_append_a(a, data->set, property);
587     }
588 }
589
590 void dav_set_property(DavResource *res, char *name, DavXmlNode *value) {
591     char *pns;
592     char *pname;
593     dav_get_property_namespace_str(res->session->context, name, &pns, &pname);
594     dav_set_property_ns(res, pns, pname, value);
595 }
596
597 void dav_set_property_ns(DavResource *res, char *ns, char *name, DavXmlNode *value) {
598     DavSession *sn = res->session;
599     UcxAllocator *a = sn->mp->allocator; 
600     DavResourceData *data = res->data;
601     
602     DavProperty *property = createprop(sn, ns, name);
603     property->value = value; // TODO: copy node?
604     
605     if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) {
606         data->crypto_set = ucx_list_append_a(a, data->crypto_set, property);
607     } else {
608         data->set = ucx_list_append_a(a, data->set, property);
609     }
610 }
611
612 void dav_remove_property(DavResource *res, char *name) {
613     char *pns;
614     char *pname;
615     dav_get_property_namespace_str(res->session->context, name, &pns, &pname);
616     dav_remove_property_ns(res, pns, pname);
617 }
618
619 void dav_remove_property_ns(DavResource *res, char *ns, char *name) {
620     DavSession *sn = res->session;
621     DavResourceData *data = res->data;
622     UcxAllocator *a = res->session->mp->allocator;
623     
624     DavProperty *property = createprop(res->session, ns, name);
625     
626     if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) {
627         data->crypto_remove = ucx_list_append_a(a, data->crypto_remove, property);
628     } else {
629         data->remove = ucx_list_append_a(a, data->remove, property);
630     }
631 }
632
633 void dav_set_encrypted_property_ns(DavResource *res, char *ns, char *name, DavXmlNode *value) {
634     UcxAllocator *a = res->session->mp->allocator;
635     DavResourceData *data = res->data;
636     
637     DavProperty *property = createprop(res->session, ns, name);
638     property->value = value; // TODO: copy node?
639     
640     data->crypto_set = ucx_list_append_a(a, data->crypto_set, property);
641 }
642
643 void dav_set_encrypted_string_property_ns(DavResource *res, char *ns, char *name, char *value) {
644     UcxAllocator *a = res->session->mp->allocator;
645     DavResourceData *data = res->data;
646     
647     DavProperty *property = createprop(res->session, ns, name);
648     property->value = dav_text_node(res->session, value);
649     
650     data->crypto_set = ucx_list_append_a(a, data->crypto_set, property);
651 }
652
653 void dav_remove_encrypted_property_ns(DavResource *res, char *ns, char *name) {
654     DavResourceData *data = res->data;
655     UcxAllocator *a = res->session->mp->allocator;
656     
657     DavProperty *property = createprop(res->session, ns, name);
658     
659     data->crypto_remove = ucx_list_append_a(a, data->crypto_remove, property);
660 }
661
662 static int compare_propname(const void *a, const void *b) {
663     const DavPropName *p1 = a;
664     const DavPropName *p2 = b;
665     
666     int result = strcmp(p1->ns, p2->ns);
667     if(result) {
668         return result;
669     } else {
670         return strcmp(p1->name, p2->name);
671     }
672 }
673
674 DavPropName* dav_get_property_names(DavResource *res, size_t *count) {
675     DavResourceData *data = res->data;
676     
677     *count = data->properties->count;
678     DavPropName *names = dav_session_calloc(
679             res->session,
680             *count,
681             sizeof(DavPropName));
682     
683     
684     UcxMapIterator i = ucx_map_iterator(data->properties);
685     DavProperty *value;
686     int j = 0;
687     UCX_MAP_FOREACH(key, value, i) {
688         DavPropName *name = &names[j];
689         
690         name->ns = value->ns->name;
691         name->name = value->name;
692         
693         j++;
694     }
695     
696     qsort(names, *count, sizeof(DavPropName), compare_propname);
697     
698     return names;
699 }
700
701
702 void dav_set_content(DavResource *res, void *stream, dav_read_func read_func, dav_seek_func seek_func) {
703     DavResourceData *data = res->data;
704     data->content = stream;
705     data->read = read_func;
706     data->seek = seek_func;
707     data->length = 0;
708 }
709
710 void dav_set_content_data(DavResource *res, char *content, size_t length) {
711     DavSession *sn = res->session;
712     DavResourceData *data = res->data;
713     data->content = dav_session_malloc(sn, length);
714     memcpy(data->content, content, length);
715     data->read = NULL;
716     data->seek = NULL;
717     data->length = length;
718 }
719
720 void dav_set_content_length(DavResource *res, size_t length) {
721     DavResourceData *data = res->data;
722     data->length = length;
723 }
724
725
726 int dav_load(DavResource *res) {
727     UcxBuffer *rqbuf = create_allprop_propfind_request();
728     int ret = dav_propfind(res->session, res, rqbuf);
729     ucx_buffer_free(rqbuf);
730     return ret;
731 }
732
733 int dav_load_prop(DavResource *res, DavPropName *properties, size_t numprop) {
734     UcxMempool *mp = ucx_mempool_new(64);
735     
736     UcxList *proplist = NULL;
737     for(size_t i=0;i<numprop;i++) {
738         DavProperty *p = ucx_mempool_malloc(mp, sizeof(DavProperty));
739         p->name = properties[i].name;
740         p->ns = ucx_mempool_malloc(mp, sizeof(DavNamespace));
741         p->ns->name = properties[i].ns;
742         if(!strcmp(properties[i].ns, "DAV:")) {
743             p->ns->prefix = "D";
744         } else {
745             p->ns->prefix = ucx_asprintf(mp->allocator, "x%d", i).ptr;
746         }
747         p->value = NULL;
748         proplist = ucx_list_append_a(mp->allocator, proplist, p);
749     }
750     
751     UcxBuffer *rqbuf = create_propfind_request(res->session, proplist, "propfind", 0);
752     int ret = dav_propfind(res->session, res, rqbuf);
753     ucx_buffer_free(rqbuf);
754     ucx_mempool_destroy(mp);
755     return ret;
756 }
757
758
759 /*
760  * read wrapper with integrated hashing
761  */
762
763 typedef struct {
764     DAV_SHA_CTX *sha;
765     void *stream;
766     dav_read_func read;
767     dav_seek_func seek;
768     int error;
769 } HashStream;
770
771 static void init_hash_stream(HashStream *hstr, void *stream, dav_read_func readfn, dav_seek_func seekfn) {
772     hstr->sha = NULL;
773     hstr->stream = stream;
774     hstr->read = readfn;
775     hstr->seek = seekfn;
776     hstr->error = 0;
777 }
778
779 static size_t dav_read_h(void *buf, size_t size, size_t nelm, void *stream) {
780     HashStream *s = stream;
781     if(!s->sha) {
782         s->sha = dav_hash_init();
783     }
784      
785     size_t r = s->read(buf, size, nelm, s->stream);
786     dav_hash_update(s->sha, buf, r);
787     return r;
788 }
789
790 static int dav_seek_h(void *stream, long offset, int whence) {
791     HashStream *s = stream;
792     if(offset == 0 && whence == SEEK_SET) {
793         unsigned char buf[DAV_SHA256_DIGEST_LENGTH];
794         dav_hash_final(s->sha, buf);
795         s->sha = NULL;
796     } else {
797         s->error = 1;
798     }
799     return s->seek(s->stream, offset, whence);
800 }
801
802
803 int dav_store(DavResource *res) {
804     DavSession *sn = res->session;
805     DavResourceData *data = res->data;
806     
807     util_set_url(sn, dav_resource_get_href(res));
808     
809     DavLock *lock = dav_get_lock(sn, res->path);
810     char *locktoken = lock ? lock->token : NULL;
811     
812     // store content
813     if(data->content) {
814         int encryption = DAV_ENCRYPT_CONTENT(sn) && sn->key;
815         CURLcode ret;
816         if(encryption) {
817             AESEncrypter *enc = NULL;
818             UcxBuffer *buf = NULL;
819             if(data->read) {
820                 enc = aes_encrypter_new(
821                         sn->key,
822                         data->content,
823                         data->read,
824                         data->seek);
825             } else {
826                 buf = ucx_buffer_new(data->content, data->length, 0);
827                 buf->size = data->length;
828                 enc = aes_encrypter_new(
829                         sn->key,
830                         buf,
831                         (dav_read_func)ucx_buffer_read,
832                         (dav_seek_func)dav_buffer_seek);
833             }
834               
835             // put resource
836             ret = do_put_request(
837                     sn,
838                     locktoken,
839                     TRUE,
840                     enc,
841                     (dav_read_func)aes_read,
842                     (dav_seek_func)aes_encrypter_reset,
843                     0);
844             
845             // get sha256 hash
846             dav_get_hash(&enc->sha256, (unsigned char*)data->hash);
847             char *enc_hash = aes_encrypt(data->hash, DAV_SHA256_DIGEST_LENGTH, sn->key);
848             
849             aes_encrypter_close(enc);
850             if(buf) {
851                 ucx_buffer_free(buf);
852             }
853             
854             // add crypto properties
855             // TODO: store the properties later
856             if(resource_add_crypto_info(sn, res->href, res->name, enc_hash)) {
857                 free(enc_hash);
858                 return 1;
859             }
860             resource_add_string_property(res, DAV_NS, "crypto-hash", enc_hash);
861             free(enc_hash);
862         } else if((sn->flags & DAV_SESSION_STORE_HASH) == DAV_SESSION_STORE_HASH) {
863             HashStream hstr;
864             UcxBuffer *iobuf = NULL;
865             if(!data->read) {
866                 iobuf = ucx_buffer_new(data->content, data->length, 0);
867                 iobuf->size = data->length;
868                 init_hash_stream(
869                         &hstr,
870                         iobuf,
871                         (dav_read_func)ucx_buffer_read,
872                         (dav_seek_func)ucx_buffer_seek);
873             } else {
874                 init_hash_stream(
875                         &hstr,
876                         data->content,
877                         data->read,
878                         data->seek);
879             }
880             
881             ret = do_put_request(
882                     sn,
883                     locktoken,
884                     TRUE,
885                     &hstr,
886                     dav_read_h,
887                     (dav_seek_func)dav_seek_h,
888                     data->length);
889             
890             if(hstr.sha) {
891                 dav_hash_final(hstr.sha, (unsigned char*)data->hash);
892                 char *hash = util_hexstr((unsigned char*)data->hash, 32);
893                 dav_set_string_property_ns(res, DAV_NS, "content-hash", hash);
894                 free(hash);
895             }
896         } else {
897             ret = do_put_request(
898                     sn,
899                     locktoken,
900                     TRUE,
901                     data->content,
902                     data->read,
903                     data->seek,
904                     data->length);
905         }
906         
907         long status = 0;
908         curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &status);
909         if(ret == CURLE_OK && (status >= 200 && status < 300)) {
910             res->session->error = 0;
911             // cleanup node data
912             if(!data->read) {
913                 ucx_mempool_free(sn->mp, data->content);
914             }
915             data->content = NULL;
916             data->read = NULL;
917             data->length = 0;
918         } else {
919             dav_session_set_error(sn, ret, status);
920             return 1;
921         }
922     }
923     
924     // generate crypto-prop content
925     if(DAV_ENCRYPT_PROPERTIES(sn) && sn->key && (data->crypto_set || data->crypto_remove)) {
926         DavResource *crypto_res = dav_resource_new_href(sn, res->href);
927         int ret = 1;
928         
929         if(crypto_res) {
930             UcxBuffer *rqbuf = create_cryptoprop_propfind_request();
931             ret = dav_propfind(res->session, res, rqbuf);
932             ucx_buffer_free(rqbuf);
933         }
934         
935         if(!ret) {
936             DavXmlNode *crypto_prop_node = dav_get_property_ns(crypto_res, DAV_NS, "crypto-prop");
937             UcxMap *crypto_props = parse_crypto_prop(sn, sn->key, crypto_prop_node);
938             if(!crypto_props) {
939                 // resource hasn't encrypted properties yet
940                 crypto_props = ucx_map_new(32); // create new map
941             }
942             
943             // remove all properties
944             UCX_FOREACH(elm, data->crypto_remove) {
945                 if(crypto_props->count == 0) {
946                     break; // map already empty, can't remove any more
947                 }
948                 
949                 DavProperty *property = elm->data;
950                 sstr_t key = dav_property_key(property->ns->name, property->name);
951                 DavProperty *existing_prop = ucx_map_sstr_remove(crypto_props, key);
952                 if(existing_prop) {
953                     // TODO: free existing_prop
954                 }                
955                 free(key.ptr);
956             }
957             
958             // set properties
959             UCX_FOREACH(elm, data->crypto_set) {
960                 DavProperty *property = elm->data;
961                 sstr_t key = dav_property_key(property->ns->name, property->name);
962                 DavProperty *existing_prop = ucx_map_sstr_remove(crypto_props, key);
963                 ucx_map_sstr_put(crypto_props, key, property);
964                 if(existing_prop) {
965                     // TODO: free existing_prop
966                 }  
967                 free(key.ptr);
968             }
969             
970             DavXmlNode *crypto_prop_value = create_crypto_prop(sn, crypto_props);
971             if(crypto_prop_value) {
972                 DavProperty *new_crypto_prop = createprop(sn, DAV_NS, "crypto-prop");
973                 new_crypto_prop->value = crypto_prop_value;
974                 data->set = ucx_list_prepend_a(sn->mp->allocator, data->set, new_crypto_prop);
975             }
976             
977             dav_resource_free(crypto_res);
978         }
979         
980         if(ret) {
981             return 1;
982         }
983     }
984     
985     // store properties
986     int r = 0;
987     sn->error = DAV_OK;
988     if(data->set || data->remove) {
989         UcxBuffer *request = create_proppatch_request(data);
990         UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
991         //printf("request:\n%.*s\n\n", request->pos, request->space);
992
993         CURLcode ret = do_proppatch_request(sn, locktoken, request, response);
994         long status = 0;
995         curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status);
996         if(ret == CURLE_OK && status == 207) {
997             //printf("%s\n", response->space);
998             // TODO: parse response
999             // TODO: cleanup node data correctly
1000             data->set = NULL;
1001             data->remove = NULL;
1002         } else {
1003             dav_session_set_error(sn, ret, status);
1004             r = -1;
1005         }
1006         
1007         ucx_buffer_free(request);
1008         ucx_buffer_free(response);
1009     }
1010      
1011     return r;
1012 }
1013
1014 #if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 32
1015 static void set_progressfunc(DavResource *res) {
1016     CURL *handle = res->session->handle;
1017     curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, dav_session_get_progress);
1018     curl_easy_setopt(handle, CURLOPT_XFERINFODATA, res);
1019     curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L);
1020 }
1021
1022 static void unset_progressfunc(DavResource *res) {
1023     CURL *handle = res->session->handle;
1024     curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, NULL);
1025     curl_easy_setopt(handle, CURLOPT_XFERINFODATA, NULL);
1026     curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1L);
1027 }
1028 #else
1029 static void set_progressfunc(DavResource *res) {
1030     
1031 }
1032 static void unset_progressfunc(DavResource *res) {
1033     
1034 }
1035 #endif
1036
1037 int dav_get_content(DavResource *res, void *stream, dav_write_func write_fnc) { 
1038     DavSession *sn = res->session;
1039     CURL *handle = sn->handle;
1040     util_set_url(res->session, dav_resource_get_href(res));
1041     
1042     // check encryption
1043     AESDecrypter *dec = NULL;
1044     DavKey *key = NULL;
1045     if(DAV_DECRYPT_CONTENT(sn)) {
1046         char *keyname = dav_get_string_property_ns(res, DAV_NS, "crypto-key");
1047         if(keyname) {
1048             key = dav_context_get_key(sn->context, keyname);
1049             if(key) {
1050                 dec = aes_decrypter_new(key, stream, write_fnc);
1051                 stream = dec;
1052                 write_fnc = (dav_write_func)aes_write;
1053             }
1054         }
1055     }
1056     
1057     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL);
1058     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL);
1059     curl_easy_setopt(handle, CURLOPT_PUT, 0L);
1060     curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
1061     
1062     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_fnc);
1063     curl_easy_setopt(handle, CURLOPT_WRITEDATA, stream);
1064     
1065     if(sn->get_progress) {
1066         set_progressfunc(res);
1067     }
1068     
1069     long status = 0;
1070     CURLcode ret = dav_session_curl_perform(sn, &status);
1071     
1072     if(sn->get_progress) {
1073         unset_progressfunc(res);
1074     }
1075     
1076     char *hash = NULL;
1077     if(dec) {
1078         aes_decrypter_shutdown(dec); // get final bytes
1079         
1080         // get hash
1081         unsigned char sha[DAV_SHA256_DIGEST_LENGTH];
1082         dav_get_hash(&dec->sha256, sha);
1083         hash = util_hexstr(sha, DAV_SHA256_DIGEST_LENGTH);
1084         
1085         aes_decrypter_close(dec);
1086     }
1087     
1088     if(ret == CURLE_OK && (status >= 200 && status < 300)) {
1089         int verify_failed = 0;
1090         if(DAV_DECRYPT_CONTENT(sn) && key) {
1091             // try to verify the content
1092             char *res_hash = dav_get_string_property_ns(res, DAV_NS, "crypto-hash");
1093
1094             if(res_hash) {
1095                 size_t len = 0;
1096                 char *dec_hash = aes_decrypt(res_hash, &len, key);
1097                 char *hex_hash = util_hexstr((unsigned char*)dec_hash, len);
1098                 if(strcmp(hash, hex_hash)) {
1099                     verify_failed = 1;
1100                 }
1101                 free(dec_hash);
1102                 free(hex_hash);
1103             }
1104         }
1105         if(hash) {
1106             free(hash);
1107         }
1108         
1109         if(verify_failed) {
1110             res->session->error = DAV_CONTENT_VERIFICATION_ERROR;
1111             return 1;
1112         }
1113         
1114         res->session->error = DAV_OK;
1115         return 0;
1116     } else {
1117         if(hash) {
1118             free(hash);
1119         }
1120         dav_session_set_error(res->session, ret, status);
1121         return 1;
1122     }
1123 }
1124
1125 DavResource* dav_create_child(DavResource *parent, char *name) {
1126     DavResource *res = dav_resource_new_child(parent->session, parent, name);
1127     if(dav_create(res)) {
1128         dav_resource_free(res);
1129         return NULL;
1130     } else {
1131         return res;
1132     }
1133 }
1134
1135 int dav_delete(DavResource *res) {
1136     CURL *handle = res->session->handle;
1137     util_set_url(res->session, dav_resource_get_href(res));
1138     
1139     DavLock *lock = dav_get_lock(res->session, res->path);
1140     char *locktoken = lock ? lock->token : NULL;
1141     
1142     UcxBuffer *response = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
1143     CURLcode ret = do_delete_request(res->session, locktoken, response);
1144     long status = 0;
1145     curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
1146     int r = 0;
1147     if(ret == CURLE_OK && (status >= 200 && status < 300)) {
1148         res->session->error = DAV_OK;
1149         res->exists = 0;
1150         
1151         // TODO: parse response
1152         // TODO: free res
1153     } else {
1154         dav_session_set_error(res->session, ret, status);
1155         r = 1;
1156     }
1157     
1158     ucx_buffer_free(response);
1159     return r;
1160 }
1161
1162 static int create_ancestors(DavSession *sn, char *href, char *path) {
1163     CURL *handle = sn->handle;
1164     CURLcode code;
1165     
1166     DavLock *lock = dav_get_lock(sn, path);
1167     char *locktoken = lock ? lock->token : NULL;
1168     
1169     long status = 0;
1170     int ret = 0;
1171     
1172     if(strlen(path) <= 1) {
1173         return 0;
1174     }
1175     
1176     char *p = util_parent_path(path);
1177     char *h = util_parent_path(href);
1178     
1179     for(int i=0;i<2;i++) {
1180         util_set_url(sn, h);
1181         code = do_mkcol_request(sn, locktoken);
1182         curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
1183         if(status == 201) {
1184             // resource successfully created
1185             char *name = util_resource_name(p);
1186             int len = strlen(name);
1187             if(name[len - 1] == '/') {
1188                 name[len - 1] = '\0';
1189             }
1190             if(resource_add_crypto_info(sn, h, name, NULL)) {
1191                 sn->error = DAV_ERROR;
1192                 dav_session_set_errstr(sn, "Cannot set crypto properties for ancestor");
1193             }
1194             break;
1195         } else if(status == 405) {
1196             // parent already exists
1197             break;
1198         } else if(status == 409) {
1199             // parent doesn't exist
1200             if(create_ancestors(sn, h, p)) {
1201                 ret = 1;
1202                 break;
1203             }
1204         } else {
1205             dav_session_set_error(sn, code, status);
1206             ret = 1;
1207             break;
1208         }
1209     }
1210     
1211     free(p);
1212     free(h);
1213     return ret;
1214 }
1215
1216 static int create_resource(DavResource *res, int *status) {
1217     DavSession *sn = res->session;
1218     CURL *handle = sn->handle;
1219     util_set_url(sn, dav_resource_get_href(res));
1220     
1221     DavLock *lock = dav_get_lock(res->session, res->path);
1222     char *locktoken = lock ? lock->token : NULL;
1223     
1224     CURLcode code;
1225     if(res->iscollection) {
1226         code = do_mkcol_request(sn, locktoken);
1227     } else {
1228         code = do_put_request(sn, locktoken, TRUE, "", NULL, NULL, 0); 
1229     }
1230     long s = 0;
1231     curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &s);
1232     *status = s;
1233     if(code == CURLE_OK && (s >= 200 && s < 300)) {
1234         sn->error = DAV_OK;
1235         // if the session has encrypted file names, add crypto infos
1236         if(!resource_add_crypto_info(sn, res->href, res->name, NULL)) {
1237             // do a minimal propfind request
1238             UcxBuffer *rqbuf = create_propfind_request(sn, NULL, "propfind", 0);
1239             int ret = dav_propfind(sn, res, rqbuf);
1240             ucx_buffer_free(rqbuf);
1241             return ret;
1242         } else {
1243             return 1;
1244         }
1245     } else {
1246         dav_session_set_error(sn, code, s);
1247         return 1;
1248     }
1249 }
1250
1251 int dav_create(DavResource *res) {
1252     int status;
1253     if(!create_resource(res, &status)) {
1254         // resource successfully created
1255         res->exists = 1;
1256         return 0;
1257     }
1258     
1259     if(status == 403 || status == 409 || status == 404) {
1260         // create intermediate collections
1261         if(create_ancestors(res->session, res->href, res->path)) {
1262             return 1;
1263         }
1264     }
1265     
1266     return create_resource(res, &status);
1267 }
1268
1269 int dav_exists(DavResource *res) {
1270     if(!dav_load_prop(res, NULL, 0)) {
1271         res->exists = 1;
1272         return 1;
1273     } else {
1274         if(res->session->error == DAV_NOT_FOUND) {
1275             res->exists = 0;
1276         }
1277         return 0;
1278     }
1279 }
1280
1281 static int dav_cp_mv_url(DavResource *res, char *desturl, _Bool copy, _Bool override) {
1282     DavSession *sn = res->session;
1283     CURL *handle = sn->handle;
1284     util_set_url(sn, dav_resource_get_href(res));
1285     
1286     DavLock *lock = dav_get_lock(sn, res->path);
1287     char *locktoken = lock ? lock->token : NULL;
1288     
1289     CURLcode ret = do_copy_move_request(sn, desturl, locktoken, copy, override);
1290     
1291     long status = 0;
1292     curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
1293     if(ret == CURLE_OK && (status >= 200 && status < 300)) {
1294         return 0;
1295     } else {
1296         dav_session_set_error(sn, ret, status);
1297         return 1;
1298     }
1299 }
1300
1301 static int dav_cp_mv(DavResource *res, char *newpath, _Bool copy, _Bool override) {
1302     char *dest = dav_session_get_href(res->session, newpath);
1303     char *desturl = util_get_url(res->session, dest);
1304     dav_session_free(res->session, dest);
1305     
1306     int ret = dav_cp_mv_url(res, desturl, copy, override);
1307     free(desturl);
1308     return ret;
1309 }
1310
1311 int dav_copy(DavResource *res, char *newpath) {
1312     return dav_cp_mv(res, newpath, true, false);
1313 }
1314
1315 int dav_move(DavResource *res, char *newpath) {
1316     return dav_cp_mv(res, newpath, false, false);
1317 }
1318
1319 int dav_copy_o(DavResource *res, char *newpath, DavBool override) {
1320     return dav_cp_mv(res, newpath, true, override);
1321 }
1322
1323 int dav_move_o(DavResource *res, char *newpath, DavBool override) {
1324     return dav_cp_mv(res, newpath, false, override);
1325 }
1326
1327 int dav_copyto(DavResource *res, char *url, DavBool override) {
1328     return dav_cp_mv_url(res, url, true, override);
1329 }
1330
1331 int dav_moveto(DavResource *res, char *url, DavBool override) {
1332     return dav_cp_mv_url(res, url, false, override);
1333 }
1334
1335 int dav_lock(DavResource *res) {
1336     return dav_lock_t(res, 0);
1337 }
1338
1339 int dav_lock_t(DavResource *res, time_t timeout) {
1340     DavSession *sn = res->session;
1341     CURL *handle = sn->handle;
1342     util_set_url(sn, dav_resource_get_href(res));
1343     
1344     UcxBuffer *request = create_lock_request();
1345     UcxBuffer *response = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
1346     CURLcode ret = do_lock_request(sn, request, response, timeout);
1347     
1348     //printf("\nlock\n");
1349     //printf("%.*s\n\n", request->size, request->space);
1350     //printf("%.*s\n\n", response->size, response->space);
1351     
1352     ucx_buffer_free(request);
1353     
1354     long status = 0;
1355     curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
1356     if(ret == CURLE_OK && (status >= 200 && status < 300)) {
1357         LockDiscovery lock;
1358         if(parse_lock_response(sn, response, &lock)) {
1359             sn->error = DAV_ERROR;
1360             ucx_buffer_free(response);
1361             return -1;
1362         }
1363         ucx_buffer_free(response);
1364         
1365         DavLock *l = dav_create_lock(sn, lock.locktoken, lock.timeout);
1366         free(lock.locktoken);
1367         free(lock.timeout);
1368         
1369         int r = 0;
1370         if(res->iscollection) {
1371             r = dav_add_collection_lock(sn, res->path, l);
1372         } else {
1373             r = dav_add_resource_lock(sn, res->path, l);
1374         }
1375         
1376         if(r == 0) {
1377             return 0;
1378         } else {
1379             (void)dav_unlock(res);
1380             sn->error = DAV_ERROR;
1381             dav_destroy_lock(sn, l);
1382             return -1;
1383         }
1384     } else {
1385         dav_session_set_error(sn, ret, status);
1386         ucx_buffer_free(response);
1387         return -1;
1388     }
1389 }
1390
1391 int dav_unlock(DavResource *res) {
1392     DavSession *sn = res->session;
1393     CURL *handle = sn->handle;
1394     util_set_url(sn, dav_resource_get_href(res));
1395     
1396     DavLock *lock = dav_get_lock(res->session, res->path);
1397     if(!lock) {
1398         sn->error = DAV_ERROR;
1399         return -1;
1400     }
1401     
1402     CURLcode ret = do_unlock_request(sn, lock->token);
1403     long status = 0;
1404     curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
1405     if(ret == CURLE_OK && (status >= 200 && status < 300)) {
1406         dav_remove_lock(sn, res->path, lock);
1407     } else {
1408         dav_session_set_error(sn, ret, status);
1409         return 1;
1410     }
1411     
1412     return 0;
1413 }
1414
1415
1416 int resource_add_crypto_info(DavSession *sn, const char *href, const char *name, const char *hash) {
1417     if(!DAV_IS_ENCRYPTED(sn)) {
1418         return 0;
1419     }
1420     
1421     UcxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name, hash);
1422     UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
1423     
1424     util_set_url(sn, href);
1425     // TODO: lock
1426     CURLcode ret = do_proppatch_request(sn, NULL, request, response);
1427     ucx_buffer_free(request);
1428     long status = 0;
1429     curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status);
1430     if(ret == CURLE_OK && status == 207) {
1431         // TODO: parse response
1432         sn->error = DAV_OK;   
1433         ucx_buffer_free(response);
1434         return 0;
1435     } else {
1436         dav_session_set_error(sn, ret, status);
1437         ucx_buffer_free(response);
1438         return 1;
1439     }
1440 }
1441
1442 /* ----------------------------- crypto-prop  ----------------------------- */
1443
1444 DavXmlNode* create_crypto_prop(DavSession *sn, UcxMap *properties) {
1445     if(!sn->key) {
1446         return NULL;
1447     }
1448     
1449     UcxBuffer *content = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND);
1450     
1451     // create an xml document containing all properties
1452     UcxMap *nsmap = ucx_map_new(8);
1453     ucx_map_cstr_put(nsmap, "DAV:", strdup("D"));
1454     
1455     ucx_buffer_puts(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1456     ucx_buffer_puts(content, "<D:prop xmlns:D=\"DAV:\">\n");
1457     
1458     UcxMapIterator i = ucx_map_iterator(properties);
1459     DavProperty *prop;
1460     UCX_MAP_FOREACH(key, prop, i) {
1461         DavXmlNode pnode;
1462         pnode.type = DAV_XML_ELEMENT;
1463         pnode.namespace = prop->ns->name;
1464         pnode.name = prop->name;
1465         pnode.prev = NULL;
1466         pnode.next = NULL;
1467         pnode.children = prop->value;
1468         pnode.parent = NULL;
1469         pnode.attributes = NULL;
1470         pnode.content = NULL;
1471         pnode.contentlength = 0;
1472         
1473         dav_print_node(content, (write_func)ucx_buffer_write, nsmap, &pnode);
1474         ucx_buffer_putc(content, '\n');
1475     }
1476     
1477     ucx_buffer_puts(content, "</D:prop>");
1478     
1479     ucx_map_free_content(nsmap, (ucx_destructor)free);
1480     ucx_map_free(nsmap);
1481     
1482     // encrypt xml document
1483     char *crypto_prop_content = aes_encrypt(content->space, content->size, sn->key);
1484     ucx_buffer_free(content);
1485     
1486     DavXmlNode *ret = NULL;
1487     if(crypto_prop_content) {
1488         ret = dav_text_node(sn, crypto_prop_content);
1489         free(crypto_prop_content);
1490     }    
1491     return ret;
1492 }
1493
1494 UcxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node) {
1495     if(!node || node->type != DAV_XML_TEXT || node->contentlength == 0) {
1496         return NULL;
1497     }
1498     
1499     return parse_crypto_prop_str(sn, key, node->content);
1500 }
1501
1502 UcxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content) {
1503     size_t len = 0;
1504     char *dec_str = aes_decrypt(content, &len, key);
1505     
1506     xmlDoc *doc = xmlReadMemory(dec_str, len, NULL, NULL, 0);
1507     free(dec_str);
1508     if(!doc) {
1509         return NULL;
1510     }
1511     
1512     int err = 0;
1513     xmlNode *xml_root = xmlDocGetRootElement(doc);
1514     if(xml_root) {
1515         if(
1516                 !xml_root->ns ||
1517                 !xstreq(xml_root->name, "prop") ||
1518                 !xstreq(xml_root->ns->href, "DAV:"))
1519         {
1520             err = 1;
1521         }
1522     } else {
1523         err = 1;
1524     }
1525     
1526     if(err) {
1527         xmlFreeDoc(doc);
1528         return NULL;
1529     }
1530     
1531     // ready to get the properties
1532     UcxMap *map = ucx_map_new(32);
1533     xmlNode *n = xml_root->children;
1534     while(n) {
1535         if(n->type == XML_ELEMENT_NODE && n->ns && n->ns->href) {
1536             DavProperty *property = dav_session_malloc(sn, sizeof(DavProperty));
1537             property->name = dav_session_strdup(sn, (const char*)n->name);
1538             property->ns = dav_session_malloc(sn, sizeof(DavNamespace));
1539             property->ns->name = dav_session_strdup(sn, (const char*)n->ns->href);
1540             property->ns->prefix = n->ns->prefix ?
1541                     dav_session_strdup(sn, (const char*)n->ns->prefix) : NULL;
1542             property->value = n->children ? dav_convert_xml(sn, n->children) : NULL;
1543             
1544             sstr_t key = dav_property_key(property->ns->name, property->name);
1545             ucx_map_sstr_put(map, key, property);
1546             free(key.ptr);
1547         }
1548         n = n->next;
1549     }
1550     
1551     xmlFreeDoc(doc);
1552     if(map->count == 0) {
1553         ucx_map_free(map);
1554         return NULL;
1555     }
1556     return map;
1557 }