Sun, 17 Dec 2023 15:33:50 +0100
fix faulty string to int conversion utilities
Probably it was expected that errno is set to EINVAL when illegal characters are encountered. But this is not standard and does not happen on every system, allowing illegal strings to be parsed as valid integers.
/* * 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 <cx/utils.h> #include <cx/printf.h> #include <cx/hash_map.h> #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) int dav_buffer_seek(CxBuffer *b, curl_off_t offset, int origin) { return cxBufferSeek(b, offset, origin) == 0 ? 0:CURL_SEEKFUNC_CANTSEEK; } /* ----------------------------- PROPFIND ----------------------------- */ CURLcode do_propfind_request( DavSession *sn, CxBuffer *request, CxBuffer *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, cxBufferRead); curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); curl_easy_setopt(handle, CURLOPT_READDATA, request); curl_easy_setopt(handle, CURLOPT_SEEKDATA, request); curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); CxMap *respheaders = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); respheaders->simple_destructor = free; 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 = cxMapGet(respheaders, cx_hash_key_str("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); cxMapDestroy(respheaders); return ret; } CxBuffer* create_allprop_propfind_request(void) { CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); cxstring s; s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<D:propfind xmlns:D=\"DAV:\">\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<D:allprop/></D:propfind>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); return buf; } CxBuffer* create_cryptoprop_propfind_request(void) { CxBuffer *buf = cxBufferCreate(NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); cxstring s; s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<D:propfind xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<D:prop><idav:crypto-prop/></D:prop></D:propfind>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); return buf; } CxBuffer* create_propfind_request(DavSession *sn, CxList *properties, char *rootelm, DavBool nocrypt) { CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); cxstring s; int add_crypto_name = 1; int add_crypto_key = 1; int add_crypto_hash = 1; char *crypto_ns = "idav"; CxMap *namespaces = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8); if(properties) { CxIterator i = cxListIterator(properties); cx_foreach(DavProperty*, p, i) { if(strcmp(p->ns->name, "DAV:")) { cxMapPut(namespaces, cx_hash_key_str(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; cxMapPut(namespaces, cx_hash_key_str("idav"), &idav_ns); } s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); // write root element and namespaces cx_bprintf(buf, "<D:%s xmlns:D=\"DAV:\"", rootelm); CxIterator mapi = cxMapIteratorValues(namespaces); cx_foreach(DavNamespace*, ns, mapi) { s = CX_STR(" xmlns:"); cxBufferWrite(s.ptr, 1, s.length, buf); s = cx_str(ns->prefix); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("=\""); cxBufferWrite(s.ptr, 1, s.length, buf); s = cx_str(ns->name); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("\""); cxBufferWrite(s.ptr, 1, s.length, buf); } s = CX_STR(">\n"); cxBufferWrite(s.ptr, 1, s.length, buf); // default properties s = CX_STR("<D:prop>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<D:creationdate />\n<D:getlastmodified />\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<D:getcontentlength />\n<D:getcontenttype />\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<D:resourcetype />\n"); cxBufferWrite(s.ptr, 1, s.length, buf); // crypto properties if(DAV_CRYPTO(sn) && !nocrypt) { if(add_crypto_name) { cxBufferPut(buf, '<'); cxBufferPutString(buf, crypto_ns); s = CX_STR(":crypto-name />\n"); cxBufferWrite(s.ptr, 1, s.length, buf); } if(add_crypto_key) { cxBufferPut(buf, '<'); cxBufferPutString(buf, crypto_ns); s = CX_STR(":crypto-key />\n"); cxBufferWrite(s.ptr, 1, s.length, buf); } if(add_crypto_hash) { cxBufferPut(buf, '<'); cxBufferPutString(buf, crypto_ns); s = CX_STR(":crypto-hash />\n"); cxBufferWrite(s.ptr, 1, s.length, buf); } } // extra properties if(properties) { CxIterator i = cxListIterator(properties); cx_foreach(DavProperty*, prop, i) { s = CX_STR("<"); cxBufferWrite(s.ptr, 1, s.length, buf); s = cx_str(prop->ns->prefix); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR(":"); cxBufferWrite(s.ptr, 1, s.length, buf); s = cx_str(prop->name); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR(" />\n"); cxBufferWrite(s.ptr, 1, s.length, buf); } } // end cx_bprintf(buf, "</D:prop>\n</D:%s>\n", rootelm); cxMapDestroy(namespaces); return buf; } CxBuffer* create_basic_propfind_request(void) { CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); cxstring s; s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<D:propfind xmlns:D=\"DAV:\" xmlns:i=\""); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR(DAV_NS); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("\" >\n"); cxBufferWrite(s.ptr, 1, s.length, buf); // properties s = CX_STR("<D:prop>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<D:resourcetype />\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<i:crypto-key />\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<i:crypto-name />\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<i:crypto-hash />\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("</D:prop>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); // end s = CX_STR("</D:propfind>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); return buf; } PropfindParser* create_propfind_parser(CxBuffer *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; char *crypto_name = NULL; // name set by crypto-name property char *crypto_key = NULL; result->properties = cxLinkedListCreateSimple(CX_STORE_POINTERS); // xmlNode list 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; } cxstring status_str = cx_str((char*)status_node->content); if(status_str.length < 13) { // error return -1; } status_str = cx_strsubsl(status_str, 9, 3); if(!cx_strcmp(status_str, CX_STR("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) { cxListAdd(result->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->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) { cxListDestroy(result->properties); } } int hrefeq(DavSession *sn, const char *href1, const char *href2) { cxmutstr href_s = cx_mutstr(util_url_decode(sn, href1)); cxmutstr href_r = cx_mutstr(util_url_decode(sn, href2)); int ret = 0; if(!cx_strcmp(cx_strcast(href_s), cx_strcast(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(!cx_strcmp(cx_strcast(href_s), cx_strcast(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(!cx_strcmp(cx_strcast(href_s), cx_strcast(href_r))) { ret = 1; } } } free(href_s.ptr); free(href_r.ptr); return ret; } DavResource* parse_propfind_response(DavSession *sn, DavResource *root, CxBuffer *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 { cxstring resname = cx_str(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 if(response->properties) { CxIterator i = cxListIterator(response->properties); cx_foreach(xmlNode*, prop, i) { 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) { CxMap *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; const char *href = NULL; CxList *properties = cxLinkedListCreateSimple(CX_STORE_POINTERS); // 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((const 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; } cxstring status_str = cx_str((char*)status_node->content); if(status_str.length < 13) { sn->error = DAV_ERROR; return 1; } status_str = cx_strsubsl(status_str, 9, 3); if(!cx_strcmp(status_str, CX_STR("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) { cxListAdd(properties, n); if(xstreq(n->name, "resourcetype")) { if(parse_resource_type(n)) { iscollection = TRUE; } } else if(n->ns && 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 { cxstring resname = cx_str(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); } char *href_cp = dav_session_strdup(sn, href); res = dav_resource_new_full(sn, resource->path, name, href_cp); dav_session_free(sn, name); } res->iscollection = iscollection; // add properties int decrypt_props = DAV_ENCRYPT_PROPERTIES(res->session); xmlNode *crypto_prop = NULL; CxIterator i = cxListIterator(properties); cx_foreach(xmlNode*, prop, i) { if(!prop->ns) { continue; } 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; } } } cxListDestroy(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) { CxMap *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, CxBuffer *request, CxBuffer *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 = cx_asprintf("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, cxBufferRead); curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); curl_easy_setopt(handle, CURLOPT_READDATA, request); curl_easy_setopt(handle, CURLOPT_SEEKDATA, request); curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); cxBufferSeek(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; } CxBuffer* create_proppatch_request(DavResourceData *data) { CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); cxstring s; CxMap *namespaces = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8); namespaces->simple_destructor = free; char prefix[8]; int pfxnum = 0; if(data->set) { CxIterator i = cxListIterator(data->set); cx_foreach(DavProperty*, p, i) { if(strcmp(p->ns->name, "DAV:")) { snprintf(prefix, 8, "x%d", pfxnum++); cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix)); } } } if(data->remove) { CxIterator i = cxListIterator(data->remove); cx_foreach(DavProperty*, p, i) { if(strcmp(p->ns->name, "DAV:")) { snprintf(prefix, 8, "x%d", pfxnum++); cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix)); } } } s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); // write root element and namespaces s = CX_STR("<D:propertyupdate xmlns:D=\"DAV:\""); cxBufferWrite(s.ptr, 1, s.length, buf); CxIterator mapi = cxMapIterator(namespaces); cx_foreach(CxMapEntry*, entry, mapi) { s = CX_STR(" xmlns:"); cxBufferWrite(s.ptr, 1, s.length, buf); s = cx_str(entry->value); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("=\""); cxBufferWrite(s.ptr, 1, s.length, buf); s = cx_strn(entry->key->data, entry->key->len); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("\""); cxBufferWrite(s.ptr, 1, s.length, buf); } s = CX_STR(">\n"); cxBufferWrite(s.ptr, 1, s.length, buf); if(data->set) { s = CX_STR("<D:set>\n<D:prop>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); CxIterator i = cxListIterator(data->set); cx_foreach(DavProperty*, property, i) { char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name)); if(!prefix) { prefix = "D"; } // begin tag s = CX_STR("<"); cxBufferWrite(s.ptr, 1, s.length, buf); s = cx_str(prefix); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR(":"); cxBufferWrite(s.ptr, 1, s.length, buf); s = cx_str(property->name); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR(">"); cxBufferWrite(s.ptr, 1, s.length, buf); // content DavXmlNode *content = property->value; if(content->type == DAV_XML_TEXT && !content->next) { cxBufferWrite(content->content, 1, content->contentlength, buf); } else { dav_print_node(buf, (cx_write_func)cxBufferWrite, namespaces, content); } // end tag s = CX_STR("</"); cxBufferWrite(s.ptr, 1, s.length, buf); s = cx_str(prefix); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR(":"); cxBufferWrite(s.ptr, 1, s.length, buf); s = cx_str(property->name); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR(">\n"); cxBufferWrite(s.ptr, 1, s.length, buf); } s = CX_STR("</D:prop>\n</D:set>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); } if(data->remove) { s = CX_STR("<D:remove>\n<D:prop>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); CxIterator i = cxListIterator(data->remove); cx_foreach(DavProperty*, property, i) { char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name)); s = CX_STR("<"); cxBufferWrite(s.ptr, 1, s.length, buf); s = cx_str(prefix); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR(":"); cxBufferWrite(s.ptr, 1, s.length, buf); s = cx_str(property->name); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR(" />\n"); cxBufferWrite(s.ptr, 1, s.length, buf); } s = CX_STR("</D:prop>\n</D:remove>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); } s = CX_STR("</D:propertyupdate>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); // cleanup namespace map cxMapDestroy(namespaces); return buf; } CxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash) { CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); cxstring s; s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<D:propertyupdate xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<D:set>\n<D:prop>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); if(DAV_ENCRYPT_NAME(sn)) { s = CX_STR("<idav:crypto-name>"); cxBufferWrite(s.ptr, 1, s.length, buf); char *crname = aes_encrypt(name, strlen(name), key); cxBufferPutString(buf, crname); free(crname); s = CX_STR("</idav:crypto-name>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); } s = CX_STR("<idav:crypto-key>"); cxBufferWrite(s.ptr, 1, s.length, buf); cxBufferPutString(buf, key->name); s = CX_STR("</idav:crypto-key>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); if(hash) { s = CX_STR("<idav:crypto-hash>"); cxBufferWrite(s.ptr, 1, s.length, buf); cxBufferPutString(buf, hash); s = CX_STR("</idav:crypto-hash>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); } s = CX_STR("</D:prop>\n</D:set>\n</D:propertyupdate>\n"); cxBufferWrite(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 = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; free(url); } else { ltheader = cx_asprintf("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); CxBuffer *buf = NULL; if(!read_func) { buf = cxBufferCreate(data, length, cxDefaultAllocator, 0); buf->size = length; data = buf; read_func = (dav_read_func)cxBufferRead; 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_SEEKDATA, data); 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) { cxBufferFree(buf); } return ret; } CURLcode do_delete_request(DavSession *sn, char *lock, CxBuffer *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 = cx_asprintf("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, cxBufferWrite); 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 = cx_asprintf("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 = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; headers = curl_slist_append(headers, ltheader); free(ltheader); } //cxstring deststr = ucx_sprintf("Destination: %s", dest); cxmutstr deststr = cx_strcat(2, CX_STR("Destination: "), cx_str(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; } CxBuffer* create_lock_request(void) { CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); cxstring s; s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("<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"); cxBufferWrite(s.ptr, 1, s.length, buf); s = CX_STR("</D:lockinfo>\n"); cxBufferWrite(s.ptr, 1, s.length, buf); return buf; } int parse_lock_response(DavSession *sn, CxBuffer *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, CxBuffer *request, CxBuffer *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) { cxmutstr thdr; if(timeout < 0) { thdr = cx_asprintf("%s", "Timeout: Infinite"); } else { thdr = cx_asprintf("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, cxBufferRead); curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); curl_easy_setopt(handle, CURLOPT_READDATA, request); curl_easy_setopt(handle, CURLOPT_SEEKDATA, request); curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); 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 cxmutstr ltheader = cx_asprintf("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 cxmutstr ltheader; struct curl_slist *headers = NULL; if(locktoken) { ltheader = cx_asprintf("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, CxBuffer *request, CxBuffer *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, cxBufferRead); curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); curl_easy_setopt(handle, CURLOPT_READDATA, request); curl_easy_setopt(handle, CURLOPT_SEEKDATA, request); curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); 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; }