diff -r c9d37bb97ea8 -r 0bbbb0341606 libidav/methods.c --- /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 +#include +#include + +#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("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("\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("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + // write root element and namespaces + s = S("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("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("\n\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("\n\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = S("\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("\n\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("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + // write root element and namespaces + s = S("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + if(data->set) { + s = S("\n\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("name); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(">\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + s = S("\n\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + if(data->remove) { + s = S("\n\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("name); + ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S(">\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + s = S("\n\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + + s = S("\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; +}