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 respheaders->simple_destructor = 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"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 namespaces->simple_destructor = free; 835 836 char prefix[8]; 837 int pfxnum = 0; 838 if(data->set) { 839 CxIterator i = cxListIterator(data->set); 840 cx_foreach(DavProperty*, p, i) { 841 if(strcmp(p->ns->name, "DAV:")) { 842 snprintf(prefix, 8, "x%d", pfxnum++); 843 cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix)); 844 } 845 } 846 } 847 if(data->remove) { 848 CxIterator i = cxListIterator(data->remove); 849 cx_foreach(DavProperty*, p, i) { 850 if(strcmp(p->ns->name, "DAV:")) { 851 snprintf(prefix, 8, "x%d", pfxnum++); 852 cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix)); 853 } 854 } 855 } 856 857 s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); 858 cxBufferWrite(s.ptr, 1, s.length, buf); 859 860 // write root element and namespaces 861 s = CX_STR("<D:propertyupdate xmlns:D=\"DAV:\""); 862 cxBufferWrite(s.ptr, 1, s.length, buf); 863 CxIterator mapi = cxMapIterator(namespaces); 864 cx_foreach(CxMapEntry*, entry, mapi) { 865 s = CX_STR(" xmlns:"); 866 cxBufferWrite(s.ptr, 1, s.length, buf); 867 s = cx_str(entry->value); 868 cxBufferWrite(s.ptr, 1, s.length, buf); 869 s = CX_STR("=\""); 870 cxBufferWrite(s.ptr, 1, s.length, buf); 871 s = cx_strn(entry->key->data, entry->key->len); 872 cxBufferWrite(s.ptr, 1, s.length, buf); 873 s = CX_STR("\""); 874 cxBufferWrite(s.ptr, 1, s.length, buf); 875 } 876 s = CX_STR(">\n"); 877 cxBufferWrite(s.ptr, 1, s.length, buf); 878 879 if(data->set) { 880 s = CX_STR("<D:set>\n<D:prop>\n"); 881 cxBufferWrite(s.ptr, 1, s.length, buf); 882 CxIterator i = cxListIterator(data->set); 883 cx_foreach(DavProperty*, property, i) { 884 char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name)); 885 if(!prefix) { 886 prefix = "D"; 887 } 888 889 // begin tag 890 s = CX_STR("<"); 891 cxBufferWrite(s.ptr, 1, s.length, buf); 892 s = cx_str(prefix); 893 cxBufferWrite(s.ptr, 1, s.length, buf); 894 s = CX_STR(":"); 895 cxBufferWrite(s.ptr, 1, s.length, buf); 896 s = cx_str(property->name); 897 cxBufferWrite(s.ptr, 1, s.length, buf); 898 s = CX_STR(">"); 899 cxBufferWrite(s.ptr, 1, s.length, buf); 900 901 // content 902 DavXmlNode *content = property->value; 903 if(content->type == DAV_XML_TEXT && !content->next) { 904 cxBufferWrite(content->content, 1, content->contentlength, buf); 905 } else { 906 dav_print_node(buf, (cx_write_func)cxBufferWrite, namespaces, content); 907 } 908 909 // end tag 910 s = CX_STR("</"); 911 cxBufferWrite(s.ptr, 1, s.length, buf); 912 s = cx_str(prefix); 913 cxBufferWrite(s.ptr, 1, s.length, buf); 914 s = CX_STR(":"); 915 cxBufferWrite(s.ptr, 1, s.length, buf); 916 s = cx_str(property->name); 917 cxBufferWrite(s.ptr, 1, s.length, buf); 918 s = CX_STR(">\n"); 919 cxBufferWrite(s.ptr, 1, s.length, buf); 920 } 921 s = CX_STR("</D:prop>\n</D:set>\n"); 922 cxBufferWrite(s.ptr, 1, s.length, buf); 923 } 924 if(data->remove) { 925 s = CX_STR("<D:remove>\n<D:prop>\n"); 926 cxBufferWrite(s.ptr, 1, s.length, buf); 927 CxIterator i = cxListIterator(data->remove); 928 cx_foreach(DavProperty*, property, i) { 929 char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name)); 930 931 s = CX_STR("<"); 932 cxBufferWrite(s.ptr, 1, s.length, buf); 933 s = cx_str(prefix); 934 cxBufferWrite(s.ptr, 1, s.length, buf); 935 s = CX_STR(":"); 936 cxBufferWrite(s.ptr, 1, s.length, buf); 937 s = cx_str(property->name); 938 cxBufferWrite(s.ptr, 1, s.length, buf); 939 s = CX_STR(" />\n"); 940 cxBufferWrite(s.ptr, 1, s.length, buf); 941 } 942 s = CX_STR("</D:prop>\n</D:remove>\n"); 943 cxBufferWrite(s.ptr, 1, s.length, buf); 944 } 945 946 s = CX_STR("</D:propertyupdate>\n"); 947 cxBufferWrite(s.ptr, 1, s.length, buf); 948 949 // cleanup namespace map 950 cxMapDestroy(namespaces); 951 952 return buf; 953 } 954 955 CxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash) { 956 CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 957 cxstring s; 958 959 s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); 960 cxBufferWrite(s.ptr, 1, s.length, buf); 961 962 s = CX_STR("<D:propertyupdate xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n"); 963 cxBufferWrite(s.ptr, 1, s.length, buf); 964 965 s = CX_STR("<D:set>\n<D:prop>\n"); 966 cxBufferWrite(s.ptr, 1, s.length, buf); 967 968 if(DAV_ENCRYPT_NAME(sn)) { 969 s = CX_STR("<idav:crypto-name>"); 970 cxBufferWrite(s.ptr, 1, s.length, buf); 971 char *crname = aes_encrypt(name, strlen(name), key); 972 cxBufferPutString(buf, crname); 973 free(crname); 974 s = CX_STR("</idav:crypto-name>\n"); 975 cxBufferWrite(s.ptr, 1, s.length, buf); 976 } 977 978 s = CX_STR("<idav:crypto-key>"); 979 cxBufferWrite(s.ptr, 1, s.length, buf); 980 cxBufferPutString(buf, key->name); 981 s = CX_STR("</idav:crypto-key>\n"); 982 cxBufferWrite(s.ptr, 1, s.length, buf); 983 984 if(hash) { 985 s = CX_STR("<idav:crypto-hash>"); 986 cxBufferWrite(s.ptr, 1, s.length, buf); 987 cxBufferPutString(buf, hash); 988 s = CX_STR("</idav:crypto-hash>\n"); 989 cxBufferWrite(s.ptr, 1, s.length, buf); 990 } 991 992 s = CX_STR("</D:prop>\n</D:set>\n</D:propertyupdate>\n"); 993 cxBufferWrite(s.ptr, 1, s.length, buf); 994 995 return buf; 996 } 997 998 /* ----------------------------- PUT ----------------------------- */ 999 1000 static size_t dummy_write(void *buf, size_t s, size_t n, void *data) { 1001 //fwrite(buf, s, n, stdout); 1002 return s*n; 1003 } 1004 1005 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) { 1006 CURL *handle = sn->handle; 1007 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL); 1008 curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L); 1009 1010 // clear headers 1011 struct curl_slist *headers = NULL; 1012 if(lock) { 1013 char *url = NULL; 1014 curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); 1015 char *ltheader = NULL; 1016 if(create) { 1017 url = util_parent_path(url); 1018 ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; 1019 free(url); 1020 } else { 1021 ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; 1022 } 1023 headers = curl_slist_append(headers, ltheader); 1024 free(ltheader); 1025 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1026 } 1027 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1028 1029 CxBuffer *buf = NULL; 1030 if(!read_func) { 1031 buf = cxBufferCreate(data, length, cxDefaultAllocator, 0); 1032 buf->size = length; 1033 data = buf; 1034 read_func = (dav_read_func)cxBufferRead; 1035 curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length); 1036 } else if(length == 0) { 1037 headers = curl_slist_appe (headers, "Transfer-Encoding: chunked"); 1038 curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)1); 1039 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1040 } else { 1041 curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length); 1042 } 1043 1044 curl_easy_setopt(handle, CURLOPT_READFUNCTION, read_func); 1045 curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, seek_func); 1046 curl_easy_setopt(handle, CURLOPT_SEEKDATA, data); 1047 curl_easy_setopt(handle, CURLOPT_READDATA, data); 1048 1049 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); 1050 curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); 1051 1052 CURLcode ret = dav_session_curl_perform(sn, NULL); 1053 curl_slist_free_all(headers); 1054 if(buf) { 1055 cxBufferFree(buf); 1056 } 1057 1058 return ret; 1059 } 1060 1061 CURLcode do_delete_request(DavSession *sn, char *lock, CxBuffer *response) { 1062 CURL *handle = sn->handle; 1063 struct curl_slist *headers = NULL; 1064 if(lock) { 1065 char *url = NULL; 1066 curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); 1067 char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; 1068 headers = curl_slist_append(headers, ltheader); 1069 free(ltheader); 1070 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1071 } else { 1072 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL); 1073 } 1074 1075 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "DELETE"); 1076 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); 1077 1078 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); 1079 curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); 1080 1081 CURLcode ret = dav_session_curl_perform(sn, NULL); 1082 curl_slist_free_all(headers); 1083 return ret; 1084 } 1085 1086 CURLcode do_mkcol_request(DavSession *sn, char *lock) { 1087 CURL *handle = sn->handle; 1088 struct curl_slist *headers = NULL; 1089 if(lock) { 1090 char *url = NULL; 1091 curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); 1092 url = util_parent_path(url); 1093 char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; 1094 free(url); 1095 headers = curl_slist_append(headers, ltheader); 1096 free(ltheader); 1097 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1098 } else { 1099 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL); 1100 } 1101 1102 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MKCOL"); 1103 curl_easy_setopt(handle, CURLOPT_PUT, 0L); 1104 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); 1105 1106 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); 1107 curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); 1108 1109 CURLcode ret = dav_session_curl_perform(sn, NULL); 1110 curl_slist_free_all(headers); 1111 return ret; 1112 } 1113 1114 1115 CURLcode do_head_request(DavSession *sn) { 1116 CURL *handle = sn->handle; 1117 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "HEAD"); 1118 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); 1119 curl_easy_setopt(handle, CURLOPT_NOBODY, 1L); 1120 1121 // clear headers 1122 struct curl_slist *headers = NULL; 1123 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1124 1125 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); 1126 curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); 1127 1128 CURLcode ret = dav_session_curl_perform(sn, NULL); 1129 curl_easy_setopt(handle, CURLOPT_NOBODY, 0L); 1130 return ret; 1131 } 1132 1133 1134 CURLcode do_copy_move_request(DavSession *sn, char *dest, char *lock, DavBool copy, DavBool override) { 1135 CURL *handle = sn->handle; 1136 if(copy) { 1137 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "COPY"); 1138 } else { 1139 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MOVE"); 1140 } 1141 curl_easy_setopt(handle, CURLOPT_PUT, 0L); 1142 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); 1143 1144 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); 1145 curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); 1146 1147 struct curl_slist *headers = NULL; 1148 if(lock) { 1149 char *url = NULL; 1150 curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); 1151 char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; 1152 headers = curl_slist_append(headers, ltheader); 1153 free(ltheader); 1154 } 1155 //cxstring deststr = ucx_sprintf("Destination: %s", dest); 1156 cxmutstr deststr = cx_strcat(2, CX_STR("Destination: "), cx_str(dest)); 1157 headers = curl_slist_append(headers, deststr.ptr); 1158 if(override) { 1159 headers = curl_slist_append(headers, "Overwrite: T"); 1160 } else { 1161 headers = curl_slist_app d(headers, "Overwrite: F"); 1162 } 1163 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1164 1165 CURLcode ret = dav_session_curl_perform(sn, NULL); 1166 free(deststr.ptr); 1167 curl_slist_free_all(headers); 1168 headers = NULL; 1169 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1170 return ret; 1171 } 1172 1173 1174 CxBuffer* create_lock_request(void) { 1175 CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 1176 cxstring s; 1177 1178 s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); 1179 cxBufferWrite(s.ptr, 1, s.length, buf); 1180 1181 s = CX_STR("<D:lockinfo xmlns:D=\"DAV:\">\n" 1182 "<D:lockscope><D:exclusive/></D:lockscope>\n" 1183 "<D:locktype><D:write/></D:locktype>\n" 1184 "<D:owner><D:href>http://davutils.org/libidav/</D:href></D:owner>\n"); 1185 cxBufferWrite(s.ptr, 1, s.length, buf); 1186 1187 s = CX_STR("</D:lockinfo>\n"); 1188 cxBufferWrite(s.ptr, 1, s.length, buf); 1189 1190 return buf; 1191 } 1192 1193 int parse_lock_response(DavSession *sn, CxBuffer *response, LockDiscovery *lock) { 1194 lock->locktoken = NULL; 1195 lock->timeout = NULL; 1196 1197 xmlDoc *doc = xmlReadMemory(response->space, response->size, NULL, NULL, 0); 1198 if(!doc) { 1199 sn->error = DAV_ERROR; 1200 return -1; 1201 } 1202 1203 char *timeout = NULL; 1204 char *locktoken = NULL; 1205 1206 int ret = -1; 1207 xmlNode *xml_root = xmlDocGetRootElement(doc); 1208 DavBool lockdiscovery = 0; 1209 if(xml_root) { 1210 xmlNode *node = xml_root->children; 1211 while(node) { 1212 if(node->type == XML_ELEMENT_NODE) { 1213 if(xstreq(node->name, "lockdiscovery")) { 1214 node = node->children; 1215 lockdiscovery = 1; 1216 continue; 1217 } 1218 1219 if(xstreq(node->name, "activelock")) { 1220 node = node->children; 1221 continue; 1222 } 1223 1224 if(lockdiscovery) { 1225 if(xstreq(node->name, "timeout")) { 1226 timeout = util_xml_get_text(node); 1227 } else if(xstreq(node->name, "locktoken")) { 1228 xmlNode *n = node->children; 1229 while(n) { 1230 if(xstreq(n->name, "href")) { 1231 locktoken = util_xml_get_text(n); 1232 break; 1233 } 1234 n = n->next; 1235 } 1236 } 1237 } 1238 } 1239 node = node->next; 1240 } 1241 } 1242 1243 if(timeout && locktoken) { 1244 lock->timeout = strdup(timeout); 1245 lock->locktoken = strdup(locktoken); 1246 ret = 0; 1247 } 1248 1249 xmlFreeDoc(doc); 1250 return ret; 1251 } 1252 1253 CURLcode do_lock_request(DavSession *sn, CxBuffer *request, CxBuffer *response, time_t timeout) { 1254 CURL *handle = sn->handle; 1255 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "LOCK"); 1256 curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L); 1257 request->pos = 0; 1258 1259 // clear headers 1260 struct curl_slist *headers = NULL; 1261 1262 if(timeout != 0) { 1263 cxmutstr thdr; 1264 if(timeout < 0) { 1265 thdr = cx_asprintf("%s", "Timeout: Infinite"); 1266 } else { 1267 thdr = cx_asprintf("Timeout: Second-%u", (unsigned int)timeout); 1268 } 1269 headers = curl_slist_append(headers, thdr.ptr); 1270 free(thdr.ptr); 1271 } 1272 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1273 1274 curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 1275 curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead); 1276 curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); 1277 curl_easy_setopt(handle, CURLOPT_READDATA, request); 1278 curl_easy_setopt(handle, CURLOPT_SEEKDATA, request); 1279 curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); 1280 1281 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); 1282 curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); 1283 1284 CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL); 1285 1286 if(headers) { 1287 curl_slist_free_all(headers); 1288 } 1289 1290 return ret; 1291 } 1292 1293 CURLcode do_unlock_request(DavSession *sn, char *locktoken) { 1294 CURL *handle = sn->handle; 1295 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "UNLOCK"); 1296 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); 1297 1298 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); 1299 curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); 1300 1301 // set lock-token header 1302 cxmutstr ltheader = cx_asprintf("Lock-Token: <%s>", locktoken); 1303 struct curl_slist *headers = curl_slist_append(NULL, ltheader.ptr); 1304 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1305 1306 CURLcode ret = dav_session_curl_perform(sn, NULL); 1307 curl_slist_free_all(headers); 1308 free(ltheader.ptr); 1309 1310 return ret; 1311 } 1312 1313 CURLcode do_simple_request(DavSession *sn, char *method, char *locktoken) { 1314 CURL *handle = sn->handle; 1315 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, method); 1316 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); 1317 1318 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); 1319 curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); 1320 1321 // set lock-token header 1322 cxmutstr ltheader; 1323 struct curl_slist *headers = NULL; 1324 if(locktoken) { 1325 ltheader = cx_asprintf("Lock-Token: <%s>", locktoken); 1326 headers = curl_slist_append(NULL, ltheader.ptr); 1327 } 1328 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1329 1330 CURLcode ret = dav_session_curl_perform(sn, NULL); 1331 if(locktoken) { 1332 curl_slist_free_all(headers); 1333 free(ltheader.ptr); 1334 } 1335 1336 return ret; 1337 } 1338 1339 1340 CURLcode do_report_request(DavSession *sn, CxBuffer *request, CxBuffer *response) { 1341 CURL *handle = sn->handle; 1342 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "REPORT"); 1343 1344 curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 1345 curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead); 1346 curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); 1347 curl_easy_setopt(handle, CURLOPT_READDATA, request); 1348 curl_easy_setopt(handle, CURLOPT_SEEKDATA, request); 1349 curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); 1350 1351 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); 1352 curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); 1353 1354 struct curl_slist *headers = NULL; 1355 headers = curl_slist_append(headers, "Content-Type: text/xml"); 1356 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 1357 1358 request->pos = 0; 1359 response->size = response->pos = 0; 1360 CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL); 1361 1362 curl_slist_free_all(headers); 1363 1364 return ret; 1365 } 1366