2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 2018 Olaf Wintermann. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
33 #include <libxml/tree.h>
39 #include "ucx/buffer.h"
40 #include "ucx/utils.h"
44 #include "davqlexec.h"
46 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
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);
55 DavResource *res = dav_resource_new_full(sn, parent, name, href);
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);
69 DavResource* dav_resource_new_href(DavSession *sn, char *href) {
70 DavResource *res = ucx_mempool_calloc(sn->mp, 1, sizeof(DavResource));
73 // set name, path and href
74 resource_set_info(res, href);
76 // initialize resource data
77 res->data = resource_data_new(sn);
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++) {
88 if(c == '/' || c == '\\') {
89 n = sstr(util_resource_name(href));
94 // remove trailing '/'
95 if(n.length > 0 && n.ptr[n.length-1] == '/') {
99 DavResource *res = ucx_mempool_calloc(sn->mp, 1, sizeof(DavResource));
102 // set name, path and href
103 res->name = sstrdup_a(sn->mp->allocator, n).ptr;
105 char *path = util_concat_path(parent_path, name);
106 res->path = dav_session_strdup(sn, path);
110 // initialize resource data
111 res->data = resource_data_new(sn);
115 dav_session_cache_path(sn, sstr(path), sstr(href));
122 void resource_free_properties(DavSession *sn, UcxMap *properties) {
123 if(!properties) return;
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);
131 ucx_map_free(properties);
134 void dav_resource_free(DavResource *res) {
135 DavSession *sn = res->session;
137 dav_session_free(sn, res->name);
138 dav_session_free(sn, res->path);
140 dav_session_free(sn, res->href);
143 DavResourceData *data = res->data;
144 resource_free_properties(sn, data->properties);
145 resource_free_properties(sn, data->crypto_properties);
147 UCX_FOREACH(elm, data->set) {
148 DavProperty *p = elm->data;
149 dav_session_free(sn, p->ns->name);
151 dav_session_free(sn, p->ns->prefix);
153 dav_session_free(sn, p->ns);
155 dav_session_free(sn, p->name);
156 dav_session_free(sn, p->value);
157 dav_session_free(sn, p);
160 UCX_FOREACH(elm, data->remove) {
161 DavProperty *p = elm->data;
162 dav_session_free(sn, p->ns->name);
164 dav_session_free(sn, p->ns->prefix);
166 dav_session_free(sn, p->ns);
168 dav_session_free(sn, p->name);
169 dav_session_free(sn, p);
172 if(!data->read && data->content) {
173 dav_session_free(sn, data->content);
175 dav_session_free(sn, data);
177 dav_session_free(sn, res);
180 void dav_resource_free_all(DavResource *res) {
181 DavResource *child = res->children;
182 dav_resource_free(res);
184 DavResource *next = child->next;
185 dav_resource_free_all(child);
190 void resource_set_href(DavResource *res, sstr_t href) {
191 res->href = sstrdup_a(res->session->mp->allocator, href).ptr;
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);
200 sstr_t base_href = sstr(util_url_path(res->session->base_url));
201 sstr_t path = sstrsubs(href, base_href.length - 1);
203 UcxAllocator *a = res->session->mp->allocator;
204 CURL *handle = res->session->handle;
207 char *uname = curl_easy_unescape(handle, name.ptr, name.length , &nlen);
209 char *upath = curl_easy_unescape(handle, path.ptr, path.length, &plen);
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;
219 DavResourceData* resource_data_new(DavSession *sn) {
220 DavResourceData *data = ucx_mempool_malloc(
222 sizeof(DavResourceData));
226 data->properties = ucx_map_new_a(sn->mp->allocator, 32);
227 data->crypto_properties = NULL;
230 data->crypto_set = NULL;
231 data->crypto_remove = NULL;
233 data->content = NULL;
239 char* dav_resource_get_href(DavResource *resource) {
240 if(!resource->href) {
241 resource->href = dav_session_get_href(
245 return resource->href;
248 void resource_add_prop(DavResource *res, const char *ns, const char *name, DavXmlNode *val) {
249 DavSession *sn = res->session;
251 DavNamespace *namespace = dav_session_malloc(sn, sizeof(DavNamespace));
252 namespace->prefix = NULL;
253 namespace->name = dav_session_strdup(sn, ns);
255 DavProperty *prop = dav_session_malloc(sn, sizeof(DavProperty));
256 prop->name = dav_session_strdup(sn, name);
257 prop->ns = namespace;
260 sstr_t key = dav_property_key(ns, name);
261 ucx_map_sstr_put(((DavResourceData*)res->data)->properties, key, prop);
265 void resource_add_property(DavResource *res, const char *ns, const char *name, xmlNode *val) {
270 resource_add_prop(res, ns, name, dav_convert_xml(res->session, val));
273 void resource_add_string_property(DavResource *res, char *ns, char *name, char *val) {
278 resource_add_prop(res, ns, name, dav_text_node(res->session, val));
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;
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);
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);
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);
309 return property ? property->value : NULL;
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);
316 return property ? property->value : NULL;
319 sstr_t dav_property_key(const char *ns, const char *name) {
320 return dav_property_key_a(ucx_default_allocator(), ns, name);
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);
327 return sstrcat_a(a, 4, ns_str, S("\0"), name_str, S("\0"));
333 void resource_add_child(DavResource *parent, DavResource *child) {
335 if(parent->children) {
336 DavResource *last = parent->children;
344 parent->children = child;
346 child->parent = parent;
349 static int resource_cmp(DavResource *res1, DavResource *res2, DavOrderCriterion *cr) {
350 if(!(res1 && res2)) {
356 switch(cr->column.resprop) {
357 case DAVQL_RES_NAME: {
358 ret = strcmp(res1->name, res2->name);
361 case DAVQL_RES_PATH: {
362 ret = strcmp(res1->path, res2->path);
365 case DAVQL_RES_HREF: {
366 ret = strcmp(res1->href, res2->href);
369 case DAVQL_RES_CONTENTLENGTH: {
370 int c = res1->contentlength == res2->contentlength;
371 ret = c ? 0 : (res1->contentlength < res2->contentlength?-1:1);
374 case DAVQL_RES_CONTENTTYPE: {
375 ret = strcmp(res1->contenttype, res2->contenttype);
378 case DAVQL_RES_CREATIONDATE: {
379 int c = res1->creationdate == res2->creationdate;
380 ret = c ? 0 : (res1->creationdate < res2->creationdate?-1:1);
383 case DAVQL_RES_LASTMODIFIED: {
384 int c = res1->lastmodified == res2->lastmodified;
385 ret = c ? 0 : (res1->lastmodified < res2->lastmodified?-1:1);
388 case DAVQL_RES_ISCOLLECTION: {
389 int c = res1->iscollection == res2->iscollection;
390 ret = c ? 0 : (res1->iscollection < res2->iscollection?-1:1);
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);
401 ret = value2 ? -1 : 0;
403 ret = value1 ? 1 : 0;
405 ret = strcmp(value1, value2);
411 return cr->descending ? -ret : ret;
414 void resource_add_ordered_child(DavResource *parent, DavResource *child, UcxList *ordercr) {
416 resource_add_child(parent, child);
420 child->parent = parent;
422 if(!parent->children) {
425 parent->children = child;
427 DavResource *resource = parent->children;
430 UCX_FOREACH(elm, ordercr) {
431 DavOrderCriterion *cr = elm->data;
432 r = resource_cmp(child, resource, cr);
439 // insert child before resource
440 child->prev = resource->prev;
441 child->next = resource;
443 resource->prev->next = child;
445 parent->children = child;
447 resource->prev = child;
449 } if(!resource->next) {
451 child->prev = resource;
453 resource->next = child;
456 resource = resource->next;
462 char* dav_get_string_property(DavResource *res, char *name) {
465 dav_get_property_namespace_str(res->session->context, name, &pns, &pname);
469 return dav_get_string_property_ns(res, pns, pname);
472 char* dav_get_string_property_ns(DavResource *res, char *ns, char *name) {
473 DavXmlNode *prop = dav_get_property_ns(res, ns, name);
477 return dav_xml_getstring(prop);
480 DavXmlNode* dav_get_property(DavResource *res, char *name) {
483 dav_get_property_namespace_str(res->session->context, name, &pns, &pname);
487 return dav_get_property_ns(res, pns, pname);
490 static DavXmlNode* get_property_ns(DavResource *res, DavBool encrypted, const char *ns, const char *name) {
495 DavResourceData *data = res->data;
497 DavXmlNode *property = NULL;
498 UcxList *remove_list = NULL;
499 UcxList *set_list = NULL;
502 // check if crypto_properties because it will only be created
503 // if the resource has encrypted properties
504 if(!data->crypto_properties) {
507 property = resource_get_encrypted_property(res, ns, name);
508 remove_list = data->crypto_remove;
509 set_list = data->crypto_set;
511 property = resource_get_property(res, ns, name);
512 remove_list = data->remove;
513 set_list = data->set;
516 // resource_get_property only returns persistent properties
517 // check the remove and set list
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)) {
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
535 // no property update
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);
543 if(!property_value && DAV_DECRYPT_PROPERTIES(res->session)) {
544 property_value = get_property_ns(res, TRUE, ns, name);
547 return property_value;
550 DavXmlNode* dav_get_encrypted_property_ns(DavResource *res, const char *ns, const char *name) {
551 return get_property_ns(res, TRUE, ns, name);
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;
559 DavNamespace *namespace = dav_session_malloc(sn, sizeof(DavNamespace));
560 namespace->prefix = NULL;
561 namespace->name = dav_session_strdup(sn, ns);
563 property->ns = namespace;
568 void dav_set_string_property(DavResource *res, char *name, char *value) {
571 dav_get_property_namespace_str(res->session->context, name, &pns, &pname);
572 dav_set_string_property_ns(res, pns, pname, value);
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;
580 DavProperty *property = createprop(res->session, ns, name);
581 property->value = dav_text_node(res->session, value);
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);
586 data->set = ucx_list_append_a(a, data->set, property);
590 void dav_set_property(DavResource *res, char *name, DavXmlNode *value) {
593 dav_get_property_namespace_str(res->session->context, name, &pns, &pname);
594 dav_set_property_ns(res, pns, pname, value);
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;
602 DavProperty *property = createprop(sn, ns, name);
603 property->value = value; // TODO: copy node?
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);
608 data->set = ucx_list_append_a(a, data->set, property);
612 void dav_remove_property(DavResource *res, char *name) {
615 dav_get_property_namespace_str(res->session->context, name, &pns, &pname);
616 dav_remove_property_ns(res, pns, pname);
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;
624 DavProperty *property = createprop(res->session, ns, name);
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);
629 data->remove = ucx_list_append_a(a, data->remove, property);
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;
637 DavProperty *property = createprop(res->session, ns, name);
638 property->value = value; // TODO: copy node?
640 data->crypto_set = ucx_list_append_a(a, data->crypto_set, property);
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;
647 DavProperty *property = createprop(res->session, ns, name);
648 property->value = dav_text_node(res->session, value);
650 data->crypto_set = ucx_list_append_a(a, data->crypto_set, property);
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;
657 DavProperty *property = createprop(res->session, ns, name);
659 data->crypto_remove = ucx_list_append_a(a, data->crypto_remove, property);
662 static int compare_propname(const void *a, const void *b) {
663 const DavPropName *p1 = a;
664 const DavPropName *p2 = b;
666 int result = strcmp(p1->ns, p2->ns);
670 return strcmp(p1->name, p2->name);
674 DavPropName* dav_get_property_names(DavResource *res, size_t *count) {
675 DavResourceData *data = res->data;
677 *count = data->properties->count;
678 DavPropName *names = dav_session_calloc(
681 sizeof(DavPropName));
684 UcxMapIterator i = ucx_map_iterator(data->properties);
687 UCX_MAP_FOREACH(key, value, i) {
688 DavPropName *name = &names[j];
690 name->ns = value->ns->name;
691 name->name = value->name;
696 qsort(names, *count, sizeof(DavPropName), compare_propname);
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;
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);
717 data->length = length;
720 void dav_set_content_length(DavResource *res, size_t length) {
721 DavResourceData *data = res->data;
722 data->length = length;
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);
733 int dav_load_prop(DavResource *res, DavPropName *properties, size_t numprop) {
734 UcxMempool *mp = ucx_mempool_new(64);
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:")) {
745 p->ns->prefix = ucx_asprintf(mp->allocator, "x%d", i).ptr;
748 proplist = ucx_list_append_a(mp->allocator, proplist, p);
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);
760 * read wrapper with integrated hashing
771 static void init_hash_stream(HashStream *hstr, void *stream, dav_read_func readfn, dav_seek_func seekfn) {
773 hstr->stream = stream;
779 static size_t dav_read_h(void *buf, size_t size, size_t nelm, void *stream) {
780 HashStream *s = stream;
782 s->sha = dav_hash_init();
785 size_t r = s->read(buf, size, nelm, s->stream);
786 dav_hash_update(s->sha, buf, r);
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);
799 return s->seek(s->stream, offset, whence);
803 int dav_store(DavResource *res) {
804 DavSession *sn = res->session;
805 DavResourceData *data = res->data;
807 util_set_url(sn, dav_resource_get_href(res));
809 DavLock *lock = dav_get_lock(sn, res->path);
810 char *locktoken = lock ? lock->token : NULL;
814 int encryption = DAV_ENCRYPT_CONTENT(sn) && sn->key;
817 AESEncrypter *enc = NULL;
818 UcxBuffer *buf = NULL;
820 enc = aes_encrypter_new(
826 buf = ucx_buffer_new(data->content, data->length, 0);
827 buf->size = data->length;
828 enc = aes_encrypter_new(
831 (dav_read_func)ucx_buffer_read,
832 (dav_seek_func)dav_buffer_seek);
836 ret = do_put_request(
841 (dav_read_func)aes_read,
842 (dav_seek_func)aes_encrypter_reset,
846 dav_get_hash(&enc->sha256, (unsigned char*)data->hash);
847 char *enc_hash = aes_encrypt(data->hash, DAV_SHA256_DIGEST_LENGTH, sn->key);
849 aes_encrypter_close(enc);
851 ucx_buffer_free(buf);
854 // add crypto properties
855 // TODO: store the properties later
856 if(resource_add_crypto_info(sn, res->href, res->name, enc_hash)) {
860 resource_add_string_property(res, DAV_NS, "crypto-hash", enc_hash);
862 } else if((sn->flags & DAV_SESSION_STORE_HASH) == DAV_SESSION_STORE_HASH) {
864 UcxBuffer *iobuf = NULL;
866 iobuf = ucx_buffer_new(data->content, data->length, 0);
867 iobuf->size = data->length;
871 (dav_read_func)ucx_buffer_read,
872 (dav_seek_func)ucx_buffer_seek);
881 ret = do_put_request(
887 (dav_seek_func)dav_seek_h,
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);
897 ret = do_put_request(
908 curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &status);
909 if(ret == CURLE_OK && (status >= 200 && status < 300)) {
910 res->session->error = 0;
913 ucx_mempool_free(sn->mp, data->content);
915 data->content = NULL;
919 dav_session_set_error(sn, ret, status);
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);
930 UcxBuffer *rqbuf = create_cryptoprop_propfind_request();
931 ret = dav_propfind(res->session, res, rqbuf);
932 ucx_buffer_free(rqbuf);
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);
939 // resource hasn't encrypted properties yet
940 crypto_props = ucx_map_new(32); // create new map
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
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);
953 // TODO: free existing_prop
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);
965 // TODO: free existing_prop
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);
977 dav_resource_free(crypto_res);
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);
993 CURLcode ret = do_proppatch_request(sn, locktoken, request, response);
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
1001 data->remove = NULL;
1003 dav_session_set_error(sn, ret, status);
1007 ucx_buffer_free(request);
1008 ucx_buffer_free(response);
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);
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);
1029 static void set_progressfunc(DavResource *res) {
1032 static void unset_progressfunc(DavResource *res) {
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));
1043 AESDecrypter *dec = NULL;
1045 if(DAV_DECRYPT_CONTENT(sn)) {
1046 char *keyname = dav_get_string_property_ns(res, DAV_NS, "crypto-key");
1048 key = dav_context_get_key(sn->context, keyname);
1050 dec = aes_decrypter_new(key, stream, write_fnc);
1052 write_fnc = (dav_write_func)aes_write;
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);
1062 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_fnc);
1063 curl_easy_setopt(handle, CURLOPT_WRITEDATA, stream);
1065 if(sn->get_progress) {
1066 set_progressfunc(res);
1070 CURLcode ret = dav_session_curl_perform(sn, &status);
1072 if(sn->get_progress) {
1073 unset_progressfunc(res);
1078 aes_decrypter_shutdown(dec); // get final bytes
1081 unsigned char sha[DAV_SHA256_DIGEST_LENGTH];
1082 dav_get_hash(&dec->sha256, sha);
1083 hash = util_hexstr(sha, DAV_SHA256_DIGEST_LENGTH);
1085 aes_decrypter_close(dec);
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");
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)) {
1110 res->session->error = DAV_CONTENT_VERIFICATION_ERROR;
1114 res->session->error = DAV_OK;
1120 dav_session_set_error(res->session, ret, status);
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);
1135 int dav_delete(DavResource *res) {
1136 CURL *handle = res->session->handle;
1137 util_set_url(res->session, dav_resource_get_href(res));
1139 DavLock *lock = dav_get_lock(res->session, res->path);
1140 char *locktoken = lock ? lock->token : NULL;
1142 UcxBuffer *response = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
1143 CURLcode ret = do_delete_request(res->session, locktoken, response);
1145 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
1147 if(ret == CURLE_OK && (status >= 200 && status < 300)) {
1148 res->session->error = DAV_OK;
1151 // TODO: parse response
1154 dav_session_set_error(res->session, ret, status);
1158 ucx_buffer_free(response);
1162 static int create_ancestors(DavSession *sn, char *href, char *path) {
1163 CURL *handle = sn->handle;
1166 DavLock *lock = dav_get_lock(sn, path);
1167 char *locktoken = lock ? lock->token : NULL;
1172 if(strlen(path) <= 1) {
1176 char *p = util_parent_path(path);
1177 char *h = util_parent_path(href);
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);
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';
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");
1195 } else if(status == 405) {
1196 // parent already exists
1198 } else if(status == 409) {
1199 // parent doesn't exist
1200 if(create_ancestors(sn, h, p)) {
1205 dav_session_set_error(sn, code, status);
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));
1221 DavLock *lock = dav_get_lock(res->session, res->path);
1222 char *locktoken = lock ? lock->token : NULL;
1225 if(res->iscollection) {
1226 code = do_mkcol_request(sn, locktoken);
1228 code = do_put_request(sn, locktoken, TRUE, "", NULL, NULL, 0);
1231 curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &s);
1233 if(code == CURLE_OK && (s >= 200 && s < 300)) {
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);
1246 dav_session_set_error(sn, code, s);
1251 int dav_create(DavResource *res) {
1253 if(!create_resource(res, &status)) {
1254 // resource successfully created
1259 if(status == 403 || status == 409 || status == 404) {
1260 // create intermediate collections
1261 if(create_ancestors(res->session, res->href, res->path)) {
1266 return create_resource(res, &status);
1269 int dav_exists(DavResource *res) {
1270 if(!dav_load_prop(res, NULL, 0)) {
1274 if(res->session->error == DAV_NOT_FOUND) {
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));
1286 DavLock *lock = dav_get_lock(sn, res->path);
1287 char *locktoken = lock ? lock->token : NULL;
1289 CURLcode ret = do_copy_move_request(sn, desturl, locktoken, copy, override);
1292 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
1293 if(ret == CURLE_OK && (status >= 200 && status < 300)) {
1296 dav_session_set_error(sn, ret, status);
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);
1306 int ret = dav_cp_mv_url(res, desturl, copy, override);
1311 int dav_copy(DavResource *res, char *newpath) {
1312 return dav_cp_mv(res, newpath, true, false);
1315 int dav_move(DavResource *res, char *newpath) {
1316 return dav_cp_mv(res, newpath, false, false);
1319 int dav_copy_o(DavResource *res, char *newpath, DavBool override) {
1320 return dav_cp_mv(res, newpath, true, override);
1323 int dav_move_o(DavResource *res, char *newpath, DavBool override) {
1324 return dav_cp_mv(res, newpath, false, override);
1327 int dav_copyto(DavResource *res, char *url, DavBool override) {
1328 return dav_cp_mv_url(res, url, true, override);
1331 int dav_moveto(DavResource *res, char *url, DavBool override) {
1332 return dav_cp_mv_url(res, url, false, override);
1335 int dav_lock(DavResource *res) {
1336 return dav_lock_t(res, 0);
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));
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);
1348 //printf("\nlock\n");
1349 //printf("%.*s\n\n", request->size, request->space);
1350 //printf("%.*s\n\n", response->size, response->space);
1352 ucx_buffer_free(request);
1355 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
1356 if(ret == CURLE_OK && (status >= 200 && status < 300)) {
1358 if(parse_lock_response(sn, response, &lock)) {
1359 sn->error = DAV_ERROR;
1360 ucx_buffer_free(response);
1363 ucx_buffer_free(response);
1365 DavLock *l = dav_create_lock(sn, lock.locktoken, lock.timeout);
1366 free(lock.locktoken);
1370 if(res->iscollection) {
1371 r = dav_add_collection_lock(sn, res->path, l);
1373 r = dav_add_resource_lock(sn, res->path, l);
1379 (void)dav_unlock(res);
1380 sn->error = DAV_ERROR;
1381 dav_destroy_lock(sn, l);
1385 dav_session_set_error(sn, ret, status);
1386 ucx_buffer_free(response);
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));
1396 DavLock *lock = dav_get_lock(res->session, res->path);
1398 sn->error = DAV_ERROR;
1402 CURLcode ret = do_unlock_request(sn, lock->token);
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);
1408 dav_session_set_error(sn, ret, status);
1416 int resource_add_crypto_info(DavSession *sn, const char *href, const char *name, const char *hash) {
1417 if(!DAV_IS_ENCRYPTED(sn)) {
1421 UcxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name, hash);
1422 UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
1424 util_set_url(sn, href);
1426 CURLcode ret = do_proppatch_request(sn, NULL, request, response);
1427 ucx_buffer_free(request);
1429 curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status);
1430 if(ret == CURLE_OK && status == 207) {
1431 // TODO: parse response
1433 ucx_buffer_free(response);
1436 dav_session_set_error(sn, ret, status);
1437 ucx_buffer_free(response);
1442 /* ----------------------------- crypto-prop ----------------------------- */
1444 DavXmlNode* create_crypto_prop(DavSession *sn, UcxMap *properties) {
1449 UcxBuffer *content = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND);
1451 // create an xml document containing all properties
1452 UcxMap *nsmap = ucx_map_new(8);
1453 ucx_map_cstr_put(nsmap, "DAV:", strdup("D"));
1455 ucx_buffer_puts(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1456 ucx_buffer_puts(content, "<D:prop xmlns:D=\"DAV:\">\n");
1458 UcxMapIterator i = ucx_map_iterator(properties);
1460 UCX_MAP_FOREACH(key, prop, i) {
1462 pnode.type = DAV_XML_ELEMENT;
1463 pnode.namespace = prop->ns->name;
1464 pnode.name = prop->name;
1467 pnode.children = prop->value;
1468 pnode.parent = NULL;
1469 pnode.attributes = NULL;
1470 pnode.content = NULL;
1471 pnode.contentlength = 0;
1473 dav_print_node(content, (write_func)ucx_buffer_write, nsmap, &pnode);
1474 ucx_buffer_putc(content, '\n');
1477 ucx_buffer_puts(content, "</D:prop>");
1479 ucx_map_free_content(nsmap, (ucx_destructor)free);
1480 ucx_map_free(nsmap);
1482 // encrypt xml document
1483 char *crypto_prop_content = aes_encrypt(content->space, content->size, sn->key);
1484 ucx_buffer_free(content);
1486 DavXmlNode *ret = NULL;
1487 if(crypto_prop_content) {
1488 ret = dav_text_node(sn, crypto_prop_content);
1489 free(crypto_prop_content);
1494 UcxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node) {
1495 if(!node || node->type != DAV_XML_TEXT || node->contentlength == 0) {
1499 return parse_crypto_prop_str(sn, key, node->content);
1502 UcxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content) {
1504 char *dec_str = aes_decrypt(content, &len, key);
1506 xmlDoc *doc = xmlReadMemory(dec_str, len, NULL, NULL, 0);
1513 xmlNode *xml_root = xmlDocGetRootElement(doc);
1517 !xstreq(xml_root->name, "prop") ||
1518 !xstreq(xml_root->ns->href, "DAV:"))
1531 // ready to get the properties
1532 UcxMap *map = ucx_map_new(32);
1533 xmlNode *n = xml_root->children;
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;
1544 sstr_t key = dav_property_key(property->ns->name, property->name);
1545 ucx_map_sstr_put(map, key, property);
1552 if(map->count == 0) {