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 33 #include "utils.h" 34 #include "methods.h" 35 #include "crypto.h" 36 #include "session.h" 37 #include "xml.h" 38 39 #include <cx/utils.h> 40 #include <cx/printf.h> 41 #include <cx/hash_map.h> 42 43 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) 44 45 46 int dav_buffer_seek(CxBuffer *b, curl_off_t offset, int origin) { 47 return cxBufferSeek(b, offset, origin) == 0 ? 0:CURL_SEEKFUNC_CANTSEEK; 48 } 49 50 /* ----------------------------- PROPFIND ----------------------------- */ 51 52 CURLcode do_propfind_request( 53 DavSession *sn, 54 CxBuffer *request, 55 CxBuffer *response) 56 { 57 CURL *handle = sn->handle; 58 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPFIND"); 59 60 // always try to get information about possible children 61 int depth = 1; 62 63 int maxretry = 2; 64 65 struct curl_slist *headers = NULL; 66 CURLcode ret = 0; 67 68 curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 69 curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead); 70 curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); 71 curl_easy_setopt(handle, CURLOPT_READDATA, request); 72 curl_easy_setopt(handle, CURLOPT_SEEKDATA, request); 73 curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); 74 75 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); 76 curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); 77 CxMap *respheaders = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); 78 cxDefineDestructor(respheaders, free); 79 util_capture_header(handle, respheaders); 80 81 for(int i=0;i<maxretry;i++) { 82 if (depth == 1) { 83 headers = curl_slist_append(headers, "Depth: 1"); 84 } else if (depth == -1) { 85 headers = curl_slist_append(headers, "Depth: infinity"); 86 } else { 87 headers = curl_slist_append(headers, "Depth: 0"); 88 } 89 headers = curl_slist_append(headers, "Content-Type: text/xml"); 90 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 91 92 // reset buffers and perform request 93 request->pos = 0; 94 response->size = response->pos = 0; 95 ret = dav_session_curl_perform_buf(sn, request, response, NULL); 96 curl_slist_free_all(headers); 97 headers = NULL; 98 99 /* 100 * Handle two cases: 101 * 1. We communicate with IIS and get a X-MSDAVEXT_Error: 589831 102 * => try with depth 0 next time, it's not a collection 103 * 2. Other cases 104 * => the server handled our request and we can stop requesting 105 */ 106 char *msdavexterror; 107 msdavexterror = cxMapGet(respheaders, cx_hash_key_str("x-msdavext_error")); 108 int iishack = depth == 1 && 109 msdavexterror && !strncmp(msdavexterror, "589831;", 7); 110 111 if(iishack) { 112 depth = 0; 113 } else { 114 break; 115 } 116 } 117 118 // deactivate header capturing and free captured map 119 util_capture_header(handle, NULL); 120 cxMapDestroy(respheaders); 121 122 return ret; 123 } 124 125 CxBuffer* create_allprop_propfind_request(void) { 126 CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 127 cxstring s; 128 129 s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); 130 cxBufferWrite(s.ptr, 1, s.length, buf); 131 132 s = CX_STR("<D:propfind xmlns:D=\"DAV:\">\n"); 133 cxBufferWrite(s.ptr, 1, s.length, buf); 134 135 s = CX_STR("<D:allprop/></D:propfind>\n"); 136 cxBufferWrite(s.ptr, 1, s.length, buf); 137 138 return buf; 139 } 140 141 CxBuffer* create_cryptoprop_propfind_request(void) { 142 CxBuffer *buf = cxBufferCreate(NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 143 cxstring s; 144 145 s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); 146 cxBufferWrite(s.ptr, 1, s.length, buf); 147 148 s = CX_STR("<D:propfind xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n"); 149 cxBufferWrite(s.ptr, 1, s.length, buf); 150 151 s = CX_STR("<D:prop><idav:crypto-prop/></D:prop></D:propfind>\n"); 152 cxBufferWrite(s.ptr, 1, s.length, buf); 153 154 return buf; 155 } 156 157 CxBuffer* create_propfind_request(DavSession *sn, CxList *properties, char *rootelm, DavBool nocrypt) { 158 CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 159 cxstring s; 160 161 int add_crypto_name = 1; 162 int add_crypto_key = 1; 163 int add_crypto_hash = 1; 164 char *crypto_ns = "idav"; 165 CxMap *namespaces = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8); 166 if(properties) { 167 CxIterator i = cxListIterator(properties); 168 cx_foreach(DavProperty*, p, i) { 169 if(strcmp(p->ns->name, "DAV:")) { 170 cxMapPut(namespaces, cx_hash_key_str(p->ns->prefix), p->ns); 171 } 172 173 // if the properties list contains the idav properties crypto-name 174 // and crypto-key, mark them as existent 175 if(!strcmp(p->ns->name, DAV_NS)) { 176 if(!strcmp(p->name, "crypto-name")) { 177 add_crypto_name = 0; 178 crypto_ns = p->ns->prefix; 179 } else if(!strcmp(p->name, "crypto-key")) { 180 add_crypto_key = 0; 181 crypto_ns = p->ns->prefix; 182 } else if(!strcmp(p->name, "crypto-hash")) { 183 add_crypto_hash = 0; 184 crypto_ns = p->ns->prefix; 185 } 186 } 187 } 188 } 189 190 DavNamespace idav_ns; 191 if(add_crypto_name && add_crypto_key && DAV_CRYPTO(sn) && !nocrypt) { 192 idav_ns.prefix = "idav"; 193 idav_ns.name = DAV_NS; 194 cxMapPut(namespaces, cx_hash_key_str("idav"), &idav_ns); 195 } 196 197 s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); 198 cxBufferWrite(s.ptr, 1, s.length, buf); 199 200 // write root element and namespaces 201 cx_bprintf(buf, "<D:%s xmlns:D=\"DAV:\"", rootelm); 202 203 CxIterator mapi = cxMapIteratorValues(namespaces); 204 cx_foreach(DavNamespace*, ns, mapi) { 205 s = CX_STR(" xmlns:"); 206 cxBufferWrite(s.ptr, 1, s.length, buf); 207 s = cx_str(ns->prefix); 208 cxBufferWrite(s.ptr, 1, s.length, buf); 209 s = CX_STR("=\""); 210 cxBufferWrite(s.ptr, 1, s.length, buf); 211 s = cx_str(ns->name); 212 cxBufferWrite(s.ptr, 1, s.length, buf); 213 s = CX_STR("\""); 214 cxBufferWrite(s.ptr, 1, s.length, buf); 215 } 216 s = CX_STR(">\n"); 217 cxBufferWrite(s.ptr, 1, s.length, buf); 218 219 // default properties 220 s = CX_STR("<D:prop>\n"); 221 cxBufferWrite(s.ptr, 1, s.length, buf); 222 223 s = CX_STR("<D:creationdate />\n<D:getlastmodified />\n"); 224 cxBufferWrite(s.ptr, 1, s.length, buf); 225 226 s = CX_STR("<D:getcontentlength />\n<D:getcontenttype />\n"); 227 cxBufferWrite(s.ptr, 1, s.length, buf); 228 229 s = CX_STR("<D:resourcetype />\n"); 230 cxBufferWrite(s.ptr, 1, s.length, buf); 231 232 // crypto properties 233 if(DAV_CRYPTO(sn) && !nocrypt) { 234 if(add_crypto_name) { 235 cxBufferPut(buf, '<'); 236 cxBufferPutString(buf, crypto_ns); 237 s = CX_STR(":crypto-name />\n"); 238 cxBufferWrite(s.ptr, 1, s.length, buf); 239 } 240 if(add_crypto_key) { 241 cxBufferPut(buf, '<'); 242 cxBufferPutString(buf, crypto_ns); 243 s = CX_STR(":crypto-key />\n"); 244 cxBufferWrite(s.ptr, 1, s.length, buf); 245 } 246 if(add_crypto_hash) { 247 cxBufferPut(buf, '<'); 248 cxBufferPutString(buf, crypto_ns); 249 s = CX_STR(":crypto-hash />\n"); 250 cxBufferWrite(s.ptr, 1, s.length, buf); 251 } 252 } 253 254 // extra properties 255 if(properties) { 256 CxIterator i = cxListIterator(properties); 257 cx_foreach(DavProperty*, prop, i) { 258 s = CX_STR("<"); 259 cxBufferWrite(s.ptr, 1, s.length, buf); 260 s = cx_str(prop->ns->prefix); 261 cxBufferWrite(s.ptr, 1, s.length, buf); 262 s = CX_STR(":"); 263 cxBufferWrite(s.ptr, 1, s.length, buf); 264 s = cx_str(prop->name); 265 cxBufferWrite(s.ptr, 1, s.length, buf); 266 s = CX_STR(" />\n"); 267 cxBufferWrite(s.ptr, 1, s.length, buf); 268 } 269 } 270 271 // end 272 cx_bprintf(buf, "</D:prop>\n</D:%s>\n", rootelm); 273 274 cxMapDestroy(namespaces); 275 return buf; 276 } 277 278 CxBuffer* create_basic_propfind_request(void) { 279 CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 280 cxstring s; 281 282 s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); 283 cxBufferWrite(s.ptr, 1, s.length, buf); 284 285 s = CX_STR("<D:propfind xmlns:D=\"DAV:\" xmlns:i=\""); 286 cxBufferWrite(s.ptr, 1, s.length, buf); 287 s = CX_STR(DAV_NS); 288 cxBufferWrite(s.ptr, 1, s.length, buf); 289 s = CX_STR("\" >\n"); 290 cxBufferWrite(s.ptr, 1, s.length, buf); 291 292 // properties 293 s = CX_STR("<D:prop>\n"); 294 cxBufferWrite(s.ptr, 1, s.length, buf); 295 s = CX_STR("<D:resourcetype />\n"); 296 cxBufferWrite(s.ptr, 1, s.length, buf); 297 s = CX_STR("<i:crypto-key />\n"); 298 cxBufferWrite(s.ptr, 1, s.length, buf); 299 s = CX_STR("<i:crypto-name />\n"); 300 cxBufferWrite(s.ptr, 1, s.length, buf); 301 s = CX_STR("<i:crypto-hash />\n"); 302 cxBufferWrite(s.ptr, 1, s.length, buf); 303 s = CX_STR("</D:prop>\n"); 304 cxBufferWrite(s.ptr, 1, s.length, buf); 305 306 // end 307 s = CX_STR("</D:propfind>\n"); 308 cxBufferWrite(s.ptr, 1, s.length, buf); 309 310 return buf; 311 } 312 313 PropfindParser* create_propfind_parser(CxBuffer *response, char *url) { 314 PropfindParser *parser = malloc(sizeof(PropfindParser)); 315 if(!parser) { 316 return NULL; 317 } 318 parser->document = xmlReadMemory(response->space, response->pos, url, NULL, 0); 319 parser->current = NULL; 320 if(parser->document) { 321 xmlNode *xml_root = xmlDocGetRootElement(parser->document); 322 if(xml_root) { 323 xmlNode *node = xml_root->children; 324 while(node) { 325 // find first response tag 326 if(node->type == XML_ELEMENT_NODE) { 327 if(xstreq(node->name, "response")) { 328 parser->current = node; 329 break; 330 } 331 } 332 node = node->next; 333 } 334 return parser; 335 } else { 336 xmlFreeDoc(parser->document); 337 } 338 } 339 free(parser); 340 return NULL; 341 } 342 343 void destroy_propfind_parser(PropfindParser *parser) { 344 if(parser->document) { 345 xmlFreeDoc(parser->document); 346 } 347 free(parser); 348 } 349 350 int get_propfind_response(PropfindParser *parser, ResponseTag *result) { 351 if(parser->current == NULL) { 352 return 0; 353 } 354 355 char *href = NULL; 356 int iscollection = 0; 357 char *crypto_name = NULL; // name set by crypto-name property 358 char *crypto_key = NULL; 359 360 result->properties = cxLinkedListCreateSimple(CX_STORE_POINTERS); // xmlNode list 361 362 xmlNode *node = parser->current->children; 363 while(node) { 364 if(node->type == XML_ELEMENT_NODE) { 365 if(xstreq(node->name, "href")) { 366 xmlNode *href_node = node->children; 367 if(href_node->type != XML_TEXT_NODE) { 368 // error 369 return -1; 370 } 371 href = (char*)href_node->content; 372 } else if(xstreq(node->name, "propstat")) { 373 xmlNode *n = node->children; 374 xmlNode *prop_node = NULL; 375 int ok = 0; 376 // get the status code 377 while(n) { 378 if(n->type == XML_ELEMENT_NODE) { 379 if(xstreq(n->name, "prop")) { 380 prop_node = n; 381 } else if(xstreq(n->name, "status")) { 382 xmlNode *status_node = n->children; 383 if(status_node->type != XML_TEXT_NODE) { 384 // error 385 return -1; 386 } 387 cxstring status_str = cx_str((char*)status_node->content); 388 if(status_str.length < 13) { 389 // error 390 return -1; 391 } 392 status_str = cx_strsubsl(status_str, 9, 3); 393 if(!cx_strcmp(status_str, CX_STR("200"))) { 394 ok = 1; 395 } 396 } 397 } 398 n = n->next; 399 } 400 // if status is ok, get all properties 401 if(ok) { 402 n = prop_node->children; 403 while(n) { 404 if(n->type == XML_ELEMENT_NODE) { 405 cxListAdd(result->properties, n); 406 if(xstreq(n->name, "resourcetype")) { 407 if(parse_resource_type(n)) { 408 iscollection = TRUE; 409 } 410 } else if(xstreq(n->ns->href, DAV_NS)) { 411 if(xstreq(n->name, "crypto-name")) { 412 crypto_name = util_xml_get_text(n); 413 } else if(xstreq(n->name, "crypto-key")) { 414 crypto_key = util_xml_get_text(n); 415 } 416 } 417 } 418 n = n->next; 419 } 420 } 421 } 422 } 423 node = node->next; 424 } 425 426 result->href = util_url_path(href); 427 result->iscollection = iscollection; 428 result->crypto_name = crypto_name; 429 result->crypto_key = crypto_key; 430 431 // find next response tag 432 xmlNode *next = parser->current->next; 433 while(next) { 434 if(next->type == XML_ELEMENT_NODE) { 435 if(xstreq(next->name, "response")) { 436 break; 437 } 438 } 439 next = next->next; 440 } 441 parser->current = next; 442 443 return 1; 444 } 445 446 void cleanup_response(ResponseTag *result) { 447 if(result) { 448 cxListDestroy(result->properties); 449 } 450 } 451 452 int hrefeq(DavSession *sn, const char *href1, const char *href2) { 453 cxmutstr href_s = cx_mutstr(util_url_decode(sn, href1)); 454 cxmutstr href_r = cx_mutstr(util_url_decode(sn, href2)); 455 int ret = 0; 456 if(!cx_strcmp(cx_strcast(href_s), cx_strcast(href_r))) { 457 ret = 1; 458 } else if(href_s.length == href_r.length + 1) { 459 if(href_s.ptr[href_s.length-1] == '/') { 460 href_s.length--; 461 if(!cx_strcmp(cx_strcast(href_s), cx_strcast(href_r))) { 462 ret = 1; 463 } 464 } 465 } else if(href_r.length == href_s.length + 1) { 466 if(href_r.ptr[href_r.length-1] == '/') { 467 href_r.length--; 468 if(!cx_strcmp(cx_strcast(href_s), cx_strcast(href_r))) { 469 ret = 1; 470 } 471 } 472 } 473 474 free(href_s.ptr); 475 free(href_r.ptr); 476 477 return ret; 478 } 479 480 481 DavResource* parse_propfind_response(DavSession *sn, DavResource *root, CxBuffer *response) { 482 char *url = NULL; 483 curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &url); 484 if(!root) { 485 printf("methods.c: TODO: remove\n"); 486 root = dav_resource_new_href(sn, util_url_path(url)); // TODO: remove 487 } 488 489 //printf("%.*s\n\n", response->size, response->space); 490 xmlDoc *doc = xmlReadMemory(response->space, response->size, url, NULL, 0); 491 if(!doc) { 492 // TODO: free stuff 493 sn->error = DAV_ERROR; 494 return NULL; 495 } 496 497 xmlNode *xml_root = xmlDocGetRootElement(doc); 498 xmlNode *node = xml_root->children; 499 while(node) { 500 if(node->type == XML_ELEMENT_NODE) { 501 if(xstreq(node->name, "response")) { 502 parse_response_tag(root, node); 503 } 504 } 505 node = node->next; 506 } 507 xmlFreeDoc(doc); 508 509 return root; 510 } 511 512 DavResource* response2resource(DavSession *sn, ResponseTag *response, char *parent_path) { 513 // create resource 514 char *name = NULL; 515 DavKey *key = NULL; 516 if(DAV_DECRYPT_NAME(sn) && response->crypto_name && (key = dav_context_get_key(sn->context, response->crypto_key))) { 517 if(!response->crypto_key) { 518 sn->error = DAV_ERROR; 519 dav_session_set_errstr(sn, "Missing crypto-key property"); 520 return NULL; 521 } 522 name = util_decrypt_str_k(sn, response->crypto_name, key); 523 if(!name) { 524 sn->error = DAV_ERROR; 525 dav_session_set_errstr(sn, "Cannot decrypt resource name"); 526 return NULL; 527 } 528 } else { 529 cxstring resname = cx_str(util_resource_name(response->href)); 530 int nlen = 0; 531 char *uname = curl_easy_unescape( 532 sn->handle, 533 resname.ptr, 534 resname.length, 535 &nlen); 536 name = dav_session_strdup(sn, uname); 537 curl_free(uname); 538 } 539 540 char *href = dav_session_strdup(sn, response->href); 541 DavResource *res = NULL; 542 if(parent_path) { 543 res = dav_resource_new_full(sn, parent_path, name, href); 544 } else { 545 res = dav_resource_new_href(sn, href); 546 } 547 dav_session_free(sn, name); 548 549 add_properties(res, response); 550 return res; 551 } 552 553 void add_properties(DavResource *res, ResponseTag *response) { 554 res->iscollection = response->iscollection; 555 556 int decrypt_props = DAV_ENCRYPT_PROPERTIES(res->session); 557 xmlNode *crypto_prop = NULL; 558 char *crypto_key = NULL; 559 560 // add properties 561 if(response->properties) { 562 CxIterator i = cxListIterator(response->properties); 563 cx_foreach(xmlNode*, prop, i) { 564 resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children); 565 566 if (decrypt_props && 567 prop->children && 568 prop->children->type == XML_TEXT_NODE && 569 xstreq(prop->ns->href, DAV_NS)) 570 { 571 if(xstreq(prop->name, "crypto-prop")) { 572 crypto_prop = prop; 573 } else if(xstreq(prop->name, "crypto-key")) { 574 crypto_key = util_xml_get_text(prop); 575 } 576 } 577 } 578 } 579 580 if(crypto_prop && crypto_key) { 581 char *crypto_prop_content = util_xml_get_text(crypto_prop); 582 DavKey *key = dav_context_get_key(res->session->context, crypto_key); 583 if(crypto_prop_content) { 584 CxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content); 585 resource_set_crypto_properties(res, cprops); 586 } 587 } 588 589 set_davprops(res); 590 } 591 592 int parse_response_tag(DavResource *resource, xmlNode *node) { 593 DavSession *sn = resource->session; 594 595 //DavResource *res = resource; 596 DavResource *res = NULL; 597 const char *href = NULL; 598 CxList *properties = cxLinkedListCreateSimple(CX_STORE_POINTERS); // xmlNode list 599 char *crypto_name = NULL; // name set by crypto-name property 600 char *crypto_key = NULL; 601 602 int iscollection = 0; // TODO: remove 603 604 node = node->children; 605 while(node) { 606 if(node->type == XML_ELEMENT_NODE) { 607 if(xstreq(node->name, "href")) { 608 xmlNode *href_node = node->children; 609 if(href_node->type != XML_TEXT_NODE) { 610 // error 611 sn->error = DAV_ERROR; 612 return 1; 613 } 614 //char *href = (char*)href_node->content; 615 href = util_url_path((const char*)href_node->content); 616 617 char *href_s = util_url_decode(resource->session, href); 618 char *href_r = util_url_decode(resource->session, resource->href); 619 620 if(hrefeq(sn, href_s, href_r)) { 621 res = resource; 622 } 623 624 free(href_s); 625 free(href_r); 626 } else if(xstreq(node->name, "propstat")) { 627 xmlNode *n = node->children; 628 xmlNode *prop_node = NULL; 629 int ok = 0; 630 // get the status code 631 while(n) { 632 if(n->type == XML_ELEMENT_NODE) { 633 if(xstreq(n->name, "prop")) { 634 prop_node = n; 635 } else if(xstreq(n->name, "status")) { 636 xmlNode *status_node = n->children; 637 if(status_node->type != XML_TEXT_NODE) { 638 sn->error = DAV_ERROR; 639 return 1; 640 } 641 cxstring status_str = cx_str((char*)status_node->content); 642 if(status_str.length < 13) { 643 sn->error = DAV_ERROR; 644 return 1; 645 } 646 status_str = cx_strsubsl(status_str, 9, 3); 647 if(!cx_strcmp(status_str, CX_STR("200"))) { 648 ok = 1; 649 } 650 } 651 } 652 n = n->next; 653 } 654 // if status is ok, get all properties 655 if(ok) { 656 n = prop_node->children; 657 while(n) { 658 if(n->type == XML_ELEMENT_NODE) { 659 cxListAdd(properties, n); 660 if(xstreq(n->name, "resourcetype")) { 661 if(parse_resource_type(n)) { 662 iscollection = TRUE; 663 } 664 } else if(n->ns && xstreq(n->ns->href, DAV_NS)) { 665 if(xstreq(n->name, "crypto-name")) { 666 crypto_name = util_xml_get_text(n); 667 } else if(xstreq(n->name, "crypto-key")) { 668 crypto_key = util_xml_get_text(n); 669 } 670 } 671 } 672 n = n->next; 673 } 674 } 675 } 676 } 677 678 node = node->next; 679 } 680 681 if(!res) { 682 // create new resource object 683 char *name = NULL; 684 if(DAV_DECRYPT_NAME(sn) && crypto_name) { 685 if(!crypto_key) { 686 sn->error = DAV_ERROR; 687 dav_session_set_errstr(sn, "Missing crypto-key property"); 688 return -1; 689 } 690 name = util_decrypt_str(sn, crypto_name, crypto_key); 691 if(!name) { 692 sn->error = DAV_ERROR; 693 dav_session_set_errstr(sn, "Cannot decrypt resource name"); 694 return -1; 695 } 696 } else { 697 cxstring resname = cx_str(util_resource_name(href)); 698 int nlen = 0; 699 char *uname = curl_easy_unescape( 700 sn->handle, 701 resname.ptr, 702 resname.length, 703 &nlen); 704 name = dav_session_strdup(sn, uname); 705 curl_free(uname); 706 } 707 708 char *href_cp = dav_session_strdup(sn, href); 709 res = dav_resource_new_full(sn, resource->path, name, href_cp); 710 711 dav_session_free(sn, name); 712 } 713 res->iscollection = iscollection; 714 715 // add properties 716 int decrypt_props = DAV_ENCRYPT_PROPERTIES(res->session); 717 xmlNode *crypto_prop = NULL; 718 719 CxIterator i = cxListIterator(properties); 720 cx_foreach(xmlNode*, prop, i) { 721 if(!prop->ns) { 722 continue; 723 } 724 resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children); 725 726 if (decrypt_props && 727 prop->children && 728 prop->children->type == XML_TEXT_NODE && 729 xstreq(prop->ns->href, DAV_NS)) 730 { 731 if(xstreq(prop->name, "crypto-prop")) { 732 crypto_prop = prop; 733 } 734 } 735 } 736 cxListDestroy(properties); 737 738 if(crypto_prop && crypto_key) { 739 char *crypto_prop_content = util_xml_get_text(crypto_prop); 740 DavKey *key = dav_context_get_key(res->session->context, crypto_key); 741 if(crypto_prop_content && key) { 742 CxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content); 743 resource_set_crypto_properties(res, cprops); 744 } 745 } 746 747 748 set_davprops(res); 749 if(res != resource) { 750 resource_add_child(resource, res); 751 } 752 753 return 0; 754 } 755 756 void set_davprops(DavResource *res) { 757 char *cl = dav_get_string_property_ns(res, "DAV:", "getcontentlength"); 758 char *ct = dav_get_string_property_ns(res, "DAV:", "getcontenttype"); 759 char *cd = dav_get_string_property_ns(res, "DAV:", "creationdate"); 760 char *lm = dav_get_string_property_ns(res, "DAV:", "getlastmodified"); 761 762 res->contenttype = ct; 763 if(cl) { 764 char *end = NULL; 765 res->contentlength = strtoull(cl, &end, 0); 766 } 767 res->creationdate = util_parse_creationdate(cd); 768 res->lastmodified = util_parse_lastmodified(lm); 769 } 770 771 int parse_resource_type(xmlNode *node) { 772 int collection = FALSE; 773 xmlNode *c = node->children; 774 while(c) { 775 if(c->type == XML_ELEMENT_NODE) { 776 if(xstreq(c->ns->href, "DAV:") && xstreq(c->name, "collection")) { 777 collection = TRUE; 778 break; 779 } 780 } 781 c = c->next; 782 } 783 return collection; 784 } 785 786 787 /* ----------------------------- PROPPATCH ----------------------------- */ 788 789 CURLcode do_proppatch_request( 790 DavSession *sn, 791 char *lock, 792 CxBuffer *request, 793 CxBuffer *response) 794 { 795 CURL *handle = sn->handle; 796 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPPATCH"); 797 798 struct curl_slist *headers = NULL; 799 headers = curl_slist_append(headers, "Content-Type: text/xml"); 800 if(lock) { 801 char *url = NULL; 802 curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); 803 char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; 804 headers = curl_slist_append(headers, ltheader); 805 free(ltheader); 806 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 807 } 808 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 809 810 curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 811 curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead); 812 curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); 813 curl_easy_setopt(handle, CURLOPT_READDATA, request); 814 curl_easy_setopt(handle, CURLOPT_SEEKDATA, request); 815 curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); 816 817 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); 818 curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); 819 820 cxBufferSeek(request, 0, SEEK_SET); 821 CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL); 822 curl_slist_free_all(headers); 823 824 //printf("proppatch: \n%.*s\n", request->size, request->space); 825 826 return ret; 827 } 828 829 CxBuffer* create_proppatch_request(DavResourceData *data) { 830 CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 831 cxstring s; 832 833 CxMap *namespaces = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8); 834 cxDefineDestructor(namespaces, free); 835 836 { 837 char prefix[8]; 838 int pfxnum = 0; 839 if (data->set && cxListSize(data->set) > 0) { 840 CxIterator i = cxListIterator(data->set); 841 cx_foreach(DavProperty*, p, i) { 842 if (strcmp(p->ns->name, "DAV:")) { 843 snprintf(prefix, 8, "x%d", pfxnum++); 844 cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix)); 845 } 846 } 847 } 848 if (data->remove && cxListSize(data->remove) > 0) { 849 CxIterator i = cxListIterator(data->remove); 850 cx_foreach(DavProperty*, p, i) { 851 if (strcmp(p->ns->name, "DAV:")) { 852 snprintf(prefix, 8, "x%d", pfxnum++); 853 cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix)); 854 } 855 } 856 } 857 } 858 859 s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); 860 cxBufferWrite(s.ptr, 1, s.length, buf); 861 862 // write root element and namespaces 863 s = CX_STR("<D:propertyupdate xmlns:D=\"DAV:\""); 864 cxBufferWrite(s.ptr, 1, s.length, buf); 865 CxIterator mapi = cxMapIterator(namespaces); 866 cx_foreach(CxMapEntry*, entry, mapi) { 867 s = CX_STR(" xmlns:"); 868 cxBufferWrite(s.ptr, 1, s.length, buf); 869 s = cx_str(entry->value); 870 cxBufferWrite(s.ptr, 1, s.length, buf); 871 s = CX_STR("=\""); 872 cxBufferWrite(s.ptr, 1, s.length, buf); 873 s = cx_strn(entry->key->data, entry->key->len); 874 cxBufferWrite(s.ptr, 1, s.length, buf); 875 s = CX_STR("\""); 876 cxBufferWrite(s.ptr, 1, s.length, buf); 877 } 878 s = CX_STR(">\n"); 879 cxBufferWrite(s.ptr, 1, s.length, buf); 880 881 if(data->set) { 882 s = CX_STR("<D:set>\n<D:prop>\n"); 883 cxBufferWrite(s.ptr, 1, s.length, buf); 884 CxIterator i = cxListIterator(data->set); 885 cx_foreach(DavProperty*, property, i) { 886 char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name)); 887 if(!prefix) { 888 prefix = "D"; 889 } 890 891 // begin tag 892 s = CX_STR("<"); 893 cxBufferWrite(s.ptr, 1, s.length, buf); 894 s = cx_str(prefix); 895 cxBufferWrite(s.ptr, 1, s.length, buf); 896 s = CX_STR(":"); 897 cxBufferWrite(s.ptr, 1, s.length, buf); 898 s = cx_str(property->name); 899 cxBufferWrite(s.ptr, 1, s.length, buf); 900 s = CX_STR(">"); 901 cxBufferWrite(s.ptr, 1, s.length, buf); 902 903 // content 904 DavXmlNode *content = property->value; 905 if(content->type == DAV_XML_TEXT && !content->next) { 906 cxBufferWrite(content->content, 1, content->contentlength, buf); 907 } else { 908 dav_print_node(buf, (cx_write_func)cxBufferWrite, namespaces, content); 909 } 910 911 // end tag 912 s = CX_STR("</"); 913 cxBufferWrite(s.ptr, 1, s.length, buf); 914 s = cx_str(prefix); 915 cxBufferWrite(s.ptr, 1, s.length, buf); 916 s = CX_STR(":"); 917 cxBufferWrite(s.ptr, 1, s.length, buf); 918 s = cx_str(property->name); 919 cxBufferWrite(s.ptr, 1, s.length, buf); 920 s = CX_STR(">\n"); 921 cxBufferWrite(s.ptr, 1, s.length, buf); 922 } 923 s = CX_STR("</D:prop>\n</D:set>\n"); 924 cxBufferWrite(s.ptr, 1, s.length, buf); 925 } 926 if(data->remove) { 927 s = CX_STR("<D:remove>\n<D:prop>\n"); 928 cxBufferWrite(s.ptr, 1, s.length, buf); 929 CxIterator i = cxListIterator(data->remove); 930 cx_foreach(DavProperty*, property, i) { 931 char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name)); 932 933 s = CX_STR("<"); 934 cxBufferWrite(s.ptr, 1, s.length, buf); 935 s = cx_str(prefix); 936 cxBufferWrite(s.ptr, 1, s.length, buf); 937 s = CX_STR(":"); 938 cxBufferWrite(s.ptr, 1, s.length, buf); 939 s = cx_str(property->name); 940 cxBufferWrite(s.ptr, 1, s.length, buf); 941 s = CX_STR(" />\n"); 942 cxBufferWrite(s.ptr, 1, s.length, buf); 943 } 944 s = CX_STR("</D:prop>\n</D:remove>\n"); 945 cxBufferWrite(s.ptr, 1, s.length, buf); 946 } 947 948 s = CX_STR("</D:propertyupdate>\n"); 949 cxBufferWrite(s.ptr, 1, s.length, buf); 950 951 // cleanup namespace map 952 cxMapDestroy(namespaces); 953 954 return buf; 955 } 956 957 CxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash) { 958 CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 959 cxstring s; 960 961 s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); 962 cxBufferWrite(s.ptr, 1, s.length, buf); 963 964 s = CX_STR("<D:propertyupdate xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n"); 965 cxBufferWrite(s.ptr, 1, s.length, buf); 966 967 s = CX_STR("<D:set>\n<D:prop>\n"); 968 cxBufferWrite(s.ptr, 1, s.length, buf); 969 970 if(DAV_ENCRYPT_NAME(sn)) { 971 s = CX_STR("<idav:crypto-name>"); 972 cxBufferWrite(s.ptr, 1, s.length, buf); 973 char *crname = aes_encrypt(name, strlen(name), key); 974 cxBufferPutString(buf, crname); 975 free(crname); 976 s = CX_STR("</idav:crypto-name>\n"); 977 cxBufferWrite(s.ptr, 1, s.length, buf); 978 } 979 980 s = CX_STR("<idav:crypto-key>"); 981 cxBufferWrite(s.ptr, 1, s.length, buf); 982 cxBufferPutString(buf, key->name); 983 s = CX_STR("</idav:crypto-key>\n"); 984 cxBufferWrite(s.ptr, 1, s.length, buf); 985 986 if(hash) { 987 s = CX_STR("<idav:crypto-hash>"); 988 cxBufferWrite(s.ptr, 1, s.length, buf); 989 cxBufferPutString(buf, hash); 990 s = CX_STR("</idav:crypto-hash>\n"); 991 cxBufferWrite(s.ptr, 1, s.length, buf); 992 } 993 994 s = CX_STR("</D:prop>\n</D:set>\n</D:propertyupdate>\n"); 995 cxBufferWrite(s.ptr, 1, s.length, buf); 996 997 return buf; 998 } 999 1000 /* ----------------------------- PUT ----------------------------- */ 1001 1002 static size_t dummy_write(void *buf, size_t s, size_t n, void *data) { 1003 //fwrite(buf, s, n, stdout); 1004 return s*n; 1005 } 1006 1007 CURLcode do_put_request(DavSession *sn, char *lock, DavBool create, void *data, dav_read_func read_func, dav_seek_func seek_func, size_t length) { 1008 CURL *handle = sn->handle; 1009 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL); 1010 curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L); 1011 1012 // clear headers 1013 struct curl_slist *headers = NULL; 1014 if(lock) { 1015 char *url = NULL; 1016 curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); 1017 char *ltheader = NULL; 1018 if(create) { 1019 url = util_parent_path(url); 1020 ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; 1021 free(url); 1022 } else { 1023 ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; 1024 } 1025 headers = curl_slist_append(headers, ltheader); 1026 free(ltheader); 1027 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1028 } 1029 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1030 1031 CxBuffer *buf = NULL; 1032 if(!read_func) { 1033 buf = cxBufferCreate(data, length, cxDefaultAllocator, 0); 1034 buf->size = length; 1035 data = buf; 1036 read_func = (dav_read_func)cxBufferRead; 1037 curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length); 1038 } else if(length == 0) { 1039 headers = curl_slist_append(headers, "Transfer-Encoding: chunked"); 1040 curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)-1); 1041 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1042 } else { 1043 curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length); 1044 } 1045 1046 curl_easy_setopt(handle, CURLOPT_READFUNCTION, read_func); 1047 curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, seek_func); 1048 curl_easy_setopt(handle, CURLOPT_SEEKDATA, data); 1049 curl_easy_setopt(handle, CURLOPT_READDATA, data); 1050 1051 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); 1052 curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); 1053 1054 CURLcode ret = dav_session_curl_perform(sn, NULL); 1055 curl_slist_free_all(headers); 1056 if(buf) { 1057 cxBufferFree(buf); 1058 } 1059 1060 return ret; 1061 } 1062 1063 CURLcode do_delete_request(DavSession *sn, char *lock, CxBuffer *response) { 1064 CURL *handle = sn->handle; 1065 struct curl_slist *headers = NULL; 1066 if(lock) { 1067 char *url = NULL; 1068 curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); 1069 char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; 1070 headers = curl_slist_append(headers, ltheader); 1071 free(ltheader); 1072 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1073 } else { 1074 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL); 1075 } 1076 1077 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "DELETE"); 1078 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); 1079 1080 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); 1081 curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); 1082 1083 CURLcode ret = dav_session_curl_perform(sn, NULL); 1084 curl_slist_free_all(headers); 1085 return ret; 1086 } 1087 1088 CURLcode do_mkcol_request(DavSession *sn, char *lock) { 1089 CURL *handle = sn->handle; 1090 struct curl_slist *headers = NULL; 1091 if(lock) { 1092 char *url = NULL; 1093 curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); 1094 url = util_parent_path(url); 1095 char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; 1096 free(url); 1097 headers = curl_slist_append(headers, ltheader); 1098 free(ltheader); 1099 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1100 } else { 1101 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL); 1102 } 1103 1104 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MKCOL"); 1105 curl_easy_setopt(handle, CURLOPT_PUT, 0L); 1106 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); 1107 1108 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); 1109 curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); 1110 1111 CURLcode ret = dav_session_curl_perform(sn, NULL); 1112 curl_slist_free_all(headers); 1113 return ret; 1114 } 1115 1116 1117 CURLcode do_head_request(DavSession *sn) { 1118 CURL *handle = sn->handle; 1119 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "HEAD"); 1120 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); 1121 curl_easy_setopt(handle, CURLOPT_NOBODY, 1L); 1122 1123 // clear headers 1124 struct curl_slist *headers = NULL; 1125 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1126 1127 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); 1128 curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); 1129 1130 CURLcode ret = dav_session_curl_perform(sn, NULL); 1131 curl_easy_setopt(handle, CURLOPT_NOBODY, 0L); 1132 return ret; 1133 } 1134 1135 1136 CURLcode do_copy_move_request(DavSession *sn, char *dest, char *lock, DavBool copy, DavBool override) { 1137 CURL *handle = sn->handle; 1138 if(copy) { 1139 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "COPY"); 1140 } else { 1141 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MOVE"); 1142 } 1143 curl_easy_setopt(handle, CURLOPT_PUT, 0L); 1144 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); 1145 1146 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); 1147 curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); 1148 1149 struct curl_slist *headers = NULL; 1150 if(lock) { 1151 char *url = NULL; 1152 curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); 1153 char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; 1154 headers = curl_slist_append(headers, ltheader); 1155 free(ltheader); 1156 } 1157 //cxstring deststr = ucx_sprintf("Destination: %s", dest); 1158 cxmutstr deststr = cx_strcat(2, CX_STR("Destination: "), cx_str(dest)); 1159 headers = curl_slist_append(headers, deststr.ptr); 1160 if(override) { 1161 headers = curl_slist_append(headers, "Overwrite: T"); 1162 } else { 1163 headers = curl_slist_append(headers, "Overwrite: F"); 1164 } 1165 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1166 1167 CURLcode ret = dav_session_curl_perform(sn, NULL); 1168 free(deststr.ptr); 1169 curl_slist_free_all(headers); 1170 headers = NULL; 1171 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1172 return ret; 1173 } 1174 1175 1176 CxBuffer* create_lock_request(void) { 1177 CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 1178 cxstring s; 1179 1180 s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); 1181 cxBufferWrite(s.ptr, 1, s.length, buf); 1182 1183 s = CX_STR("<D:lockinfo xmlns:D=\"DAV:\">\n" 1184 "<D:lockscope><D:exclusive/></D:lockscope>\n" 1185 "<D:locktype><D:write/></D:locktype>\n" 1186 "<D:owner><D:href>http://davutils.org/libidav/</D:href></D:owner>\n"); 1187 cxBufferWrite(s.ptr, 1, s.length, buf); 1188 1189 s = CX_STR("</D:lockinfo>\n"); 1190 cxBufferWrite(s.ptr, 1, s.length, buf); 1191 1192 return buf; 1193 } 1194 1195 int parse_lock_response(DavSession *sn, CxBuffer *response, LockDiscovery *lock) { 1196 lock->locktoken = NULL; 1197 lock->timeout = NULL; 1198 1199 xmlDoc *doc = xmlReadMemory(response->space, response->size, NULL, NULL, 0); 1200 if(!doc) { 1201 sn->error = DAV_ERROR; 1202 return -1; 1203 } 1204 1205 char *timeout = NULL; 1206 char *locktoken = NULL; 1207 1208 int ret = -1; 1209 xmlNode *xml_root = xmlDocGetRootElement(doc); 1210 DavBool lockdiscovery = 0; 1211 if(xml_root) { 1212 xmlNode *node = xml_root->children; 1213 while(node) { 1214 if(node->type == XML_ELEMENT_NODE) { 1215 if(xstreq(node->name, "lockdiscovery")) { 1216 node = node->children; 1217 lockdiscovery = 1; 1218 continue; 1219 } 1220 1221 if(xstreq(node->name, "activelock")) { 1222 node = node->children; 1223 continue; 1224 } 1225 1226 if(lockdiscovery) { 1227 if(xstreq(node->name, "timeout")) { 1228 timeout = util_xml_get_text(node); 1229 } else if(xstreq(node->name, "locktoken")) { 1230 xmlNode *n = node->children; 1231 while(n) { 1232 if(xstreq(n->name, "href")) { 1233 locktoken = util_xml_get_text(n); 1234 break; 1235 } 1236 n = n->next; 1237 } 1238 } 1239 } 1240 } 1241 node = node->next; 1242 } 1243 } 1244 1245 if(timeout && locktoken) { 1246 lock->timeout = strdup(timeout); 1247 lock->locktoken = strdup(locktoken); 1248 ret = 0; 1249 } 1250 1251 xmlFreeDoc(doc); 1252 return ret; 1253 } 1254 1255 CURLcode do_lock_request(DavSession *sn, CxBuffer *request, CxBuffer *response, time_t timeout) { 1256 CURL *handle = sn->handle; 1257 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "LOCK"); 1258 curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L); 1259 request->pos = 0; 1260 1261 // clear headers 1262 struct curl_slist *headers = NULL; 1263 1264 if(timeout != 0) { 1265 cxmutstr thdr; 1266 if(timeout < 0) { 1267 thdr = cx_asprintf("%s", "Timeout: Infinite"); 1268 } else { 1269 thdr = cx_asprintf("Timeout: Second-%u", (unsigned int)timeout); 1270 } 1271 headers = curl_slist_append(headers, thdr.ptr); 1272 free(thdr.ptr); 1273 } 1274 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1275 1276 curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 1277 curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead); 1278 curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); 1279 curl_easy_setopt(handle, CURLOPT_READDATA, request); 1280 curl_easy_setopt(handle, CURLOPT_SEEKDATA, request); 1281 curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); 1282 1283 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); 1284 curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); 1285 1286 CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL); 1287 1288 if(headers) { 1289 curl_slist_free_all(headers); 1290 } 1291 1292 return ret; 1293 } 1294 1295 CURLcode do_unlock_request(DavSession *sn, char *locktoken) { 1296 CURL *handle = sn->handle; 1297 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "UNLOCK"); 1298 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); 1299 1300 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); 1301 curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); 1302 1303 // set lock-token header 1304 cxmutstr ltheader = cx_asprintf("Lock-Token: <%s>", locktoken); 1305 struct curl_slist *headers = curl_slist_append(NULL, ltheader.ptr); 1306 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1307 1308 CURLcode ret = dav_session_curl_perform(sn, NULL); 1309 curl_slist_free_all(headers); 1310 free(ltheader.ptr); 1311 1312 return ret; 1313 } 1314 1315 CURLcode do_simple_request(DavSession *sn, char *method, char *locktoken) { 1316 CURL *handle = sn->handle; 1317 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, method); 1318 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); 1319 1320 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); 1321 curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); 1322 1323 // set lock-token header 1324 cxmutstr ltheader; 1325 struct curl_slist *headers = NULL; 1326 if(locktoken) { 1327 ltheader = cx_asprintf("Lock-Token: <%s>", locktoken); 1328 headers = curl_slist_append(NULL, ltheader.ptr); 1329 } 1330 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1331 1332 CURLcode ret = dav_session_curl_perform(sn, NULL); 1333 if(locktoken) { 1334 curl_slist_free_all(headers); 1335 free(ltheader.ptr); 1336 } 1337 1338 return ret; 1339 } 1340 1341 1342 CURLcode do_report_request(DavSession *sn, CxBuffer *request, CxBuffer *response) { 1343 CURL *handle = sn->handle; 1344 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "REPORT"); 1345 1346 curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 1347 curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead); 1348 curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); 1349 curl_easy_setopt(handle, CURLOPT_READDATA, request); 1350 curl_easy_setopt(handle, CURLOPT_SEEKDATA, request); 1351 curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); 1352 1353 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); 1354 curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); 1355 1356 struct curl_slist *headers = NULL; 1357 headers = curl_slist_append(headers, "Content-Type: text/xml"); 1358 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1359 1360 request->pos = 0; 1361 response->size = response->pos = 0; 1362 CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL); 1363 1364 curl_slist_free_all(headers); 1365 1366 return ret; 1367 } 1368