Sun, 10 Nov 2024 09:12:30 +0100
toolkit update, small UI adjustments
/* * 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); cxDefineDestructor(respheaders, 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); cxDefineDestructor(namespaces, 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; }