diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/resource.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libidav/resource.c Mon Aug 26 14:42:09 2013 +0200 @@ -0,0 +1,498 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "utils.h" +#include "methods.h" +#include "davql.h" +#include "ucx/buffer.h" +#include "ucx/utils.h" + +#include "resource.h" + +#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) + +DavResource* dav_resource_new(DavSession *sn, char *path) { + char *url = util_concat_path(sn->base_url, path); + char *href = util_url_path(url); + DavResource *res = dav_resource_new_href(sn, href); + free(url); + return res; +} + + +DavResource* dav_resource_new_href(DavSession *sn, char *href) { + UcxMempool *mp = sn->mp; + UcxAllocator *a = sn->allocator; + + DavResource *res = ucx_mempool_calloc(mp, 1, sizeof(DavResource)); + res->session = sn; + + // set name, path and href + resource_set_info(res, href); + + // initialize resource data + res->data = resource_data_new(sn); + + return res; +} + +void resource_set_info(DavResource *res, char *href_str) { + char *url_str = NULL; + curl_easy_getinfo(res->session->handle, CURLINFO_EFFECTIVE_URL, &url_str); + sstr_t name = sstr(util_resource_name(href_str)); + sstr_t href = sstr(href_str); + + sstr_t base_href = sstr(util_url_path(res->session->base_url)); + sstr_t path = sstrsubs(href, base_href.length - 1); + + UcxAllocator *a = res->session->allocator; + res->name = sstrdup_a(a, name).ptr; + res->href = sstrdup_a(a, href).ptr; + res->path = sstrdup_a(a, path).ptr; +} + +DavResourceData* resource_data_new(DavSession *sn) { + DavResourceData *data = ucx_mempool_malloc( + sn->mp, + sizeof(DavResourceData)); + if(!data) { + return NULL; + } + data->properties = ucx_map_new_a(sn->allocator, 32); + data->set = NULL; + data->remove = NULL; + data->content = NULL; + data->read = NULL; + data->length = 0; + return data; +} + +void resource_add_property(DavResource *res, char *ns, char *name, char *val) { + if(!val) { + return; + } + UcxAllocator *a = res->session->allocator; + + UcxKey key = dav_property_key(ns, name); + sstr_t v = sstrdup_a(a, sstr(val)); + ucx_map_put(((DavResourceData*)res->data)->properties, key, v.ptr); + free(key.data); +} + +char* resource_get_property(DavResource *res, char *ns, char *name) { + UcxKey key = dav_property_key(ns, name); + DavResourceData *data = (DavResourceData*)res->data; + char *property = ucx_map_get(data->properties, key); + free(key.data); + return property; +} + +UcxKey dav_property_key(char *ns, char *name) { + sstr_t ns_str = sstr(ns); + sstr_t name_str = sstr(name); + + sstr_t key; + key.length = ns_str.length + name_str.length + 1; + key.ptr = malloc(key.length + 1); + key = sstrncat(key, 3, ns_str, S(" "), name_str); + + return ucx_key(key.ptr, key.length); +} + + + + +void resource_add_child(DavResource *parent, DavResource *child) { + child->next = NULL; + if(parent->children) { + DavResource *last = parent->children; + while(last->next) { + last = last->next; + } + last->next = child; + child->prev = last; + } else { + child->prev = NULL; + parent->children = child; + } + child->parent = parent; +} + +char* dav_get_property(DavResource *res, char *name) { + char *pns; + char *pname; + dav_get_property_namespace(res->session->context, name, &pns, &pname); + return dav_get_property_ns(res, pns, pname); +} + +char* dav_get_property_ns(DavResource *res, char *ns, char *name) { + char *property = resource_get_property(res, ns, name); + DavResourceData *data = res->data; + // resource_get_property only returns persistent properties + // check the remove and set list + if(property) { + // if the property is in the remove list, we return NULL + UCX_FOREACH(elm, data->remove) { + DavProperty *p = elm->data; + if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) { + return NULL; + } + } + } + // the set list contains property updates + // we return an updated property if possible + UCX_FOREACH(elm, data->set) { + DavProperty *p = elm->data; + if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) { + return p->value; + } + } + // no property update + return property; +} + +void dav_set_property(DavResource *res, char *name, char *value) { + char *pns; + char *pname; + dav_get_property_namespace(res->session->context, name, &pns, &pname); + dav_set_property_ns(res, pns, pname, value); +} + +void dav_set_property_ns(DavResource *res, char *ns, char *name, char *value) { + UcxAllocator *a = res->session->allocator; + DavResourceData *data = res->data; + + DavProperty *property = a->malloc(a->pool, sizeof(DavProperty)); + property->name = sstrdup_a(a, sstr(name)).ptr; + property->value = sstrdup_a(a, sstr(value)).ptr; + DavNamespace *namespace = a->malloc(a->pool, sizeof(DavNamespace)); + namespace->prefix = NULL; + namespace->name = sstrdup_a(a, sstr(ns)).ptr; + property->ns = namespace; + + data->set = ucx_list_append_a(a, data->set, property); +} + +void dav_remove_property(DavResource *res, char *name) { + char *pns; + char *pname; + dav_get_property_namespace(res->session->context, name, &pns, &pname); + dav_remove_property_ns(res, pns, pname); +} + +void dav_remove_property_ns(DavResource *res, char *ns, char *name) { + UcxAllocator *a = res->session->allocator; + DavResourceData *data = res->data; + + DavProperty *property = a->malloc(a->pool, sizeof(DavProperty)); + property->name = sstrdup_a(a, sstr(name)).ptr; + property->value = NULL; + DavNamespace *namespace = a->malloc(a->pool, sizeof(DavNamespace)); + namespace->prefix = NULL; + namespace->name = sstrdup_a(a, sstr(ns)).ptr; + property->ns = namespace; + + data->remove = ucx_list_append_a(a, data->remove, property); +} + + +void dav_set_content(DavResource *res, void *stream, dav_read_func read_func) { + DavResourceData *data = res->data; + data->content = stream; + data->read = read_func; + data->length = 0; +} + +void dav_set_content_data(DavResource *res, char *content, size_t length) { + DavSession *sn = res->session; + DavResourceData *data = res->data; + data->content = content; + data->read = NULL; + data->length = length; +} + + +int dav_load(DavResource *res) { + DavSession *sn = res->session; + DavResourceData *data = res->data; + // clean map + UcxKey key; + void *value; + UcxMapIterator i = ucx_map_iterator(data->properties); + UCX_MAP_FOREACH(key, value, i) { + ucx_map_remove(data->properties, key); + } + + char *url = util_concat_path(sn->base_url, res->path); + + CURL *handle = sn->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + + UcxBuffer *rqbuf = create_allprop_propfind_request(); + UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); + + //fwrite(rpbuf->space, 1, rpbuf->size, stdout); + //printf("\n"); + + CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf); + int status = 0; + curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK) { + //printf("response\n%s\n", rpbuf->space); + // TODO: use parse_propfind_response() + xmlDoc *doc = xmlReadMemory(rpbuf->space, rpbuf->size, url, NULL, 0); + if(!doc) { + return 1; + } + + xmlNode *xml_root = xmlDocGetRootElement(doc); + xmlNode *node = xml_root->children; + while(node) { + if(node->type == XML_ELEMENT_NODE) { + if(xstreq(node->name, "response")) { + parse_response_tag(res, node, NULL, 0); + } + } + node = node->next; + } + + set_davprops(res); + } else { + session_set_error(sn, ret, status); + } + return 0; +} + +int dav_store(DavResource *res) { + DavSession *sn = res->session; + DavResourceData *data = res->data; + + char *url = util_concat_path(sn->base_url, res->path); + CURL *handle = res->session->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + + // store content + if(data->content) { + CURLcode ret = do_put_request(handle, data->content, data->read, data->length); + int status = 0; + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK && (status >= 200 && status < 300)) { + res->session->error = 0; + // cleanup node data + if(!data->read) { + ucx_mempool_free(sn->mp, data->content); + } + data->content = NULL; + data->read = NULL; + data->length = 0; + } else { + session_set_error(sn, ret, status); + return 1; + } + } + + // store properties + if(data->set || data->remove) { + UcxBuffer *request = create_proppatch_request(data); + UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); + + CURLcode ret = do_proppatch_request(handle, request, response); + int status = 0; + curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK && status == 207) { + //printf("%s\n", response->space); + // TODO: parse response + // TODO: cleanup node data correctly + data->set = NULL; + data->remove = NULL; + } else { + session_set_error(sn, ret, status); + return 1; + } + } + sn->error = DAV_OK; + return 0; +} + +int dav_get_content(DavResource *res, void *stream, dav_write_func write_fnc) { + char *url = util_concat_path(res->session->base_url, res->path); + CURL *handle = res->session->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL); + curl_easy_setopt(handle, CURLOPT_PUT, 0L); + curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); + + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_fnc); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, stream); + + CURLcode ret = curl_easy_perform(handle); + int status = 0; + curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK && (status >= 200 && status < 300)) { + res->session->error = DAV_OK; + return 0; + } else { + session_set_error(res->session, ret, status); + return 1; + } +} + +DavResource* dav_create_child(DavResource *parent, char *name) { + // TODO + return NULL; +} + +int dav_delete(DavResource *res) { + char *url = util_concat_path(res->session->base_url, res->path); + CURL *handle = res->session->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + + UcxBuffer *response = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); + CURLcode ret = do_delete_request(handle, response); + int status = 0; + curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK && (status >= 200 && status < 300)) { + res->session->error = DAV_OK; + + // TODO: parse response + // TODO: free res + + return 0; + } else { + session_set_error(res->session, ret, status); + return 1; + } +} + +int dav_create(DavResource *res) { + char *url = util_concat_path(res->session->base_url, res->path); + char *parent = util_parent_path(res->path); + + DavSession *sn = res->session; + DavResource *parent_res = dav_get(sn, parent, NULL); + if(!parent_res && sn->error == DAV_NOT_FOUND) { + parent_res = dav_resource_new(sn, parent); + parent_res->iscollection = 1; + int r = dav_create(parent_res); + if(r) { + free(parent); + return r; + } + } else if(parent_res && !parent_res->iscollection) { + sn->error = DAV_FORBIDDEN; + return 1; + } else if(sn->error != DAV_OK) { + return 1; + } + + CURL *handle = res->session->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + free(parent); + + // create new collection or do an empty put request + CURLcode ret; + if(res->iscollection) { + ret = do_mkcol_request(handle); + } else { + ret = do_put_request(handle, "", NULL, 0); + } + int status = 0; + curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK && (status >= 200 && status < 300)) { + res->session->error = DAV_OK; + } else { + session_set_error(res->session, ret, status); + return 1; + } + + // do an minimal propfind request + UcxBuffer *rqbuf = create_propfind_request(NULL); + UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); + + //fwrite(rpbuf->space, 1, rpbuf->size, stdout); + //printf("\n"); + + ret = do_propfind_request(handle, rqbuf, rpbuf); + status = 0; + curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK && (status >= 200 && status < 300)) { + //printf("response\n%s\n", rpbuf->space); + // TODO: use parse_propfind_response() + xmlDoc *doc = xmlReadMemory(rpbuf->space, rpbuf->size, url, NULL, 0); + if(!doc) { + return 1; + } + + xmlNode *xml_root = xmlDocGetRootElement(doc); + xmlNode *node = xml_root->children; + while(node) { + if(node->type == XML_ELEMENT_NODE) { + if(xstreq(node->name, "response")) { + parse_response_tag(res, node, NULL, 0); + } + } + node = node->next; + } + + set_davprops(res); + return 0; + } else { + session_set_error(sn, ret, status); + return 1; + } +} + +int dav_exists(DavResource *res) { + DavSession *sn = res->session; + char *url = util_concat_path(sn->base_url, res->path); + CURL *handle = sn->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + + CURLcode ret = do_head_request(handle); + int status = 0; + curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK && (status >= 200 && status < 300)) { + return 1; + } else { + session_set_error(sn, ret, status); + return 0; + } +}