|
1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2013 Olaf Wintermann. All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * |
|
12 * 2. Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
26 * POSSIBILITY OF SUCH DAMAGE. |
|
27 */ |
|
28 |
|
29 #include <stdio.h> |
|
30 #include <stdlib.h> |
|
31 #include <string.h> |
|
32 #include <libxml/tree.h> |
|
33 |
|
34 #include "utils.h" |
|
35 #include "methods.h" |
|
36 #include "davql.h" |
|
37 #include "ucx/buffer.h" |
|
38 #include "ucx/utils.h" |
|
39 |
|
40 #include "resource.h" |
|
41 |
|
42 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) |
|
43 |
|
44 DavResource* dav_resource_new(DavSession *sn, char *path) { |
|
45 char *url = util_concat_path(sn->base_url, path); |
|
46 char *href = util_url_path(url); |
|
47 DavResource *res = dav_resource_new_href(sn, href); |
|
48 free(url); |
|
49 return res; |
|
50 } |
|
51 |
|
52 |
|
53 DavResource* dav_resource_new_href(DavSession *sn, char *href) { |
|
54 UcxMempool *mp = sn->mp; |
|
55 UcxAllocator *a = sn->allocator; |
|
56 |
|
57 DavResource *res = ucx_mempool_calloc(mp, 1, sizeof(DavResource)); |
|
58 res->session = sn; |
|
59 |
|
60 // set name, path and href |
|
61 resource_set_info(res, href); |
|
62 |
|
63 // initialize resource data |
|
64 res->data = resource_data_new(sn); |
|
65 |
|
66 return res; |
|
67 } |
|
68 |
|
69 void resource_set_info(DavResource *res, char *href_str) { |
|
70 char *url_str = NULL; |
|
71 curl_easy_getinfo(res->session->handle, CURLINFO_EFFECTIVE_URL, &url_str); |
|
72 sstr_t name = sstr(util_resource_name(href_str)); |
|
73 sstr_t href = sstr(href_str); |
|
74 |
|
75 sstr_t base_href = sstr(util_url_path(res->session->base_url)); |
|
76 sstr_t path = sstrsubs(href, base_href.length - 1); |
|
77 |
|
78 UcxAllocator *a = res->session->allocator; |
|
79 res->name = sstrdup_a(a, name).ptr; |
|
80 res->href = sstrdup_a(a, href).ptr; |
|
81 res->path = sstrdup_a(a, path).ptr; |
|
82 } |
|
83 |
|
84 DavResourceData* resource_data_new(DavSession *sn) { |
|
85 DavResourceData *data = ucx_mempool_malloc( |
|
86 sn->mp, |
|
87 sizeof(DavResourceData)); |
|
88 if(!data) { |
|
89 return NULL; |
|
90 } |
|
91 data->properties = ucx_map_new_a(sn->allocator, 32); |
|
92 data->set = NULL; |
|
93 data->remove = NULL; |
|
94 data->content = NULL; |
|
95 data->read = NULL; |
|
96 data->length = 0; |
|
97 return data; |
|
98 } |
|
99 |
|
100 void resource_add_property(DavResource *res, char *ns, char *name, char *val) { |
|
101 if(!val) { |
|
102 return; |
|
103 } |
|
104 UcxAllocator *a = res->session->allocator; |
|
105 |
|
106 UcxKey key = dav_property_key(ns, name); |
|
107 sstr_t v = sstrdup_a(a, sstr(val)); |
|
108 ucx_map_put(((DavResourceData*)res->data)->properties, key, v.ptr); |
|
109 free(key.data); |
|
110 } |
|
111 |
|
112 char* resource_get_property(DavResource *res, char *ns, char *name) { |
|
113 UcxKey key = dav_property_key(ns, name); |
|
114 DavResourceData *data = (DavResourceData*)res->data; |
|
115 char *property = ucx_map_get(data->properties, key); |
|
116 free(key.data); |
|
117 return property; |
|
118 } |
|
119 |
|
120 UcxKey dav_property_key(char *ns, char *name) { |
|
121 sstr_t ns_str = sstr(ns); |
|
122 sstr_t name_str = sstr(name); |
|
123 |
|
124 sstr_t key; |
|
125 key.length = ns_str.length + name_str.length + 1; |
|
126 key.ptr = malloc(key.length + 1); |
|
127 key = sstrncat(key, 3, ns_str, S(" "), name_str); |
|
128 |
|
129 return ucx_key(key.ptr, key.length); |
|
130 } |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 void resource_add_child(DavResource *parent, DavResource *child) { |
|
136 child->next = NULL; |
|
137 if(parent->children) { |
|
138 DavResource *last = parent->children; |
|
139 while(last->next) { |
|
140 last = last->next; |
|
141 } |
|
142 last->next = child; |
|
143 child->prev = last; |
|
144 } else { |
|
145 child->prev = NULL; |
|
146 parent->children = child; |
|
147 } |
|
148 child->parent = parent; |
|
149 } |
|
150 |
|
151 char* dav_get_property(DavResource *res, char *name) { |
|
152 char *pns; |
|
153 char *pname; |
|
154 dav_get_property_namespace(res->session->context, name, &pns, &pname); |
|
155 return dav_get_property_ns(res, pns, pname); |
|
156 } |
|
157 |
|
158 char* dav_get_property_ns(DavResource *res, char *ns, char *name) { |
|
159 char *property = resource_get_property(res, ns, name); |
|
160 DavResourceData *data = res->data; |
|
161 // resource_get_property only returns persistent properties |
|
162 // check the remove and set list |
|
163 if(property) { |
|
164 // if the property is in the remove list, we return NULL |
|
165 UCX_FOREACH(elm, data->remove) { |
|
166 DavProperty *p = elm->data; |
|
167 if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) { |
|
168 return NULL; |
|
169 } |
|
170 } |
|
171 } |
|
172 // the set list contains property updates |
|
173 // we return an updated property if possible |
|
174 UCX_FOREACH(elm, data->set) { |
|
175 DavProperty *p = elm->data; |
|
176 if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) { |
|
177 return p->value; |
|
178 } |
|
179 } |
|
180 // no property update |
|
181 return property; |
|
182 } |
|
183 |
|
184 void dav_set_property(DavResource *res, char *name, char *value) { |
|
185 char *pns; |
|
186 char *pname; |
|
187 dav_get_property_namespace(res->session->context, name, &pns, &pname); |
|
188 dav_set_property_ns(res, pns, pname, value); |
|
189 } |
|
190 |
|
191 void dav_set_property_ns(DavResource *res, char *ns, char *name, char *value) { |
|
192 UcxAllocator *a = res->session->allocator; |
|
193 DavResourceData *data = res->data; |
|
194 |
|
195 DavProperty *property = a->malloc(a->pool, sizeof(DavProperty)); |
|
196 property->name = sstrdup_a(a, sstr(name)).ptr; |
|
197 property->value = sstrdup_a(a, sstr(value)).ptr; |
|
198 DavNamespace *namespace = a->malloc(a->pool, sizeof(DavNamespace)); |
|
199 namespace->prefix = NULL; |
|
200 namespace->name = sstrdup_a(a, sstr(ns)).ptr; |
|
201 property->ns = namespace; |
|
202 |
|
203 data->set = ucx_list_append_a(a, data->set, property); |
|
204 } |
|
205 |
|
206 void dav_remove_property(DavResource *res, char *name) { |
|
207 char *pns; |
|
208 char *pname; |
|
209 dav_get_property_namespace(res->session->context, name, &pns, &pname); |
|
210 dav_remove_property_ns(res, pns, pname); |
|
211 } |
|
212 |
|
213 void dav_remove_property_ns(DavResource *res, char *ns, char *name) { |
|
214 UcxAllocator *a = res->session->allocator; |
|
215 DavResourceData *data = res->data; |
|
216 |
|
217 DavProperty *property = a->malloc(a->pool, sizeof(DavProperty)); |
|
218 property->name = sstrdup_a(a, sstr(name)).ptr; |
|
219 property->value = NULL; |
|
220 DavNamespace *namespace = a->malloc(a->pool, sizeof(DavNamespace)); |
|
221 namespace->prefix = NULL; |
|
222 namespace->name = sstrdup_a(a, sstr(ns)).ptr; |
|
223 property->ns = namespace; |
|
224 |
|
225 data->remove = ucx_list_append_a(a, data->remove, property); |
|
226 } |
|
227 |
|
228 |
|
229 void dav_set_content(DavResource *res, void *stream, dav_read_func read_func) { |
|
230 DavResourceData *data = res->data; |
|
231 data->content = stream; |
|
232 data->read = read_func; |
|
233 data->length = 0; |
|
234 } |
|
235 |
|
236 void dav_set_content_data(DavResource *res, char *content, size_t length) { |
|
237 DavSession *sn = res->session; |
|
238 DavResourceData *data = res->data; |
|
239 data->content = content; |
|
240 data->read = NULL; |
|
241 data->length = length; |
|
242 } |
|
243 |
|
244 |
|
245 int dav_load(DavResource *res) { |
|
246 DavSession *sn = res->session; |
|
247 DavResourceData *data = res->data; |
|
248 // clean map |
|
249 UcxKey key; |
|
250 void *value; |
|
251 UcxMapIterator i = ucx_map_iterator(data->properties); |
|
252 UCX_MAP_FOREACH(key, value, i) { |
|
253 ucx_map_remove(data->properties, key); |
|
254 } |
|
255 |
|
256 char *url = util_concat_path(sn->base_url, res->path); |
|
257 |
|
258 CURL *handle = sn->handle; |
|
259 curl_easy_setopt(handle, CURLOPT_URL, url); |
|
260 free(url); |
|
261 |
|
262 UcxBuffer *rqbuf = create_allprop_propfind_request(); |
|
263 UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); |
|
264 |
|
265 //fwrite(rpbuf->space, 1, rpbuf->size, stdout); |
|
266 //printf("\n"); |
|
267 |
|
268 CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf); |
|
269 int status = 0; |
|
270 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); |
|
271 if(ret == CURLE_OK) { |
|
272 //printf("response\n%s\n", rpbuf->space); |
|
273 // TODO: use parse_propfind_response() |
|
274 xmlDoc *doc = xmlReadMemory(rpbuf->space, rpbuf->size, url, NULL, 0); |
|
275 if(!doc) { |
|
276 return 1; |
|
277 } |
|
278 |
|
279 xmlNode *xml_root = xmlDocGetRootElement(doc); |
|
280 xmlNode *node = xml_root->children; |
|
281 while(node) { |
|
282 if(node->type == XML_ELEMENT_NODE) { |
|
283 if(xstreq(node->name, "response")) { |
|
284 parse_response_tag(res, node, NULL, 0); |
|
285 } |
|
286 } |
|
287 node = node->next; |
|
288 } |
|
289 |
|
290 set_davprops(res); |
|
291 } else { |
|
292 session_set_error(sn, ret, status); |
|
293 } |
|
294 return 0; |
|
295 } |
|
296 |
|
297 int dav_store(DavResource *res) { |
|
298 DavSession *sn = res->session; |
|
299 DavResourceData *data = res->data; |
|
300 |
|
301 char *url = util_concat_path(sn->base_url, res->path); |
|
302 CURL *handle = res->session->handle; |
|
303 curl_easy_setopt(handle, CURLOPT_URL, url); |
|
304 free(url); |
|
305 |
|
306 // store content |
|
307 if(data->content) { |
|
308 CURLcode ret = do_put_request(handle, data->content, data->read, data->length); |
|
309 int status = 0; |
|
310 curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status); |
|
311 if(ret == CURLE_OK && (status >= 200 && status < 300)) { |
|
312 res->session->error = 0; |
|
313 // cleanup node data |
|
314 if(!data->read) { |
|
315 ucx_mempool_free(sn->mp, data->content); |
|
316 } |
|
317 data->content = NULL; |
|
318 data->read = NULL; |
|
319 data->length = 0; |
|
320 } else { |
|
321 session_set_error(sn, ret, status); |
|
322 return 1; |
|
323 } |
|
324 } |
|
325 |
|
326 // store properties |
|
327 if(data->set || data->remove) { |
|
328 UcxBuffer *request = create_proppatch_request(data); |
|
329 UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); |
|
330 |
|
331 CURLcode ret = do_proppatch_request(handle, request, response); |
|
332 int status = 0; |
|
333 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); |
|
334 if(ret == CURLE_OK && status == 207) { |
|
335 //printf("%s\n", response->space); |
|
336 // TODO: parse response |
|
337 // TODO: cleanup node data correctly |
|
338 data->set = NULL; |
|
339 data->remove = NULL; |
|
340 } else { |
|
341 session_set_error(sn, ret, status); |
|
342 return 1; |
|
343 } |
|
344 } |
|
345 sn->error = DAV_OK; |
|
346 return 0; |
|
347 } |
|
348 |
|
349 int dav_get_content(DavResource *res, void *stream, dav_write_func write_fnc) { |
|
350 char *url = util_concat_path(res->session->base_url, res->path); |
|
351 CURL *handle = res->session->handle; |
|
352 curl_easy_setopt(handle, CURLOPT_URL, url); |
|
353 free(url); |
|
354 |
|
355 curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); |
|
356 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL); |
|
357 curl_easy_setopt(handle, CURLOPT_PUT, 0L); |
|
358 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); |
|
359 |
|
360 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_fnc); |
|
361 curl_easy_setopt(handle, CURLOPT_WRITEDATA, stream); |
|
362 |
|
363 CURLcode ret = curl_easy_perform(handle); |
|
364 int status = 0; |
|
365 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); |
|
366 if(ret == CURLE_OK && (status >= 200 && status < 300)) { |
|
367 res->session->error = DAV_OK; |
|
368 return 0; |
|
369 } else { |
|
370 session_set_error(res->session, ret, status); |
|
371 return 1; |
|
372 } |
|
373 } |
|
374 |
|
375 DavResource* dav_create_child(DavResource *parent, char *name) { |
|
376 // TODO |
|
377 return NULL; |
|
378 } |
|
379 |
|
380 int dav_delete(DavResource *res) { |
|
381 char *url = util_concat_path(res->session->base_url, res->path); |
|
382 CURL *handle = res->session->handle; |
|
383 curl_easy_setopt(handle, CURLOPT_URL, url); |
|
384 free(url); |
|
385 |
|
386 UcxBuffer *response = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); |
|
387 CURLcode ret = do_delete_request(handle, response); |
|
388 int status = 0; |
|
389 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); |
|
390 if(ret == CURLE_OK && (status >= 200 && status < 300)) { |
|
391 res->session->error = DAV_OK; |
|
392 |
|
393 // TODO: parse response |
|
394 // TODO: free res |
|
395 |
|
396 return 0; |
|
397 } else { |
|
398 session_set_error(res->session, ret, status); |
|
399 return 1; |
|
400 } |
|
401 } |
|
402 |
|
403 int dav_create(DavResource *res) { |
|
404 char *url = util_concat_path(res->session->base_url, res->path); |
|
405 char *parent = util_parent_path(res->path); |
|
406 |
|
407 DavSession *sn = res->session; |
|
408 DavResource *parent_res = dav_get(sn, parent, NULL); |
|
409 if(!parent_res && sn->error == DAV_NOT_FOUND) { |
|
410 parent_res = dav_resource_new(sn, parent); |
|
411 parent_res->iscollection = 1; |
|
412 int r = dav_create(parent_res); |
|
413 if(r) { |
|
414 free(parent); |
|
415 return r; |
|
416 } |
|
417 } else if(parent_res && !parent_res->iscollection) { |
|
418 sn->error = DAV_FORBIDDEN; |
|
419 return 1; |
|
420 } else if(sn->error != DAV_OK) { |
|
421 return 1; |
|
422 } |
|
423 |
|
424 CURL *handle = res->session->handle; |
|
425 curl_easy_setopt(handle, CURLOPT_URL, url); |
|
426 free(url); |
|
427 free(parent); |
|
428 |
|
429 // create new collection or do an empty put request |
|
430 CURLcode ret; |
|
431 if(res->iscollection) { |
|
432 ret = do_mkcol_request(handle); |
|
433 } else { |
|
434 ret = do_put_request(handle, "", NULL, 0); |
|
435 } |
|
436 int status = 0; |
|
437 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); |
|
438 if(ret == CURLE_OK && (status >= 200 && status < 300)) { |
|
439 res->session->error = DAV_OK; |
|
440 } else { |
|
441 session_set_error(res->session, ret, status); |
|
442 return 1; |
|
443 } |
|
444 |
|
445 // do an minimal propfind request |
|
446 UcxBuffer *rqbuf = create_propfind_request(NULL); |
|
447 UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); |
|
448 |
|
449 //fwrite(rpbuf->space, 1, rpbuf->size, stdout); |
|
450 //printf("\n"); |
|
451 |
|
452 ret = do_propfind_request(handle, rqbuf, rpbuf); |
|
453 status = 0; |
|
454 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); |
|
455 if(ret == CURLE_OK && (status >= 200 && status < 300)) { |
|
456 //printf("response\n%s\n", rpbuf->space); |
|
457 // TODO: use parse_propfind_response() |
|
458 xmlDoc *doc = xmlReadMemory(rpbuf->space, rpbuf->size, url, NULL, 0); |
|
459 if(!doc) { |
|
460 return 1; |
|
461 } |
|
462 |
|
463 xmlNode *xml_root = xmlDocGetRootElement(doc); |
|
464 xmlNode *node = xml_root->children; |
|
465 while(node) { |
|
466 if(node->type == XML_ELEMENT_NODE) { |
|
467 if(xstreq(node->name, "response")) { |
|
468 parse_response_tag(res, node, NULL, 0); |
|
469 } |
|
470 } |
|
471 node = node->next; |
|
472 } |
|
473 |
|
474 set_davprops(res); |
|
475 return 0; |
|
476 } else { |
|
477 session_set_error(sn, ret, status); |
|
478 return 1; |
|
479 } |
|
480 } |
|
481 |
|
482 int dav_exists(DavResource *res) { |
|
483 DavSession *sn = res->session; |
|
484 char *url = util_concat_path(sn->base_url, res->path); |
|
485 CURL *handle = sn->handle; |
|
486 curl_easy_setopt(handle, CURLOPT_URL, url); |
|
487 free(url); |
|
488 |
|
489 CURLcode ret = do_head_request(handle); |
|
490 int status = 0; |
|
491 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); |
|
492 if(ret == CURLE_OK && (status >= 200 && status < 300)) { |
|
493 return 1; |
|
494 } else { |
|
495 session_set_error(sn, ret, status); |
|
496 return 0; |
|
497 } |
|
498 } |