libidav/webdav.c

changeset 33
0bbbb0341606
child 36
c8755c87ce7f
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libxml/tree.h>
+
+#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<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_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;
+}
+
+
+
+

mercurial