libidav/resource.c

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

mercurial