libidav/webdav.c

Fri, 05 Jun 2015 10:10:14 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 05 Jun 2015 10:10:14 +0200
changeset 131
32e7b3e6b482
parent 128
649eb328674a
child 134
4bccc18820e8
permissions
-rw-r--r--

added missing keywords + fixed missing source text for boolean primaries

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2015 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 "session.h"
#include "methods.h"
#include "davql.h"
#include "ucx/buffer.h"
#include "ucx/utils.h"


DavContext* dav_context_new() {
    // initialize
    DavContext *context = calloc(1, sizeof(DavContext));
    if(!context) {
        return NULL;
    }
    context->sessions = NULL;
    context->http_proxy = calloc(1, sizeof(DavProxy));
    if(!context->http_proxy) {
        dav_context_destroy(context);
        return NULL;
    }
    context->https_proxy = calloc(1, sizeof(DavProxy));
    if(!context->https_proxy) {
        dav_context_destroy(context);
        return NULL;
    }
    context->namespaces = ucx_map_new(16);
    if(!context->namespaces) {
        dav_context_destroy(context);
        return NULL;
    }
    context->keys = ucx_map_new(16);
    if(!context->keys) {
        dav_context_destroy(context);
        return NULL;
    }
    
    // add DAV: namespace
    DavNamespace *davns = malloc(sizeof(DavNamespace));
    if(!davns) {
        free(davns);
        dav_context_destroy(context);
        return NULL;
    }
    davns->prefix = strdup("D");
    if(!davns->prefix) {
        free(davns);
        dav_context_destroy(context);
        return NULL;
    }
    davns->name = strdup("DAV:");
    if(!davns->name) {
        free(davns->prefix);
        free(davns);
        dav_context_destroy(context);
        return NULL;
    }
    if(ucx_map_cstr_put(context->namespaces, "D", davns)) {
        free(davns->prefix);
        free(davns->name);
        free(davns);
        dav_context_destroy(context);
        return NULL;
    }
    
    // add idav namespace
    DavNamespace *idavns = malloc(sizeof(DavNamespace));
    if(!idavns) {
        free(idavns);
        dav_context_destroy(context);
        return NULL;
    }
    idavns->prefix = strdup("idav");
    if(!idavns->prefix) {
        free(idavns);
        dav_context_destroy(context);
        return NULL;
    }
    idavns->name = strdup(DAV_NS);
    if(!idavns->name) {
        free(idavns->prefix);
        free(idavns);
        dav_context_destroy(context);
        return NULL;
    }
    if(ucx_map_cstr_put(context->namespaces, "idav", idavns)) {
        free(idavns->prefix);
        free(idavns->name);
        free(idavns);
        dav_context_destroy(context);
        return NULL;
    }
    
    return context;
}

void dav_context_destroy(DavContext *ctx) {
    // destroy all sessions assoziated with this context
    UcxList *elm = ctx->sessions;
    while(elm) {
        DavSession *sn = elm->data;
        elm = elm->next;
        dav_session_destroy(sn);
    }
    if(ctx->http_proxy) {
        free(ctx->http_proxy);
    }
    if(ctx->https_proxy) {
        free(ctx->https_proxy);
    }
    
    if(ctx->namespaces) {
        UcxMapIterator i = ucx_map_iterator(ctx->namespaces);
        UcxKey k;
        DavNamespace *ns;
        UCX_MAP_FOREACH(k, ns, i) {
            if(!ns) continue;
            if(ns->prefix) {
                free(ns->prefix);
            }
            if(ns->name) {
                free(ns->name);
            }
            free(ns);
        }
        ucx_map_free(ctx->namespaces);
    }
    if(ctx->keys) {
        UcxMapIterator i = ucx_map_iterator(ctx->keys);
        UcxKey k;
        DavKey *key;
        UCX_MAP_FOREACH(k, key, i) {
            if(!key) continue;
            if(key->name) {
                free(key->name);
            }
            if(key->data) {
                free(key->data);
            }
            free(key);
        }
        ucx_map_free(ctx->keys);
    }    
    
    free(ctx);
}

void dav_context_add_key(DavContext *context, DavKey *key) {
    ucx_map_cstr_put(context->keys, key->name, key);
}

DavKey* dav_context_get_key(DavContext *context, char *name) {
    if(name) {
        return ucx_map_cstr_get(context->keys, name);
    } else {
        return NULL;
    }
}

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));
        if(ns) {
            pns = ns->name;
            pname++;
        } else {
            pns = NULL;
            pname = NULL;
        }
    } else {
        pname = prefixed_name;
    }
    *ns = pns;
    *name = pname;
}


DavResource* dav_get(DavSession *sn, char *path, char *properties) {  
    CURL *handle = sn->handle;
    DavResource *resource = dav_resource_new(sn, path);
    util_set_url(sn, dav_resource_get_href(resource));
    
    UcxList *proplist = NULL;
    if(properties) {
        proplist = parse_properties_string(sn->context, sstr(properties));
    }
    UcxBuffer *rqbuf = create_propfind_request(sn, proplist);
    UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
    
    //fwrite(rqbuf->space, 1, rqbuf->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 && status == 207) {
        //printf("response\n%s\n", rpbuf->space);
        resource = parse_propfind_response(sn, resource, rpbuf, NULL, 0);
        sn->error = DAV_OK;
    } else  {
        dav_session_set_error(sn, ret, status);
        dav_resource_free(resource);
        resource = NULL;
    }
    
    ucx_buffer_free(rqbuf);
    ucx_buffer_free(rpbuf);
    
    return resource;
}

int dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf, DavQOp *cond, size_t len) {
    // clean resource properties
    DavResourceData *data = root->data;
    size_t pcount = data->properties->count;
    if(pcount > 0) {
        UcxKey key;
        void *value;
        UcxMapIterator i = ucx_map_iterator(data->properties);
        UcxKey mkeys[pcount];
        int index = 0;
        UCX_MAP_FOREACH(key, value, i) {
            mkeys[index] = key;
            index++;
        }
        for(int j=0;j<index;j++) {
            ucx_map_remove(data->properties, mkeys[j]);
        }
    }
    
    CURL *handle = sn->handle;
    util_set_url(sn, dav_resource_get_href(root));
     
    UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
    DavResource *resource = root;
    CURLcode ret = do_propfind_request(handle, rqbuf, rpbuf);
    int status = 0;
    int error = 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  {
        dav_session_set_error(sn, ret, status);
        error = 1;
    }
    ucx_buffer_free(rpbuf);
    return error;
}

static 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_query_get(DavSession *sn, DavGetQuery *query) {
    char *path;
    int depth = query->depth;
    /*
    if(parse_path_query(query->from, &path, &depth)) {
        sn->error = DAV_ERROR;
        return NULL;
    }
    */
    path = sstrdup(query->from).ptr;
    
    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(sn, NULL);
    } else {
        UcxList *proplist = parse_properties_string(sn->context, ps);
        rqbuf = create_propfind_request(sn, proplist);
        UCX_FOREACH(elm, proplist) {
            DavProperty *prop = elm->data;
            free(prop->name);
            free(prop);
        }
        ucx_list_free(proplist);
    }
    
    //fwrite(rqbuf->space, 1, rqbuf->size, stdout);
    //printf("\n");
    
    DavResource *resource = dav_resource_new(sn, path);
    free(path);
    if(dav_propfind(sn, resource, rqbuf, query->condition, query->condlen)) {
        dav_resource_free(resource);
        resource = NULL;
    }
    
    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
            if(dav_propfind(sn, sr, rqbuf, query->condition, query->condlen)) {
                error = 1;
                printf("subrequest failed\n");
                break;
            }
            stack = propfind_stack_push(stack, sr->children); // add children
        }
    }
    
    ucx_buffer_free(rqbuf);
    return resource;
}

UcxList* parse_properties_string(DavContext *context, sstr_t str) {
    UcxList *proplist = NULL;
    ssize_t nprops = 0;
    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 = sstrtrim(nspre);
            dp->ns = dav_get_namespace_s(context, pre);
            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_query_get(sn, q.command_data);
            free_get_query(q.command_data);
            break;
        }
        case DAV_QUERY_ERROR: {
            // TODO
            break;
        }
    }
    return res;
}



mercurial