Thu, 21 Dec 2017 19:48:27 +0100
davql: allow ANYWHERE keyword in SELECT statements
This may seem pointless, but users might want to be explicit about this and the grammar is more consistent.
This commit also adds some no-ops to the functions body of the SET parser, because some day the grammar might allow more clauses after the WHERE clause.
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2016 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 "crypto.h" #include "session.h" #include "xml.h" #include <ucx/utils.h> #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) /* ----------------------------- PROPFIND ----------------------------- */ CURLcode do_propfind_request( DavSession *sn, UcxBuffer *request, UcxBuffer *response) { CURL *handle = sn->handle; curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPFIND"); // always try to get information about possible children int depth = 1; int maxretry = 2; struct curl_slist *headers = NULL; CURLcode ret = 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); UcxMap *respheaders = ucx_map_new(32); util_capture_header(handle, respheaders); for(int i=0;i<maxretry;i++) { if (depth == 1) { headers = curl_slist_append(headers, "Depth: 1"); } else if (depth == -1) { headers = curl_slist_append(headers, "Depth: infinity"); } else { headers = curl_slist_append(headers, "Depth: 0"); } headers = curl_slist_append(headers, "Content-Type: text/xml"); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); // reset buffers and perform request request->pos = 0; response->size = response->pos = 0; ret = dav_session_curl_perform_buf(sn, request, response, NULL); curl_slist_free_all(headers); headers = NULL; /* * Handle two cases: * 1. We communicate with IIS and get a X-MSDAVEXT_Error: 589831 * => try with depth 0 next time, it's not a collection * 2. Other cases * => the server handled our request and we can stop requesting */ char *msdavexterror; msdavexterror = ucx_map_cstr_get(respheaders, "x-msdavext_error"); int iishack = depth == 1 && msdavexterror && !strncmp(msdavexterror, "589831;", 7); if(iishack) { depth = 0; } else { break; } } // deactivate header capturing and free captured map util_capture_header(handle, NULL); ucx_map_free_content(respheaders, free); ucx_map_free(respheaders); return ret; } UcxBuffer* create_allprop_propfind_request(void) { UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOFREE); 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(DavSession *sn, UcxList *properties) { UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); sstr_t s; int add_crypto_name = 1; int add_crypto_key = 1; int add_crypto_hash = 1; char *crypto_ns = "idav"; 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); } // if the properties list contains the idav properties crypto-name // and crypto-key, mark them as existent if(!strcmp(p->ns->name, DAV_NS)) { if(!strcmp(p->name, "crypto-name")) { add_crypto_name = 0; crypto_ns = p->ns->prefix; } else if(!strcmp(p->name, "crypto-key")) { add_crypto_key = 0; crypto_ns = p->ns->prefix; } else if(!strcmp(p->name, "crypto-hash")) { add_crypto_hash = 0; crypto_ns = p->ns->prefix; } } } DavNamespace idav_ns; if(add_crypto_name && add_crypto_key && DAV_CRYPTO(sn)) { idav_ns.prefix = "idav"; idav_ns.name = DAV_NS; ucx_map_cstr_put(namespaces, "idav", &idav_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); // crypto properties if(DAV_CRYPTO(sn)) { if(add_crypto_name) { ucx_buffer_putc(buf, '<'); ucx_buffer_puts(buf, crypto_ns); s = S(":crypto-name />\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); } if(add_crypto_key) { ucx_buffer_putc(buf, '<'); ucx_buffer_puts(buf, crypto_ns); s = S(":crypto-key />\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); } if(add_crypto_hash) { ucx_buffer_putc(buf, '<'); ucx_buffer_puts(buf, crypto_ns); s = S(":crypto-hash />\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); ucx_map_free(namespaces); return buf; } UcxBuffer* create_basic_propfind_request(void) { UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); 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:\" xmlns:i=\""); ucx_buffer_write(s.ptr, 1, s.length, buf); s = S(DAV_NS); ucx_buffer_write(s.ptr, 1, s.length, buf); s = S("\" >\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); // properties s = S("<D:prop>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = S("<D:resourcetype />\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = S("<i:crypto-key />\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = S("<i:crypto-name />\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = S("<i:crypto-hash />\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = S("</D:prop>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); // end s = S("</D:propfind>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); return buf; } PropfindParser* create_propfind_parser(UcxBuffer *response, char *url) { PropfindParser *parser = malloc(sizeof(PropfindParser)); if(!parser) { return NULL; } parser->document = xmlReadMemory(response->space, response->pos, url, NULL, 0); parser->current = NULL; if(parser->document) { xmlNode *xml_root = xmlDocGetRootElement(parser->document); if(xml_root) { xmlNode *node = xml_root->children; while(node) { // find first response tag if(node->type == XML_ELEMENT_NODE) { if(xstreq(node->name, "response")) { parser->current = node; break; } } node = node->next; } return parser; } else { xmlFreeDoc(parser->document); } } free(parser); return NULL; } void destroy_propfind_parser(PropfindParser *parser) { if(parser->document) { xmlFreeDoc(parser->document); } free(parser); } int get_propfind_response(PropfindParser *parser, ResponseTag *result) { if(parser->current == NULL) { return 0; } char *href = NULL; int iscollection = 0; UcxList *properties = NULL; // xmlNode list char *crypto_name = NULL; // name set by crypto-name property char *crypto_key = NULL; result->properties = NULL; xmlNode *node = parser->current->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 return -1; } href = (char*)href_node->content; } 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) { // error return -1; } sstr_t status_str = sstr((char*)status_node->content); if(status_str.length < 13) { // 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) { properties = ucx_list_append(properties, n); if(xstreq(n->name, "resourcetype")) { if(parse_resource_type(n)) { iscollection = TRUE; } } else if(xstreq(n->ns->href, DAV_NS)) { if(xstreq(n->name, "crypto-name")) { crypto_name = util_xml_get_text(n); } else if(xstreq(n->name, "crypto-key")) { crypto_key = util_xml_get_text(n); } } } n = n->next; } } } } node = node->next; } result->href = util_url_path(href); result->iscollection = iscollection; result->properties = properties; result->crypto_name = crypto_name; result->crypto_key = crypto_key; // find next response tag xmlNode *next = parser->current->next; while(next) { if(next->type == XML_ELEMENT_NODE) { if(xstreq(next->name, "response")) { break; } } next = next->next; } parser->current = next; return 1; } void cleanup_response(ResponseTag *result) { if(result) { ucx_list_free(result->properties); } } int hrefeq(DavSession *sn, char *href1, char *href2) { sstr_t href_s = sstr(util_url_decode(sn, href1)); sstr_t href_r = sstr(util_url_decode(sn, href2)); int ret = 0; if(!sstrcmp(href_s, href_r)) { ret = 1; } else if(href_s.length == href_r.length + 1) { if(href_s.ptr[href_s.length-1] == '/') { href_s.length--; if(!sstrcmp(href_s, href_r)) { ret = 1; } } } else if(href_r.length == href_s.length + 1) { if(href_r.ptr[href_r.length-1] == '/') { href_r.length--; if(!sstrcmp(href_s, href_r)) { ret = 1; } } } free(href_s.ptr); free(href_r.ptr); return ret; } DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response) { char *url = NULL; curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &url); if(!root) { printf("methods.c: TODO: remove\n"); root = dav_resource_new_href(sn, util_url_path(url)); // TODO: remove } //printf("%.*s\n\n", response->size, response->space); 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); } } node = node->next; } xmlFreeDoc(doc); return root; } DavResource* response2resource(DavSession *sn, ResponseTag *response, char *parent_path) { // create resource char *name = NULL; DavKey *key = NULL; if(DAV_DECRYPT_NAME(sn) && response->crypto_name && (key = dav_context_get_key(sn->context, response->crypto_key))) { if(!response->crypto_key) { sn->error = DAV_ERROR; dav_session_set_errstr(sn, "Missing crypto-key property"); return NULL; } name = util_decrypt_str_k(sn, response->crypto_name, key); if(!name) { sn->error = DAV_ERROR; dav_session_set_errstr(sn, "Cannot decrypt resource name"); return NULL; } } else { sstr_t resname = sstr(util_resource_name(response->href)); int nlen = 0; char *uname = curl_easy_unescape( sn->handle, resname.ptr, resname.length, &nlen); name = dav_session_strdup(sn, uname); curl_free(uname); } char *href = dav_session_strdup(sn, response->href); DavResource *res = NULL; if(parent_path) { res = dav_resource_new_full(sn, parent_path, name, href); } else { res = dav_resource_new_href(sn, href); } dav_session_free(sn, name); add_properties(res, response); return res; } void add_properties(DavResource *res, ResponseTag *response) { res->iscollection = response->iscollection; // add properties UCX_FOREACH(elm, response->properties) { xmlNode *prop = elm->data; resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children); } set_davprops(res); } int parse_response_tag(DavResource *resource, xmlNode *node) { DavSession *sn = resource->session; //DavResource *res = resource; DavResource *res = NULL; char *href = NULL; UcxList *properties = NULL; // xmlNode list char *crypto_name = NULL; // name set by crypto-name property char *crypto_key = NULL; int iscollection = 0; // TODO: remove 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 sn->error = DAV_ERROR; return 1; } //char *href = (char*)href_node->content; href = util_url_path((char*)href_node->content); char *href_s = util_url_decode(resource->session, href); char *href_r = util_url_decode(resource->session, resource->href); if(hrefeq(sn, href_s, href_r)) { res = resource; } free(href_s); free(href_r); } 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) { sn->error = DAV_ERROR; return 1; } sstr_t status_str = sstr((char*)status_node->content); if(status_str.length < 13) { sn->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) { properties = ucx_list_append(properties, n); if(xstreq(n->name, "resourcetype")) { if(parse_resource_type(n)) { iscollection = TRUE; } } else if(xstreq(n->ns->href, DAV_NS)) { if(xstreq(n->name, "crypto-name")) { crypto_name = util_xml_get_text(n); } else if(xstreq(n->name, "crypto-key")) { crypto_key = util_xml_get_text(n); } } } n = n->next; } } } } node = node->next; } if(!res) { // create new resource object char *name = NULL; if(DAV_DECRYPT_NAME(sn) && crypto_name) { if(!crypto_key) { sn->error = DAV_ERROR; dav_session_set_errstr(sn, "Missing crypto-key property"); return -1; } name = util_decrypt_str(sn, crypto_name, crypto_key); if(!name) { sn->error = DAV_ERROR; dav_session_set_errstr(sn, "Cannot decrypt resource name"); return -1; } } else { sstr_t resname = sstr(util_resource_name(href)); int nlen = 0; char *uname = curl_easy_unescape( sn->handle, resname.ptr, resname.length, &nlen); name = dav_session_strdup(sn, uname); curl_free(uname); } href = dav_session_strdup(sn, href); res = dav_resource_new_full(sn, resource->path, name, href); dav_session_free(sn, name); } res->iscollection = iscollection; // add properties UCX_FOREACH(elm, properties) { xmlNode *prop = elm->data; resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children); } ucx_list_free(properties); set_davprops(res); if(res != resource) { resource_add_child(resource, res); } return 0; } void set_davprops(DavResource *res) { char *cl = dav_get_string_property_ns(res, "DAV:", "getcontentlength"); char *ct = dav_get_string_property_ns(res, "DAV:", "getcontenttype"); char *cd = dav_get_string_property_ns(res, "DAV:", "creationdate"); char *lm = dav_get_string_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); } int parse_resource_type(xmlNode *node) { int collection = FALSE; xmlNode *c = node->children; while(c) { if(c->type == XML_ELEMENT_NODE) { if(xstreq(c->ns->href, "DAV:") && xstreq(c->name, "collection")) { collection = TRUE; break; } } c = c->next; } return collection; } /* ----------------------------- PROPPATCH ----------------------------- */ CURLcode do_proppatch_request( DavSession *sn, char *lock, UcxBuffer *request, UcxBuffer *response) { CURL *handle = sn->handle; curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPPATCH"); struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Content-Type: text/xml"); if(lock) { char *url = NULL; curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr; headers = curl_slist_append(headers, ltheader); free(ltheader); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); } curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 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); CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL); curl_slist_free_all(headers); return ret; } UcxBuffer* create_proppatch_request(DavResourceData *data) { UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); 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", pfxnum++); ucx_map_cstr_put(namespaces, p->ns->name, strdup(prefix)); } } UCX_FOREACH(elm, data->remove) { DavProperty *p = elm->data; if(strcmp(p->ns->name, "DAV:")) { snprintf(prefix, 8, "x%d", pfxnum++); ucx_map_cstr_put(namespaces, p->ns->name, strdup(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); if(!prefix) { prefix = "D"; } // begin tag 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); // content DavXmlNode *content = property->value; if(content->type == DAV_XML_TEXT) { ucx_buffer_write(content->content, 1, content->contentlength, buf); } else { // TODO: implement } // end tag 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:remove>\n<D:prop>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); UCX_FOREACH(elm, data->remove) { 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(" />\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); } s = S("</D:prop>\n</D:remove>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); } s = S("</D:propertyupdate>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); // cleanup namespace map ucx_map_free_content(namespaces, free); ucx_map_free(namespaces); return buf; } UcxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, char *name, char *hash) { UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); 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:propertyupdate xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = S("<D:set>\n<D:prop>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); if(DAV_ENCRYPT_NAME(sn)) { s = S("<idav:crypto-name>"); ucx_buffer_write(s.ptr, 1, s.length, buf); char *crname = aes_encrypt(name, strlen(name), key); ucx_buffer_puts(buf, crname); free(crname); s = S("</idav:crypto-name>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); } s = S("<idav:crypto-key>"); ucx_buffer_write(s.ptr, 1, s.length, buf); ucx_buffer_puts(buf, key->name); s = S("</idav:crypto-key>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); if(hash) { s = S("<idav:crypto-hash>"); ucx_buffer_write(s.ptr, 1, s.length, buf); ucx_buffer_puts(buf, hash); s = S("</idav:crypto-hash>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); } s = S("</D:prop>\n</D:set>\n</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(DavSession *sn, char *lock, DavBool create, void *data, dav_read_func read_func, size_t length) { CURL *handle = sn->handle; curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L); // clear headers struct curl_slist *headers = NULL; if(lock) { char *url = NULL; curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); char *ltheader = NULL; if(create) { url = util_parent_path(url); ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr; free(url); } else { ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr; } headers = curl_slist_append(headers, ltheader); free(ltheader); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); } curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 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) { 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 = dav_session_curl_perform(sn, NULL); curl_slist_free_all(headers); if(buf) { ucx_buffer_free(buf); } return ret; } CURLcode do_delete_request(DavSession *sn, char *lock, UcxBuffer *response) { CURL *handle = sn->handle; struct curl_slist *headers = NULL; if(lock) { char *url = NULL; curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr; headers = curl_slist_append(headers, ltheader); free(ltheader); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); } else { curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL); } curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); CURLcode ret = dav_session_curl_perform(sn, NULL); curl_slist_free_all(headers); return ret; } CURLcode do_mkcol_request(DavSession *sn, char *lock) { CURL *handle = sn->handle; struct curl_slist *headers = NULL; if(lock) { char *url = NULL; curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); url = util_parent_path(url); char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr; free(url); headers = curl_slist_append(headers, ltheader); free(ltheader); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); } else { curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL); } 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_WRITEFUNCTION, dummy_write); curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); CURLcode ret = dav_session_curl_perform(sn, NULL); curl_slist_free_all(headers); return ret; } CURLcode do_head_request(DavSession *sn) { CURL *handle = sn->handle; curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "HEAD"); curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(handle, CURLOPT_NOBODY, 1L); // clear headers struct curl_slist *headers = NULL; curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); CURLcode ret = dav_session_curl_perform(sn, NULL); curl_easy_setopt(handle, CURLOPT_NOBODY, 0L); return ret; } CURLcode do_copy_move_request(DavSession *sn, char *dest, char *lock, DavBool copy, DavBool override) { CURL *handle = sn->handle; if(copy) { curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "COPY"); } else { curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MOVE"); } curl_easy_setopt(handle, CURLOPT_PUT, 0L); curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); struct curl_slist *headers = NULL; if(lock) { char *url = NULL; curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr; headers = curl_slist_append(headers, ltheader); free(ltheader); } //sstr_t deststr = ucx_sprintf("Destination: %s", dest); sstr_t deststr = sstrcat(2, S("Destination: "), sstr(dest)); headers = curl_slist_append(headers, deststr.ptr); if(override) { headers = curl_slist_append(headers, "Overwrite: T"); } else { headers = curl_slist_append(headers, "Overwrite: F"); } curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); CURLcode ret = dav_session_curl_perform(sn, NULL); free(deststr.ptr); curl_slist_free_all(headers); headers = NULL; curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); return ret; } UcxBuffer* create_lock_request(void) { UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); 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:lockinfo xmlns:D=\"DAV:\">\n" "<D:lockscope><D:exclusive/></D:lockscope>\n" "<D:locktype><D:write/></D:locktype>\n" "<D:owner><D:href>http://davutils.org/libidav/</D:href></D:owner>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = S("</D:lockinfo>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); return buf; } int parse_lock_response(DavSession *sn, UcxBuffer *response, LockDiscovery *lock) { lock->locktoken = NULL; lock->timeout = NULL; xmlDoc *doc = xmlReadMemory(response->space, response->size, NULL, NULL, 0); if(!doc) { sn->error = DAV_ERROR; return -1; } char *timeout = NULL; char *locktoken = NULL; int ret = -1; xmlNode *xml_root = xmlDocGetRootElement(doc); DavBool lockdiscovery = 0; if(xml_root) { xmlNode *node = xml_root->children; while(node) { if(node->type == XML_ELEMENT_NODE) { if(xstreq(node->name, "lockdiscovery")) { node = node->children; lockdiscovery = 1; continue; } if(xstreq(node->name, "activelock")) { node = node->children; continue; } if(lockdiscovery) { if(xstreq(node->name, "timeout")) { timeout = util_xml_get_text(node); } else if(xstreq(node->name, "locktoken")) { xmlNode *n = node->children; while(n) { if(xstreq(n->name, "href")) { locktoken = util_xml_get_text(n); break; } n = n->next; } } } } node = node->next; } } if(timeout && locktoken) { lock->timeout = strdup(timeout); lock->locktoken = strdup(locktoken); ret = 0; } xmlFreeDoc(doc); return ret; } CURLcode do_lock_request(DavSession *sn, UcxBuffer *request, UcxBuffer *response, time_t timeout) { CURL *handle = sn->handle; curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "LOCK"); curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L); request->pos = 0; // clear headers struct curl_slist *headers = NULL; if(timeout != 0) { sstr_t thdr; if(timeout < 0) { thdr = ucx_sprintf("%s", "Timeout: Infinite"); } else { thdr = ucx_sprintf("Timeout: Second-%u", (unsigned int)timeout); } headers = curl_slist_append(headers, thdr.ptr); free(thdr.ptr); } curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); 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); CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL); if(headers) { curl_slist_free_all(headers); } return ret; } CURLcode do_unlock_request(DavSession *sn, char *locktoken) { CURL *handle = sn->handle; curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "UNLOCK"); curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write); curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); // set lock-token header sstr_t ltheader = ucx_sprintf("Lock-Token: <%s>", locktoken); struct curl_slist *headers = curl_slist_append(NULL, ltheader.ptr); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); CURLcode ret = dav_session_curl_perform(sn, NULL); curl_slist_free_all(headers); free(ltheader.ptr); return ret; }