# HG changeset patch # User Olaf Wintermann # Date 1622823895 -7200 # Node ID 35a421f441d5a407d1ac7311d15558920181bd80 # Parent 56e6b5ccbf110ac9459130d018801d58d109f5cf add stream API diff -r 56e6b5ccbf11 -r 35a421f441d5 dav/main.c --- a/dav/main.c Fri Jun 04 18:24:39 2021 +0200 +++ b/dav/main.c Fri Jun 04 18:24:55 2021 +0200 @@ -66,13 +66,16 @@ } } +static Repository* url2repo(char *url, char **path); +static DavSession* connect_to_repo(Repository *repo, char *path, CmdArgs *a); + //define DO_THE_TEST //include //include //include "tags.h" //include -void test() { +void test(CmdArgs *a) { } @@ -103,6 +106,7 @@ int ret = dav_main(argc, argv_utf8); + for(int i=0;iiv, ivl); } EVP_EncryptUpdate(enc->ctx, out + ivl, &outlen, in, in_len); + // I think we don't need this + /* if(in_len != len) { int newoutlen = 16; EVP_EncryptFinal_ex(enc->ctx, out + ivl + outlen, &newoutlen); outlen += newoutlen; enc->end = 1; } + */ } else { out = malloc(16); EVP_EncryptFinal_ex(enc->ctx, out, &outlen); @@ -641,12 +644,15 @@ CCCryptorStatus status; size_t avail = outlen; status = CCCryptorUpdate(enc->ctx, in, in_len, out + ivl, avail, &outlen); + // TODO: check if this still works + /* if(in_len != len) { size_t newoutlen = 16; status = CCCryptorFinal(enc->ctx, out + ivl + outlen, 16, &newoutlen); outlen += newoutlen; enc->end = 1; } + */ } else { out = malloc(32); CCCryptorStatus status; 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; +} + diff -r 56e6b5ccbf11 -r 35a421f441d5 libidav/resource.h --- a/libidav/resource.h Fri Jun 04 18:24:39 2021 +0200 +++ b/libidav/resource.h Fri Jun 04 18:24:55 2021 +0200 @@ -30,6 +30,7 @@ #define RESOURCE_H #include "webdav.h" +#include "crypto.h" #include #ifdef __cplusplus @@ -73,6 +74,41 @@ char hash[32]; }; +/* + * read wrapper with integrated hashing + */ +typedef struct { + DAV_SHA_CTX *sha; + void *stream; + dav_read_func read; + dav_seek_func seek; + int error; +} HashStream; + +struct DavInputStream { + DavResource *res; + CURLM *m; + CURL *c; + AESDecrypter *dec; + char *buffer; + size_t alloc; + size_t size; + size_t pos; + int eof; +}; + +struct DavOutputStream { + DavResource *res; + CURLM *m; + CURL *c; + AESEncrypter *enc; + HashStream *hstr; + const char *buffer; + size_t size; + size_t pos; + int eof; +}; + DavResource* dav_resource_new_full(DavSession *sn, char *parent_path, char *name, char *href); void resource_free_properties(DavSession *sn, UcxMap *properties); diff -r 56e6b5ccbf11 -r 35a421f441d5 libidav/webdav.h --- a/libidav/webdav.h Fri Jun 04 18:24:39 2021 +0200 +++ b/libidav/webdav.h Fri Jun 04 18:24:55 2021 +0200 @@ -49,18 +49,21 @@ #define FALSE 0 #endif -typedef struct DavContext DavContext; -typedef struct DavProxy DavProxy; -typedef struct DavSession DavSession; -typedef struct DavResource DavResource; -typedef struct DavResult DavResult; -typedef struct DavNamespace DavNamespace; -typedef struct DavProperty DavProperty; -typedef struct DavPropName DavPropName; -typedef struct DavKey DavKey; -typedef struct DavNSInfo DavNSInfo; -typedef struct DavXmlNode DavXmlNode; -typedef struct DavXmlAttr DavXmlAttr; +typedef struct DavContext DavContext; +typedef struct DavProxy DavProxy; +typedef struct DavSession DavSession; +typedef struct DavResource DavResource; +typedef struct DavResult DavResult; +typedef struct DavNamespace DavNamespace; +typedef struct DavProperty DavProperty; +typedef struct DavPropName DavPropName; +typedef struct DavKey DavKey; +typedef struct DavNSInfo DavNSInfo; +typedef struct DavXmlNode DavXmlNode; +typedef struct DavXmlAttr DavXmlAttr; + +typedef struct DavInputStream DavInputStream; +typedef struct DavOutputStream DavOutputStream; typedef size_t(*dav_read_func)(void*, size_t, size_t, void*); typedef size_t(*dav_write_func)(const void*, size_t, size_t, void*); @@ -335,6 +338,14 @@ int dav_store(DavResource *res); int dav_get_content(DavResource *res, void *stream, dav_write_func write_func); +DavInputStream* dav_inputstream_open(DavResource *res); +size_t dav_read(void *buf, size_t size, size_t nitems, DavInputStream *in); +void dav_inputstream_close(DavInputStream *in); + +DavOutputStream* dav_outputstream_open(DavResource *res); +size_t dav_write(const void *buf, size_t size, size_t nitems, DavOutputStream *out); +int dav_outputstream_close(DavOutputStream *out); + // private int dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf);