--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dav/webdav.c Mon Aug 12 14:40:19 2013 +0200 @@ -0,0 +1,471 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libxml/tree.h> + +#include "utils.h" +#include "webdav.h" +#include "methods.h" +#include "ucx/buffer.h" + +#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) + +DavContext* dav_context_new() { + DavContext *context = malloc(sizeof(DavContext)); + if(!context) { + return NULL; + } + context->namespaces = ucx_map_new(16); + if(!context->namespaces) { + free(context); + return NULL; + } + DavNamespace *davns = malloc(sizeof(DavNamespace)); + if(!davns) { + ucx_map_free(context->namespaces); + free(context); + return NULL; + } + davns->prefix = "D"; + davns->name = "DAV:"; + if(ucx_map_cstr_put(context->namespaces, "D", davns)) { + free(davns); + ucx_map_free(context->namespaces); + free(context); + return NULL; + } + + return context; +} + +int dav_add_namespace(DavContext *context, char *prefix, char *name) { + DavNamespace *namespace = malloc(sizeof(DavNamespace)); + if(!namespace) { + return 1; + } + namespace->prefix = strdup(prefix); + namespace->name = strdup(name); + return ucx_map_cstr_put(context->namespaces, prefix, namespace); +} + +DavNamespace* dav_get_namespace(DavContext *context, char *prefix) { + return ucx_map_cstr_get(context->namespaces, prefix); +} + +DavNamespace* dav_get_namespace_s(DavContext *context, sstr_t prefix) { + return ucx_map_sstr_get(context->namespaces, prefix); +} + +DavSession* dav_session_new(DavContext *context, char *base_url) { + if(!base_url) { + return NULL; + } + sstr_t url = sstr(base_url); + if(url.length == 0) { + return NULL; + } + DavSession *sn = malloc(sizeof(DavSession)); + if(url.ptr[url.length - 1] == '/') { + sn->base_url = strdup(base_url); + } else { + char *url_str = malloc(url.length + 2); + memcpy(url_str, base_url, url.length); + url_str[url.length] = '/'; + url_str[url.length + 1] = '\0'; + sn->base_url = url_str; + } + sn->context = context; + sn->handle = curl_easy_init(); + curl_easy_setopt(sn->handle, CURLOPT_URL, base_url); + + sn->mp = ucx_mempool_new(1024); + sn->allocator = ucx_mempool_allocator(sn->mp); + + return sn; +} + +DavSession* dav_session_new_auth(DavContext *context, char *base_url, char *user, char *password) { + DavSession *sn = dav_session_new(context, base_url); + if(!sn) { + return NULL; + } + dav_session_set_auth(sn, user, password); + return sn; +} + +void dav_session_set_auth(DavSession *sn, char *user, char *password) { + if(user && password) { + size_t ulen = strlen(user); + size_t plen = strlen(password); + size_t upwdlen = ulen + plen + 2; + char *upwdbuf = malloc(upwdlen); + snprintf(upwdbuf, upwdlen, "%s:%s\0", user, password); + curl_easy_setopt(sn->handle, CURLOPT_USERPWD, upwdbuf); + free(upwdbuf); + } +} + +DavResource* dav_get(DavSession *sn, char *path, char *properties) { + char *url = util_concat_path(sn->base_url, path); + + CURL *handle = sn->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + + UcxList *proplist = NULL; + if(properties) { + proplist = parse_properties_string(sn->context, sstr(properties)); + } + UcxBuffer *rqbuf = create_propfind_request(proplist); + UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); + + //fwrite(rqbuf->space, 1, rqbuf->size, stdout); + //printf("\n"); + + DavResource *resource = NULL; + CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf); + int status = 0; + curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); + if(ret == CURLE_OK && status == 207) { + //printf("response\n%s\n", rpbuf->space); + resource = parse_propfind_response(sn, rpbuf); + } + return resource; +} + +UcxList* parse_properties_string(DavContext *context, sstr_t str) { + UcxList *proplist = NULL; + size_t nprops; + sstr_t *props = sstrsplit(str, S(","), &nprops); + for(int i=0;i<nprops;i++) { + sstr_t s = props[i]; + sstr_t nsname = sstrchr(s, ':'); + if(nsname.length > 0) { + sstr_t nspre = sstrsubsl(s, 0, nsname.ptr - s.ptr); + nsname.ptr++; + nsname.length--; + + DavProperty *dp = malloc(sizeof(DavProperty)); + sstr_t pre = sstrdup(sstrtrim(nspre)); + dp->ns = dav_get_namespace(context, pre.ptr); + free(pre.ptr); + dp->name = sstrdup(nsname).ptr; + if(dp->ns && dp->name) { + proplist = ucx_list_append(proplist, dp); + } else { + free(dp->name); + free(dp); + } + } + free(s.ptr); + } + free(props); + return proplist; +} + + + + +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 = resource_new_href(sn, href); + free(url); + return res; +} + +DavResource* 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 node data + res->data = node_data_new(sn); + + return res; +} + +void resource_add_property(DavResource *res, char *ns, char *name, char *value) { + if(!value) { + return; + } + UcxMempool *mp = res->session->mp; + UcxAllocator *a = res->session->allocator; + + UcxKey key = dav_property_key(ns, name); + sstr_t v = sstrdup_a(a, sstr(value)); + ucx_map_put(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); + return ucx_map_get(res->data->properties, key); +} + +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; + } +} + +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; +} + +DavNodeData* node_data_new(DavSession *sn) { + DavNodeData *data = ucx_mempool_malloc(sn->mp, sizeof(DavNodeData)); + 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; +} + +int dav_load(DavResource *res) { + DavSession *sn = res->session; + // clean map + UcxKey key; + void *value; + UcxMapIterator i = ucx_map_iterator(res->data->properties); + UCX_MAP_FOREACH(key, value, i) { + ucx_map_remove(res->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); + if(ret == CURLE_OK) { + //printf("response\n%s\n", rpbuf->space); + + 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); + } + } + node = node->next; + } + } + return 0; +} + +int dav_store(DavResource *res) { + DavSession *sn = res->session; + + char *url = util_concat_path(sn->base_url, res->path); + CURL *handle = res->session->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + + DavNodeData *data = res->data; + + // 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 { + res->session->error = 1; + 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 { + res->session->error = 1; + return 1; + } + } + + return 0; +} + +char* dav_get_property_ns(DavResource *res, char *ns, char *name) { + char *property = resource_get_property(res, ns, name); + if(property && res->data->remove) { + // TODO + } else if(!property && res->data->set) { + // TODO + } + return property; +} + +void dav_set_property_ns(DavResource *res, char *ns, char *name, char *value) { + UcxAllocator *a = res->session->allocator; + + 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; + + res->data->set = ucx_list_append_a(a, res->data->set, property); +} + +void dav_remove_property_ns(DavResource *res, char *ns, char *name, char *value) { + UcxAllocator *a = res->session->allocator; + + 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; + + res->data->remove = ucx_list_append_a(a, res->data->remove, property); +} + + +void dav_set_content(DavResource *res, void *stream, dav_read_func read_func) { + DavNodeData *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; + DavNodeData *data = res->data; + data->content = content; + data->read = NULL; + data->length = length; +} + +int dav_get_content(DavResource *res, void *stream, dav_write_func write_func) { + 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_func); + 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)) { + return 0; + } else { + return 1; + } +} +