Sat, 23 Nov 2019 15:14:50 +0100
improve shell completion
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2018 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) int dav_buffer_seek(UcxBuffer *b, curl_off_t offset, int origin) { return ucx_buffer_seek(b, offset, origin) == 0 ? 0:CURL_SEEKFUNC_CANTSEEK; } /* ----------------------------- 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_SEEKFUNCTION, dav_buffer_seek); 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_cryptoprop_propfind_request(void) { UcxBuffer *buf = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOFREE); scstr_t s; s = SC("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = SC("<D:propfind xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = SC("<D:prop><idav:crypto-prop/></D:prop></D:propfind>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); return buf; } UcxBuffer* create_propfind_request(DavSession *sn, UcxList *properties, char *rootelm, DavBool nocrypt) { 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) && !nocrypt) { 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 ucx_bprintf(buf, "<D:%s xmlns:D=\"DAV:\"", rootelm); 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) && !nocrypt) { 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 ucx_bprintf(buf, "</D:prop>\n</D:%s>\n", rootelm); 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; int decrypt_props = DAV_ENCRYPT_PROPERTIES(res->session); xmlNode *crypto_prop = NULL; char *crypto_key = NULL; // add properties UCX_FOREACH(elm, response->properties) { xmlNode *prop = elm->data; resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children); if (decrypt_props && prop->children && prop->children->type == XML_TEXT_NODE && xstreq(prop->ns->href, DAV_NS)) { if(xstreq(prop->name, "crypto-prop")) { crypto_prop = prop; } else if(xstreq(prop->name, "crypto-key")) { crypto_key = util_xml_get_text(prop); } } } if(crypto_prop && crypto_key) { char *crypto_prop_content = util_xml_get_text(crypto_prop); DavKey *key = dav_context_get_key(res->session->context, crypto_key); if(crypto_prop_content) { UcxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content); resource_set_crypto_properties(res, cprops); } } 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 int decrypt_props = DAV_ENCRYPT_PROPERTIES(res->session); xmlNode *crypto_prop = NULL; UCX_FOREACH(elm, properties) { xmlNode *prop = elm->data; resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children); if (decrypt_props && prop->children && prop->children->type == XML_TEXT_NODE && xstreq(prop->ns->href, DAV_NS)) { if(xstreq(prop->name, "crypto-prop")) { crypto_prop = prop; } } } ucx_list_free(properties); if(crypto_prop && crypto_key) { char *crypto_prop_content = util_xml_get_text(crypto_prop); DavKey *key = dav_context_get_key(res->session->context, crypto_key); if(crypto_prop_content && key) { UcxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content); resource_set_crypto_properties(res, cprops); } } 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_SEEKFUNCTION, dav_buffer_seek); 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); //printf("proppatch: \n%.*s\n", request->size, request->space); return ret; } UcxBuffer* create_proppatch_request(DavResourceData *data) { UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); scstr_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 = SC("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); // write root element and namespaces s = SC("<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 = SC(" xmlns:"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = scstr(pfxval); ucx_buffer_write(s.ptr, 1, s.length, buf); s = SC("=\""); ucx_buffer_write(s.ptr, 1, s.length, buf); s = scstrn(key.data, key.len); ucx_buffer_write(s.ptr, 1, s.length, buf); s = SC("\""); ucx_buffer_write(s.ptr, 1, s.length, buf); } s = SC(">\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); if(data->set) { s = SC("<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 = SC("<"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = scstr(prefix); ucx_buffer_write(s.ptr, 1, s.length, buf); s = SC(":"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = scstr(property->name); ucx_buffer_write(s.ptr, 1, s.length, buf); s = SC(">"); ucx_buffer_write(s.ptr, 1, s.length, buf); // content DavXmlNode *content = property->value; if(content->type == DAV_XML_TEXT && !content->next) { ucx_buffer_write(content->content, 1, content->contentlength, buf); } else { dav_print_node(buf, (write_func)ucx_buffer_write, namespaces, content); } // end tag s = SC("</"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = scstr(prefix); ucx_buffer_write(s.ptr, 1, s.length, buf); s = SC(":"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = scstr(property->name); ucx_buffer_write(s.ptr, 1, s.length, buf); s = SC(">\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); } s = SC("</D:prop>\n</D:set>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); } if(data->remove) { s = SC("<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 = SC("<"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = scstr(prefix); ucx_buffer_write(s.ptr, 1, s.length, buf); s = SC(":"); ucx_buffer_write(s.ptr, 1, s.length, buf); s = scstr(property->name); ucx_buffer_write(s.ptr, 1, s.length, buf); s = SC(" />\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); } s = SC("</D:prop>\n</D:remove>\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); } s = SC("</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, const char *name, const 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, dav_seek_func seek_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_SEEKFUNCTION, seek_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_SEEKFUNCTION, dav_buffer_seek); 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; } CURLcode do_simple_request(DavSession *sn, char *method, char *locktoken) { CURL *handle = sn->handle; curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, method); 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; struct curl_slist *headers = NULL; if(locktoken) { ltheader = ucx_sprintf("Lock-Token: <%s>", locktoken); headers = curl_slist_append(NULL, ltheader.ptr); } curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); CURLcode ret = dav_session_curl_perform(sn, NULL); if(locktoken) { curl_slist_free_all(headers); free(ltheader.ptr); } return ret; } CURLcode do_report_request(DavSession *sn, UcxBuffer *request, UcxBuffer *response) { CURL *handle = sn->handle; curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "REPORT"); curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read); curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, dav_buffer_seek); 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); struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Content-Type: text/xml"); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); request->pos = 0; response->size = response->pos = 0; CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL); curl_slist_free_all(headers); return ret; }