dav/webdav.c

changeset 5
88625853ae74
child 11
5db6178d8b58
equal deleted inserted replaced
4:ae5a98f0545c 5:88625853ae74
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

mercurial