add stream API

Fri, 04 Jun 2021 18:24:55 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Fri, 04 Jun 2021 18:24:55 +0200
changeset 728
35a421f441d5
parent 727
56e6b5ccbf11
child 729
5433f0f3dd48

add stream API

dav/main.c file | annotate | diff | comparison | revisions
libidav/crypto.c file | annotate | diff | comparison | revisions
libidav/resource.c file | annotate | diff | comparison | revisions
libidav/resource.h file | annotate | diff | comparison | revisions
libidav/webdav.h file | annotate | diff | comparison | revisions
--- 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 <libidav/davqlparser.h>
 //include <libidav/davqlexec.h>
 //include "tags.h"
 //include <libidav/resource.h>
 
-void test() {
+void test(CmdArgs *a) {
     
 }
 
@@ -103,6 +106,7 @@
     
     int ret = dav_main(argc, argv_utf8);
     
+
     for(int i=0;i<argc;i++) {
         free(argv_utf8[i]);
     }
@@ -142,7 +146,7 @@
     int ret = EXIT_FAILURE;
     printxmlerror = 0;
 #ifdef DO_THE_TEST
-    test();
+    test(args);
     return 0;
 #endif
     if(!strcmp(cmd, "check") || !strcmp(cmd, "check-config")) {
--- a/libidav/crypto.c	Fri Jun 04 18:24:39 2021 +0200
+++ b/libidav/crypto.c	Fri Jun 04 18:24:55 2021 +0200
@@ -208,12 +208,15 @@
             memcpy(out, enc->iv, 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;
--- 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;
+}
+
--- 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 <ucx/string.h>
 
 #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);
--- 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);
 

mercurial