diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/webdav.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libidav/webdav.c Mon Aug 26 14:42:09 2013 +0200 @@ -0,0 +1,399 @@ +/* + * 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 "webdav.h" +#include "methods.h" +#include "davql.h" +#include "ucx/buffer.h" +#include "ucx/utils.h" + + + +DavContext* dav_context_new() { + DavContext *context = malloc(sizeof(DavContext)); + if(!context) { + return NULL; + } + context->sessions = NULL; + context->namespaces = ucx_map_new(16); + context->http_proxy = NULL; + context->https_proxy = NULL; + context->no_proxy = NULL; + 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; +} + +void dav_context_destroy(DavContext *ctx) { + // destroy all sessions assoziated with this context + UCX_FOREACH(elm, ctx->sessions) { + dav_session_destroy(elm->data); + } + + UcxMapIterator i = ucx_map_iterator(ctx->namespaces); + UcxKey k; + DavNamespace *ns; + // TODO: free map elements + ucx_map_free(ctx->namespaces); + free(ctx); +} + +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); +} + +void dav_get_property_namespace( + DavContext *ctx, + char *prefixed_name, + char **ns, + char **name) +{ + char *pname = strchr(prefixed_name, ':'); + char *pns = "DAV:"; + if(pname) { + DavNamespace *ns = dav_get_namespace_s( + ctx, + sstrn(prefixed_name, pname-prefixed_name)); + pns = ns->name; + pname++; + } else { + pname = prefixed_name; + } + *ns = pns; + *name = pname; +} + +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)); + sn->errorstr = NULL; + sn->error = DAV_OK; + 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_VERBOSE, 1L); + //curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr); + + // set proxy + if(sstrprefix(url, S("https"))) { + if(context->https_proxy) { + //printf("use https_proxy: %s\n", context->https_proxy); + curl_easy_setopt(sn->handle, CURLOPT_PROXY, context->https_proxy); + } + } else { + if(context->http_proxy) { + //printf("use http_proxy: %s\n", context->http_proxy); + curl_easy_setopt(sn->handle, CURLOPT_PROXY, context->http_proxy); + } + } + if(context->no_proxy) { + //printf("use no_proxy: %s\n", context->no_proxy); + curl_easy_setopt(sn->handle, CURLOPT_NOPROXY, context->no_proxy); + } + // set url + curl_easy_setopt(sn->handle, CURLOPT_URL, base_url); + + sn->mp = ucx_mempool_new(1024); + sn->allocator = ucx_mempool_allocator(sn->mp); + + context->sessions = ucx_list_append(context->sessions, sn); + + 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); + } +} + +void session_set_error(DavSession *sn, CURLcode c, int status) { + if(status > 0) { + switch(status) { + default: sn->error = DAV_ERROR; break; + case 401: sn->error = DAV_UNAUTHORIZED; break; + case 403: sn->error = DAV_FORBIDDEN; break; + case 404: sn->error = DAV_NOT_FOUND; break; + case 405: sn->error = DAV_METHOD_NOT_ALLOWED; break; + case 409: sn->error = DAV_CONFLICT; break; + } + } else { + sn->error = DAV_ERROR; + } + if(c != CURLE_OK) { + sn->errorstr = curl_easy_strerror(c); + } else { + sn->errorstr = NULL; + } +} + +void dav_session_destroy(DavSession *sn) { + // remove session from context + UcxList *sessions = sn->context->sessions; + ssize_t i = ucx_list_find(sessions, sn, ucx_ptrcmp, NULL); + if(i > 0) { + UcxList *elm = ucx_list_get(sessions, i); + if(elm) { + sn->context->sessions = ucx_list_remove(sessions, elm); + } + } + + ucx_mempool_destroy(sn->mp); + curl_easy_cleanup(sn->handle); + free(sn->base_url); + free(sn); +} + +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, NULL, rpbuf, NULL, 0); + sn->error = DAV_OK; + } else { + session_set_error(sn, ret, status); + } + return resource; +} + +DavResource* dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf, char *path, DavQOp *cond, size_t len) { + char *url = util_concat_path(sn->base_url, path); + CURL *handle = sn->handle; + curl_easy_setopt(handle, CURLOPT_URL, url); + free(url); + + UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); + DavResource *resource = root; + 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, resource, rpbuf, cond, len); + sn->error = DAV_OK; + } else { + session_set_error(sn, ret, status); + resource = NULL; + } + ucx_buffer_free(rpbuf); + return resource; +} + +UcxList* propfind_stack_push(UcxList *stack, DavResource *children) { + while(children) { + if(children->iscollection) { + stack = ucx_list_prepend(stack, children); + } + children = children->next; + } + return stack; +} + +DavResource* dav_get2(DavSession *sn, DavGetQuery *query) { + char *path; + int depth = 0; + if(parse_path_query(query->from, &path, &depth)) { + sn->error = DAV_ERROR; + return NULL; + } + + sstr_t ps = query->properties; + UcxBuffer *rqbuf; + if(!sstrcmp(ps, S("*"))) { + rqbuf = create_allprop_propfind_request(); + } else if(!sstrcmp(ps, S("-"))) { + rqbuf = create_propfind_request(NULL); + } else { + UcxList *proplist = parse_properties_string(sn->context, ps); + rqbuf = create_propfind_request(proplist); + } + + //fwrite(rqbuf->space, 1, rqbuf->size, stdout); + //printf("\n"); + + DavResource *resource = dav_propfind(sn, NULL, rqbuf, path, query->condition, query->condlen); + free(path); + int error = 0; + if(resource && depth == -1) { + UcxList *stack = NULL; // stack with davResource* elements + stack = propfind_stack_push(stack, resource->children); + while(stack) { + DavResource *sr = stack->data; // get first element from the stack + stack = ucx_list_remove(stack, stack); // remove first element + // do propfind request for sr + sr = dav_propfind(sn, sr, rqbuf, sr->path, query->condition, query->condlen); + if(!sr) { + error = 1; + printf("subrequest failed\n"); + break; + } + stack = propfind_stack_push(stack, sr->children); // add children + } + } + 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 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_query(DavSession *sn, char *query, ...) { + va_list ap; + va_start(ap, query); + DavQuery q = dav_ql_parse(query, ap); + va_end(ap); + DavResource *res = NULL; + switch(q.command) { + case DAV_QUERY_GET: { + res = dav_get2(sn, q.command_data); + free_get_query(q.command_data); + break; + } + } + return res; +} + + + +