libidav/methods.c

changeset 1
b5bb7b3cd597
equal deleted inserted replaced
0:2483f517c562 1:b5bb7b3cd597
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2018 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
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(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 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_append(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_append(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 }

mercurial