diff -r 56e6b5ccbf11 -r 35a421f441d5 libidav/resource.c --- a/libidav/resource.c Fri Jun 04 18:24:39 2021 +0200 +++ b/libidav/resource.c Fri Jun 04 18:24:55 2021 +0200 @@ -756,18 +756,6 @@ } -/* - * read wrapper with integrated hashing - */ - -typedef struct { - DAV_SHA_CTX *sha; - void *stream; - dav_read_func read; - dav_seek_func seek; - int error; -} HashStream; - static void init_hash_stream(HashStream *hstr, void *stream, dav_read_func readfn, dav_seek_func seekfn) { hstr->sha = NULL; hstr->stream = stream; @@ -1555,3 +1543,269 @@ } 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; +} +