libidav/methods.c

changeset 33
0bbbb0341606
child 38
b855f76e965b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/methods.c	Mon Aug 26 14:42:09 2013 +0200
@@ -0,0 +1,521 @@
+/*
+ * 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 = dav_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_node = node->children;
+                if(href_node->type != XML_TEXT_NODE) {
+                    // error
+                    resource->session->error = DAV_ERROR;
+                    return 1;
+                }
+                char *href = (char*)href_node->content;
+                href = util_url_path(href);
+                if(xstreq(resource->href, href)) {
+                    res = resource;
+                } else {
+                    res = dav_resource_new_href(resource->session, href);
+                    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(DavResourceData *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) {
+    fwrite(buf, s, n, stdout);
+    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