libidav/webdav.c

Mon, 02 Sep 2013 10:31:29 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 02 Sep 2013 10:31:29 +0200
changeset 36
c8755c87ce7f
parent 33
0bbbb0341606
child 38
b855f76e965b
permissions
-rw-r--r--

added proxy config

/*
 * 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->http_proxy = calloc(1, sizeof(DavProxy));
    context->https_proxy = calloc(1, sizeof(DavProxy));
    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;
}

void dav_context_destroy(DavContext *ctx) {
    // destroy all sessions assoziated with this context
    UCX_FOREACH(elm, ctx->sessions) {
        dav_session_destroy(elm->data);
    }
    free(ctx->http_proxy);
    free(ctx->https_proxy);
    
    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
    DavProxy *proxy = sstrprefix(url, S("https")) ? context->https_proxy
                                                  : context->http_proxy;

    if (proxy->url) {
        curl_easy_setopt(sn->handle, CURLOPT_PROXY, proxy->url);
        if (proxy->username) {
            curl_easy_setopt(sn->handle, CURLOPT_PROXYUSERNAME,
                proxy->username);
            if (proxy->password) {
                curl_easy_setopt(sn->handle, CURLOPT_PROXYPASSWORD,
                    proxy->password);
            } else {
                // TODO: prompt
            }
        }
        if(proxy->no_proxy) {
            curl_easy_setopt(sn->handle, CURLOPT_NOPROXY,
                proxy->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