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 <stdbool.h> #include <libxml/tree.h> #include "utils.h" #include "session.h" #include "methods.h" #include "crypto.h" #include <cx/buffer.h> #include <cx/utils.h> #include <cx/hash_map.h> #include <cx/printf.h> #include <cx/basic_mempool.h> #include <cx/array_list.h> #include "resource.h" #include "xml.h" #include "davqlexec.h" #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) DavResource* dav_resource_new(DavSession *sn, const char *path) { //char *href = util_url_path(url); //DavResource *res = dav_resource_new_href(sn, href); char *parent = util_parent_path(path); const char *name = util_resource_name(path); char *href = dav_session_create_plain_href(sn, path); DavResource *res = dav_resource_new_full(sn, parent, name, href); free(parent); return res; } DavResource* dav_resource_new_child(DavSession *sn, DavResource *parent, const char *name) { char *path = util_concat_path(parent->path, name); char *href = dav_session_create_plain_href(sn, path); DavResource *res = dav_resource_new_full(sn, parent->path, name, href); free(path); return res; } DavResource* dav_resource_new_href(DavSession *sn, const char *href) { DavResource *res = cxCalloc(sn->mp->allocator, 1, sizeof(DavResource)); res->session = sn; // set name, path and href resource_set_info(res, href); // initialize resource data res->data = resource_data_new(sn); return res; } DavResource* dav_resource_new_full(DavSession *sn, const char *parent_path, const char *name, char *href) { cxstring n = cx_str(name); // the name must not contain path separators if(n.length > 0 && href) { for(int i=0;i<n.length-1;i++) { char c = n.ptr[i]; if(c == '/' || c == '\\') { n = cx_str(util_resource_name(href)); break; } } } // remove trailing '/' if(n.length > 0 && n.ptr[n.length-1] == '/') { n.length--; } DavResource *res = cxCalloc(sn->mp->allocator, 1, sizeof(DavResource)); res->session = sn; // set name, path and href res->name = cx_strdup_a(sn->mp->allocator, n).ptr; char *path = util_concat_path(parent_path, name); res->path = dav_session_strdup(sn, path); res->href = href; // initialize resource data res->data = resource_data_new(sn); // cache href/path if(href) { dav_session_cache_path(sn, cx_str(path), cx_str(href)); } free(path); return res; } void resource_free_properties(DavSession *sn, CxMap *properties) { if(!properties) return; CxIterator i = cxMapIteratorValues(properties); DavProperty *property; cx_foreach(DavProperty*, property, i) { // TODO: free everything dav_session_free(sn, property); } cxMapDestroy(properties); } void dav_resource_free(DavResource *res) { DavSession *sn = res->session; dav_session_free(sn, res->name); dav_session_free(sn, res->path); if(res->href) { dav_session_free(sn, res->href); } DavResourceData *data = res->data; resource_free_properties(sn, data->properties); resource_free_properties(sn, data->crypto_properties); if(data->set) { CxIterator i = cxListIterator(data->set); cx_foreach(DavProperty *, p, i) { dav_session_free(sn, p->ns->name); if(p->ns->prefix) { dav_session_free(sn, p->ns->prefix); } dav_session_free(sn, p->ns); dav_session_free(sn, p->name); dav_free_xml_node_sn(sn, p->value); dav_session_free(sn, p); } } if(data->remove) { CxIterator i = cxListIterator(data->remove); cx_foreach(DavProperty *, p, i) { dav_session_free(sn, p->ns->name); if(p->ns->prefix) { dav_session_free(sn, p->ns->prefix); } dav_session_free(sn, p->ns); dav_session_free(sn, p->name); dav_session_free(sn, p); } } if(data->crypto_set) { CxIterator i = cxListIterator(data->crypto_set); cx_foreach(DavProperty *, p, i) { dav_session_free(sn, p->ns->name); if(p->ns->prefix) { dav_session_free(sn, p->ns->prefix); } dav_session_free(sn, p->ns); dav_session_free(sn, p->name); dav_free_xml_node_sn(sn, p->value); dav_session_free(sn, p); } } if(data->crypto_remove) { CxIterator i = cxListIterator(data->crypto_remove); cx_foreach(DavProperty *, p, i) { dav_session_free(sn, p->ns->name); if(p->ns->prefix) { dav_session_free(sn, p->ns->prefix); } dav_session_free(sn, p->ns); dav_session_free(sn, p->name); dav_session_free(sn, p); } } if(!data->read && data->content) { dav_session_free(sn, data->content); } dav_session_free(sn, data); dav_session_free(sn, res); } void dav_resource_free_all(DavResource *res) { DavResource *child = res->children; dav_resource_free(res); while(child) { DavResource *next = child->next; dav_resource_free_all(child); child = next; } } void resource_set_href(DavResource *res, cxstring href) { res->href = cx_strdup_a(res->session->mp->allocator, href).ptr; } void resource_set_info(DavResource *res, const char *href_str) { char *url_str = NULL; curl_easy_getinfo(res->session->handle, CURLINFO_EFFECTIVE_URL, &url_str); cxstring name = cx_str(util_resource_name(href_str)); cxstring href = cx_str(href_str); cxstring base_href = cx_str(util_url_path(res->session->base_url)); cxstring path = cx_strsubs(href, base_href.length - 1); const CxAllocator *a = res->session->mp->allocator; CURL *handle = res->session->handle; int nlen = 0; char *uname = curl_easy_unescape(handle, name.ptr, name.length , &nlen); int plen = 0; char *upath = curl_easy_unescape(handle, path.ptr, path.length, &plen); res->name = cx_strdup_a(a, cx_strn(uname, nlen)).ptr; res->href = cx_strdup_a(a, href).ptr; res->path = cx_strdup_a(a, cx_strn(upath, plen)).ptr; curl_free(uname); curl_free(upath); } DavResourceData* resource_data_new(DavSession *sn) { DavResourceData *data = cxMalloc( sn->mp->allocator, sizeof(DavResourceData)); if(!data) { return NULL; } data->properties = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, 32); data->crypto_properties = NULL; data->set = NULL; data->remove = NULL; data->crypto_set = NULL; data->crypto_remove = NULL; data->read = NULL; data->content = NULL; data->seek = NULL; data->length = 0; return data; } char* dav_resource_get_href(DavResource *resource) { if(!resource->href) { resource->href = dav_session_get_href( resource->session, resource->path); } return resource->href; } void resource_add_prop(DavResource *res, const char *ns, const char *name, DavXmlNode *val) { DavSession *sn = res->session; DavNamespace *namespace = dav_session_malloc(sn, sizeof(DavNamespace)); namespace->prefix = NULL; namespace->name = dav_session_strdup(sn, ns); DavProperty *prop = dav_session_malloc(sn, sizeof(DavProperty)); prop->name = dav_session_strdup(sn, name); prop->ns = namespace; prop->value = val; cxmutstr keystr = dav_property_key(ns, name); CxHashKey key = cx_hash_key(keystr.ptr, keystr.length); cxMapPut(((DavResourceData*)res->data)->properties, key, prop); free(keystr.ptr); } void resource_add_property(DavResource *res, const char *ns, const char *name, xmlNode *val) { if(!val) { return; } resource_add_prop(res, ns, name, dav_convert_xml(res->session, val)); } void resource_add_string_property(DavResource *res, char *ns, char *name, char *val) { if(!val) { return; } resource_add_prop(res, ns, name, dav_text_node(res->session, val)); } void resource_set_crypto_properties(DavResource *res, CxMap *cprops) { DavResourceData *data = res->data; resource_free_properties(res->session, data->crypto_properties); data->crypto_properties = cprops; } DavXmlNode* resource_get_property(DavResource *res, const char *ns, const char *name) { cxmutstr keystr = dav_property_key(ns, name); CxHashKey key = cx_hash_key(keystr.ptr, keystr.length); DavXmlNode *ret = resource_get_property_k(res, key); free(keystr.ptr); return ret; } DavXmlNode* resource_get_encrypted_property(DavResource *res, const char *ns, const char *name) { cxmutstr keystr = dav_property_key(ns, name); CxHashKey key = cx_hash_key(keystr.ptr, keystr.length); DavXmlNode *ret = resource_get_encrypted_property_k(res, key); free(keystr.ptr); return ret; } DavXmlNode* resource_get_property_k(DavResource *res, CxHashKey key) { DavResourceData *data = (DavResourceData*)res->data; DavProperty *property = cxMapGet(data->properties, key); return property ? property->value : NULL; } DavXmlNode* resource_get_encrypted_property_k(DavResource *res, CxHashKey key) { DavResourceData *data = (DavResourceData*)res->data; DavProperty *property = cxMapGet(data->crypto_properties, key); return property ? property->value : NULL; } cxmutstr dav_property_key(const char *ns, const char *name) { return dav_property_key_a(cxDefaultAllocator, ns, name); } cxmutstr dav_property_key_a(const CxAllocator *a, const char *ns, const char *name) { cxstring ns_str = cx_str(ns); cxstring name_str = cx_str(name); return cx_strcat_a(a, 4, ns_str, CX_STR("\0"), name_str, CX_STR("\0")); } void resource_add_child(DavResource *parent, DavResource *child) { child->next = NULL; if(parent->children) { DavResource *last = parent->children; while(last->next) { last = last->next; } last->next = child; child->prev = last; } else { child->prev = NULL; parent->children = child; } child->parent = parent; } static int resource_cmp(DavResource *res1, DavResource *res2, DavOrderCriterion *cr) { if(!(res1 && res2)) { return 0; } int ret; if(cr->type == 0) { switch(cr->column.resprop) { case DAVQL_RES_NAME: { ret = strcmp(res1->name, res2->name); break; } case DAVQL_RES_PATH: { ret = strcmp(res1->path, res2->path); break; } case DAVQL_RES_HREF: { ret = strcmp(res1->href, res2->href); break; } case DAVQL_RES_CONTENTLENGTH: { int c = res1->contentlength == res2->contentlength; ret = c ? 0 : (res1->contentlength < res2->contentlength?-1:1); break; } case DAVQL_RES_CONTENTTYPE: { ret = strcmp(res1->contenttype, res2->contenttype); break; } case DAVQL_RES_CREATIONDATE: { int c = res1->creationdate == res2->creationdate; ret = c ? 0 : (res1->creationdate < res2->creationdate?-1:1); break; } case DAVQL_RES_LASTMODIFIED: { int c = res1->lastmodified == res2->lastmodified; ret = c ? 0 : (res1->lastmodified < res2->lastmodified?-1:1); break; } case DAVQL_RES_ISCOLLECTION: { int c = res1->iscollection == res2->iscollection; ret = c ? 0 : (res1->iscollection < res2->iscollection?-1:1); break; } default: ret = 0; } } else if(cr->type == 1) { DavXmlNode *xvalue1 = resource_get_property_k(res1, cr->column.property); DavXmlNode *xvalue2 = resource_get_property_k(res2, cr->column.property); char *value1 = dav_xml_getstring(xvalue1); char *value2 = dav_xml_getstring(xvalue2); if(!value1) { ret = value2 ? -1 : 0; } else if(!value2) { ret = value1 ? 1 : 0; } else { ret = strcmp(value1, value2); } } else { return 0; } return cr->descending ? -ret : ret; } void resource_add_ordered_child(DavResource *parent, DavResource *child, CxList *ordercr) { if(!ordercr) { resource_add_child(parent, child); return; } child->parent = parent; if(!parent->children) { child->next = NULL; child->prev = NULL; parent->children = child; } else { DavResource *resource = parent->children; while(resource) { int r = 0; CxIterator i = cxListIterator(ordercr); cx_foreach(DavOrderCriterion*, cr, i) { r = resource_cmp(child, resource, cr); if(r != 0) { break; } } if(r < 0) { // insert child before resource child->prev = resource->prev; child->next = resource; if(resource->prev) { resource->prev->next = child; } else { parent->children = child; } resource->prev = child; break; } if(!resource->next) { // append child child->prev = resource; child->next = NULL; resource->next = child; break; } else { resource = resource->next; } } } } char* dav_get_string_property(DavResource *res, char *name) { char *pns; char *pname; dav_get_property_namespace_str(res->session->context, name, &pns, &pname); if(!pns || !pname) { return NULL; } return dav_get_string_property_ns(res, pns, pname); } char* dav_get_string_property_ns(DavResource *res, char *ns, char *name) { DavXmlNode *prop = dav_get_property_ns(res, ns, name); if(!prop) { return NULL; } return dav_xml_getstring(prop); } DavXmlNode* dav_get_property(DavResource *res, char *name) { char *pns; char *pname; dav_get_property_namespace_str(res->session->context, name, &pns, &pname); if(!pns || !pname) { return NULL; } return dav_get_property_ns(res, pns, pname); } static DavXmlNode* get_property_ns(DavResource *res, DavBool encrypted, const char *ns, const char *name) { if(!ns || !name) { return NULL; } DavResourceData *data = res->data; DavXmlNode *property = NULL; CxList *remove_list = NULL; CxList *set_list = NULL; if(encrypted) { // check if crypto_properties because it will only be created // if the resource has encrypted properties if(!data->crypto_properties) { return NULL; } property = resource_get_encrypted_property(res, ns, name); remove_list = data->crypto_remove; set_list = data->crypto_set; } else { property = resource_get_property(res, ns, name); remove_list = data->remove; set_list = data->set; } // resource_get_property only returns persistent properties // check the remove and set list if(property && remove_list) { // if the property is in the remove list, we return NULL CxIterator i = cxListIterator(remove_list); cx_foreach(DavProperty*, p, i) { if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) { return NULL; } } } // the set list contains property updates // we return an updated property if possible if(set_list) { CxIterator i = cxListIterator(set_list); cx_foreach(DavProperty*, p, i) { if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) { return p->value; // TODO: fix } } } // no property update return property; } DavXmlNode* dav_get_property_ns(DavResource *res, const char *ns, const char *name) { DavXmlNode *property_value = get_property_ns(res, FALSE, ns, name); if(!property_value && DAV_DECRYPT_PROPERTIES(res->session)) { property_value = get_property_ns(res, TRUE, ns, name); } return property_value; } DavXmlNode* dav_get_encrypted_property_ns(DavResource *res, const char *ns, const char *name) { return get_property_ns(res, TRUE, ns, name); } static DavProperty* createprop(DavSession *sn, const char *ns, const char *name) { DavProperty *property = dav_session_malloc(sn, sizeof(DavProperty)); property->name = dav_session_strdup(sn, name); property->value = NULL; DavNamespace *namespace = dav_session_malloc(sn, sizeof(DavNamespace)); namespace->prefix = NULL; namespace->name = dav_session_strdup(sn, ns); property->ns = namespace; return property; } void dav_set_string_property(DavResource *res, char *name, char *value) { char *pns; char *pname; dav_get_property_namespace_str(res->session->context, name, &pns, &pname); dav_set_string_property_ns(res, pns, pname, value); } static int add2propertylist(const CxAllocator *a, CxList **list, DavProperty *property) { if(!*list) { CxList *newlist = cxLinkedListCreate(a, NULL, CX_STORE_POINTERS); if(!newlist) { return 1; } *list = newlist; } cxListAdd(*list, property); return 0; } void dav_set_string_property_ns(DavResource *res, char *ns, char *name, char *value) { DavSession *sn = res->session; const CxAllocator *a = res->session->mp->allocator; DavResourceData *data = res->data; DavProperty *property = createprop(res->session, ns, name); property->value = dav_text_node(res->session, value); if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) { add2propertylist(a, &data->crypto_set, property); } else { add2propertylist(a, &data->set, property); } } void dav_set_property(DavResource *res, char *name, DavXmlNode *value) { char *pns; char *pname; dav_get_property_namespace_str(res->session->context, name, &pns, &pname); dav_set_property_ns(res, pns, pname, value); } void dav_set_property_ns(DavResource *res, char *ns, char *name, DavXmlNode *value) { DavSession *sn = res->session; const CxAllocator *a = sn->mp->allocator; DavResourceData *data = res->data; DavProperty *property = createprop(sn, ns, name); // TODO: this function should copy the value // but we also need a function, that doesn't create a copy property->value = value; if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) { add2propertylist(a, &data->crypto_set, property); } else { add2propertylist(a, &data->set, property); } } void dav_remove_property(DavResource *res, char *name) { char *pns; char *pname; dav_get_property_namespace_str(res->session->context, name, &pns, &pname); dav_remove_property_ns(res, pns, pname); } void dav_remove_property_ns(DavResource *res, char *ns, char *name) { DavSession *sn = res->session; DavResourceData *data = res->data; const CxAllocator *a = res->session->mp->allocator; DavProperty *property = createprop(res->session, ns, name); if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) { add2propertylist(a, &data->crypto_remove, property); } else { add2propertylist(a, &data->remove, property); } } void dav_set_encrypted_property_ns(DavResource *res, char *ns, char *name, DavXmlNode *value) { const CxAllocator *a = res->session->mp->allocator; DavResourceData *data = res->data; DavProperty *property = createprop(res->session, ns, name); property->value = value; // TODO: copy node? add2propertylist(a, &data->crypto_set, property); } void dav_set_encrypted_string_property_ns(DavResource *res, char *ns, char *name, char *value) { const CxAllocator *a = res->session->mp->allocator; DavResourceData *data = res->data; DavProperty *property = createprop(res->session, ns, name); property->value = dav_text_node(res->session, value); add2propertylist(a, &data->crypto_set, property); } void dav_remove_encrypted_property_ns(DavResource *res, char *ns, char *name) { DavResourceData *data = res->data; const CxAllocator *a = res->session->mp->allocator; DavProperty *property = createprop(res->session, ns, name); add2propertylist(a, &data->crypto_remove, property); } static int compare_propname(const void *a, const void *b) { const DavPropName *p1 = a; const DavPropName *p2 = b; int result = strcmp(p1->ns, p2->ns); if(result) { return result; } else { return strcmp(p1->name, p2->name); } } DavPropName* dav_get_property_names(DavResource *res, size_t *count) { DavResourceData *data = res->data; *count = data->properties->size; DavPropName *names = dav_session_calloc( res->session, *count, sizeof(DavPropName)); CxIterator i = cxMapIteratorValues(data->properties); DavProperty *value; int j = 0; cx_foreach(DavProperty*, value, i) { DavPropName *name = &names[j]; name->ns = value->ns->name; name->name = value->name; j++; } qsort(names, *count, sizeof(DavPropName), compare_propname); return names; } void dav_set_content(DavResource *res, void *stream, dav_read_func read_func, dav_seek_func seek_func) { DavResourceData *data = res->data; data->content = stream; data->read = read_func; data->seek = seek_func; data->length = 0; } void dav_set_content_data(DavResource *res, char *content, size_t length) { DavSession *sn = res->session; DavResourceData *data = res->data; data->content = dav_session_malloc(sn, length); memcpy(data->content, content, length); data->read = NULL; data->seek = NULL; data->length = length; } void dav_set_content_length(DavResource *res, size_t length) { DavResourceData *data = res->data; data->length = length; } int dav_load(DavResource *res) { CxBuffer *rqbuf = create_allprop_propfind_request(); int ret = dav_propfind(res->session, res, rqbuf); cxBufferFree(rqbuf); return ret; } int dav_load_prop(DavResource *res, DavPropName *properties, size_t numprop) { CxMempool *mp = cxBasicMempoolCreate(64); const CxAllocator *a = mp->allocator; CxList *proplist = cxArrayListCreate(a, NULL, sizeof(DavProperty), numprop); for(size_t i=0;i<numprop;i++) { DavProperty p; p.name = properties[i].name; p.ns = cxMalloc(a, sizeof(DavNamespace)); p.ns->name = properties[i].ns; if(!strcmp(properties[i].ns, "DAV:")) { p.ns->prefix = "D"; } else { p.ns->prefix = cx_asprintf_a(a, "x%d", (int)i).ptr; } p.value = NULL; cxListAdd(proplist, &p); } CxBuffer *rqbuf = create_propfind_request(res->session, proplist, "propfind", 0); int ret = dav_propfind(res->session, res, rqbuf); cxBufferFree(rqbuf); cxMempoolDestroy(mp); return ret; } static void init_hash_stream(HashStream *hstr, void *stream, dav_read_func readfn, dav_seek_func seekfn) { hstr->sha = NULL; hstr->stream = stream; hstr->read = readfn; hstr->seek = seekfn; hstr->error = 0; } static size_t dav_read_h(void *buf, size_t size, size_t nelm, void *stream) { HashStream *s = stream; if(!s->sha) { s->sha = dav_hash_init(); } size_t r = s->read(buf, size, nelm, s->stream); dav_hash_update(s->sha, buf, r); return r; } static int dav_seek_h(void *stream, long offset, int whence) { HashStream *s = stream; if(offset == 0 && whence == SEEK_SET) { unsigned char buf[DAV_SHA256_DIGEST_LENGTH]; dav_hash_final(s->sha, buf); s->sha = NULL; } else { s->error = 1; } return s->seek(s->stream, offset, whence); } int dav_store(DavResource *res) { DavSession *sn = res->session; DavResourceData *data = res->data; util_set_url(sn, dav_resource_get_href(res)); DavLock *lock = dav_get_lock(sn, res->path); char *locktoken = lock ? lock->token : NULL; // store content if(data->content) { int encryption = DAV_ENCRYPT_CONTENT(sn) && sn->key; CURLcode ret; if(encryption) { AESEncrypter *enc = NULL; CxBuffer *buf = NULL; if(data->read) { enc = aes_encrypter_new( sn->key, data->content, data->read, data->seek); } else { buf = cxBufferCreate(data->content, data->length, cxDefaultAllocator, 0); buf->size = data->length; enc = aes_encrypter_new( sn->key, buf, (dav_read_func)cxBufferRead, (dav_seek_func)cxBufferSeek); } // put resource ret = do_put_request( sn, locktoken, TRUE, enc, (dav_read_func)aes_read, (dav_seek_func)aes_encrypter_reset, 0); // get sha256 hash dav_get_hash(&enc->sha256, (unsigned char*)data->hash); char *enc_hash = aes_encrypt(data->hash, DAV_SHA256_DIGEST_LENGTH, sn->key); aes_encrypter_close(enc); if(buf) { cxBufferFree(buf); } // add crypto properties // TODO: store the properties later if(resource_add_crypto_info(sn, res->href, res->name, enc_hash)) { free(enc_hash); return 1; } resource_add_string_property(res, DAV_NS, "crypto-hash", enc_hash); free(enc_hash); } else if((sn->flags & DAV_SESSION_STORE_HASH) == DAV_SESSION_STORE_HASH) { HashStream hstr; CxBuffer *iobuf = NULL; if(!data->read) { iobuf = cxBufferCreate(data->content, data->length, cxDefaultAllocator, 0); iobuf->size = data->length; init_hash_stream( &hstr, iobuf, (dav_read_func)cxBufferRead, (dav_seek_func)cxBufferSeek); } else { init_hash_stream( &hstr, data->content, data->read, data->seek); } ret = do_put_request( sn, locktoken, TRUE, &hstr, dav_read_h, (dav_seek_func)dav_seek_h, data->length); if(hstr.sha) { dav_hash_final(hstr.sha, (unsigned char*)data->hash); char *hash = util_hexstr((unsigned char*)data->hash, 32); dav_set_string_property_ns(res, DAV_NS, "content-hash", hash); free(hash); } } else { ret = do_put_request( sn, locktoken, TRUE, data->content, data->read, data->seek, data->length); } long status = 0; curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && (status >= 200 && status < 300)) { res->session->error = 0; // cleanup node data if(!data->read) { cxFree(sn->mp->allocator, data->content); } data->content = NULL; data->read = NULL; data->length = 0; } else { dav_session_set_error(sn, ret, status); return 1; } } // generate crypto-prop content if(DAV_ENCRYPT_PROPERTIES(sn) && sn->key && (data->crypto_set || data->crypto_remove)) { DavResource *crypto_res = dav_resource_new_href(sn, res->href); int ret = 1; if(crypto_res) { CxBuffer *rqbuf = create_cryptoprop_propfind_request(); ret = dav_propfind(res->session, res, rqbuf); cxBufferFree(rqbuf); } if(!ret) { DavXmlNode *crypto_prop_node = dav_get_property_ns(crypto_res, DAV_NS, "crypto-prop"); CxMap *crypto_props = parse_crypto_prop(sn, sn->key, crypto_prop_node); if(!crypto_props) { // resource hasn't encrypted properties yet crypto_props = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); // create new map } // remove all properties if(data->crypto_remove) { CxIterator i = cxListIterator(data->crypto_remove); cx_foreach(DavProperty *, property, i) { if(crypto_props->size == 0) { break; // map already empty, can't remove any more } cxmutstr key = dav_property_key(property->ns->name, property->name); DavProperty *existing_prop = cxMapGet(crypto_props, cx_hash_key(key.ptr, key.length)); if(existing_prop) { // TODO: free existing_prop } free(key.ptr); } } // set properties if(data->crypto_set) { CxIterator i = cxListIterator(data->crypto_set); cx_foreach(DavProperty *, property, i) { cxmutstr keystr = dav_property_key(property->ns->name, property->name); CxHashKey key = cx_hash_key(keystr.ptr, keystr.length); DavProperty *existing_prop = cxMapRemoveAndGet(crypto_props, key); cxMapPut(crypto_props, key, property); if(existing_prop) { // TODO: free existing_prop } free(keystr.ptr); } } DavXmlNode *crypto_prop_value = create_crypto_prop(sn, crypto_props); if(crypto_prop_value) { DavProperty *new_crypto_prop = createprop(sn, DAV_NS, "crypto-prop"); new_crypto_prop->value = crypto_prop_value; add2propertylist(sn->mp->allocator, &data->set, new_crypto_prop); } dav_resource_free(crypto_res); } if(ret) { return 1; } } // store properties int r = 0; sn->error = DAV_OK; if(data->set || data->remove) { CxBuffer *request = create_proppatch_request(data); CxBuffer *response = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); //printf("request:\n%.*s\n\n", request->pos, request->space); CURLcode ret = do_proppatch_request(sn, locktoken, request, response); long status = 0; curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && status == 207) { //printf("%s\n", response->space); // TODO: parse response // TODO: cleanup node data correctly data->set = NULL; data->remove = NULL; } else { dav_session_set_error(sn, ret, status); r = -1; } cxBufferFree(request); cxBufferFree(response); } return r; } #if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 32 static void set_progressfunc(DavResource *res) { CURL *handle = res->session->handle; curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, dav_session_get_progress); curl_easy_setopt(handle, CURLOPT_XFERINFODATA, res); curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L); } static void unset_progressfunc(DavResource *res) { CURL *handle = res->session->handle; curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, NULL); curl_easy_setopt(handle, CURLOPT_XFERINFODATA, NULL); curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1L); } #else static void set_progressfunc(DavResource *res) { } static void unset_progressfunc(DavResource *res) { } #endif int dav_get_content(DavResource *res, void *stream, dav_write_func write_fnc) { DavSession *sn = res->session; CURL *handle = sn->handle; util_set_url(res->session, dav_resource_get_href(res)); // check encryption AESDecrypter *dec = NULL; DavKey *key = NULL; if(DAV_DECRYPT_CONTENT(sn)) { char *keyname = dav_get_string_property_ns(res, DAV_NS, "crypto-key"); if(keyname) { key = dav_context_get_key(sn->context, keyname); if(key) { dec = aes_decrypter_new(key, stream, write_fnc); stream = dec; write_fnc = (dav_write_func)aes_write; } } } curl_easy_setopt(handle, CURLOPT_HTTPHEADER, NULL); curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(handle, CURLOPT_PUT, 0L); curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_fnc); curl_easy_setopt(handle, CURLOPT_WRITEDATA, stream); if(sn->get_progress) { set_progressfunc(res); } long status = 0; CURLcode ret = dav_session_curl_perform(sn, &status); if(sn->get_progress) { unset_progressfunc(res); } char *hash = NULL; if(dec) { aes_decrypter_shutdown(dec); // get final bytes // get hash unsigned char sha[DAV_SHA256_DIGEST_LENGTH]; dav_get_hash(&dec->sha256, sha); hash = util_hexstr(sha, DAV_SHA256_DIGEST_LENGTH); aes_decrypter_close(dec); } if(ret == CURLE_OK && (status >= 200 && status < 300)) { int verify_failed = 0; if(DAV_DECRYPT_CONTENT(sn) && key) { // try to verify the content char *res_hash = dav_get_string_property_ns(res, DAV_NS, "crypto-hash"); if(res_hash) { size_t len = 0; char *dec_hash = aes_decrypt(res_hash, &len, key); char *hex_hash = util_hexstr((unsigned char*)dec_hash, len); if(strcmp(hash, hex_hash)) { verify_failed = 1; } free(dec_hash); free(hex_hash); } } if(hash) { free(hash); } if(verify_failed) { res->session->error = DAV_CONTENT_VERIFICATION_ERROR; return 1; } res->session->error = DAV_OK; return 0; } else { if(hash) { free(hash); } dav_session_set_error(res->session, ret, status); return 1; } } DavResource* dav_create_child(DavResource *parent, char *name) { DavResource *res = dav_resource_new_child(parent->session, parent, name); if(dav_create(res)) { dav_resource_free(res); return NULL; } else { return res; } } int dav_delete(DavResource *res) { CURL *handle = res->session->handle; util_set_url(res->session, dav_resource_get_href(res)); DavLock *lock = dav_get_lock(res->session, res->path); char *locktoken = lock ? lock->token : NULL; CxBuffer *response = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); CURLcode ret = do_delete_request(res->session, locktoken, response); long status = 0; curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); int r = 0; if(ret == CURLE_OK && (status >= 200 && status < 300)) { res->session->error = DAV_OK; res->exists = 0; // TODO: parse response // TODO: free res } else { dav_session_set_error(res->session, ret, status); r = 1; } cxBufferFree(response); return r; } static int create_ancestors(DavSession *sn, char *href, char *path) { CURL *handle = sn->handle; CURLcode code; DavLock *lock = dav_get_lock(sn, path); char *locktoken = lock ? lock->token : NULL; long status = 0; int ret = 0; if(strlen(path) <= 1) { return 0; } char *p = util_parent_path(path); char *h = util_parent_path(href); for(int i=0;i<2;i++) { util_set_url(sn, h); code = do_mkcol_request(sn, locktoken); curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status); if(status == 201) { // resource successfully created char *name = (char*)util_resource_name(p); int len = strlen(name); if(name[len - 1] == '/') { name[len - 1] = '\0'; } if(resource_add_crypto_info(sn, h, name, NULL)) { sn->error = DAV_ERROR; dav_session_set_errstr(sn, "Cannot set crypto properties for ancestor"); } break; } else if(status == 405) { // parent already exists break; } else if(status == 409) { // parent doesn't exist if(create_ancestors(sn, h, p)) { ret = 1; break; } } else { dav_session_set_error(sn, code, status); ret = 1; break; } } free(p); free(h); return ret; } static int create_resource(DavResource *res, int *status) { DavSession *sn = res->session; CURL *handle = sn->handle; util_set_url(sn, dav_resource_get_href(res)); DavLock *lock = dav_get_lock(res->session, res->path); char *locktoken = lock ? lock->token : NULL; CURLcode code; if(res->iscollection) { code = do_mkcol_request(sn, locktoken); } else { code = do_put_request(sn, locktoken, TRUE, "", NULL, NULL, 0); } long s = 0; curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &s); *status = s; if(code == CURLE_OK && (s >= 200 && s < 300)) { sn->error = DAV_OK; // if the session has encrypted file names, add crypto infos if(!resource_add_crypto_info(sn, res->href, res->name, NULL)) { // do a minimal propfind request CxBuffer *rqbuf = create_propfind_request(sn, NULL, "propfind", 0); int ret = dav_propfind(sn, res, rqbuf); cxBufferFree(rqbuf); return ret; } else { return 1; } } else { dav_session_set_error(sn, code, s); return 1; } } int dav_create(DavResource *res) { int status; if(!create_resource(res, &status)) { // resource successfully created res->exists = 1; return 0; } if(status == 403 || status == 409 || status == 404) { // create intermediate collections if(create_ancestors(res->session, res->href, res->path)) { return 1; } } return create_resource(res, &status); } int dav_exists(DavResource *res) { if(!dav_load_prop(res, NULL, 0)) { res->exists = 1; return 1; } else { if(res->session->error == DAV_NOT_FOUND) { res->exists = 0; } return 0; } } static int dav_cp_mv_url(DavResource *res, char *desturl, _Bool copy, _Bool override) { DavSession *sn = res->session; CURL *handle = sn->handle; util_set_url(sn, dav_resource_get_href(res)); DavLock *lock = dav_get_lock(sn, res->path); char *locktoken = lock ? lock->token : NULL; CURLcode ret = do_copy_move_request(sn, desturl, locktoken, copy, override); long status = 0; curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && (status >= 200 && status < 300)) { return 0; } else { dav_session_set_error(sn, ret, status); return 1; } } static int dav_cp_mv(DavResource *res, char *newpath, _Bool copy, _Bool override) { char *dest = dav_session_get_href(res->session, newpath); char *desturl = util_get_url(res->session, dest); dav_session_free(res->session, dest); int ret = dav_cp_mv_url(res, desturl, copy, override); free(desturl); return ret; } int dav_copy(DavResource *res, char *newpath) { return dav_cp_mv(res, newpath, true, false); } int dav_move(DavResource *res, char *newpath) { return dav_cp_mv(res, newpath, false, false); } int dav_copy_o(DavResource *res, char *newpath, DavBool override) { return dav_cp_mv(res, newpath, true, override); } int dav_move_o(DavResource *res, char *newpath, DavBool override) { return dav_cp_mv(res, newpath, false, override); } int dav_copyto(DavResource *res, char *url, DavBool override) { return dav_cp_mv_url(res, url, true, override); } int dav_moveto(DavResource *res, char *url, DavBool override) { return dav_cp_mv_url(res, url, false, override); } int dav_lock(DavResource *res) { return dav_lock_t(res, 0); } int dav_lock_t(DavResource *res, time_t timeout) { DavSession *sn = res->session; CURL *handle = sn->handle; util_set_url(sn, dav_resource_get_href(res)); CxBuffer *request = create_lock_request(); CxBuffer *response = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); CURLcode ret = do_lock_request(sn, request, response, timeout); //printf("\nlock\n"); //printf("%.*s\n\n", request->size, request->space); //printf("%.*s\n\n", response->size, response->space); cxBufferFree(request); long status = 0; curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && (status >= 200 && status < 300)) { LockDiscovery lock; int parse_error = parse_lock_response(sn, response, &lock); cxBufferFree(response); if(parse_error) { sn->error = DAV_ERROR; return -1; } DavLock *l = dav_create_lock(sn, lock.locktoken, lock.timeout); free(lock.locktoken); free(lock.timeout); int r = 0; if(res->iscollection) { r = dav_add_collection_lock(sn, res->path, l); } else { r = dav_add_resource_lock(sn, res->path, l); } if(r == 0) { return 0; } else { (void)dav_unlock(res); sn->error = DAV_ERROR; dav_destroy_lock(sn, l); return -1; } } else { dav_session_set_error(sn, ret, status); cxBufferFree(response); return -1; } } int dav_unlock(DavResource *res) { DavSession *sn = res->session; CURL *handle = sn->handle; util_set_url(sn, dav_resource_get_href(res)); DavLock *lock = dav_get_lock(res->session, res->path); if(!lock) { sn->error = DAV_ERROR; return -1; } CURLcode ret = do_unlock_request(sn, lock->token); long status = 0; curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && (status >= 200 && status < 300)) { dav_remove_lock(sn, res->path, lock); } else { dav_session_set_error(sn, ret, status); return 1; } return 0; } int resource_add_crypto_info(DavSession *sn, const char *href, const char *name, const char *hash) { if(!DAV_IS_ENCRYPTED(sn)) { return 0; } CxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name, hash); CxBuffer *response = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); util_set_url(sn, href); // TODO: lock CURLcode ret = do_proppatch_request(sn, NULL, request, response); cxBufferFree(request); long status = 0; curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && status == 207) { // TODO: parse response sn->error = DAV_OK; cxBufferFree(response); return 0; } else { dav_session_set_error(sn, ret, status); cxBufferFree(response); return 1; } } /* ----------------------------- crypto-prop ----------------------------- */ DavXmlNode* create_crypto_prop(DavSession *sn, CxMap *properties) { if(!sn->key) { return NULL; } CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); // create an xml document containing all properties CxMap *nsmap = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8); nsmap->simple_destructor = free; cxMapPut(nsmap, cx_hash_key_str("DAV:"), strdup("D")); cxBufferPutString(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); cxBufferPutString(content, "<D:prop xmlns:D=\"DAV:\">\n"); CxIterator i = cxMapIteratorValues(properties); DavProperty *prop; cx_foreach(DavProperty*, prop, i) { DavXmlNode pnode; pnode.type = DAV_XML_ELEMENT; pnode.namespace = prop->ns->name; pnode.name = prop->name; pnode.prev = NULL; pnode.next = NULL; pnode.children = prop->value; pnode.parent = NULL; pnode.attributes = NULL; pnode.content = NULL; pnode.contentlength = 0; dav_print_node(content, (cx_write_func)cxBufferWrite, nsmap, &pnode); cxBufferPut(content, '\n'); } cxBufferPutString(content, "</D:prop>"); cxMapDestroy(nsmap); // encrypt xml document char *crypto_prop_content = aes_encrypt(content->space, content->size, sn->key); cxBufferDestroy(content); DavXmlNode *ret = NULL; if(crypto_prop_content) { ret = dav_text_node(sn, crypto_prop_content); free(crypto_prop_content); } return ret; } CxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node) { if(!node || node->type != DAV_XML_TEXT || node->contentlength == 0) { return NULL; } return parse_crypto_prop_str(sn, key, node->content); } CxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content) { size_t len = 0; char *dec_str = aes_decrypt(content, &len, key); xmlDoc *doc = xmlReadMemory(dec_str, len, NULL, NULL, 0); free(dec_str); if(!doc) { return NULL; } int err = 0; xmlNode *xml_root = xmlDocGetRootElement(doc); if(xml_root) { if( !xml_root->ns || !xstreq(xml_root->name, "prop") || !xstreq(xml_root->ns->href, "DAV:")) { err = 1; } } else { err = 1; } if(err) { xmlFreeDoc(doc); return NULL; } // ready to get the properties CxMap *map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); xmlNode *n = xml_root->children; while(n) { if(n->type == XML_ELEMENT_NODE && n->ns && n->ns->href) { DavProperty *property = dav_session_malloc(sn, sizeof(DavProperty)); property->name = dav_session_strdup(sn, (const char*)n->name); property->ns = dav_session_malloc(sn, sizeof(DavNamespace)); property->ns->name = dav_session_strdup(sn, (const char*)n->ns->href); property->ns->prefix = n->ns->prefix ? dav_session_strdup(sn, (const char*)n->ns->prefix) : NULL; property->value = n->children ? dav_convert_xml(sn, n->children) : NULL; cxmutstr key = dav_property_key(property->ns->name, property->name); cxMapPut(map, cx_hash_key(key.ptr, key.length), property); free(key.ptr); } n = n->next; } xmlFreeDoc(doc); if(map->size == 0) { cxMapDestroy(map); return NULL; } return map; } /* ----------------------------- streams ----------------------------- */ static size_t in_write(const char *ptr, size_t size, size_t nitems, void *in_stream) { DavInputStream *in = in_stream; size_t len = size * nitems; if(in->alloc < len) { char *newb = realloc(in->buffer, len); if(!newb) { if(in->buffer) free(in->buffer); in->eof = 1; return 0; } in->buffer = newb; in->alloc = len; } memcpy(in->buffer, ptr, len); in->size = len; in->pos = 0; return nitems; } /* DavInputStream* dav_inputstream_open(DavResource *res) { DavSession *sn = res->session; DavInputStream *in = dav_session_malloc(sn, sizeof(DavInputStream)); if(!in) { return NULL; } memset(in, 0, sizeof(DavInputStream)); in->res = res; in->c = curl_easy_duphandle(sn->handle); char *url = util_get_url(sn, dav_resource_get_href(res)); curl_easy_setopt(in->c, CURLOPT_URL, url); free(url); in->m = curl_multi_init(); curl_easy_setopt(in->c, CURLOPT_HTTPHEADER, NULL); curl_easy_setopt(in->c, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(in->c, CURLOPT_PUT, 0L); curl_easy_setopt(in->c, CURLOPT_UPLOAD, 0L); curl_multi_add_handle(in->m, in->c); dav_write_func write_fnc = (dav_write_func)in_write; void *stream = in; // check encryption AESDecrypter *dec = NULL; DavKey *key = NULL; if(DAV_DECRYPT_CONTENT(sn)) { char *keyname = dav_get_string_property_ns(res, DAV_NS, "crypto-key"); if(keyname) { key = dav_context_get_key(sn->context, keyname); if(key) { dec = aes_decrypter_new(key, stream, write_fnc); stream = dec; write_fnc = (dav_write_func)aes_write; } } } curl_easy_setopt(in->c, CURLOPT_WRITEFUNCTION, write_fnc); curl_easy_setopt(in->c, CURLOPT_WRITEDATA, stream); in->dec = dec; return in; } size_t dav_read(void *buf, size_t size, size_t nitems, DavInputStream *in) { size_t len = in->size - in->pos; size_t rl = size * nitems; if(len > 0) { len = rl > len ? len : rl; len -= len % size; memcpy(buf, in->buffer + in->pos, len); in->pos += len; return len / size; } in->size = 0; if(in->eof) { if(in->dec) { aes_decrypter_shutdown(in->dec); // get final bytes aes_decrypter_close(in->dec); in->dec = NULL; } else { return 0; } } else { int running; while(!in->eof && in->size == 0) { CURLMcode r = curl_multi_perform(in->m, &running); if(r != CURLM_OK || running == 0) { in->eof = 1; break; } int numfds; if(curl_multi_poll(in->m, NULL, 0, 5000, &numfds) != CURLM_OK) { in->eof = 1; } } } return in->size > 0 ? dav_read(buf, size, nitems, in) : 0; } void dav_inputstream_close(DavInputStream *in) { curl_multi_cleanup(in->m); curl_easy_cleanup(in->c); if(in->buffer) free(in->buffer); dav_session_free(in->res->session, in); } static size_t out_read(char *ptr, size_t size, size_t nitems, void *out_stream) { DavOutputStream *out = out_stream; size_t len = size * nitems; size_t available = out->size - out->pos; if(available == 0) { return 0; } size_t r = len > available ? available : len; r -= r % size; memcpy(ptr, out->buffer + out->pos, r); out->pos += r; return r / size; } static size_t dummy_write(void *buf, size_t s, size_t n, void *data) { return s*n; } DavOutputStream* dav_outputstream_open(DavResource *res) { DavSession *sn = res->session; DavOutputStream *out = dav_session_malloc(sn, sizeof(DavOutputStream)); if(!out) { return NULL; } memset(out, 0, sizeof(DavOutputStream)); out->res = res; out->c = curl_easy_duphandle(sn->handle); char *url = util_get_url(sn, dav_resource_get_href(res)); curl_easy_setopt(out->c, CURLOPT_URL, url); free(url); out->m = curl_multi_init(); curl_multi_add_handle(out->m, out->c); void *stream = out; dav_read_func read_fnc = (dav_read_func)out_read; // if encryption or hashing in enabled, we need a stream wrapper if(DAV_ENCRYPT_CONTENT(sn) && sn->key) { AESEncrypter *enc = aes_encrypter_new(sn->key, out, (dav_read_func)out_read, NULL); out->enc = enc; stream = enc; read_fnc = (dav_read_func)aes_read; } else if((sn->flags & DAV_SESSION_STORE_HASH) == DAV_SESSION_STORE_HASH) { HashStream *hstr = dav_session_malloc(sn, sizeof(HashStream)); out->hstr = hstr; init_hash_stream(hstr, out, (dav_read_func)out_read, NULL); stream = hstr; read_fnc = (dav_read_func)dav_read_h; } curl_easy_setopt(out->c, CURLOPT_HEADERFUNCTION, NULL); curl_easy_setopt(out->c, CURLOPT_HTTPHEADER, NULL); curl_easy_setopt(out->c, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(out->c, CURLOPT_PUT, 1L); curl_easy_setopt(out->c, CURLOPT_UPLOAD, 1L); curl_easy_setopt(out->c, CURLOPT_READFUNCTION, read_fnc); curl_easy_setopt(out->c, CURLOPT_READDATA, stream); curl_easy_setopt(out->c, CURLOPT_SEEKFUNCTION, NULL); curl_easy_setopt(out->c, CURLOPT_INFILESIZE, -1); curl_easy_setopt(out->c, CURLOPT_INFILESIZE_LARGE, -1L); curl_easy_setopt(out->c, CURLOPT_WRITEFUNCTION, dummy_write); curl_easy_setopt(out->c, CURLOPT_WRITEDATA, NULL); return out; } size_t dav_write(const void *buf, size_t size, size_t nitems, DavOutputStream *out) { if(out->eof) return 0; out->buffer = buf; out->size = size * nitems; out->pos = 0; int running; while(!out->eof && (out->size == 0 || out->size - out->pos > 0)) { CURLMcode r = curl_multi_perform(out->m, &running); if(r != CURLM_OK || running == 0) { out->eof = 1; break; } int numfds; if(curl_multi_poll(out->m, NULL, 0, 5000, &numfds) != CURLM_OK) { out->eof = 1; } } return (out->size - out->pos) / size; } int dav_outputstream_close(DavOutputStream *out) { DavSession *sn = out->res->session; DavResource *res = out->res; DavResourceData *data = res->data; int ret = 0; dav_write(NULL, 1, 0, out); curl_multi_cleanup(out->m); curl_easy_cleanup(out->c); int store = 0; if(out->enc) { // get sha256 hash char hash[32]; dav_get_hash(&out->enc->sha256, (unsigned char*)data->hash); aes_encrypter_close(out->enc); char *enc_hash = aes_encrypt(hash, DAV_SHA256_DIGEST_LENGTH, sn->key); // add crypto properties if(resource_add_crypto_info(sn, out->res->href, out->res->name, enc_hash)) { ret = 1; } free(enc_hash); } else if(out->hstr) { dav_hash_final(out->hstr->sha, (unsigned char*)data->hash); char *hash = util_hexstr((unsigned char*)data->hash, 32); dav_set_string_property_ns(res, DAV_NS, "content-hash", hash); free(hash); dav_session_free(sn, out->hstr); store = 1; } if(store) { ret = dav_store(out->res); } dav_session_free(out->res->session, out); return ret; } */