UNIXworkcode

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)); 28 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 == re ->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 et_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 * s, 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 curl_easy_setopt(sn->handle, CURLOPT_XFERINFOFUNCTION, dav_session_put_progress); 860 curl_easy_setopt(sn->handle, CURLOPT_XFERINFODATA, res); 861 curl_easy_setopt(sn->handle, CURLOPT_NOPROGRESS, 0L); 862 863 int encryption = DAV_ENCRYPT_CONTENT(sn) && sn->key; 864 CURLcode ret; 865 if(encryption) { 866 AESEncrypter *enc = NULL; 867 CxBuffer *buf = NULL; 868 if(data->read) { 869 enc = aes_encrypter_new( 870 sn->key, 871 data->content, 872 data->read, 873 data->seek); 874 } else { 875 buf = cxBufferCreate(data->content, data->length, cxDefaultAllocator, 0); 876 buf->size = data->length; 877 enc = aes_encrypter_new( 878 sn->key, 879 buf, 880 (dav_read_func)cxBufferRead, 881 (dav_seek_func)cxBufferSeek); 882 } 883 884 // put resource 885 ret = do_put_request( 886 sn, 887 locktoken, 888 TRUE, 889 enc, 890 (dav_read_func)aes_read, 891 (dav_seek_func)aes_encrypter_reset, 892 0); 893 894 // get sha256 hash 895 dav_get_hash(&enc->sha256, (unsigned char*)data->hash); 896 char *enc_hash = aes_encrypt(data->hash, DAV_SHA256_DIGEST_LENGTH, sn->key); 897 898 aes_encrypter_close(enc); 899 if(buf) { 900 cxBufferFree(buf); 901 } 902 903 // add crypto properties 904 // TODO: store the properties later 905 if(resource_add_crypto_info(sn, res->href, res->name, enc_hash)) { 906 free(enc_hash); 907 curl_easy_setopt(sn->handle, CURLOPT_XFERINFOFUNCTION, NULL); 908 curl_easy_setopt(sn->handle, CURLOPT_NOPROGRESS, 1L); 909 return 1; 910 } 911 resource_add_string_property(res, DAV_NS, "crypto-hash", enc_hash); 912 free(enc_hash); 913 } else if((sn->flags & DAV_SESSION_STORE_HASH) == DAV_SESSION_STORE_HASH) { 914 HashStream hstr; 915 CxBuffer *iobuf = NULL; 916 if(!data->read) { 917 iobuf = cxBufferCreate(data->content, data->length, cxDefaultAllocator, 0); 918 iobuf->size = data->length; 919 init_hash_stream( 920 &hstr, 921 iobuf, 922 (dav_read_func)cxBufferRead, 923 (dav_seek_func)cxBufferSeek); 924 } else { 925 init_hash_stream( 926 &hstr, 927 data->content, 928 data->read, 929 data->seek); 930 } 931 932 ret = do_put_request( 933 sn, 934 locktoken, 935 TRUE, 936 &hstr, 937 dav_read_h, 938 (dav_seek_func)dav_seek_h, 939 data->length); 940 941 if(hstr.sha) { 942 dav_hash_final(hstr.sha, (unsigned char*)data->hash); 943 char *hash = util_hexstr((unsigned char*)data->hash, 32); 944 dav_set_string_property_ns(res, DAV_NS, "content-hash", hash); 945 free(hash); 946 } 947 } else { 948 ret = do_put_request( 949 sn, 950 locktoken, 951 TRUE, 952 data->content, 953 data->read, 954 data->seek, 955 data->length); 956 } 957 958 curl_easy_setopt(sn->handle, CURLOPT_XFERINFOFUNCTION, NULL); 959 curl_easy_setopt(sn->handle, CURLOPT_NOPROGRESS, 1L); 960 961 long status = 0; 962 curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &status); 963 if(ret == CURLE_OK && (status >= 200 && status < 300)) { 964 res->session->error = 0; 965 // cleanup node data 966 if(!data->read) { 967 cxFree(sn->mp->allocator, data->content); 968 } 969 data->content = NULL; 970 data->read = NULL; 971 data->length = 0; 972 } else { 973 dav_session_set_error(sn, ret, status); 974 return 1; 975 } 976 } 977 978 // generate crypto-prop content 979 if(DAV_ENCRYPT_PROPERTIES(sn) && sn->key && (data->crypto_set || data->crypto_remove)) { 980 DavResource *crypto_res = dav_resource_new_href(sn, res->href); 981 int ret = 1; 982 983 if(crypto_res) { 984 CxBuffer *rqbuf = create_cryptoprop_propfind_request(); 985 ret = dav_propfind(res->session, res, rqbuf); 986 cxBufferFree(rqbuf); 987 } 988 989 if(!ret) { 990 DavXmlNode *crypto_prop_node = dav_get_property_ns(crypto_res, DAV_NS, "crypto-prop"); 991 CxMap *crypto_props = parse_crypto_prop(sn, sn->key, crypto_prop_node); 992 if(!crypto_props) { 993 // resource hasn't encrypted prope ies yet 994 crypto_props = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); // create new map 995 } 996 997 // remove all properties 998 if(data->crypto_remove) { 999 CxIterator i = cxListIterator(data->crypto_remove); 1000 cx_foreach(DavProperty *, property, i) { 1001 if(crypto_props->size == 0) { 1002 break; // map already empty, can't remove any more 1003 } 1004 1005 cxmutstr key = dav_property_key(property->ns->name, property->name); 1006 DavProperty *existing_prop = cxMapGet(crypto_props, cx_hash_key(key.ptr, key.length)); 1007 if(existing_prop) { 1008 // TODO: free existing_prop 1009 } 1010 free(key.ptr); 1011 } 1012 } 1013 1014 // set properties 1015 if(data->crypto_set) { 1016 CxIterator i = cxListIterator(data->crypto_set); 1017 cx_foreach(DavProperty *, property, i) { 1018 cxmutstr keystr = dav_property_key(property->ns->name, property->name); 1019 CxHashKey key = cx_hash_key(keystr.ptr, keystr.length); 1020 DavProperty *existing_prop = cxMapRemoveAndGet(crypto_props, key); 1021 cxMapPut(crypto_props, key, property); 1022 if(existing_prop) { 1023 // TODO: free existing_prop 1024 } 1025 free(keystr.ptr); 1026 } 1027 } 1028 1029 DavXmlNode *crypto_prop_value = create_crypto_prop(sn, crypto_props); 1030 if(crypto_prop_value) { 1031 DavProperty *new_crypto_prop = createprop(sn, DAV_NS, "crypto-prop"); 1032 new_crypto_prop->value = crypto_prop_value; 1033 add2propertylist(sn->mp->allocator, &data->set, new_crypto_prop); 1034 } 1035 1036 dav_resource_free(crypto_res); 1037 } 1038 1039 if(ret) { 1040 return 1; 1041 } 1042 } 1043 1044 // store properties 1045 int r = 0; 1046 sn->error = DAV_OK; 1047 if(data->set || data->remove) { 1048 CxBuffer *request = create_proppatch_request(data); 1049 CxBuffer *response = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 1050 //printf("request:\n%.*s\n\n", request->pos, request->space); 1051 1052 CURLcode ret = do_proppatch_request(sn, locktoken, request, response); 1053 long status = 0; 1054 curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status); 1055 if(ret == CURLE_OK && status == 207) { 1056 //printf("%s\n", response->space); 1057 // TODO: parse response 1058 // TODO: cleanup node data correctly 1059 data->set = NULL; 1060 data->remove = NULL; 1061 } else { 1062 dav_session_set_error(sn, ret, status); 1063 r = -1; 1064 } 1065 1066 cxBufferFree(request); 1067 cxBufferFree(response); 1068 } 1069 1070 return r; 1071 } 1072 1073 #if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 32 1074 static void set_progressfunc(DavResource *res) { 1075 CURL *handle = res->session->handle; 1076 curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, dav_session_get_progress); 1077 curl_easy_setopt(handle, CURLOPT_XFERINFODATA, res); 1078 curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L); 1079 } 1080 1081 static void unset_progressfunc(DavResource *res) { 1082 CURL *handle = res->session->handle; 1083 curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, NULL); 1084 curl_easy_setopt(handle, CURLOPT_XFERINFODATA, NULL); 1085 curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1L); 1086 } 1087 #else 1088 static void set_progressfunc(DavResource *res) { 1089 1090 } 1091 static void unset_progressfunc(DavResource *res) { 1092 1093 } 1094 #endif 1095 1096 int dav_get_content(DavResource *res, void *stream, dav_write_func write_fnc) { 1097 DavSession *sn = res->session; 1098 CURL *handle = sn->handle; 1099 util_set_url(res->session, dav_resource_get_href(res)); 1100 1101 // check encryption 1102 AESDecrypter *dec = NULL; 1103 DavKey *key = NULL; 1104 if(DAV_DECRYPT_CONTENT(sn)) { 1105 char *keyname = dav_get_string_property_ns(res, DAV_NS, "crypto-key"); 1106 if(keyname) { 1107 key = dav_context_get_key(sn->context, keyname); 1108 if(key) { 1109 dec = aes_decrypter_new(key, stream, write_fnc); 1110 stream = dec; 1111 write_fnc = (dav_write_func)aes_write; 1112 } 1113 } 1114 } 1115 1116 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL); 1117 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL); 1118 curl_easy_setopt(handle, CURLOPT_PUT, 0L); 1119 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); 1120 1121 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_fnc); 1122 curl_easy_setopt(handle, CURLOPT_WRITEDATA, stream); 1123 1124 if(sn->get_progress) { 1125 set_progressfunc(res); 1126 } 1127 1128 long status = 0; 1129 CURLcode ret = dav_session_curl_perform(sn, &status); 1130 1131 if(sn->get_progress) { 1132 unset_progressfunc(res); 1133 } 1134 1135 char *hash = NULL; 1136 if(dec) { 1137 aes_decrypter_shutdown(dec); // get final bytes 1138 1139 // get hash 1140 unsigned char sha[DAV_SHA256_DIGEST_LENGTH]; 1141 dav_get_hash(&dec->sha256, sha); 1142 hash = util_hexstr(sha, DAV_SHA256_DIGEST_LENGTH); 1143 1144 aes_decrypter_close(dec); 1145 } 1146 1147 if(ret == CURLE_OK && (status >= 200 && status < 300)) { 1148 int verify_failed = 0; 1149 if(DAV_DECRYPT_CONTENT(sn) && key) { 1150 // try to verify the content 1151 char *res_hash = dav_get_string_property_ns(res, DAV_NS, "crypto-hash"); 1152 1153 if(res_hash) { 1154 size_t len = 0; 1155 char *dec_hash = aes_decrypt(res_hash, &len, key); 1156 char *hex_hash = util_hexstr((unsigned char*)dec_hash, len); 1157 if(strcmp(hash, hex_hash)) { 1158 verify_failed = 1; 1159 } 1160 free(dec_hash); 1161 free(hex_hash); 1162 } 1163 } 1164 if(hash) { 1165 free(hash); 1166 } 1167 1168 if(verify_failed) { 1169 res->session->error = DAV_CONTENT_VERIFICATION_ERROR; 1170 return 1; 1171 } 1172 1173 res->session->error = DAV_OK; 1174 return 0; 1175 } else { 1176 if(hash) { 1177 free(hash); 1178 } 1179 dav_session_set_error(res->session, ret, status); 1180 return 1; 1181 } 1182 } 1183 1184 DavResource* dav_create_child(DavResource *parent, char *name) { 1185 DavResource *res = dav_resource_new_child(parent->session, parent, name); 1186 if(dav_create(res)) { 1187 dav_resource_free(res); 1188 return NULL; 1189 } else { 1190 return res; 1191 } 1192 } 1193 1194 int dav_delete(DavResource *res) { 1195 CURL *handle = res->session->handle; 1196 util_set_url(res->session, dav_resource_get_href(res)); 1197 1198 DavLock *lock = dav_get_lock(res->session, res->path); 1199 char *locktoken = lock ? lock->token : NULL; 1200 1201 CxBuffer *response = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 1202 CURLcode ret = do_delete_request(res->session, locktoken, response); 1203 long status = 0; 1204 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); 1205 int r = 0; 1206 if(ret == CURLE_OK && (status >= 200 && status < 300)) { 1207 res->session->error = DAV_OK; 1208 res->exists = 0; 1209 1210 // TODO: parse response 1211 // TODO: free res 1212 } else { 1213 dav_session_set_error(res->session, ret, status); 1214 r = 1; 1215 } 1216 1217 cxBufferFree(response); 1218 return r; 1219 } 1220 1221 static int create_ancestors(DavSession *sn, char *href, char *path) { 1222 CURL *handle = sn->handle; 1223 CURLcode code; 1224 1225 DavLock *lock = dav_get_lock(sn, path); 1226 char *locktoken = lock ? lock->token : NULL; 1227 1228 long status = 0; 1229 int ret = 0; 1230 1231 if(strlen(path) <= 1) { 1232 return 0; 1233 } 1234 1235 char *p = util_parent_path(path); 1236 char *h = util_parent_path(href); 1237 1238 for(int i=0;i<2;i++) { 1239 util_set_url(sn, h); 1240 code = do_mkcol_request(sn, locktoken); 1241 curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status); 1242 if(status == 201) { 1243 // resource successfully created 1244 char *name = (char*)util_resource_name(p); 1245 int len = strlen(name); 1246 if(name[len - 1] == '/') { 1247 name[len - 1] = '\0'; 1248 } 1249 if(resource_add_crypto_info(sn, h, name, NULL)) { 1250 sn->error = DAV_ERROR; 1251 dav_session_set_errstr(sn, "Cannot set crypto properties for ancestor"); 1252 } 1253 break; 1254 } else if(status == 405) { // parent already exists 1256 break; 1257 } else if(status == 409) { 1258 // parent doesn't exist 1259 if(create_ancestors(sn, h, p)) { 1260 ret = 1; 1261 break; 1262 } 1263 } else { 1264 dav_session_set_error(sn, code, status); 1265 ret = 1; 1266 break; 1267 } 1268 } 1269 1270 free(p); 1271 free(h); 1272 return ret; 1273 } 1274 1275 static int create_resource(DavResource *res, int *status) { 1276 DavSession *sn = res->session; 1277 CURL *handle = sn->handle; 1278 util_set_url(sn, dav_resource_get_href(res)); 1279 1280 DavLock *lock = dav_get_lock(res->session, res->path); 1281 char *locktoken = lock ? lock->token : NULL; 1282 1283 CURLcode code; 1284 if(res->iscollection) { 1285 code = do_mkcol_request(sn, locktoken); 1286 } else { 1287 code = do_put_request(sn, locktoken, TRUE, "", NULL, NULL, 0); 1288 } 1289 long s = 0; 1290 curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &s); 1291 *status = s; 1292 if(code == CURLE_OK && (s >= 200 && s < 300)) { 1293 sn->error = DAV_OK; 1294 // if the session has encrypted file names, add crypto infos 1295 if(!resource_add_crypto_info(sn, res->href, res->name, NULL)) { 1296 // do a minimal propfind request 1297 CxBuffer *rqbuf = create_propfind_request(sn, NULL, "propfind", 0); 1298 int ret = dav_propfind(sn, res, rqbuf); 1299 cxBufferFree(rqbuf); 1300 return ret; 1301 } else { 1302 return 1; 1303 } 1304 } else { 1305 dav_session_set_error(sn, code, s); 1306 return 1; 1307 } 1308 } 1309 1310 int dav_create(DavResource *res) { 1311 int status; 1312 if(!create_resource(res, &status)) { 1313 // resource successfully created 1314 res->exists = 1; 1315 return 0; 1316 } 1317 1318 if(status == 403 || status == 409 || status == 404) { 1319 // create intermediate collections 1320 if(create_ancestors(res->session, res->href, res->path)) { 1321 return 1; 1322 } 1323 } 1324 1325 return create_resource(res, &status); 1326 } 1327 1328 int dav_exists(DavResource *res) { 1329 if(!dav_load_prop(res, NULL, 0)) { 1330 res->exists = 1; 1331 return 1; 1332 } else { 1333 if(res->session->error == DAV_NOT_FOUND) { 1334 res->exists = 0; 1335 } 1336 return 0; 1337 } 1338 } 1339 1340 static int dav_cp_mv_url(DavResource *res, char *desturl, _Bool copy, _Bool override) { 1341 DavSession *sn = res->session; 1342 CURL *handle = sn->handle; 1343 util_set_url(sn, dav_resource_get_href(res)); 1344 1345 DavLock *lock = dav_get_lock(sn, res->path); 1346 char *locktoken = lock ? lock->token : NULL; 1347 1348 CURLcode ret = do_copy_move_request(sn, desturl, locktoken, copy, override); 1349 1350 long status = 0; 1351 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); 1352 if(ret == CURLE_OK && (status >= 200 && status < 300)) { 1353 return 0; 1354 } else { 1355 dav_session_set_error(sn, ret, status); 1356 return 1; 1357 } 1358 } 1359 1360 static int dav_cp_mv(DavResource *res, char *newpath, _Bool copy, _Bool override) { 1361 char *dest = dav_session_get_href(res->session, newpath); 1362 char *desturl = util_get_url(res->session, dest); 1363 dav_session_free(res->session, dest); 1364 1365 int ret = dav_cp_mv_url(res, desturl, copy, override); 1366 free(desturl); 1367 return ret; 1368 } 1369 1370 int dav_copy(DavResource *res, char *newpath) { 1371 return dav_cp_mv(res, newpath, true, false); 1372 } 1373 1374 int dav_move(DavResource *res, char *newpath) { 1375 return dav_cp_mv(res, newpath, false, false); 1376 } 1377 1378 int dav_copy_o(DavResource *res, char *newpath, DavBool override) { 1379 return dav_cp_mv(res, newpath, true, override); 1380 } 1381 1382 int dav_move_o(DavResource *res, char *newpath, DavBool override) { 1383 return dav_cp_mv(res, newpath, false, override); 1384 } 1385 1386 int dav_copyto(DavResource *res, char *url, DavBool override) { 1387 return dav_cp_mv_url(res, url, true, override); 1388 } 1389 1390 int dav_moveto(DavResource *res, char *url, DavBool override) { 1391 return dav_cp_mv_url(res, url, false, override); 1392 } 1393 1394 int dav_lock(DavResource *res) { 1395 return dav_lock_t(res, 0); 1396 } 1397 1398 int dav_lock_t(DavResource *res, time_t timeout) { 1399 DavSession *sn = res->session; 1400 CURL *handle = sn->handle; 1401 util_set_url(sn, dav_resource_get_href(res)); 1402 1403 CxBuffer *request = create_lock_request(); 1404 CxBuffer *response = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 1405 CURLcode ret = do_lock_request(sn, request, response, timeout); 1406 1407 //printf("\nlock\n"); 1408 //printf("%.*s\n\n", request->size, request->space); 1409 //printf("%.*s\n\n", response->size, response->space); 1410 1411 cxBufferFree(request); 1412 1413 long status = 0; 1414 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); 1415 if(ret == CURLE_OK && (status >= 200 && status < 300)) { 1416 LockDiscovery lock; 1417 int parse_error = parse_lock_response(sn, response, &lock); 1418 cxBufferFree(response); 1419 if(parse_error) { 1420 sn->error = DAV_ERROR; 1421 return -1; 1422 } 1423 1424 DavLock *l = dav_create_lock(sn, lock.locktoken, lock.timeout); 1425 free(lock.locktoken); 1426 free(lock.timeout); 1427 1428 int r = 0; 1429 if(res->iscollection) { 1430 r = dav_add_collection_lock(sn, res->path, l); 1431 } else { 1432 r = dav_add_resource_lock(sn, res->path, l); 1433 } 1434 1435 if(r == 0) { 1436 return 0; 1437 } else { 1438 (void)dav_unlock(res); 1439 sn->error = DAV_ERROR; 1440 dav_destroy_lock(sn, l); 1441 return -1; 1442 } 1443 } else { 1444 dav_session_set_error(sn, ret, status); 1445 cxBufferFree(response); 1446 return -1; 1447 } 1448 } 1449 1450 int dav_unlock(DavResource *res) { 1451 DavSession *sn = res->session; 1452 CURL *handle = sn->handle; 1453 util_set_url(sn, dav_resource_get_href(res)); 1454 1455 DavLock *lock = dav_get_lock(res->session, res->path); 1456 if(!lock) { 1457 sn->error = DAV_ERROR; 1458 return -1; 1459 } 1460 1461 CURLcode ret = do_unlock_request(sn, lock->token); 1462 long status = 0; 1463 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); 1464 if(ret == CURLE_OK && (status >= 200 && status < 300)) { 1465 dav_remove_lock(sn, res->path, lock); 1466 } else { 1467 dav_session_set_error(sn, ret, status); 1468 return 1; 1469 } 1470 1471 return 0; 1472 } 1473 1474 1475 int resource_add_crypto_info(DavSession *sn, const char *href, const char *name, const char *hash) { 1476 if(!DAV_IS_ENCRYPTED(sn)) { 1477 return 0; 1478 } 1479 1480 CxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name, hash); 1481 CxBuffer *response = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 1482 1483 util_set_url(sn, href); 1484 // TODO: lock 1485 CURLcode ret = do_proppatch_request(sn, NULL, request, response); 1486 cxBufferFree(request); 1487 long status = 0; 1488 curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status); 1489 if(ret == CURLE_OK && status == 207) { 1490 // TODO: parse response 1491 sn->error = DAV_OK; 1492 cxBufferFree(response); 1493 return 0; 1494 } else { 1495 dav_session_set_error(sn, ret, status); 1496 cxBufferFree(response); 1497 return 1; 1498 } 1499 } 1500 1501 /* ----------------------------- crypto-prop ----------------------------- */ 1502 1503 DavXmlNode* create_crypto_prop(DavSession *sn, CxMap *properties) { 1504 if(!sn->key) { 1505 return NULL; 1506 } 1507 1508 CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 1509 1510 // create an xml document containing all properties 1511 CxMap *nsmap = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8); 1512 nsmap->simple_destructor = free; 1513 cxMapPut(nsmap, cx_hash_key_str("DAV:"), strdup("D")); 1514 1515 cxBufferPutString(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); 1516 cxBufferPutString(content, "<D:prop xmlns:D=\"DAV:\">\n"); 1517 1518 CxIterator i = cxMapIteratorValues(properties); 1519 DavProperty *prop; 1520 cx_foreach(DavProperty*, prop, i) { a class="c2html-lineno" name="l1521" href="#l1521">1521 DavXmlNode pnode; 1522 pnode.type = DAV_XML_ELEMENT; 1523 pnode.namespace = prop->ns->name; 1524 pnode.name = prop->name; 1525 pnode.prev = NULL; 1526 pnode.next = NULL; 1527 pnode.children = prop->value; 1528 pnode.parent = NULL; 1529 pnode.attributes = NULL; 1530 pnode.content = NULL; 1531 pnode.contentlength = 0; 1532 1533 dav_print_node(content, (cx_write_func)cxBufferWrite, nsmap, &pnode); 1534 cxBufferPut(content, '\n'); 1535 } 1536 1537 cxBufferPutString(content, "</D:prop>"); 1538 1539 cxMapDestroy(nsmap); 1540 1541 // encrypt xml document 1542 char *crypto_prop_content = aes_encrypt(content->space, content->size, sn->key); 1543 cxBufferDestroy(content); 1544 1545 DavXmlNode *ret = NULL; 1546 if(crypto_prop_content) { 1547 ret = dav_text_node(sn, crypto_prop_content); 1548 free(crypto_prop_content); 1549 } 1550 return ret; 1551 } 1552 1553 CxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node) { 1554 if(!node || node->type != DAV_XML_TEXT || node->contentlength == 0) { 1555 return NULL; 1556 } 1557 1558 return parse_crypto_prop_str(sn, key, node->content); 1559 } 1560 1561 CxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content) { 1562 size_t len = 0; 1563 char *dec_str = aes_decrypt(content, &len, key); 1564 1565 xmlDoc *doc = xmlReadMemory(dec_str, len, NULL, NULL, 0); 1566 free(dec_str); 1567 if(!doc) { 1568 return NULL; 1569 } 1570 1571 int err = 0; 1572 xmlNode *xml_root = xmlDocGetRootElement(doc); 1573 if(xml_root) { 1574 if( 1575 !xml_root->ns || 1576 !xstreq(xml_root->name, "prop") || 1577 !xstreq(xml_root->ns->href, "DAV:")) 1578 { 1579 err = 1; 1580 } 1581 } else { 1582 err = 1; 1583 } 1584 1585 if(err) { 1586 xmlFreeDoc(doc); 1587 return NULL; 1588 } 1589 1590 // ready to get the properties 1591 CxMap *map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); 1592 xmlNode *n = xml_root->children; 1593 while(n) { 1594 if(n->type == XML_ELEMENT_NODE && n->ns && n->ns->href) { 1595 DavProperty *property = dav_session_malloc(sn, sizeof(DavProperty)); 1596 property->name = dav_session_strdup(sn, (const char*)n->name); 1597 property->ns = dav_session_malloc(sn, sizeof(DavNamespace)); 1598 property->ns->name = dav_session_strdup(sn, (const char*)n->ns->href); 1599 property->ns->prefix = n->ns->prefix ? 1600 dav_session_strdup(sn, (const char*)n->ns->prefix) : NULL; 1601 property->value = n->children ? dav_convert_xml(sn, n->children) : NULL; 1602 1603 cxmutstr key = dav_property_key(property->ns->name, property->name); 1604 cxMapPut(map, cx_hash_key(key.ptr, key.length), property); 1605 free(key.ptr); 1606 } 1607 n = n->next; 1608 } 1609 1610 xmlFreeDoc(doc); 1611 if(map->size == 0) { 1612 cxMapDestroy(map); 1613 return NULL; 1614 } 1615 return map; 1616 } 1617 1618 1619 /* ----------------------------- streams ----------------------------- */ 1620 1621 static size_t in_write(const char *ptr, size_t size, size_t nitems, void *in_stream) { 1622 DavInputStream *in = in_stream; 1623 size_t len = size * nitems; 1624 1625 if(in->alloc < len) { 1626 char *newb = realloc(in->buffer, len); 1627 if(!newb) { 1628 if(in->buffer) free(in->buffer); 1629 in->eof = 1; 1630 return 0; 1631 } 1632 1633 in->buffer = newb; 1634 in->alloc = len; 1635 } 1636 1637 memcpy(in->buffer, ptr, len); 1638 1639 in->size = len; 1640 in->pos = 0; 1641 1642 return nitems; 1643 } 1644 1645 /* 1646 DavInputStream* dav_inputstream_open(DavResource *res) { 1647 DavSession *sn = res->session; 1648 1649 DavInputStream *in = dav_session_malloc(sn, sizeof(DavInputStream)); 1650 if(!in) { 1651 return NULL; 1652 } 1653 memset(in, 0, sizeof(DavInputStream)); 1654 1655 in->res = res; 1656 1657 in->c = curl_easy_duphandle(sn->handle); 1658 char *url = util_get_url(sn, dav_resource_get_href(res)); 1659 curl_easy_setopt(in->c, CURLOPT_URL, url); 1660 free(url); 1661 1662 in->m = curl_multi_init(); 1663 1664 curl_easy_setopt(in->c, CURLOPT_HTTPHEADER, NULL); 1665 curl_easy_setopt(in->c, CURLOPT_CUSTOMREQUEST, NULL); 1666 curl_easy_setopt(in->c, CURLOPT_PUT, 0L); 1667 curl_easy_setopt(in->c, CURLOPT_UPLOAD, 0L); 1668 1669 curl_multi_add_handle(in->m, in->c); 1670 1671 dav_write_func write_fnc = (dav_write_func)in_write; 1672 void *stream = in; 1673 1674 // check encryption 1675 AESDecrypter *dec = NULL; 1676 DavKey *key = NULL; 1677 if(DAV_DECRYPT_CONTENT(sn)) { 1678 char *keyname = dav_get_string_property_ns(res, DAV_NS, "crypto-key"); 1679 if(keyname) { 1680 key = dav_context_get_key(sn->context, keyname); 1681 if(key) { 1682 dec = aes_decrypter_new(key, stream, write_fnc); 1683 stream = dec; 1684 write_fnc = (dav_write_func)aes_write; 1685 } 1686 } 1687 } 1688 1689 curl_easy_setopt(in->c, CURLOPT_WRITEFUNCTION, write_fnc); 1690 curl_easy_setopt(in->c, CURLOPT_WRITEDATA, stream); 1691 1692 in->dec = dec; 1693 1694 return in; 1695 } 1696 1697 size_t dav_read(void *buf, size_t size, size_t nitems, DavInputStream *in) { 1698 size_t len = in->size - in->pos; 1699 size_t rl = size * nitems; 1700 if(len > 0) { 1701 len = rl > len ? len : rl; 1702 len -= len % size; 1703 memcpy(buf, in->buffer + in->pos, len); 1704 in->pos += len; 1705 return len / size; 1706 } 1707 in->size = 0; 1708 1709 if(in->eof) { 1710 if(in->dec) { 1711 aes_decrypter_shutdown(in->dec); // get final bytes 1712 aes_decrypter_close(in->dec); 1713 in->dec = NULL; 1714 } else { 1715 return 0; 1716 } 1717 } else { 1718 int running; 1719 while(!in->eof && in->size == 0) { 1720 CURLMcode r = curl_multi_perform(in->m, &running); 1721 if(r != CURLM_OK || running == 0) { 1722 in->eof = 1; 1723 break; 1724 } 1725 1726 int numfds; 1727 if(curl_multi_poll(in->m, NULL, 0, 5000, &numfds) != CURLM_OK) { 1728 in->eof = 1; 1729 } 1730 } 1731 } 1732 1733 return in->size > 0 ? dav_read(buf, size, nitems, in) : 0; 1734 } 1735 1736 void dav_inputstream_close(DavInputStream *in) { 1737 curl_multi_cleanup(in->m); 1738 curl_easy_cleanup(in->c); 1739 if(in->buffer) free(in->buffer); 1740 dav_session_free(in->res->session, in); 1741 } 1742 1743 1744 static size_t out_read(char *ptr, size_t size, size_t nitems, void *out_stream) { 1745 DavOutputStream *out = out_stream; 1746 size_t len = size * nitems; 1747 size_t available = out->size - out->pos; 1748 if(available == 0) { 1749 return 0; 1750 } 1751 1752 size_t r = len > available ? available : len; 1753 r -= r % size; 1754 memcpy(ptr, out->buffer + out->pos, r); 1755 1756 out->pos += r; 1757 1758 return r / size; 1759 } 1760 1761 static size_t dummy_write(void *buf, size_t s, size_t n, void *data) { 1762 return s*n; 1763 } 1764 1765 DavOutputStream* dav_outputstream_open(DavResource *res) { 1766 DavSession *sn = res->session; 1767 1768 DavOutputStream *out = dav_session_malloc(sn, sizeof(DavOutputStream)); 1769 if(!out) { 1770 return NULL; 1771 } 1772 memset(out, 0, sizeof(DavOutputStream)); 1773 1774 out->res = res; 1775 1776 out->c = curl_easy_duphandle(sn->handle); 1777 char *url = util_get_url(sn, dav_resource_get_href(res)); 1778 curl_easy_setopt(out->c, CURLOPT_URL, url); 1779 free(url); 1780 1781 out->m = curl_multi_init(); 1782 curl_multi_add_handle(out->m, out->c); 1783 1784 void *stream = out; 1785 dav_read_func read_fnc = (dav_read_func)out_read; 1786 1787 // if encryption or hashing in enabled, we need a stream wrapper 1788 if(DAV_ENCRYPT_CONTENT(sn) && sn->key) { 1789 AESEncrypter *enc = aes_encrypter_new(sn->key, out, (dav_read_func)out_read, NULL); 1790 out->enc = enc; 1791 stream = enc; 1792 read_fnc = (dav_read_func)aes_read; 1793 } else if((sn->flags & DAV_SESSION_STORE_HASH) == DAV_SESSION_STORE_HASH) { 1794 HashStream *hstr = dav_session_malloc(sn, sizeof(HashStream)); 1795 out->hstr = hstr; 1796 init_hash_stream(hstr, out, (dav_read_func)out_read, NULL); 1797 stream = hstr; 1798 read_fnc = (dav_read_func)dav_read_h; 1799 } 1800 1801 curl_easy_setopt(out->c, CURLOPT_HEADERFUNCTION, NULL); 1802 curl_easy_setopt(out->c, CURLOPT_HTTPHEADER, NULL); 1803 curl_easy_setopt(out->c, CURLOPT_CUSTOMREQUEST, NULL); 1804 curl_easy_setopt(out->c, CURLOPT_PUT, 1L); 1805 curl_easy_setopt(out->c, CURLOPT_UPLOAD, 1L); 1806 curl_easy_setopt(out->c, CURLOPT_READFUNCTION, read_fnc); 1807 curl_easy_setopt(out->c, CURLOPT_READDATA, stream); 1808 curl_easy_setopt(out->c, CURLOPT_SEEKFUNCTION, NULL); 1809 curl_easy_setopt(out->c, CURLOPT_INFILESIZE, -1); 1810 curl_easy_setopt(out->c, CURLOPT_INFILESIZE_LARGE, -1L); 1811 1812 curl_easy_setopt(out->c, CURLOPT_WRITEFUNCTION, dummy_write); 1813 curl_easy_setopt(out->c, CURLOPT_WRITEDATA, NULL); 1814 1815 return out; 1816 } 1817 1818 size_t dav_write(const void *buf, size_t size, size_t nitems, DavOutputStream *out) { 1819 if(out->eof) return 0; 1820 1821 out->buffer = buf; 1822 out->size = size * nitems; 1823 out->pos = 0; 1824 1825 int running; 1826 while(!out->eof && (out->size == 0 || out->size - out->pos > 0)) { 1827 CURLMcode r = curl_multi_perform(out->m, &running); 1828 if(r != CURLM_OK || running == 0) { 1829 out->eof = 1; 1830 break; 1831 } 1832 1833 int numfds; 1834 if(curl_multi_poll(out->m, NULL, 0, 5000, &numfds) != CURLM_OK) { 1835 out->eof = 1; 1836 } 1837 } 1838 1839 return (out->size - out->pos) / size; 1840 } 1841 1842 int dav_outputstream_close(DavOutputStream *out) { 1843 DavSession *sn = out->res->session; 1844 DavResource *res = out->res; 1845 DavResourceData *data = res->data; 1846 1847 int ret = 0; 1848 1849 dav_write(NULL, 1, 0, out); 1850 1851 curl_multi_cleanup(out->m); 1852 curl_easy_cleanup(out->c); 1853 1854 int store = 0; 1855 if(out->enc) { 1856 // get sha256 hash 1857 char hash[32]; 1858 dav_get_hash(&out->enc->sha256, (unsigned char*)data->hash); 1859 aes_encrypter_close(out->enc); 1860 char *enc_hash = aes_encrypt(hash, DAV_SHA256_DIGEST_LENGTH, sn->key); 1861 // add crypto properties 1862 if(resource_add_crypto_info(sn, out->res->href, out->res->name, enc_hash)) { 1863 ret = 1; 1864 } 1865 free(enc_hash); 1866 } else if(out->hstr) { 1867 dav_hash_final(out->hstr->sha, (unsigned char*)data->hash); 1868 char *hash = util_hexstr((unsigned char*)data->hash, 32); 1869 dav_set_string_property_ns(res, DAV_NS, "content-hash", hash); 1870 free(hash); 1871 dav_session_free(sn, out->hstr); 1872 store = 1; 1873 } 1874 1875 if(store) { 1876 ret = dav_store(out->res); 1877 } 1878 1879 dav_session_free(out->res->session, out); 1880 1881 return ret; 1882 } 1883 1884 */ 1885