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