|
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 "webdav.h" |
|
36 #include "methods.h" |
|
37 #include "ucx/buffer.h" |
|
38 |
|
39 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) |
|
40 |
|
41 DavContext* dav_context_new() { |
|
42 DavContext *context = malloc(sizeof(DavContext)); |
|
43 if(!context) { |
|
44 return NULL; |
|
45 } |
|
46 context->namespaces = ucx_map_new(16); |
|
47 if(!context->namespaces) { |
|
48 free(context); |
|
49 return NULL; |
|
50 } |
|
51 DavNamespace *davns = malloc(sizeof(DavNamespace)); |
|
52 if(!davns) { |
|
53 ucx_map_free(context->namespaces); |
|
54 free(context); |
|
55 return NULL; |
|
56 } |
|
57 davns->prefix = "D"; |
|
58 davns->name = "DAV:"; |
|
59 if(ucx_map_cstr_put(context->namespaces, "D", davns)) { |
|
60 free(davns); |
|
61 ucx_map_free(context->namespaces); |
|
62 free(context); |
|
63 return NULL; |
|
64 } |
|
65 |
|
66 return context; |
|
67 } |
|
68 |
|
69 int dav_add_namespace(DavContext *context, char *prefix, char *name) { |
|
70 DavNamespace *namespace = malloc(sizeof(DavNamespace)); |
|
71 if(!namespace) { |
|
72 return 1; |
|
73 } |
|
74 namespace->prefix = strdup(prefix); |
|
75 namespace->name = strdup(name); |
|
76 return ucx_map_cstr_put(context->namespaces, prefix, namespace); |
|
77 } |
|
78 |
|
79 DavNamespace* dav_get_namespace(DavContext *context, char *prefix) { |
|
80 return ucx_map_cstr_get(context->namespaces, prefix); |
|
81 } |
|
82 |
|
83 DavNamespace* dav_get_namespace_s(DavContext *context, sstr_t prefix) { |
|
84 return ucx_map_sstr_get(context->namespaces, prefix); |
|
85 } |
|
86 |
|
87 DavSession* dav_session_new(DavContext *context, char *base_url) { |
|
88 if(!base_url) { |
|
89 return NULL; |
|
90 } |
|
91 sstr_t url = sstr(base_url); |
|
92 if(url.length == 0) { |
|
93 return NULL; |
|
94 } |
|
95 DavSession *sn = malloc(sizeof(DavSession)); |
|
96 if(url.ptr[url.length - 1] == '/') { |
|
97 sn->base_url = strdup(base_url); |
|
98 } else { |
|
99 char *url_str = malloc(url.length + 2); |
|
100 memcpy(url_str, base_url, url.length); |
|
101 url_str[url.length] = '/'; |
|
102 url_str[url.length + 1] = '\0'; |
|
103 sn->base_url = url_str; |
|
104 } |
|
105 sn->context = context; |
|
106 sn->handle = curl_easy_init(); |
|
107 curl_easy_setopt(sn->handle, CURLOPT_URL, base_url); |
|
108 |
|
109 sn->mp = ucx_mempool_new(1024); |
|
110 sn->allocator = ucx_mempool_allocator(sn->mp); |
|
111 |
|
112 return sn; |
|
113 } |
|
114 |
|
115 DavSession* dav_session_new_auth(DavContext *context, char *base_url, char *user, char *password) { |
|
116 DavSession *sn = dav_session_new(context, base_url); |
|
117 if(!sn) { |
|
118 return NULL; |
|
119 } |
|
120 dav_session_set_auth(sn, user, password); |
|
121 return sn; |
|
122 } |
|
123 |
|
124 void dav_session_set_auth(DavSession *sn, char *user, char *password) { |
|
125 if(user && password) { |
|
126 size_t ulen = strlen(user); |
|
127 size_t plen = strlen(password); |
|
128 size_t upwdlen = ulen + plen + 2; |
|
129 char *upwdbuf = malloc(upwdlen); |
|
130 snprintf(upwdbuf, upwdlen, "%s:%s\0", user, password); |
|
131 curl_easy_setopt(sn->handle, CURLOPT_USERPWD, upwdbuf); |
|
132 free(upwdbuf); |
|
133 } |
|
134 } |
|
135 |
|
136 DavResource* dav_get(DavSession *sn, char *path, char *properties) { |
|
137 char *url = util_concat_path(sn->base_url, path); |
|
138 |
|
139 CURL *handle = sn->handle; |
|
140 curl_easy_setopt(handle, CURLOPT_URL, url); |
|
141 free(url); |
|
142 |
|
143 UcxList *proplist = NULL; |
|
144 if(properties) { |
|
145 proplist = parse_properties_string(sn->context, sstr(properties)); |
|
146 } |
|
147 UcxBuffer *rqbuf = create_propfind_request(proplist); |
|
148 UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); |
|
149 |
|
150 //fwrite(rqbuf->space, 1, rqbuf->size, stdout); |
|
151 //printf("\n"); |
|
152 |
|
153 DavResource *resource = NULL; |
|
154 CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf); |
|
155 int status = 0; |
|
156 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); |
|
157 if(ret == CURLE_OK && status == 207) { |
|
158 //printf("response\n%s\n", rpbuf->space); |
|
159 resource = parse_propfind_response(sn, rpbuf); |
|
160 } |
|
161 return resource; |
|
162 } |
|
163 |
|
164 UcxList* parse_properties_string(DavContext *context, sstr_t str) { |
|
165 UcxList *proplist = NULL; |
|
166 size_t nprops; |
|
167 sstr_t *props = sstrsplit(str, S(","), &nprops); |
|
168 for(int i=0;i<nprops;i++) { |
|
169 sstr_t s = props[i]; |
|
170 sstr_t nsname = sstrchr(s, ':'); |
|
171 if(nsname.length > 0) { |
|
172 sstr_t nspre = sstrsubsl(s, 0, nsname.ptr - s.ptr); |
|
173 nsname.ptr++; |
|
174 nsname.length--; |
|
175 |
|
176 DavProperty *dp = malloc(sizeof(DavProperty)); |
|
177 sstr_t pre = sstrdup(sstrtrim(nspre)); |
|
178 dp->ns = dav_get_namespace(context, pre.ptr); |
|
179 free(pre.ptr); |
|
180 dp->name = sstrdup(nsname).ptr; |
|
181 if(dp->ns && dp->name) { |
|
182 proplist = ucx_list_append(proplist, dp); |
|
183 } else { |
|
184 free(dp->name); |
|
185 free(dp); |
|
186 } |
|
187 } |
|
188 free(s.ptr); |
|
189 } |
|
190 free(props); |
|
191 return proplist; |
|
192 } |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 DavResource* dav_resource_new(DavSession *sn, char *path) { |
|
198 char *url = util_concat_path(sn->base_url, path); |
|
199 char *href = util_url_path(url); |
|
200 DavResource *res = resource_new_href(sn, href); |
|
201 free(url); |
|
202 return res; |
|
203 } |
|
204 |
|
205 DavResource* resource_new_href(DavSession *sn, char *href) { |
|
206 UcxMempool *mp = sn->mp; |
|
207 UcxAllocator *a = sn->allocator; |
|
208 |
|
209 DavResource *res = ucx_mempool_calloc(mp, 1, sizeof(DavResource)); |
|
210 res->session = sn; |
|
211 |
|
212 // set name, path and href |
|
213 resource_set_info(res, href); |
|
214 |
|
215 // initialize node data |
|
216 res->data = node_data_new(sn); |
|
217 |
|
218 return res; |
|
219 } |
|
220 |
|
221 void resource_add_property(DavResource *res, char *ns, char *name, char *value) { |
|
222 if(!value) { |
|
223 return; |
|
224 } |
|
225 UcxMempool *mp = res->session->mp; |
|
226 UcxAllocator *a = res->session->allocator; |
|
227 |
|
228 UcxKey key = dav_property_key(ns, name); |
|
229 sstr_t v = sstrdup_a(a, sstr(value)); |
|
230 ucx_map_put(res->data->properties, key, v.ptr); |
|
231 free(key.data); |
|
232 } |
|
233 |
|
234 char* resource_get_property(DavResource *res, char *ns, char *name) { |
|
235 UcxKey key = dav_property_key(ns, name); |
|
236 return ucx_map_get(res->data->properties, key); |
|
237 } |
|
238 |
|
239 UcxKey dav_property_key(char *ns, char *name) { |
|
240 sstr_t ns_str = sstr(ns); |
|
241 sstr_t name_str = sstr(name); |
|
242 |
|
243 sstr_t key; |
|
244 key.length = ns_str.length + name_str.length + 1; |
|
245 key.ptr = malloc(key.length + 1); |
|
246 key = sstrncat(key, 3, ns_str, S(" "), name_str); |
|
247 |
|
248 return ucx_key(key.ptr, key.length); |
|
249 } |
|
250 |
|
251 void resource_add_child(DavResource *parent, DavResource *child) { |
|
252 child->next = NULL; |
|
253 if(parent->children) { |
|
254 DavResource *last = parent->children; |
|
255 while(last->next) { |
|
256 last = last->next; |
|
257 } |
|
258 last->next = child; |
|
259 child->prev = last; |
|
260 } else { |
|
261 child->prev = NULL; |
|
262 parent->children = child; |
|
263 } |
|
264 } |
|
265 |
|
266 void resource_set_info(DavResource *res, char *href_str) { |
|
267 char *url_str = NULL; |
|
268 curl_easy_getinfo(res->session->handle, CURLINFO_EFFECTIVE_URL, &url_str); |
|
269 sstr_t name = sstr(util_resource_name(href_str)); |
|
270 sstr_t href = sstr(href_str); |
|
271 |
|
272 sstr_t base_href = sstr(util_url_path(res->session->base_url)); |
|
273 sstr_t path = sstrsubs(href, base_href.length - 1); |
|
274 |
|
275 UcxAllocator *a = res->session->allocator; |
|
276 res->name = sstrdup_a(a, name).ptr; |
|
277 res->href = sstrdup_a(a, href).ptr; |
|
278 res->path = sstrdup_a(a, path).ptr; |
|
279 } |
|
280 |
|
281 DavNodeData* node_data_new(DavSession *sn) { |
|
282 DavNodeData *data = ucx_mempool_malloc(sn->mp, sizeof(DavNodeData)); |
|
283 if(!data) { |
|
284 return NULL; |
|
285 } |
|
286 data->properties = ucx_map_new_a(sn->allocator, 32); |
|
287 data->set = NULL; |
|
288 data->remove = NULL; |
|
289 data->content = NULL; |
|
290 data->read = NULL; |
|
291 data->length = 0; |
|
292 return data; |
|
293 } |
|
294 |
|
295 int dav_load(DavResource *res) { |
|
296 DavSession *sn = res->session; |
|
297 // clean map |
|
298 UcxKey key; |
|
299 void *value; |
|
300 UcxMapIterator i = ucx_map_iterator(res->data->properties); |
|
301 UCX_MAP_FOREACH(key, value, i) { |
|
302 ucx_map_remove(res->data->properties, key); |
|
303 } |
|
304 |
|
305 char *url = util_concat_path(sn->base_url, res->path); |
|
306 |
|
307 CURL *handle = sn->handle; |
|
308 curl_easy_setopt(handle, CURLOPT_URL, url); |
|
309 free(url); |
|
310 |
|
311 UcxBuffer *rqbuf = create_allprop_propfind_request(); |
|
312 UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); |
|
313 |
|
314 //fwrite(rpbuf->space, 1, rpbuf->size, stdout); |
|
315 //printf("\n"); |
|
316 |
|
317 CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf); |
|
318 if(ret == CURLE_OK) { |
|
319 //printf("response\n%s\n", rpbuf->space); |
|
320 |
|
321 xmlDoc *doc = xmlReadMemory(rpbuf->space, rpbuf->size, url, NULL, 0); |
|
322 if(!doc) { |
|
323 return 1; |
|
324 } |
|
325 |
|
326 xmlNode *xml_root = xmlDocGetRootElement(doc); |
|
327 xmlNode *node = xml_root->children; |
|
328 while(node) { |
|
329 if(node->type == XML_ELEMENT_NODE) { |
|
330 if(xstreq(node->name, "response")) { |
|
331 parse_response_tag(res, node); |
|
332 } |
|
333 } |
|
334 node = node->next; |
|
335 } |
|
336 } |
|
337 return 0; |
|
338 } |
|
339 |
|
340 int dav_store(DavResource *res) { |
|
341 DavSession *sn = res->session; |
|
342 |
|
343 char *url = util_concat_path(sn->base_url, res->path); |
|
344 CURL *handle = res->session->handle; |
|
345 curl_easy_setopt(handle, CURLOPT_URL, url); |
|
346 free(url); |
|
347 |
|
348 DavNodeData *data = res->data; |
|
349 |
|
350 // store content |
|
351 if(data->content) { |
|
352 CURLcode ret = do_put_request(handle, data->content, data->read, data->length); |
|
353 int status = 0; |
|
354 curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status); |
|
355 if(ret == CURLE_OK && (status >= 200 && status < 300)) { |
|
356 res->session->error = 0; |
|
357 // cleanup node data |
|
358 if(!data->read) { |
|
359 ucx_mempool_free(sn->mp, data->content); |
|
360 } |
|
361 data->content = NULL; |
|
362 data->read = NULL; |
|
363 data->length = 0; |
|
364 } else { |
|
365 res->session->error = 1; |
|
366 return 1; |
|
367 } |
|
368 } |
|
369 |
|
370 // store properties |
|
371 if(data->set || data->remove) { |
|
372 UcxBuffer *request = create_proppatch_request(data); |
|
373 UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); |
|
374 |
|
375 CURLcode ret = do_proppatch_request(handle, request, response); |
|
376 int status = 0; |
|
377 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); |
|
378 if(ret == CURLE_OK && status == 207) { |
|
379 //printf("%s\n", response->space); |
|
380 // TODO: parse response |
|
381 // TODO: cleanup node data correctly |
|
382 data->set = NULL; |
|
383 data->remove = NULL; |
|
384 } else { |
|
385 res->session->error = 1; |
|
386 return 1; |
|
387 } |
|
388 } |
|
389 |
|
390 return 0; |
|
391 } |
|
392 |
|
393 char* dav_get_property_ns(DavResource *res, char *ns, char *name) { |
|
394 char *property = resource_get_property(res, ns, name); |
|
395 if(property && res->data->remove) { |
|
396 // TODO |
|
397 } else if(!property && res->data->set) { |
|
398 // TODO |
|
399 } |
|
400 return property; |
|
401 } |
|
402 |
|
403 void dav_set_property_ns(DavResource *res, char *ns, char *name, char *value) { |
|
404 UcxAllocator *a = res->session->allocator; |
|
405 |
|
406 DavProperty *property = a->malloc(a->pool, sizeof(DavProperty)); |
|
407 property->name = sstrdup_a(a, sstr(name)).ptr; |
|
408 property->value = sstrdup_a(a, sstr(value)).ptr; |
|
409 DavNamespace *namespace = a->malloc(a->pool, sizeof(DavNamespace)); |
|
410 namespace->prefix = NULL; |
|
411 namespace->name = sstrdup_a(a, sstr(ns)).ptr; |
|
412 property->ns = namespace; |
|
413 |
|
414 res->data->set = ucx_list_append_a(a, res->data->set, property); |
|
415 } |
|
416 |
|
417 void dav_remove_property_ns(DavResource *res, char *ns, char *name, char *value) { |
|
418 UcxAllocator *a = res->session->allocator; |
|
419 |
|
420 DavProperty *property = a->malloc(a->pool, sizeof(DavProperty)); |
|
421 property->name = sstrdup_a(a, sstr(name)).ptr; |
|
422 property->value = NULL; |
|
423 DavNamespace *namespace = a->malloc(a->pool, sizeof(DavNamespace)); |
|
424 namespace->prefix = NULL; |
|
425 namespace->name = sstrdup_a(a, sstr(ns)).ptr; |
|
426 property->ns = namespace; |
|
427 |
|
428 res->data->remove = ucx_list_append_a(a, res->data->remove, property); |
|
429 } |
|
430 |
|
431 |
|
432 void dav_set_content(DavResource *res, void *stream, dav_read_func read_func) { |
|
433 DavNodeData *data = res->data; |
|
434 data->content = stream; |
|
435 data->read = read_func; |
|
436 data->length = 0; |
|
437 } |
|
438 |
|
439 void dav_set_content_data(DavResource *res, char *content, size_t length) { |
|
440 DavSession *sn = res->session; |
|
441 DavNodeData *data = res->data; |
|
442 data->content = content; |
|
443 data->read = NULL; |
|
444 data->length = length; |
|
445 } |
|
446 |
|
447 int dav_get_content(DavResource *res, void *stream, dav_write_func write_func) { |
|
448 char *url = util_concat_path(res->session->base_url, res->path); |
|
449 CURL *handle = res->session->handle; |
|
450 curl_easy_setopt(handle, CURLOPT_URL, url); |
|
451 free(url); |
|
452 |
|
453 curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); |
|
454 curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL); |
|
455 curl_easy_setopt(handle, CURLOPT_PUT, 0L); |
|
456 curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); |
|
457 |
|
458 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_func); |
|
459 curl_easy_setopt(handle, CURLOPT_WRITEDATA, stream); |
|
460 |
|
461 CURLcode ret = curl_easy_perform(handle); |
|
462 |
|
463 int status = 0; |
|
464 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); |
|
465 if(ret == CURLE_OK && (status >= 200 && status < 300)) { |
|
466 return 0; |
|
467 } else { |
|
468 return 1; |
|
469 } |
|
470 } |
|
471 |