dav/methods.c

Thu, 22 Aug 2013 12:45:12 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 22 Aug 2013 12:45:12 +0200
changeset 29
938957a4eea7
parent 27
e584c351b402
permissions
-rw-r--r--

added date command

/*
 * 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 "utils.h"
#include "methods.h"
#include "davql.h"

#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)

/* ----------------------------- PROPFIND ----------------------------- */

CURLcode do_propfind_request(
        CURL *handle,
        UcxBuffer *request,
        UcxBuffer *response)
{
    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPFIND");
    
    struct curl_slist *headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: text/xml");
    headers = curl_slist_append(headers, "Depth: 1");
    curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
    
    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
    
    curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 
    curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read);
    curl_easy_setopt(handle, CURLOPT_READDATA, request); 
    curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size);
    
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
    
    ucx_buffer_seek(request, 0, SEEK_SET);
    return curl_easy_perform(handle);
}

UcxBuffer* create_allprop_propfind_request() {
    UcxBuffer *buf = ucx_buffer_new(NULL, 512, 0);
    sstr_t s;
    
    s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    
    s = S("<D:propfind xmlns:D=\"DAV:\">\n");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    
    s = S("<D:allprop/></D:propfind>\n");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    
    return buf;
}

UcxBuffer* create_propfind_request(UcxList *properties) {
    UcxBuffer *buf = ucx_buffer_new(NULL, 512, 0);
    sstr_t s;
    
    UcxMap *namespaces = ucx_map_new(8);
    UCX_FOREACH(elm, properties) {
        DavProperty *p = elm->data;
        if(strcmp(p->ns->name, "DAV:")) {
            ucx_map_cstr_put(namespaces, p->ns->prefix, p->ns);
        }
    }
    
    s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    
    // write root element and namespaces
    s = S("<D:propfind xmlns:D=\"DAV:\"");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    UcxMapIterator mapi = ucx_map_iterator(namespaces);
    UcxKey key;
    DavNamespace *ns;
    UCX_MAP_FOREACH(key, ns, mapi) {
        s = S(" xmlns:");
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        s = sstr(ns->prefix);
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        s = S("=\"");
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        s = sstr(ns->name);
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        s = S("\"");
        ucx_buffer_write(s.ptr, 1, s.length, buf);
    }
    s = S(">\n");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    
    // default properties
    s = S("<D:prop>\n");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    
    s = S("<D:creationdate />\n<D:getlastmodified />\n");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    
    s = S("<D:getcontentlength />\n<D:getcontenttype />\n");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    
    s = S("<D:resourcetype />\n");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    
    // extra properties
    UCX_FOREACH(elm, properties) {
        DavProperty *prop = elm->data;
        s = S("<");
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        s = sstr(prop->ns->prefix);
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        s = S(":");
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        s = sstr(prop->name);
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        s = S(" />\n");
        ucx_buffer_write(s.ptr, 1, s.length, buf);
    }
    
    // end
    s = S("</D:prop>\n</D:propfind>\n");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    
    return buf;
}

DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response, DavQOp *cond, size_t len) {
    char *url = NULL;
    curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &url);
    if(!root) {
        root = resource_new_href(sn, util_url_path(url));
    }
    
    xmlDoc *doc = xmlReadMemory(response->space, response->size, url, NULL, 0);
    if(!doc) {
        // TODO: free stuff
        sn->error = DAV_ERROR;
        return NULL;
    }
    
    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(root, node, cond, len);
            }
        }
        
        node = node->next;
    }
    
    return root;
}

int parse_response_tag(DavResource *resource, xmlNode *node, DavQOp *cond, size_t clen) {
    DavResource *res = resource;
    node = node->children;
    while(node) {
        if(node->type == XML_ELEMENT_NODE) {
            if(xstreq(node->name, "href")) {
                xmlNode *href_content = node->children;
                if(href_content->type != XML_TEXT_NODE) {
                    // error
                    resource->session->error = DAV_ERROR;
                    return 1;
                }
                if(xstreq(resource->href, href_content->content)) {
                    res = resource;
                } else {
                    res = resource_new_href(resource->session, (char*)href_content->content);
                    res->parent = resource;
                }
            } else if(xstreq(node->name, "propstat")) {
                xmlNode *n = node->children;
                xmlNode *prop_node = NULL;
                int ok = 0;
                // get the status code
                while(n) {
                    if(n->type == XML_ELEMENT_NODE) {
                        if(xstreq(n->name, "prop")) {
                            prop_node = n;
                        } else if(xstreq(n->name, "status")) {
                            xmlNode *status_node = n->children;
                            if(status_node->type != XML_TEXT_NODE) {
                                resource->session->error = DAV_ERROR;
                                return 1;
                            }
                            sstr_t status_str = sstr((char*)status_node->content);
                            if(status_str.length < 13) {
                                resource->session->error = DAV_ERROR;
                                return 1;
                            }
                            status_str = sstrsubsl(status_str, 9, 3);
                            if(!sstrcmp(status_str, S("200"))) {
                                ok = 1;
                            }
                        }
                    }    
                    n = n->next;
                }
                // if status is ok, get all properties
                if(ok) {
                    n = prop_node->children;
                    while(n) {
                        if(n->type == XML_ELEMENT_NODE) {
                            if(xstreq(n->name, "resourcetype")) {
                                xmlNode *rsnode = n->children;
                                if(rsnode && rsnode->type == XML_ELEMENT_NODE) {
                                    // TODO: this is a ugly lazy hack
                                    resource_add_property(res, "DAV:", (char*)n->name, "collection");
                                    res->iscollection = 1;
                                }
                            } else {
                                xmlNode *content = n->children;
                                if(content) {
                                    resource_add_property(
                                            res,
                                            (char*)n->ns->href,
                                            (char*)n->name,
                                            (char*)content->content);
                                }
                            }
                        }
                        n = n->next;
                    }
                }
            }
        }
        
        node = node->next;
    }
    
    set_davprops(res);
    if(res != resource) {
        if(clen > 0) {
            if(!condition_eval(res, cond, clen)) {
                // skip resource
                return 0;
            }
        }
        resource_add_child(resource, res);
    }
    
    return 0;
}

void set_davprops(DavResource *res) {
    char *cl = dav_get_property_ns(res, "DAV:", "getcontentlength");
    char *ct = dav_get_property_ns(res, "DAV:", "getcontenttype");
    char *cd = dav_get_property_ns(res, "DAV:", "creationdate");
    char *lm = dav_get_property_ns(res, "DAV:", "getlastmodified");
    
    res->contenttype = ct;
    if(cl) {
        char *end = NULL;
        res->contentlength = strtoull(cl, &end, 0);
    }
    res->creationdate = util_parse_creationdate(cd);
    res->lastmodified = util_parse_lastmodified(lm);
}


/* ----------------------------- PROPPATCH ----------------------------- */

CURLcode do_proppatch_request(
        CURL *handle,
        UcxBuffer *request,
        UcxBuffer *response)
{
    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPPATCH");
    
    struct curl_slist *headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: text/xml");
    curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
    
    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
    
    curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 
    curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read);
    curl_easy_setopt(handle, CURLOPT_READDATA, request); 
    curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size);
    
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
    
    ucx_buffer_seek(request, 0, SEEK_SET);
    return curl_easy_perform(handle);
}

UcxBuffer* create_proppatch_request(DavNodeData *data) {
    UcxBuffer *buf = ucx_buffer_new(NULL, 512, 0);
    sstr_t s;
    
    UcxMap *namespaces = ucx_map_new(8);
    char prefix[8];
    int pfxnum = 0;
    UCX_FOREACH(elm, data->set) {
        DavProperty *p = elm->data;
        if(strcmp(p->ns->name, "DAV:")) {
            snprintf(prefix, 8, "x%d\0", pfxnum++);
            ucx_map_cstr_put(namespaces, p->ns->name, prefix);
        }
    }
    UCX_FOREACH(elm, data->remove) {
        DavProperty *p = elm->data;
        if(strcmp(p->ns->name, "DAV:")) {
            snprintf(prefix, 8, "x%d\0", pfxnum++);
            ucx_map_cstr_put(namespaces, p->ns->name, prefix);
        }
    }
    
    s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    
    // write root element and namespaces
    s = S("<D:propertyupdate xmlns:D=\"DAV:\"");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    UcxMapIterator mapi = ucx_map_iterator(namespaces);
    UcxKey key;
    char *pfxval;
    UCX_MAP_FOREACH(key, pfxval, mapi) {
        s = S(" xmlns:");
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        s = sstr(pfxval);
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        s = S("=\"");
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        s = sstrn(key.data, key.len);
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        s = S("\"");
        ucx_buffer_write(s.ptr, 1, s.length, buf);
    }
    s = S(">\n");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    
    if(data->set) {
        s = S("<D:set>\n<D:prop>\n");
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        UCX_FOREACH(elm, data->set) {
            DavProperty *property = elm->data;
            char *prefix = ucx_map_cstr_get(namespaces, property->ns->name);
            
            s = S("<");
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = sstr(prefix);
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = S(":");
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = sstr(property->name);
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = S(">");
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = sstr(property->value);
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = S("</");
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = sstr(prefix);
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = S(":");
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = sstr(property->name);
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = S(">\n");
            ucx_buffer_write(s.ptr, 1, s.length, buf);
        }
        s = S("</D:prop>\n</D:set>\n");
        ucx_buffer_write(s.ptr, 1, s.length, buf);
    }
    if(data->remove) {
        s = S("<D:set>\n<D:prop>\n");
        ucx_buffer_write(s.ptr, 1, s.length, buf);
        UCX_FOREACH(elm, data->set) {
            DavProperty *property = elm->data;
            char *prefix = ucx_map_cstr_get(namespaces, property->ns->name);
            
            s = S("<");
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = sstr(prefix);
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = S(":");
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = sstr(property->name);
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = S(">");
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = sstr(property->value);
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = S("</");
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = sstr(prefix);
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = S(":");
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = sstr(property->name);
            ucx_buffer_write(s.ptr, 1, s.length, buf);
            s = S(">\n");
            ucx_buffer_write(s.ptr, 1, s.length, buf);
        }
        s = S("</D:prop>\n</D:set>\n");
        ucx_buffer_write(s.ptr, 1, s.length, buf);
    }
    
    s = S("</D:propertyupdate>\n");
    ucx_buffer_write(s.ptr, 1, s.length, buf);
    
    return buf;
}

/* ----------------------------- PUT ----------------------------- */

static size_t dummy_write(void *buf, size_t s, size_t n, void *data) {
    return s*n;
}

CURLcode do_put_request(CURL *handle, void *data, dav_read_func read_func, size_t length) {
    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL);
    curl_easy_setopt(handle, CURLOPT_PUT, 1L);  
    curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
    
    UcxBuffer *buf = NULL;
    if(!read_func) {
        buf = ucx_buffer_new(data, length, 0);
        buf->size = length;
        data = buf;
        read_func = (dav_read_func)ucx_buffer_read;
        curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length);
    } else if(length == 0) {
        struct curl_slist *headers = NULL;
        headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
        curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)1);
        curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
    } else {
        curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length);
    }
    
    curl_easy_setopt(handle, CURLOPT_READFUNCTION, read_func);
    curl_easy_setopt(handle, CURLOPT_READDATA, data);
    
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
    
    CURLcode ret = curl_easy_perform(handle);
    if(buf) {
        ucx_buffer_free(buf);
    }
    return ret;
}

CURLcode do_delete_request(CURL *handle, UcxBuffer *response) {
    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "DELETE");
    curl_easy_setopt(handle, CURLOPT_PUT, 0L);  
    curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
    
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
    
    CURLcode ret = curl_easy_perform(handle);
    return ret;
}

CURLcode do_mkcol_request(CURL *handle) { 
    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MKCOL");
    curl_easy_setopt(handle, CURLOPT_PUT, 0L);  
    curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
    
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
    
    CURLcode ret = curl_easy_perform(handle);
    return ret;
}


CURLcode do_head_request(CURL *handle) {
    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "HEAD");
    curl_easy_setopt(handle, CURLOPT_PUT, 0L);  
    curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0);
    curl_easy_setopt(handle, CURLOPT_NOBODY, 1L);
    
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
    
    CURLcode ret = curl_easy_perform(handle);
    curl_easy_setopt(handle, CURLOPT_NOBODY, 0L);
    return ret;
}

mercurial