libidav/resource.c

changeset 33
0bbbb0341606
child 40
a95ee94b9204
equal deleted inserted replaced
32:c9d37bb97ea8 33:0bbbb0341606
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 }

mercurial